Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/psx/mednadisc/cdrom/CDAccess_CCD.cpp
2 views
1
/* Mednafen - Multi-system Emulator
2
*
3
* This program is free software; you can redistribute it and/or modify
4
* it under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2 of the License, or
6
* (at your option) any later version.
7
*
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
12
*
13
* You should have received a copy of the GNU General Public License
14
* along with this program; if not, write to the Free Software
15
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
*/
17
18
//#define CHECK_CCD_GARBAGE_CUBQ_A
19
//#define CHECK_CCD_GARBAGE_CUBQ_B
20
//#define CHECK_CCD_GARBAGE_CUBQ_C
21
//#define CHECK_CCD_GARBAGE_CUBQ_D
22
23
#include "emuware/emuware.h"
24
#include "../general.h"
25
#include "../string/trim.h"
26
#include "CDAccess_CCD.h"
27
#include <trio/trio.h>
28
29
//wrapper to repair gettext stuff
30
#define _(X) X
31
32
#include <limits>
33
#include <limits.h>
34
#include <map>
35
36
using namespace CDUtility;
37
38
static void MDFN_strtoupper(std::string &str)
39
{
40
const size_t len = str.length();
41
42
for(size_t x = 0; x < len; x++)
43
{
44
if(str[x] >= 'a' && str[x] <= 'z')
45
{
46
str[x] = str[x] - 'a' + 'A';
47
}
48
}
49
}
50
51
typedef std::map<std::string, std::string> CCD_Section;
52
53
template<typename T>
54
static T CCD_ReadInt(CCD_Section &s, const std::string &propname, const bool have_defval = false, const int defval = 0)
55
{
56
CCD_Section::iterator zit = s.find(propname);
57
58
if(zit == s.end())
59
{
60
if(have_defval)
61
return defval;
62
else
63
throw MDFN_Error(0, _("Missing property: %s"), propname.c_str());
64
}
65
66
const std::string &v = zit->second;
67
int scan_base = 10;
68
size_t scan_offset = 0;
69
long ret = 0;
70
71
if(v.length() >= 3 && v[0] == '0' && v[1] == 'x')
72
{
73
scan_base = 16;
74
scan_offset = 2;
75
}
76
77
const char *vp = v.c_str() + scan_offset;
78
char *ep = NULL;
79
80
if(std::numeric_limits<T>::is_signed)
81
ret = strtol(vp, &ep, scan_base);
82
else
83
ret = strtoul(vp, &ep, scan_base);
84
85
if(!vp[0] || ep[0])
86
{
87
throw MDFN_Error(0, _("Property %s: Malformed integer: %s"), propname.c_str(), v.c_str());
88
}
89
90
//if(ret < minv || ret > maxv)
91
//{
92
// throw MDFN_Error(0, _("Property %s: Integer %ld out of range(accepted: %d through %d)."), propname.c_str(), ret, minv, maxv);
93
//}
94
95
return ret;
96
}
97
98
99
CDAccess_CCD::CDAccess_CCD(const std::string& path, bool image_memcache) : img_numsectors(0)
100
{
101
Load(path, image_memcache);
102
}
103
104
void CDAccess_CCD::Load(const std::string& path, bool image_memcache)
105
{
106
FileStream cf(path, FileStream::MODE_READ);
107
std::map<std::string, CCD_Section> Sections;
108
std::string linebuf;
109
std::string cur_section_name;
110
std::string dir_path, file_base, file_ext;
111
char img_extsd[4] = { 'i', 'm', 'g', 0 };
112
char sub_extsd[4] = { 's', 'u', 'b', 0 };
113
114
MDFN_GetFilePathComponents(path, &dir_path, &file_base, &file_ext);
115
116
if(file_ext.length() == 4 && file_ext[0] == '.')
117
{
118
signed char extupt[3] = { -1, -1, -1 };
119
120
for(int i = 1; i < 4; i++)
121
{
122
if(file_ext[i] >= 'A' && file_ext[i] <= 'Z')
123
extupt[i - 1] = 'A' - 'a';
124
else if(file_ext[i] >= 'a' && file_ext[i] <= 'z')
125
extupt[i - 1] = 0;
126
}
127
128
signed char av = -1;
129
for(int i = 0; i < 3; i++)
130
{
131
if(extupt[i] != -1)
132
av = extupt[i];
133
else
134
extupt[i] = av;
135
}
136
137
if(av == -1)
138
av = 0;
139
140
for(int i = 0; i < 3; i++)
141
{
142
if(extupt[i] == -1)
143
extupt[i] = av;
144
}
145
146
for(int i = 0; i < 3; i++)
147
{
148
img_extsd[i] += extupt[i];
149
sub_extsd[i] += extupt[i];
150
}
151
}
152
153
//printf("%s %d %d %d\n", file_ext.c_str(), extupt[0], extupt[1], extupt[2]);
154
155
linebuf.reserve(256);
156
157
while(cf.get_line(linebuf) >= 0)
158
{
159
MDFN_trim(linebuf);
160
161
if(linebuf.length() == 0) // Skip blank lines.
162
continue;
163
164
if(linebuf[0] == '[')
165
{
166
if(linebuf.length() < 3 || linebuf[linebuf.length() - 1] != ']')
167
throw MDFN_Error(0, _("Malformed section specifier: %s"), linebuf.c_str());
168
169
cur_section_name = linebuf.substr(1, linebuf.length() - 2);
170
MDFN_strtoupper(cur_section_name);
171
}
172
else
173
{
174
const size_t feqpos = linebuf.find('=');
175
const size_t leqpos = linebuf.rfind('=');
176
std::string k, v;
177
178
if(feqpos == std::string::npos || feqpos != leqpos)
179
throw MDFN_Error(0, _("Malformed value pair specifier: %s"), linebuf.c_str());
180
181
k = linebuf.substr(0, feqpos);
182
v = linebuf.substr(feqpos + 1);
183
184
MDFN_trim(k);
185
MDFN_trim(v);
186
187
MDFN_strtoupper(k);
188
189
Sections[cur_section_name][k] = v;
190
}
191
}
192
193
{
194
CCD_Section& ds = Sections["DISC"];
195
unsigned toc_entries = CCD_ReadInt<unsigned>(ds, "TOCENTRIES");
196
unsigned num_sessions = CCD_ReadInt<unsigned>(ds, "SESSIONS");
197
bool data_tracks_scrambled = CCD_ReadInt<unsigned>(ds, "DATATRACKSSCRAMBLED");
198
199
if(num_sessions != 1)
200
throw MDFN_Error(0, _("Unsupported number of sessions: %u"), num_sessions);
201
202
if(data_tracks_scrambled)
203
throw MDFN_Error(0, _("Scrambled CCD data tracks currently not supported."));
204
205
//printf("MOO: %d\n", toc_entries);
206
207
for(unsigned te = 0; te < toc_entries; te++)
208
{
209
char tmpbuf[64];
210
trio_snprintf(tmpbuf, sizeof(tmpbuf), "ENTRY %u", te);
211
CCD_Section& ts = Sections[std::string(tmpbuf)];
212
unsigned session = CCD_ReadInt<unsigned>(ts, "SESSION");
213
uint8 point = CCD_ReadInt<uint8>(ts, "POINT");
214
uint8 adr = CCD_ReadInt<uint8>(ts, "ADR");
215
uint8 control = CCD_ReadInt<uint8>(ts, "CONTROL");
216
uint8 pmin = CCD_ReadInt<uint8>(ts, "PMIN");
217
uint8 psec = CCD_ReadInt<uint8>(ts, "PSEC");
218
//uint8 pframe = CCD_ReadInt<uint8>(ts, "PFRAME");
219
signed plba = CCD_ReadInt<signed>(ts, "PLBA");
220
221
if(session != 1)
222
throw MDFN_Error(0, "Unsupported TOC entry Session value: %u", session);
223
224
// Reference: ECMA-394, page 5-14
225
if(point >= 1 && point <= 99)
226
{
227
tocd.tracks[point].adr = adr;
228
tocd.tracks[point].control = control;
229
tocd.tracks[point].lba = plba;
230
tocd.tracks[point].valid = true;
231
}
232
else switch(point)
233
{
234
default:
235
throw MDFN_Error(0, "Unsupported TOC entry Point value: %u", point);
236
break;
237
238
case 0xA0:
239
tocd.first_track = pmin;
240
tocd.disc_type = psec;
241
break;
242
243
case 0xA1:
244
tocd.last_track = pmin;
245
break;
246
247
case 0xA2:
248
tocd.tracks[100].adr = adr;
249
tocd.tracks[100].control = control;
250
tocd.tracks[100].lba = plba;
251
tocd.tracks[100].valid = true;
252
break;
253
}
254
}
255
}
256
257
//
258
// Open image stream.
259
{
260
std::string image_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(img_extsd), true);
261
262
if(image_memcache)
263
{
264
img_stream.reset(new MemoryStream(new FileStream(image_path, FileStream::MODE_READ)));
265
}
266
else
267
{
268
img_stream.reset(new FileStream(image_path, FileStream::MODE_READ));
269
}
270
271
uint64 ss = img_stream->size();
272
273
if(ss % 2352)
274
throw MDFN_Error(0, _("CCD image size is not evenly divisible by 2352."));
275
276
if(ss > 0x7FFFFFFF)
277
throw MDFN_Error(0, _("CCD image is too large."));
278
279
img_numsectors = ss / 2352;
280
}
281
282
//
283
// Open subchannel stream
284
{
285
std::string sub_path = MDFN_EvalFIP(dir_path, file_base + std::string(".") + std::string(sub_extsd), true);
286
FileStream sub_stream(sub_path, FileStream::MODE_READ);
287
288
if(sub_stream.size() != (uint64)img_numsectors * 96)
289
throw MDFN_Error(0, _("CCD SUB file size mismatch."));
290
291
sub_data.reset(new uint8[(uint64)img_numsectors * 96]);
292
sub_stream.read(sub_data.get(), (uint64)img_numsectors * 96);
293
}
294
295
CheckSubQSanity();
296
}
297
298
//
299
// Checks for Q subchannel mode 1(current time) data that has a correct checksum, but the data is nonsensical or corrupted nonetheless; this is the
300
// case for some bad rips floating around on the Internet. Allowing these bad rips to be used will cause all sorts of problems during emulation, so we
301
// error out here if a bad rip is detected.
302
//
303
// This check is not as aggressive or exhaustive as it could be, and will not detect all potential Q subchannel rip errors; as such, it should definitely NOT be
304
// used in an effort to "repair" a broken rip.
305
//
306
void CDAccess_CCD::CheckSubQSanity(void)
307
{
308
size_t checksum_pass_counter = 0;
309
int prev_lba = INT_MAX;
310
uint8 prev_track = 0;
311
312
for(size_t s = 0; s < img_numsectors; s++)
313
{
314
union
315
{
316
uint8 full[96];
317
struct
318
{
319
uint8 pbuf[12];
320
uint8 qbuf[12];
321
};
322
} buf;
323
324
memcpy(buf.full, &sub_data[s * 96], 96);
325
326
if(subq_check_checksum(buf.qbuf))
327
{
328
uint8 adr = buf.qbuf[0] & 0xF;
329
330
if(adr == 0x01)
331
{
332
uint8 track_bcd = buf.qbuf[1];
333
uint8 index_bcd = buf.qbuf[2];
334
uint8 rm_bcd = buf.qbuf[3];
335
uint8 rs_bcd = buf.qbuf[4];
336
uint8 rf_bcd = buf.qbuf[5];
337
uint8 am_bcd = buf.qbuf[7];
338
uint8 as_bcd = buf.qbuf[8];
339
uint8 af_bcd = buf.qbuf[9];
340
341
//printf("%2x %2x %2x\n", am_bcd, as_bcd, af_bcd);
342
343
if(!BCD_is_valid(track_bcd) || !BCD_is_valid(index_bcd) || !BCD_is_valid(rm_bcd) || !BCD_is_valid(rs_bcd) || !BCD_is_valid(rf_bcd) ||
344
!BCD_is_valid(am_bcd) || !BCD_is_valid(as_bcd) || !BCD_is_valid(af_bcd) ||
345
rs_bcd > 0x59 || rf_bcd > 0x74 || as_bcd > 0x59 || af_bcd > 0x74)
346
{
347
#ifdef CHECK_CCD_GARBAGE_SUBQ_A
348
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad BCD/out of range): %02x:%02x:%02x %02x:%02x:%02x"), rm_bcd, rs_bcd, rf_bcd, am_bcd, as_bcd, af_bcd);
349
#endif
350
}
351
else
352
{
353
int lba = ((BCD_to_U8(am_bcd) * 60 + BCD_to_U8(as_bcd)) * 75 + BCD_to_U8(af_bcd)) - 150;
354
uint8 track = BCD_to_U8(track_bcd);
355
356
#ifdef CHECK_CCD_GARBAGE_SUBQ_B
357
if(prev_lba != INT_MAX && abs(lba - prev_lba) > 100)
358
throw MDFN_Error(0, _("Garbage subchannel Q data detected(excessively large jump in AMSF)"));
359
#endif
360
361
#ifdef CHECK_CCD_GARBAGE_SUBQ_C
362
if(abs(lba - (int)s) > 100) //zero 19-jun-2015 a bit of a sneaky signed/unsigned fixup here
363
throw MDFN_Error(0, _("Garbage subchannel Q data detected(AMSF value is out of tolerance)"));
364
#endif
365
366
prev_lba = lba;
367
368
#ifdef CHECK_CCD_GARBAGE_SUBQ_D
369
if(track < prev_track)
370
throw MDFN_Error(0, _("Garbage subchannel Q data detected(bad track number)"));
371
#endif
372
//else if(prev_track && track - pre
373
374
prev_track = track;
375
}
376
checksum_pass_counter++;
377
}
378
}
379
}
380
381
//printf("%u/%u\n", checksum_pass_counter, img_numsectors);
382
}
383
384
CDAccess_CCD::~CDAccess_CCD()
385
{
386
387
}
388
389
void CDAccess_CCD::Read_Raw_Sector(uint8 *buf, int32 lba)
390
{
391
if(lba < 0)
392
{
393
synth_udapp_sector_lba(0xFF, tocd, lba, 0, buf);
394
return;
395
}
396
397
if((size_t)lba >= img_numsectors)
398
{
399
synth_leadout_sector_lba(0xFF, tocd, lba, buf);
400
return;
401
}
402
403
img_stream->seek(lba * 2352, SEEK_SET);
404
img_stream->read(buf, 2352);
405
406
subpw_interleave(&sub_data[lba * 96], buf + 2352);
407
}
408
409
bool CDAccess_CCD::Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 lba) const noexcept
410
{
411
if(lba < 0)
412
{
413
subpw_synth_udapp_lba(tocd, lba, 0, pwbuf);
414
return true;
415
}
416
417
if((size_t)lba >= img_numsectors)
418
{
419
subpw_synth_leadout_lba(tocd, lba, pwbuf);
420
return true;
421
}
422
423
subpw_interleave(&sub_data[lba * 96], pwbuf);
424
425
return true;
426
}
427
428
void CDAccess_CCD::Read_TOC(CDUtility::TOC *toc)
429
{
430
*toc = tocd;
431
}
432
433
434