Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/psx/mednadisc/cdrom/CDAccess_Image.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
/*
19
Notes and TODO:
20
21
POSTGAP in CUE sheets may not be handled properly, should the directive automatically increment the index number?
22
23
INDEX nn where 02 <= nn <= 99 is not supported in CUE sheets.
24
25
TOC reading code is extremely barebones, leaving out support for more esoteric features.
26
27
A PREGAP statement in the first track definition in a CUE sheet may not work properly(depends on what is proper);
28
it will be added onto the implicit default 00:02:00 of pregap.
29
*/
30
31
#include "emuware/emuware.h"
32
33
//undo gettext stuff
34
#define _(X) (X)
35
36
#include <sys/types.h>
37
38
#include <string.h>
39
#include <errno.h>
40
#include <time.h>
41
#include <trio/trio.h>
42
#include <memory>
43
44
#include "general.h"
45
#include "string/trim.h"
46
#include "endian.h"
47
#include "FileStream.h"
48
#include "MemoryStream.h"
49
50
#include "CDAccess.h"
51
#include "CDAccess_Image.h"
52
53
#include "CDAFReader.h"
54
55
#include <map>
56
57
using namespace CDUtility;
58
59
enum
60
{
61
CDRF_SUBM_NONE = 0,
62
CDRF_SUBM_RW = 1,
63
CDRF_SUBM_RW_RAW = 2
64
};
65
66
// Disk-image(rip) track/sector formats
67
enum
68
{
69
DI_FORMAT_AUDIO = 0x00,
70
DI_FORMAT_MODE1 = 0x01,
71
DI_FORMAT_MODE1_RAW = 0x02,
72
DI_FORMAT_MODE2 = 0x03,
73
DI_FORMAT_MODE2_FORM1 = 0x04,
74
DI_FORMAT_MODE2_FORM2 = 0x05,
75
DI_FORMAT_MODE2_RAW = 0x06,
76
DI_FORMAT_CDI_RAW = 0x07,
77
_DI_FORMAT_COUNT
78
};
79
80
static const int32 DI_Size_Table[8] =
81
{
82
2352, // Audio
83
2048, // MODE1
84
2352, // MODE1 RAW
85
2336, // MODE2
86
2048, // MODE2 Form 1
87
2324, // Mode 2 Form 2
88
2352, // MODE2 RAW
89
2352, // CD-I RAW
90
};
91
92
static const char *DI_CDRDAO_Strings[8] =
93
{
94
"AUDIO",
95
"MODE1",
96
"MODE1_RAW",
97
"MODE2",
98
"MODE2_FORM1",
99
"MODE2_FORM2",
100
"MODE2_RAW",
101
"CDI_RAW"
102
};
103
104
static const char *DI_CUE_Strings[8] =
105
{
106
"AUDIO",
107
"MODE1/2048",
108
"MODE1/2352",
109
"MODE2/2336", // FIXME: A guess
110
"MODE2/2048", // FIXME: A guess
111
"MODE2/2324", // FIXME: A guess
112
"MODE2/2352", // FIXME: A guess
113
"CDI/2352",
114
};
115
116
// Should return an offset to the start of the next argument(past any whitespace), or if there isn't a next argument,
117
// it'll return the length of the src string.
118
static size_t UnQuotify(const std::string &src, size_t source_offset, std::string &dest, bool parse_quotes = true)
119
{
120
const size_t source_len = src.length();
121
bool in_quote = 0;
122
bool already_normal = 0;
123
124
dest.clear();
125
126
while(source_offset < source_len)
127
{
128
if(src[source_offset] == ' ' || src[source_offset] == '\t')
129
{
130
if(!in_quote)
131
{
132
if(already_normal) // Trailing whitespace(IE we're done with this argument)
133
break;
134
else // Leading whitespace, ignore it.
135
{
136
source_offset++;
137
continue;
138
}
139
}
140
}
141
142
if(src[source_offset] == '"' && parse_quotes)
143
{
144
if(in_quote)
145
{
146
source_offset++;
147
// Not sure which behavior is most useful(or correct :b).
148
#if 0
149
in_quote = false;
150
already_normal = true;
151
#else
152
break;
153
#endif
154
}
155
else
156
in_quote = 1;
157
}
158
else
159
{
160
dest.push_back(src[source_offset]);
161
already_normal = 1;
162
}
163
source_offset++;
164
}
165
166
while(source_offset < source_len)
167
{
168
if(src[source_offset] != ' ' && src[source_offset] != '\t')
169
break;
170
171
source_offset++;
172
}
173
174
return source_offset;
175
}
176
177
uint32 CDAccess_Image::GetSectorCount(CDRFILE_TRACK_INFO *track)
178
{
179
if(track->DIFormat == DI_FORMAT_AUDIO)
180
{
181
if(track->AReader)
182
return(((track->AReader->FrameCount() * 4) - track->FileOffset) / 2352);
183
else
184
{
185
const int64 size = track->fp->size();
186
187
//printf("%d %d %d\n", (int)stat_buf.st_size, (int)track->FileOffset, (int)stat_buf.st_size - (int)track->FileOffset);
188
if(track->SubchannelMode)
189
return((size - track->FileOffset) / (2352 + 96));
190
else
191
return((size - track->FileOffset) / 2352);
192
}
193
}
194
else
195
{
196
const int64 size = track->fp->size();
197
198
return((size - track->FileOffset) / DI_Size_Table[track->DIFormat]);
199
}
200
201
return(0);
202
}
203
204
void CDAccess_Image::ParseTOCFileLineInfo(CDRFILE_TRACK_INFO *track, const int tracknum, const std::string &filename, const char *binoffset, const char *msfoffset, const char *length, bool image_memcache, std::map<std::string, Stream*> &toc_streamcache)
205
{
206
long offset = 0; // In bytes!
207
long tmp_long;
208
int m, s, f;
209
uint32 sector_mult;
210
long sectors;
211
std::map<std::string, Stream*>::iterator ribbit;
212
213
ribbit = toc_streamcache.find(filename);
214
215
if(ribbit != toc_streamcache.end())
216
{
217
track->FirstFileInstance = 0;
218
219
track->fp = ribbit->second;
220
}
221
else
222
{
223
std::string efn;
224
225
track->FirstFileInstance = 1;
226
227
efn = MDFN_EvalFIP(base_dir, filename);
228
229
if(image_memcache)
230
track->fp = new MemoryStream(new FileStream(efn, FileStream::MODE_READ));
231
else
232
track->fp = new FileStream(efn, FileStream::MODE_READ);
233
234
toc_streamcache[filename] = track->fp;
235
}
236
237
if(filename.length() >= 4 && !strcasecmp(filename.c_str() + filename.length() - 4, ".wav"))
238
{
239
track->AReader = CDAFR_Open(track->fp);
240
241
if(!track->AReader)
242
throw MDFN_Error(0, "TODO ERROR");
243
}
244
245
sector_mult = DI_Size_Table[track->DIFormat];
246
247
if(track->SubchannelMode)
248
sector_mult += 96;
249
250
if(binoffset && trio_sscanf(binoffset, "%ld", &tmp_long) == 1)
251
{
252
offset += tmp_long;
253
}
254
255
if(msfoffset && trio_sscanf(msfoffset, "%d:%d:%d", &m, &s, &f) == 3)
256
{
257
offset += ((m * 60 + s) * 75 + f) * sector_mult;
258
}
259
260
track->FileOffset = offset; // Make sure this is set before calling GetSectorCount()!
261
sectors = GetSectorCount(track);
262
//printf("Track: %d, offset: %ld, %ld\n", tracknum, offset, sectors);
263
264
if(length)
265
{
266
tmp_long = sectors;
267
268
if(trio_sscanf(length, "%d:%d:%d", &m, &s, &f) == 3)
269
tmp_long = (m * 60 + s) * 75 + f;
270
else if(track->DIFormat == DI_FORMAT_AUDIO)
271
{
272
char *endptr = NULL;
273
274
tmp_long = strtol(length, &endptr, 10);
275
276
// Error?
277
if(endptr == length)
278
{
279
tmp_long = sectors;
280
}
281
else
282
tmp_long /= 588;
283
284
}
285
286
if(tmp_long > sectors)
287
{
288
throw MDFN_Error(0, _("Length specified in TOC file for track %d is too large by %ld sectors!\n"), tracknum, (long)(tmp_long - sectors));
289
}
290
sectors = tmp_long;
291
}
292
293
track->sectors = sectors;
294
}
295
296
static void MDFN_strtoupper(std::string &str)
297
{
298
const size_t len = str.length();
299
300
for(size_t x = 0; x < len; x++)
301
{
302
if(str[x] >= 'a' && str[x] <= 'z')
303
{
304
str[x] = str[x] - 'a' + 'A';
305
}
306
}
307
}
308
309
#if 0
310
std::string MDFN_toupper(const std::string &str)
311
{
312
const size_t len = str.length();
313
std::string new_str;
314
315
new_str.reserve(len);
316
317
for(size_t x = 0; x < len; x++)
318
{
319
int c = str[x];
320
321
if(c >= 'a' && c <= 'z')
322
c = c - 'a' + 'A';
323
324
new_str.push_back(c);
325
}
326
}
327
#endif
328
329
void CDAccess_Image::LoadSBI(const std::string& sbi_path)
330
{
331
//test whether it exists
332
FILE* inf = fopen(sbi_path.c_str(),"rb");
333
if(inf == NULL)
334
return;
335
336
printf(_("Loading SBI file \"%s\"...\n"), sbi_path.c_str());
337
338
{
339
//MDFN_AutoIndent aind(1);
340
341
try
342
{
343
FileStream sbis(sbi_path, FileStream::MODE_READ);
344
uint8 header[4];
345
uint8 ed[4 + 10];
346
uint8 tmpq[12];
347
348
sbis.read(header, 4);
349
350
if(memcmp(header, "SBI\0", 4))
351
throw MDFN_Error(0, _("Not recognized a valid SBI file."));
352
353
while(sbis.read(ed, sizeof(ed), false) == sizeof(ed))
354
{
355
if(!BCD_is_valid(ed[0]) || !BCD_is_valid(ed[1]) || !BCD_is_valid(ed[2]))
356
throw MDFN_Error(0, _("Bad BCD MSF offset in SBI file: %02x:%02x:%02x"), ed[0], ed[1], ed[2]);
357
358
if(ed[3] != 0x01)
359
throw MDFN_Error(0, _("Unrecognized boogly oogly in SBI file: %02x"), ed[3]);
360
361
memcpy(tmpq, &ed[4], 10);
362
363
//
364
subq_generate_checksum(tmpq);
365
tmpq[10] ^= 0xFF;
366
tmpq[11] ^= 0xFF;
367
//
368
369
//printf("%02x:%02x:%02x --- ", ed[0], ed[1], ed[2]);
370
//for(unsigned i = 0; i < 12; i++)
371
// printf("%02x ", tmpq[i]);
372
//printf("\n");
373
374
uint32 aba = AMSF_to_ABA(BCD_to_U8(ed[0]), BCD_to_U8(ed[1]), BCD_to_U8(ed[2]));
375
376
memcpy(SubQReplaceMap[aba].data(), tmpq, 12);
377
}
378
printf(_("Loaded Q subchannel replacements for %zu sectors.\n"), SubQReplaceMap.size());
379
}
380
catch(MDFN_Error &e)
381
{
382
if(e.GetErrno() != ENOENT)
383
throw;
384
else
385
printf(_("Error: %s\n"), e.what());
386
}
387
catch(std::exception &e)
388
{
389
throw;
390
}
391
}
392
}
393
394
static void StringToMSF(const char* str, unsigned* m, unsigned* s, unsigned* f)
395
{
396
if(trio_sscanf(str, "%u:%u:%u", m, s, f) != 3)
397
throw MDFN_Error(0, _("M:S:F time \"%s\" is malformed."), str);
398
399
if(*m > 99 || *s > 59 || *f > 74)
400
throw MDFN_Error(0, _("M:S:F time \"%s\" contains component(s) out of range."), str);
401
}
402
403
void CDAccess_Image::ImageOpen(const std::string& path, bool image_memcache)
404
{
405
MemoryStream fp(new FileStream(path, FileStream::MODE_READ));
406
static const unsigned max_args = 4;
407
std::string linebuf;
408
std::string cmdbuf, args[max_args];
409
bool IsTOC = FALSE_0;
410
int32 active_track = -1;
411
int32 AutoTrackInc = 1; // For TOC
412
CDRFILE_TRACK_INFO TmpTrack;
413
std::string file_base, file_ext;
414
std::map<std::string, Stream*> toc_streamcache;
415
416
disc_type = DISC_TYPE_CDDA_OR_M1;
417
memset(&TmpTrack, 0, sizeof(TmpTrack));
418
419
MDFN_GetFilePathComponents(path, &base_dir, &file_base, &file_ext);
420
421
if(!strcasecmp(file_ext.c_str(), ".toc"))
422
{
423
printf(_("TOC file detected.\n"));
424
IsTOC = true;
425
}
426
427
// Check for annoying UTF-8 BOM.
428
if(!IsTOC)
429
{
430
uint8 bom_tmp[3];
431
432
if(fp.read(bom_tmp, 3, false) == 3 && bom_tmp[0] == 0xEF && bom_tmp[1] == 0xBB && bom_tmp[2] == 0xBF)
433
{
434
// Print an annoying error message, but don't actually error out.
435
printf(_("UTF-8 BOM detected at start of CUE sheet."));
436
}
437
else
438
fp.seek(0, SEEK_SET);
439
}
440
441
442
// Assign opposite maximum values so our tests will work!
443
FirstTrack = 99;
444
LastTrack = 0;
445
446
linebuf.reserve(1024);
447
while(fp.get_line(linebuf) >= 0)
448
{
449
unsigned argcount = 0;
450
451
if(IsTOC)
452
{
453
// Handle TOC format comments
454
size_t ss_loc = linebuf.find("//");
455
456
if(ss_loc != std::string::npos)
457
linebuf.resize(ss_loc);
458
}
459
460
// Call trim AFTER we handle TOC-style comments, so we'll be sure to remove trailing whitespace in lines like: MONKEY // BABIES
461
MDFN_trim(linebuf);
462
463
if(linebuf.length() == 0) // Skip blank lines.
464
continue;
465
466
// Grab command and arguments.
467
{
468
size_t offs = 0;
469
470
offs = UnQuotify(linebuf, offs, cmdbuf, false);
471
for(argcount = 0; argcount < max_args && offs < linebuf.length(); argcount++)
472
offs = UnQuotify(linebuf, offs, args[argcount]);
473
474
// Make sure unused arguments are cleared out so we don't have inter-line leaks!
475
for(unsigned x = argcount; x < max_args; x++)
476
args[x].clear();
477
478
MDFN_strtoupper(cmdbuf);
479
}
480
481
//printf("%s\n", cmdbuf.c_str()); //: %s %s %s %s\n", cmdbuf.c_str(), args[0].c_str(), args[1].c_str(), args[2].c_str(), args[3].c_str());
482
483
if(IsTOC)
484
{
485
if(cmdbuf == "TRACK")
486
{
487
if(active_track >= 0)
488
{
489
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
490
memset(&TmpTrack, 0, sizeof(TmpTrack));
491
active_track = -1;
492
}
493
494
if(AutoTrackInc > 99)
495
{
496
throw(MDFN_Error(0, _("Invalid track number: %d"), AutoTrackInc));
497
}
498
499
active_track = AutoTrackInc++;
500
if(active_track < FirstTrack)
501
FirstTrack = active_track;
502
if(active_track > LastTrack)
503
LastTrack = active_track;
504
505
int format_lookup;
506
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
507
{
508
if(!strcasecmp(args[0].c_str(), DI_CDRDAO_Strings[format_lookup]))
509
{
510
TmpTrack.DIFormat = format_lookup;
511
break;
512
}
513
}
514
515
if(format_lookup == _DI_FORMAT_COUNT)
516
{
517
throw(MDFN_Error(0, _("Invalid track format: %s"), args[0].c_str()));
518
}
519
520
if(TmpTrack.DIFormat == DI_FORMAT_AUDIO)
521
TmpTrack.RawAudioMSBFirst = TRUE_1; // Silly cdrdao...
522
523
if(!strcasecmp(args[1].c_str(), "RW"))
524
{
525
TmpTrack.SubchannelMode = CDRF_SUBM_RW;
526
throw(MDFN_Error(0, _("\"RW\" format subchannel data not supported, only \"RW_RAW\" is!")));
527
}
528
else if(!strcasecmp(args[1].c_str(), "RW_RAW"))
529
TmpTrack.SubchannelMode = CDRF_SUBM_RW_RAW;
530
531
} // end to TRACK
532
else if(cmdbuf == "SILENCE")
533
{
534
//throw MDFN_Error(0, _("Unsupported directive: %s"), cmdbuf.c_str());
535
}
536
else if(cmdbuf == "ZERO")
537
{
538
//throw MDFN_Error(0, _("Unsupported directive: %s"), cmdbuf.c_str());
539
}
540
else if(cmdbuf == "FIFO")
541
{
542
throw MDFN_Error(0, _("Unsupported directive: %s"), cmdbuf.c_str());
543
}
544
else if(cmdbuf == "FILE" || cmdbuf == "AUDIOFILE")
545
{
546
const char *binoffset = NULL;
547
const char *msfoffset = NULL;
548
const char *length = NULL;
549
550
if(args[1].c_str()[0] == '#')
551
{
552
binoffset = args[1].c_str() + 1;
553
msfoffset = args[2].c_str();
554
length = args[3].c_str();
555
}
556
else
557
{
558
msfoffset = args[1].c_str();
559
length = args[2].c_str();
560
}
561
//printf("%s, %s, %s, %s\n", args[0].c_str(), binoffset, msfoffset, length);
562
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, msfoffset, length, image_memcache, toc_streamcache);
563
}
564
else if(cmdbuf == "DATAFILE")
565
{
566
const char *binoffset = NULL;
567
const char *length = NULL;
568
569
if(args[1].c_str()[0] == '#')
570
{
571
binoffset = args[1].c_str() + 1;
572
length = args[2].c_str();
573
}
574
else
575
length = args[1].c_str();
576
577
ParseTOCFileLineInfo(&TmpTrack, active_track, args[0], binoffset, NULL, length, image_memcache, toc_streamcache);
578
}
579
else if(cmdbuf == "INDEX")
580
{
581
582
}
583
else if(cmdbuf == "PREGAP")
584
{
585
if(active_track < 0)
586
{
587
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf.c_str()));
588
}
589
590
unsigned int m,s,f;
591
592
StringToMSF(args[0].c_str(), &m, &s, &f);
593
594
TmpTrack.pregap = (m * 60 + s) * 75 + f;
595
} // end to PREGAP
596
else if(cmdbuf == "START")
597
{
598
if(active_track < 0)
599
{
600
throw(MDFN_Error(0, _("Command %s is outside of a TRACK definition!\n"), cmdbuf.c_str()));
601
}
602
603
unsigned int m,s,f;
604
605
StringToMSF(args[0].c_str(), &m, &s, &f);
606
607
TmpTrack.pregap = (m * 60 + s) * 75 + f;
608
}
609
else if(cmdbuf == "TWO_CHANNEL_AUDIO")
610
{
611
TmpTrack.subq_control &= ~SUBQ_CTRLF_4CH;
612
}
613
else if(cmdbuf == "FOUR_CHANNEL_AUDIO")
614
{
615
TmpTrack.subq_control |= SUBQ_CTRLF_4CH;
616
}
617
else if(cmdbuf == "NO")
618
{
619
MDFN_strtoupper(args[0]);
620
621
if(args[0] == "COPY")
622
{
623
TmpTrack.subq_control &= ~SUBQ_CTRLF_DCP;
624
}
625
else if(args[0] == "PRE_EMPHASIS")
626
{
627
TmpTrack.subq_control &= ~SUBQ_CTRLF_PRE;
628
}
629
else
630
{
631
throw MDFN_Error(0, _("Unsupported argument to \"NO\" directive: %s"), args[0].c_str());
632
}
633
}
634
else if(cmdbuf == "COPY")
635
{
636
TmpTrack.subq_control |= SUBQ_CTRLF_DCP;
637
}
638
else if(cmdbuf == "PRE_EMPHASIS")
639
{
640
TmpTrack.subq_control |= SUBQ_CTRLF_PRE;
641
}
642
// TODO: Confirm that these are taken from the TOC of the disc, and not synthesized by cdrdao.
643
else if(cmdbuf == "CD_DA")
644
disc_type = DISC_TYPE_CDDA_OR_M1;
645
else if(cmdbuf == "CD_ROM")
646
disc_type = DISC_TYPE_CDDA_OR_M1;
647
else if(cmdbuf == "CD_ROM_XA")
648
disc_type = DISC_TYPE_CD_XA;
649
else
650
{
651
//throw MDFN_Error(0, _("Unsupported directive: %s"), cmdbuf.c_str());
652
}
653
// TODO: CATALOG
654
655
} /*********** END TOC HANDLING ************/
656
else // now for CUE sheet handling
657
{
658
if(cmdbuf == "FILE")
659
{
660
if(active_track >= 0)
661
{
662
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
663
memset(&TmpTrack, 0, sizeof(TmpTrack));
664
active_track = -1;
665
}
666
667
std::string efn = MDFN_EvalFIP(base_dir, args[0]);
668
TmpTrack.fp = new FileStream(efn, FileStream::MODE_READ);
669
TmpTrack.FirstFileInstance = 1;
670
671
if(image_memcache)
672
TmpTrack.fp = new MemoryStream(TmpTrack.fp);
673
674
if(!strcasecmp(args[1].c_str(), "BINARY"))
675
{
676
//TmpTrack.Format = TRACK_FORMAT_DATA;
677
//struct stat stat_buf;
678
//fstat(fileno(TmpTrack.fp), &stat_buf);
679
//TmpTrack.sectors = stat_buf.st_size; // / 2048;
680
}
681
else if(!strcasecmp(args[1].c_str(), "OGG") || !strcasecmp(args[1].c_str(), "VORBIS") || !strcasecmp(args[1].c_str(), "WAVE") || !strcasecmp(args[1].c_str(), "WAV") || !strcasecmp(args[1].c_str(), "PCM")
682
|| !strcasecmp(args[1].c_str(), "MPC") || !strcasecmp(args[1].c_str(), "MP+"))
683
{
684
TmpTrack.AReader = CDAFR_Open(TmpTrack.fp);
685
if(!TmpTrack.AReader)
686
{
687
throw(MDFN_Error(0, _("Unsupported audio track file format: %s\n"), args[0].c_str()));
688
}
689
}
690
else
691
{
692
throw(MDFN_Error(0, _("Unsupported track format: %s\n"), args[1].c_str()));
693
}
694
}
695
else if(cmdbuf == "TRACK")
696
{
697
if(active_track >= 0)
698
{
699
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
700
TmpTrack.FirstFileInstance = 0;
701
TmpTrack.pregap = 0;
702
TmpTrack.pregap_dv = 0;
703
TmpTrack.postgap = 0;
704
TmpTrack.index[0] = -1;
705
TmpTrack.index[1] = 0;
706
}
707
active_track = atoi(args[0].c_str());
708
709
if(active_track < 1 || active_track > 99)
710
{
711
throw(MDFN_Error(0, _("Invalid track number: %d\n"), active_track));
712
}
713
714
if(active_track < FirstTrack)
715
FirstTrack = active_track;
716
if(active_track > LastTrack)
717
LastTrack = active_track;
718
719
int format_lookup;
720
for(format_lookup = 0; format_lookup < _DI_FORMAT_COUNT; format_lookup++)
721
{
722
if(!strcasecmp(args[1].c_str(), DI_CUE_Strings[format_lookup]))
723
{
724
TmpTrack.DIFormat = format_lookup;
725
break;
726
}
727
}
728
729
if(format_lookup == _DI_FORMAT_COUNT)
730
{
731
throw(MDFN_Error(0, _("Invalid track format: %s\n"), args[1].c_str()));
732
}
733
}
734
else if(cmdbuf == "INDEX")
735
{
736
if(active_track >= 0)
737
{
738
unsigned int m,s,f;
739
740
StringToMSF(args[1].c_str(), &m, &s, &f);
741
742
if(!strcasecmp(args[0].c_str(), "01") || !strcasecmp(args[0].c_str(), "1"))
743
TmpTrack.index[1] = (m * 60 + s) * 75 + f;
744
else if(!strcasecmp(args[0].c_str(), "00") || !strcasecmp(args[0].c_str(), "0"))
745
TmpTrack.index[0] = (m * 60 + s) * 75 + f;
746
}
747
}
748
else if(cmdbuf == "PREGAP")
749
{
750
if(active_track >= 0)
751
{
752
unsigned int m,s,f;
753
754
StringToMSF(args[0].c_str(), &m, &s, &f);
755
756
TmpTrack.pregap = (m * 60 + s) * 75 + f;
757
}
758
}
759
else if(cmdbuf == "POSTGAP")
760
{
761
if(active_track >= 0)
762
{
763
unsigned int m,s,f;
764
765
StringToMSF(args[0].c_str(), &m, &s, &f);
766
767
TmpTrack.postgap = (m * 60 + s) * 75 + f;
768
}
769
}
770
else if(cmdbuf == "REM")
771
{
772
773
}
774
else if(cmdbuf == "FLAGS")
775
{
776
TmpTrack.subq_control &= ~(SUBQ_CTRLF_PRE | SUBQ_CTRLF_DCP | SUBQ_CTRLF_4CH);
777
for(unsigned i = 0; i < argcount; i++)
778
{
779
if(args[i] == "DCP")
780
{
781
TmpTrack.subq_control |= SUBQ_CTRLF_DCP;
782
}
783
else if(args[i] == "4CH")
784
{
785
TmpTrack.subq_control |= SUBQ_CTRLF_4CH;
786
}
787
else if(args[i] == "PRE")
788
{
789
TmpTrack.subq_control |= SUBQ_CTRLF_PRE;
790
}
791
else if(args[i] == "SCMS")
792
{
793
// Not implemented, likely pointless. PROBABLY indicates that the copy bit of the subchannel Q control field is supposed to
794
// alternate between 1 and 0 at 9.375 Hz(four 1, four 0, four 1, four 0, etc.).
795
}
796
else
797
{
798
throw MDFN_Error(0, _("Unknown CUE sheet \"FLAGS\" directive flag \"%s\".\n"), args[i].c_str());
799
}
800
}
801
}
802
else if(cmdbuf == "CDTEXTFILE" || cmdbuf == "CATALOG" || cmdbuf == "ISRC" ||
803
cmdbuf == "TITLE" || cmdbuf == "PERFORMER" || cmdbuf == "SONGWRITER")
804
{
805
printf(_("Unsupported CUE sheet directive: \"%s\".\n"), cmdbuf.c_str()); // FIXME, generic logger passed by pointer to constructor
806
}
807
else
808
{
809
throw MDFN_Error(0, _("Unknown CUE sheet directive \"%s\".\n"), cmdbuf.c_str());
810
}
811
} // end of CUE sheet handling
812
} // end of fgets() loop
813
814
if(active_track >= 0)
815
memcpy(&Tracks[active_track], &TmpTrack, sizeof(TmpTrack));
816
817
if(FirstTrack > LastTrack)
818
{
819
throw(MDFN_Error(0, _("No tracks found!\n")));
820
}
821
822
FirstTrack = FirstTrack;
823
NumTracks = 1 + LastTrack - FirstTrack;
824
825
int32 RunningLBA = 0;
826
int32 LastIndex = 0;
827
long FileOffset = 0;
828
829
RunningLBA -= 150;
830
Tracks[FirstTrack].pregap += 150;
831
832
for(int x = FirstTrack; x < (FirstTrack + NumTracks); x++)
833
{
834
if(!Tracks[x].fp && !Tracks[x].AReader)
835
throw MDFN_Error(0, _("Missing track %u."), x);
836
837
if(Tracks[x].DIFormat == DI_FORMAT_AUDIO)
838
Tracks[x].subq_control &= ~SUBQ_CTRLF_DATA;
839
else
840
Tracks[x].subq_control |= SUBQ_CTRLF_DATA;
841
842
if(!IsTOC) // TOC-format disc_type calculation is handled differently.
843
{
844
if(disc_type != DISC_TYPE_CD_I)
845
{
846
switch(Tracks[x].DIFormat)
847
{
848
default: break;
849
850
case DI_FORMAT_MODE2:
851
case DI_FORMAT_MODE2_FORM1:
852
case DI_FORMAT_MODE2_FORM2:
853
case DI_FORMAT_MODE2_RAW:
854
disc_type = DISC_TYPE_CD_XA;
855
break;
856
857
case DI_FORMAT_CDI_RAW:
858
disc_type = DISC_TYPE_CD_I;
859
break;
860
}
861
}
862
}
863
864
if(IsTOC)
865
{
866
RunningLBA += Tracks[x].pregap;
867
Tracks[x].LBA = RunningLBA;
868
RunningLBA += Tracks[x].sectors;
869
RunningLBA += Tracks[x].postgap;
870
}
871
else // else handle CUE sheet...
872
{
873
if(Tracks[x].FirstFileInstance)
874
{
875
LastIndex = 0;
876
FileOffset = 0;
877
}
878
879
RunningLBA += Tracks[x].pregap;
880
881
Tracks[x].pregap_dv = 0;
882
883
if(Tracks[x].index[0] != -1)
884
Tracks[x].pregap_dv = Tracks[x].index[1] - Tracks[x].index[0];
885
886
FileOffset += Tracks[x].pregap_dv * DI_Size_Table[Tracks[x].DIFormat];
887
888
RunningLBA += Tracks[x].pregap_dv;
889
890
Tracks[x].LBA = RunningLBA;
891
892
// Make sure FileOffset this is set before the call to GetSectorCount()
893
Tracks[x].FileOffset = FileOffset;
894
Tracks[x].sectors = GetSectorCount(&Tracks[x]);
895
896
if((x + 1) >= (FirstTrack + NumTracks) || Tracks[x+1].FirstFileInstance)
897
{
898
899
}
900
else
901
{
902
// Fix the sector count if we have multiple tracks per one binary image file.
903
if(Tracks[x + 1].index[0] == -1)
904
Tracks[x].sectors = Tracks[x + 1].index[1] - Tracks[x].index[1];
905
else
906
Tracks[x].sectors = Tracks[x + 1].index[0] - Tracks[x].index[1]; //Tracks[x + 1].index - Tracks[x].index;
907
}
908
909
//printf("Poo: %d %d\n", x, Tracks[x].sectors);
910
RunningLBA += Tracks[x].sectors;
911
RunningLBA += Tracks[x].postgap;
912
913
//printf("%d, %ld %d %d %d %d\n", x, FileOffset, Tracks[x].index, Tracks[x].pregap, Tracks[x].sectors, Tracks[x].LBA);
914
915
FileOffset += Tracks[x].sectors * DI_Size_Table[Tracks[x].DIFormat];
916
} // end to cue sheet handling
917
} // end to track loop
918
919
total_sectors = RunningLBA;
920
921
//
922
// Load SBI file, if present
923
//
924
if(!IsTOC)
925
{
926
char sbi_ext[4] = { 's', 'b', 'i', 0 };
927
928
if(file_ext.length() == 4 && file_ext[0] == '.')
929
{
930
for(unsigned i = 0; i < 3; i++)
931
{
932
if(file_ext[1 + i] >= 'A' && file_ext[1 + i] <= 'Z')
933
sbi_ext[i] += 'A' - 'a';
934
}
935
}
936
937
LoadSBI(MDFN_EvalFIP(base_dir, file_base + std::string(".") + std::string(sbi_ext), true).c_str());
938
}
939
940
GenerateTOC();
941
}
942
943
void CDAccess_Image::Cleanup(void)
944
{
945
for(int32 track = 0; track < 100; track++)
946
{
947
CDRFILE_TRACK_INFO *this_track = &Tracks[track];
948
949
if(this_track->FirstFileInstance)
950
{
951
if(Tracks[track].AReader)
952
{
953
delete Tracks[track].AReader;
954
Tracks[track].AReader = NULL;
955
}
956
957
if(this_track->fp)
958
{
959
delete this_track->fp;
960
this_track->fp = NULL;
961
}
962
}
963
}
964
}
965
966
CDAccess_Image::CDAccess_Image(const std::string& path, bool image_memcache) : NumTracks(0), FirstTrack(0), LastTrack(0), total_sectors(0)
967
{
968
memset(Tracks, 0, sizeof(Tracks));
969
970
try
971
{
972
ImageOpen(path, image_memcache);
973
}
974
catch(...)
975
{
976
Cleanup();
977
throw;
978
}
979
}
980
981
CDAccess_Image::~CDAccess_Image()
982
{
983
Cleanup();
984
}
985
986
void CDAccess_Image::Read_Raw_Sector(uint8 *buf, int32 lba)
987
{
988
uint8 SimuQ[0xC];
989
int32 track;
990
CDRFILE_TRACK_INFO *ct;
991
992
//
993
// Leadout synthesis
994
//
995
if(lba >= total_sectors)
996
{
997
uint8 data_synth_mode = (disc_type == DISC_TYPE_CD_XA ? 0x02 : 0x01);
998
999
switch(Tracks[LastTrack].DIFormat)
1000
{
1001
case DI_FORMAT_AUDIO:
1002
break;
1003
1004
case DI_FORMAT_MODE1_RAW:
1005
case DI_FORMAT_MODE1:
1006
data_synth_mode = 0x01;
1007
break;
1008
1009
case DI_FORMAT_MODE2_RAW:
1010
case DI_FORMAT_MODE2_FORM1:
1011
case DI_FORMAT_MODE2_FORM2:
1012
case DI_FORMAT_MODE2:
1013
case DI_FORMAT_CDI_RAW:
1014
data_synth_mode = 0x02;
1015
break;
1016
}
1017
1018
synth_leadout_sector_lba(data_synth_mode, toc, lba, buf);
1019
return;
1020
}
1021
//
1022
//
1023
//
1024
1025
memset(buf + 2352, 0, 96);
1026
track = MakeSubPQ(lba, buf + 2352);
1027
subq_deinterleave(buf + 2352, SimuQ);
1028
1029
ct = &Tracks[track];
1030
1031
//
1032
// Handle pregap and postgap reading
1033
//
1034
if(lba < (ct->LBA - ct->pregap_dv) || lba >= (ct->LBA + ct->sectors))
1035
{
1036
int32 pg_offset = lba - ct->LBA;
1037
CDRFILE_TRACK_INFO* et = ct;
1038
1039
if(pg_offset < -150)
1040
{
1041
if((Tracks[track].subq_control & SUBQ_CTRLF_DATA) && (FirstTrack < track) && !(Tracks[track - 1].subq_control & SUBQ_CTRLF_DATA))
1042
et = &Tracks[track - 1];
1043
}
1044
1045
memset(buf, 0, 2352);
1046
switch(et->DIFormat)
1047
{
1048
case DI_FORMAT_AUDIO:
1049
break;
1050
1051
case DI_FORMAT_MODE1_RAW:
1052
case DI_FORMAT_MODE1:
1053
encode_mode1_sector(lba + 150, buf);
1054
break;
1055
1056
case DI_FORMAT_MODE2_RAW:
1057
case DI_FORMAT_MODE2_FORM1:
1058
case DI_FORMAT_MODE2_FORM2:
1059
case DI_FORMAT_MODE2:
1060
case DI_FORMAT_CDI_RAW:
1061
buf[12 + 6] = 0x20;
1062
buf[12 + 10] = 0x20;
1063
encode_mode2_form2_sector(lba + 150, buf);
1064
// TODO: Zero out optional(?) checksum bytes?
1065
break;
1066
}
1067
//printf("Pre/post-gap read, LBA=%d(LBA-track_start_LBA=%d)\n", lba, lba - ct->LBA);
1068
}
1069
else
1070
{
1071
if(ct->AReader)
1072
{
1073
int16 AudioBuf[588 * 2];
1074
uint64 frames_read = ct->AReader->Read((ct->FileOffset / 4) + (lba - ct->LBA) * 588, AudioBuf, 588);
1075
1076
ct->LastSamplePos += frames_read;
1077
1078
if(frames_read > 588) // This shouldn't happen.
1079
{
1080
printf("Error: frames_read out of range: %llu\n", (unsigned long long)frames_read);
1081
frames_read = 0;
1082
}
1083
1084
if(frames_read < 588)
1085
memset((uint8 *)AudioBuf + frames_read * 2 * sizeof(int16), 0, (588 - frames_read) * 2 * sizeof(int16));
1086
1087
for(int i = 0; i < 588 * 2; i++)
1088
MDFN_en16lsb<false>(buf + i * 2, AudioBuf[i]);
1089
}
1090
else // Binary, woo.
1091
{
1092
long SeekPos = ct->FileOffset;
1093
long LBARelPos = lba - ct->LBA;
1094
1095
SeekPos += LBARelPos * DI_Size_Table[ct->DIFormat];
1096
1097
if(ct->SubchannelMode)
1098
SeekPos += 96 * (lba - ct->LBA);
1099
1100
ct->fp->seek(SeekPos, SEEK_SET);
1101
1102
switch(ct->DIFormat)
1103
{
1104
case DI_FORMAT_AUDIO:
1105
ct->fp->read(buf, 2352);
1106
1107
if(ct->RawAudioMSBFirst)
1108
Endian_A16_Swap(buf, 588 * 2);
1109
break;
1110
1111
case DI_FORMAT_MODE1:
1112
ct->fp->read(buf + 12 + 3 + 1, 2048);
1113
encode_mode1_sector(lba + 150, buf);
1114
break;
1115
1116
case DI_FORMAT_MODE1_RAW:
1117
case DI_FORMAT_MODE2_RAW:
1118
case DI_FORMAT_CDI_RAW:
1119
ct->fp->read(buf, 2352);
1120
break;
1121
1122
case DI_FORMAT_MODE2:
1123
ct->fp->read(buf + 16, 2336);
1124
encode_mode2_sector(lba + 150, buf);
1125
break;
1126
1127
1128
// FIXME: M2F1, M2F2, does sub-header come before or after user data(standards say before, but I wonder
1129
// about cdrdao...).
1130
case DI_FORMAT_MODE2_FORM1:
1131
ct->fp->read(buf + 24, 2048);
1132
//encode_mode2_form1_sector(lba + 150, buf);
1133
break;
1134
1135
case DI_FORMAT_MODE2_FORM2:
1136
ct->fp->read(buf + 24, 2324);
1137
//encode_mode2_form2_sector(lba + 150, buf);
1138
break;
1139
1140
}
1141
1142
if(ct->SubchannelMode)
1143
ct->fp->read(buf + 2352, 96);
1144
}
1145
} // end if audible part of audio track read.
1146
}
1147
1148
bool CDAccess_Image::Fast_Read_Raw_PW_TSRE(uint8* pwbuf, int32 lba) const noexcept
1149
{
1150
int32 track;
1151
1152
if(lba >= total_sectors)
1153
{
1154
subpw_synth_leadout_lba(toc, lba, pwbuf);
1155
return(true);
1156
}
1157
1158
memset(pwbuf, 0, 96);
1159
try
1160
{
1161
track = MakeSubPQ(lba, pwbuf);
1162
}
1163
catch(...)
1164
{
1165
return(false);
1166
}
1167
1168
//
1169
// If TOC+BIN has embedded subchannel data, we can't fast-read(synthesize) it...
1170
//
1171
if(Tracks[track].SubchannelMode && lba >= (Tracks[track].LBA - Tracks[track].pregap_dv) && (lba < Tracks[track].LBA + Tracks[track].sectors))
1172
return(false);
1173
1174
return(true);
1175
}
1176
1177
//
1178
// Note: this function makes use of the current contents(as in |=) in SubPWBuf.
1179
//
1180
int32 CDAccess_Image::MakeSubPQ(int32 lba, uint8 *SubPWBuf) const
1181
{
1182
uint8 buf[0xC];
1183
int32 track;
1184
uint32 lba_relative;
1185
uint32 ma, sa, fa;
1186
uint32 m, s, f;
1187
uint8 pause_or = 0x00;
1188
bool track_found = false;
1189
1190
for(track = FirstTrack; track < (FirstTrack + NumTracks); track++)
1191
{
1192
if(lba >= (Tracks[track].LBA - Tracks[track].pregap_dv - Tracks[track].pregap) && lba < (Tracks[track].LBA + Tracks[track].sectors + Tracks[track].postgap))
1193
{
1194
track_found = true;
1195
break;
1196
}
1197
}
1198
1199
if(!track_found)
1200
throw(MDFN_Error(0, _("Could not find track for sector %u!"), lba));
1201
1202
if(lba < Tracks[track].LBA)
1203
lba_relative = Tracks[track].LBA - 1 - lba;
1204
else
1205
lba_relative = lba - Tracks[track].LBA;
1206
1207
f = (lba_relative % 75);
1208
s = ((lba_relative / 75) % 60);
1209
m = (lba_relative / 75 / 60);
1210
1211
fa = (lba + 150) % 75;
1212
sa = ((lba + 150) / 75) % 60;
1213
ma = ((lba + 150) / 75 / 60);
1214
1215
uint8 adr = 0x1; // Q channel data encodes position
1216
uint8 control = Tracks[track].subq_control;
1217
1218
// Handle pause(D7 of interleaved subchannel byte) bit, should be set to 1 when in pregap or postgap.
1219
if((lba < Tracks[track].LBA) || (lba >= Tracks[track].LBA + Tracks[track].sectors))
1220
{
1221
//printf("pause_or = 0x80 --- %d\n", lba);
1222
pause_or = 0x80;
1223
}
1224
1225
// Handle pregap between audio->data track
1226
{
1227
int32 pg_offset = (int32)lba - Tracks[track].LBA;
1228
1229
// If we're more than 2 seconds(150 sectors) from the real "start" of the track/INDEX 01, and the track is a data track,
1230
// and the preceding track is an audio track, encode it as audio(by taking the SubQ control field from the preceding track).
1231
//
1232
// TODO: Look into how we're supposed to handle subq control field in the four combinations of track types(data/audio).
1233
//
1234
if(pg_offset < -150)
1235
{
1236
if((Tracks[track].subq_control & SUBQ_CTRLF_DATA) && (FirstTrack < track) && !(Tracks[track - 1].subq_control & SUBQ_CTRLF_DATA))
1237
{
1238
//printf("Pregap part 1 audio->data: lba=%d track_lba=%d\n", lba, Tracks[track].LBA);
1239
control = Tracks[track - 1].subq_control;
1240
}
1241
}
1242
}
1243
1244
1245
memset(buf, 0, 0xC);
1246
buf[0] = (adr << 0) | (control << 4);
1247
buf[1] = U8_to_BCD(track);
1248
1249
if(lba < Tracks[track].LBA) // Index is 00 in pregap
1250
buf[2] = U8_to_BCD(0x00);
1251
else
1252
buf[2] = U8_to_BCD(0x01);
1253
1254
// Track relative MSF address
1255
buf[3] = U8_to_BCD(m);
1256
buf[4] = U8_to_BCD(s);
1257
buf[5] = U8_to_BCD(f);
1258
1259
buf[6] = 0; // Zerroooo
1260
1261
// Absolute MSF address
1262
buf[7] = U8_to_BCD(ma);
1263
buf[8] = U8_to_BCD(sa);
1264
buf[9] = U8_to_BCD(fa);
1265
1266
subq_generate_checksum(buf);
1267
1268
if(!SubQReplaceMap.empty())
1269
{
1270
//printf("%d\n", lba);
1271
auto it = SubQReplaceMap.find(LBA_to_ABA(lba));
1272
1273
if(it != SubQReplaceMap.end())
1274
{
1275
//printf("Replace: %d\n", lba);
1276
memcpy(buf, it->second.data(), 12);
1277
}
1278
}
1279
1280
for(int i = 0; i < 96; i++)
1281
SubPWBuf[i] |= (((buf[i >> 3] >> (7 - (i & 0x7))) & 1) ? 0x40 : 0x00) | pause_or;
1282
1283
return track;
1284
}
1285
1286
void CDAccess_Image::Read_TOC(TOC *rtoc)
1287
{
1288
*rtoc = toc;
1289
}
1290
1291
void CDAccess_Image::GenerateTOC(void)
1292
{
1293
toc.Clear();
1294
1295
toc.first_track = FirstTrack;
1296
toc.last_track = FirstTrack + NumTracks - 1;
1297
toc.disc_type = disc_type;
1298
1299
for(int i = FirstTrack; i < FirstTrack + NumTracks; i++)
1300
{
1301
if(Tracks[i].DIFormat == DI_FORMAT_CDI_RAW)
1302
{
1303
toc.first_track = std::min<int>(99, i + 1);
1304
toc.last_track = std::max<int>(toc.first_track, toc.last_track);
1305
}
1306
1307
toc.tracks[i].lba = Tracks[i].LBA;
1308
toc.tracks[i].adr = ADR_CURPOS;
1309
toc.tracks[i].control = Tracks[i].subq_control;
1310
toc.tracks[i].valid = true;
1311
}
1312
1313
toc.tracks[100].lba = total_sectors;
1314
toc.tracks[100].adr = ADR_CURPOS;
1315
toc.tracks[100].control = Tracks[FirstTrack + NumTracks - 1].subq_control & 0x4;
1316
toc.tracks[100].valid = true;
1317
}
1318
1319
1320
1321