1
/***************************************************************************
3
TOC parser for CHD compression frontend
4
Handles CDRDAO .toc, CDRWIN .cue, and Sega GDROM .gdi
6
Copyright Nicola Salmoria and the MAME Team.
7
Visit http://mamedev.org for licensing and usage restrictions.
9
***************************************************************************/
20
/***************************************************************************
22
***************************************************************************/
24
#define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) );
28
/***************************************************************************
30
***************************************************************************/
32
static char linebuffer[512];
36
/***************************************************************************
38
***************************************************************************/
40
/*-------------------------------------------------
41
get_file_size - get the size of a file
42
-------------------------------------------------*/
44
static UINT64 get_file_size(const char *filename)
50
filerr = osd_open(filename, OPEN_FLAG_READ, &file, &filesize);
51
if (filerr == FILERR_NONE)
58
/*-------------------------------------------------
59
tokenize - get a token from the line buffer
60
-------------------------------------------------*/
62
static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
68
while ((i < linebuffersize) && isspace((UINT8)linebuffer[i]))
73
while ((i < linebuffersize) && (j < tokensize))
75
if (!singlequote && linebuffer[i] == '"' )
77
doublequote = !doublequote;
79
else if (!doublequote && linebuffer[i] == '\'')
81
singlequote = !singlequote;
83
else if (!singlequote && !doublequote && isspace((UINT8)linebuffer[i]))
89
token[j] = linebuffer[i];
102
/*-------------------------------------------------
103
msf_to_frames - convert m:s:f into a number of frames
104
-------------------------------------------------*/
106
static int msf_to_frames( char *token )
112
if( sscanf( token, "%d:%d:%d", &m, &s, &f ) == 1 )
118
/* convert to just frames */
126
/*-------------------------------------------------
127
parse_wav_sample - takes a .WAV file, verifies
128
that the file is 16 bits, and returns the
129
length in bytes of the data and the offset in
130
bytes to where the data starts in the file.
131
-------------------------------------------------*/
132
static UINT32 parse_wav_sample(char *filename, UINT32 *dataoffs)
134
unsigned long offset = 0;
135
UINT32 length, rate, filesize;
143
filerr = osd_open(filename, OPEN_FLAG_READ, &file, &fsize);
144
if (filerr != FILERR_NONE)
150
/* read the core header and make sure it's a WAVE file */
151
osd_read(file, buf, 0, 4, &actual);
155
if (memcmp(&buf[0], "RIFF", 4) != 0)
158
/* get the total size */
159
osd_read(file, &filesize, offset, 4, &actual);
163
filesize = LITTLE_ENDIANIZE_INT32(filesize);
165
/* read the RIFF file type and make sure it's a WAVE file */
166
osd_read(file, buf, offset, 4, &actual);
170
if (memcmp(&buf[0], "WAVE", 4) != 0)
173
/* seek until we find a format tag */
176
osd_read(file, buf, offset, 4, &actual);
178
osd_read(file, &length, offset, 4, &actual);
180
length = LITTLE_ENDIANIZE_INT32(length);
181
if (memcmp(&buf[0], "fmt ", 4) == 0)
184
/* seek to the next block */
186
if (offset >= filesize)
190
/* read the format -- make sure it is PCM */
191
osd_read(file, &temp16, offset, 2, &actual);
193
temp16 = LITTLE_ENDIANIZE_INT16(temp16);
197
/* number of channels -- only mono is supported */
198
osd_read(file, &temp16, offset, 2, &actual);
200
temp16 = LITTLE_ENDIANIZE_INT16(temp16);
205
osd_read(file, &rate, offset, 4, &actual);
207
rate = LITTLE_ENDIANIZE_INT32(rate);
211
/* bytes/second and block alignment are ignored */
212
osd_read(file, buf, offset, 6, &actual);
216
osd_read(file, &bits, offset, 2, &actual);
221
/* seek past any extra data */
222
offset += length - 16;
224
/* seek until we find a data tag */
227
osd_read(file, buf, offset, 4, &actual);
229
osd_read(file, &length, offset, 4, &actual);
231
length = LITTLE_ENDIANIZE_INT32(length);
232
if (memcmp(&buf[0], "data", 4) == 0)
235
/* seek to the next block */
237
if (offset >= filesize)
241
/* if there was a 0 length data block, we're done */
252
UINT16 read_uint16(FILE *infile)
255
unsigned char buffer[2];
257
fread(buffer, 2, 1, infile);
259
res = buffer[1] | buffer[0]<<8;
264
UINT32 read_uint32(FILE *infile)
267
unsigned char buffer[4];
269
fread(buffer, 4, 1, infile);
271
res = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
276
UINT64 read_uint64(FILE *infile)
278
UINT64 res0 = U64(0), res1 = U64(0);
280
unsigned char buffer[8];
282
fread(buffer, 8, 1, infile);
284
res0 = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
285
res1 = buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24;
287
res = res0<<32 | res1;
292
/*-------------------------------------------------
293
chdcd_parse_toc - parse a CDRWin format CUE file
294
-------------------------------------------------*/
296
chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
299
unsigned char buffer[12];
300
UINT32 chain_offs, chunk_size;
303
infile = fopen(tocfname, "rt");
305
if (infile == (FILE *)NULL)
307
return CHDERR_FILE_NOT_FOUND;
310
/* clear structures */
311
memset(outtoc, 0, sizeof(cdrom_toc));
312
memset(outinfo, 0, sizeof(chdcd_track_input_info));
314
// seek to 12 bytes before the end
315
fseek(infile, -12, SEEK_END);
316
fread(buffer, 12, 1, infile);
318
if (memcmp(buffer, "NER5", 4))
320
printf("ERROR: Not a Nero 5.5 or later image!\n");
322
return CHDERR_FILE_NOT_FOUND;
325
chain_offs = buffer[11] | (buffer[10]<<8) | (buffer[9]<<16) | (buffer[8]<<24);
327
if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0))
329
printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
331
return CHDERR_FILE_NOT_FOUND;
334
// printf("NER5 detected, chain offset: %x\n", chain_offs);
342
fseek(infile, chain_offs, SEEK_SET);
343
fread(buffer, 8, 1, infile);
345
chunk_size = (buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24);
347
// printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size);
349
// we want the DAOX chunk, which has the TOC information
350
if (!memcmp(buffer, "DAOX", 4))
352
// skip second chunk size and UPC code
353
fseek(infile, 16, SEEK_CUR);
356
fread(&start, 1, 1, infile);
357
fread(&end, 1, 1, infile);
359
// printf("TOC type: %08x. Start track %d End track: %d\n", toc_type, start, end);
361
outtoc->numtrks = (end-start) + 1;
364
for (track = start; track <= end; track++)
367
UINT64 index0, index1, index2;
369
fseek(infile, 12, SEEK_CUR); // skip ISRC code
370
size = read_uint16(infile);
371
mode = read_uint32(infile);
372
index0 = read_uint64(infile);
373
index1 = read_uint64(infile);
374
index2 = read_uint64(infile);
376
// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx index2 %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, index2, (UINT32)(index1-index0)/size, (UINT32)(index2-index1)/size);
378
strcpy(outinfo->fname[track-1], tocfname);
379
outinfo->offset[track-1] = offset + (UINT32)(index1-index0);
380
outinfo->idx0offs[track-1] = 0;
381
outinfo->idx1offs[track-1] = 0;
385
case 0x00: // 2048 byte data
386
outtoc->tracks[track-1].trktype = CD_TRACK_MODE1;
387
outinfo->swap[track-1] = 0;
390
case 0x06: // 2352 byte mode 2 raw
391
outtoc->tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
392
outinfo->swap[track-1] = 0;
395
case 0x07: // 2352 byte audio
396
outtoc->tracks[track-1].trktype = CD_TRACK_AUDIO;
397
outinfo->swap[track-1] = 1;
401
printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode>>24);
405
outtoc->tracks[track-1].datasize = size;
407
outtoc->tracks[track-1].subtype = CD_SUB_NONE;
408
outtoc->tracks[track-1].subsize = 0;
410
outtoc->tracks[track-1].pregap = (UINT32)(index1-index0)/size;
411
outtoc->tracks[track-1].frames = (UINT32)(index2-index1)/size;
412
outtoc->tracks[track-1].postgap = 0;
413
outtoc->tracks[track-1].pgtype = 0;
414
outtoc->tracks[track-1].pgsub = CD_SUB_NONE;
415
outtoc->tracks[track-1].pgdatasize = 0;
416
outtoc->tracks[track-1].pgsubsize = 0;
418
offset += (UINT32)index2-index1;
422
if (!memcmp(buffer, "END!", 4))
428
chain_offs += chunk_size + 8;
437
/*-------------------------------------------------
438
chdcd_parse_gdi - parse a Sega GD-ROM rip
439
-------------------------------------------------*/
441
static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
447
infile = fopen(tocfname, "rt");
449
if (infile == (FILE *)NULL)
451
return CHDERR_FILE_NOT_FOUND;
454
/* clear structures */
455
memset(outtoc, 0, sizeof(cdrom_toc));
456
memset(outinfo, 0, sizeof(chdcd_track_input_info));
459
fgets(linebuffer,511,infile);
460
numtracks=atoi(linebuffer);
462
for(i=0;i<numtracks;++i)
471
fgets(linebuffer,511,infile);
473
tok=strtok(linebuffer," ");
477
outinfo->swap[trknum]=0;
478
outinfo->offset[trknum]=0;
480
//outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
481
outtoc->tracks[trknum].datasize = 0;
482
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
483
outtoc->tracks[trknum].subsize = 0;
485
tok=strtok(NULL," ");
486
outtoc->tracks[trknum].physframeofs=atoi(tok);
488
tok=strtok(NULL," ");
491
tok=strtok(NULL," ");
494
if(trktype==4 && trksize==2352)
496
outtoc->tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
497
outtoc->tracks[trknum].datasize=2352;
499
if(trktype==4 && trksize==2048)
501
outtoc->tracks[trknum].trktype=CD_TRACK_MODE1;
502
outtoc->tracks[trknum].datasize=2048;
506
//assert(trksize==2352);
507
outtoc->tracks[trknum].trktype=CD_TRACK_AUDIO;
508
outtoc->tracks[trknum].datasize=2352;
511
tok=strtok(NULL," ");
512
strcpy(&(outinfo->fname[trknum][0]),tok);
513
sz=get_file_size(outinfo->fname[trknum]);
515
outtoc->tracks[trknum].frames=sz/trksize;
516
outtoc->tracks[trknum].extraframes=0;
520
int dif=outtoc->tracks[trknum].physframeofs-(outtoc->tracks[trknum-1].frames+outtoc->tracks[trknum-1].physframeofs);
521
outtoc->tracks[trknum-1].frames+=dif;
527
outtoc->tracks[trknum-1].extraframes=outtoc->tracks[trknum].physframeofs-(outtoc->tracks[trknum-1].frames+outtoc->tracks[trknum-1].physframeofs);
530
hunks = (outtoc->tracks[trknum].frames+CD_FRAMES_PER_HUNK - 1) / CD_FRAMES_PER_HUNK;
531
outtoc->tracks[trknum].extraframes = hunks * CD_FRAMES_PER_HUNK - outtoc->tracks[trknum].frames;
533
//chdpos+=outtoc->tracks[trknum].frames+outtoc->tracks[trknum].extraframes;
537
for(i=0;i<numtracks;++i)
539
printf("%s %d %d %d\n",outinfo->fname[i],outtoc->tracks[i].frames,outtoc->tracks[i].extraframes,outtoc->tracks[i].physframeofs);
542
/* close the input TOC */
545
/* store the number of tracks found */
546
outtoc->numtrks = numtracks;
551
/*-------------------------------------------------
552
chdcd_parse_toc - parse a CDRWin format CUE file
553
-------------------------------------------------*/
555
chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
559
static char token[128];
560
static char lastfname[128];
561
UINT32 wavlen, wavoffs;
563
infile = fopen(tocfname, "rt");
565
if (infile == (FILE *)NULL)
567
return CHDERR_FILE_NOT_FOUND;
570
/* clear structures */
571
memset(outtoc, 0, sizeof(cdrom_toc));
572
memset(outinfo, 0, sizeof(chdcd_track_input_info));
575
wavoffs = wavlen = 0;
577
while (!feof(infile))
579
/* get the next line */
580
fgets(linebuffer, 511, infile);
582
/* if EOF didn't hit, keep going */
589
if (!strcmp(token, "FILE"))
591
/* found the data file for a track */
594
/* keep the filename */
595
strncpy(lastfname, token, 128);
597
/* get the file type */
600
if (!strcmp(token, "BINARY"))
602
outinfo->swap[trknum] = 0;
604
else if (!strcmp(token, "MOTOROLA"))
606
outinfo->swap[trknum] = 1;
608
else if (!strcmp(token, "WAVE"))
610
wavlen = parse_wav_sample(lastfname, &wavoffs);
616
err = core_fopen(lastfname, OPEN_FLAG_READ, &fhand);
617
if (err != FILERR_NONE) printf("holy moley!\n");
618
else core_fclose(fhand);
620
printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname);
621
return CHDERR_FILE_NOT_FOUND;
626
printf("ERROR: Unhandled track type %s\n", token);
627
return CHDERR_FILE_NOT_FOUND;
630
else if (!strcmp(token, "TRACK"))
632
/* get the track number */
634
trknum = strtoul(token, NULL, 10) - 1;
636
/* next token on the line is the track type */
641
outtoc->tracks[trknum].trktype = CD_TRACK_AUDIO;
642
outtoc->tracks[trknum].frames = wavlen/2352;
643
outinfo->offset[trknum] = wavoffs;
644
wavoffs = wavlen = 0;
648
outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
649
outtoc->tracks[trknum].datasize = 0;
650
outinfo->offset[trknum] = 0;
652
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
653
outtoc->tracks[trknum].subsize = 0;
654
outtoc->tracks[trknum].pregap = 0;
655
outinfo->idx0offs[trknum] = -1;
656
outinfo->idx1offs[trknum] = 0;
657
strcpy(&outinfo->fname[trknum][0], lastfname); // default filename to the last one
658
// printf("trk %d: fname %s offset %d\n", trknum, &outinfo->fname[trknum][0], outinfo->offset[trknum]);
660
cdrom_convert_type_string_to_track_info(token, &outtoc->tracks[trknum]);
661
if (outtoc->tracks[trknum].datasize == 0)
663
printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
664
return CHDERR_FILE_NOT_FOUND;
667
/* next (optional) token on the line is the subcode type */
670
cdrom_convert_subtype_string_to_track_info(token, &outtoc->tracks[trknum]);
672
else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
676
/* get index number */
678
idx = strtoul(token, NULL, 10);
682
frames = msf_to_frames( token );
686
outinfo->idx0offs[trknum] = frames;
690
outinfo->idx1offs[trknum] = frames;
691
if ((outtoc->tracks[trknum].pregap == 0) && (outinfo->idx0offs[trknum] != -1))
693
outtoc->tracks[trknum].pregap = frames - outinfo->idx0offs[trknum];
697
else if (!strcmp(token, "PREGAP"))
703
frames = msf_to_frames( token );
705
outtoc->tracks[trknum].pregap = frames;
707
else if (!strcmp(token, "POSTGAP"))
713
frames = msf_to_frames( token );
715
outtoc->tracks[trknum].postgap = frames;
720
/* close the input CUE */
723
/* store the number of tracks found */
724
outtoc->numtrks = trknum + 1;
726
/* now go over the files again and set the lengths */
727
for (trknum = 0; trknum < outtoc->numtrks; trknum++)
731
// this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
732
if (outtoc->tracks[trknum].trktype == CD_TRACK_AUDIO)
734
outinfo->swap[trknum] = 1;
737
// don't do this for .WAV tracks, we already have their length and offset filled out
738
if (outinfo->offset[trknum] == 0)
740
// is this the last track?
741
if (trknum == (outtoc->numtrks-1))
743
/* if we have the same filename as the last track, do it that way */
744
if (!strcmp(&outinfo->fname[trknum][0], &outinfo->fname[trknum-1][0]))
746
tlen = get_file_size(outinfo->fname[trknum]);
749
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum-1]);
750
return CHDERR_FILE_NOT_FOUND;
752
outinfo->offset[trknum] = outinfo->offset[trknum-1] + outtoc->tracks[trknum-1].frames * (outtoc->tracks[trknum-1].datasize + outtoc->tracks[trknum-1].subsize);
753
outtoc->tracks[trknum].frames = (tlen - outinfo->offset[trknum]) / (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
755
else /* data files are different */
757
tlen = get_file_size(outinfo->fname[trknum]);
760
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum-1]);
761
return CHDERR_FILE_NOT_FOUND;
763
tlen /= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
764
outtoc->tracks[trknum].frames = tlen;
765
outinfo->offset[trknum] = 0;
770
/* if we have the same filename as the next track, do it that way */
771
if (!strcmp(&outinfo->fname[trknum][0], &outinfo->fname[trknum+1][0]))
773
outtoc->tracks[trknum].frames = outinfo->idx1offs[trknum+1] - outinfo->idx1offs[trknum];
775
if (trknum == 0) // track 0 offset is 0
777
outinfo->offset[trknum] = 0;
781
outinfo->offset[trknum] = outinfo->offset[trknum-1] + outtoc->tracks[trknum-1].frames * (outtoc->tracks[trknum-1].datasize + outtoc->tracks[trknum-1].subsize);
784
if (!outtoc->tracks[trknum].frames)
786
printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
787
return CHDERR_FILE_NOT_FOUND;
790
else /* data files are different */
792
tlen = get_file_size(outinfo->fname[trknum]);
795
printf("ERROR: couldn't find bin file [%s]\n", outinfo->fname[trknum]);
796
return CHDERR_FILE_NOT_FOUND;
798
tlen /= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
799
outtoc->tracks[trknum].frames = tlen;
800
outinfo->offset[trknum] = 0;
804
printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc->tracks[trknum].frames, outinfo->offset[trknum]);
810
/*-------------------------------------------------
811
chdcd_parse_toc - parse a CDRDAO format TOC file
812
-------------------------------------------------*/
814
chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc *outtoc, chdcd_track_input_info *outinfo)
818
static char token[128];
820
if (strstr(tocfname,".gdi"))
822
return chdcd_parse_gdi(tocfname, outtoc, outinfo);
825
if (strstr(tocfname,".cue"))
827
return chdcd_parse_cue(tocfname, outtoc, outinfo);
830
if (strstr(tocfname,".nrg"))
832
return chdcd_parse_nero(tocfname, outtoc, outinfo);
835
infile = fopen(tocfname, "rt");
837
if (infile == (FILE *)NULL)
839
return CHDERR_FILE_NOT_FOUND;
842
/* clear structures */
843
memset(outtoc, 0, sizeof(cdrom_toc));
844
memset(outinfo, 0, sizeof(chdcd_track_input_info));
848
while (!feof(infile))
850
/* get the next line */
851
fgets(linebuffer, 511, infile);
853
/* if EOF didn't hit, keep going */
860
if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
864
/* found the data file for a track */
867
/* keep the filename */
868
strncpy(&outinfo->fname[trknum][0], token, strlen(token));
870
/* get either the offset or the length */
873
if (!strcmp(token, "SWAP"))
877
outinfo->swap[trknum] = 1;
881
outinfo->swap[trknum] = 0;
886
/* it's a decimal offset, use it */
887
f = strtoul(&token[1], NULL, 10);
889
else if (isdigit((UINT8)token[0]))
891
/* convert the time to an offset */
892
f = msf_to_frames( token );
894
f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
901
outinfo->offset[trknum] = f;
905
if (isdigit((UINT8)token[0]))
907
// this could be the length or an offset from the previous field.
908
f = msf_to_frames( token );
912
if (isdigit((UINT8)token[0]))
915
f *= (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
917
outinfo->offset[trknum] += f;
919
// this is the length.
920
f = msf_to_frames( token );
923
else if( trknum == 0 && outinfo->offset[trknum] != 0 )
925
/* the 1st track might have a length with no offset */
926
f = outinfo->offset[trknum] / (outtoc->tracks[trknum].datasize + outtoc->tracks[trknum].subsize);
927
outinfo->offset[trknum] = 0;
931
/* guesstimate the track length? */
935
outtoc->tracks[trknum].frames = f;
937
else if (!strcmp(token, "TRACK"))
941
/* next token on the line is the track type */
944
outtoc->tracks[trknum].trktype = CD_TRACK_MODE1;
945
outtoc->tracks[trknum].datasize = 0;
946
outtoc->tracks[trknum].subtype = CD_SUB_NONE;
947
outtoc->tracks[trknum].subsize = 0;
949
cdrom_convert_type_string_to_track_info(token, &outtoc->tracks[trknum]);
950
if (outtoc->tracks[trknum].datasize == 0)
952
printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
953
return CHDERR_FILE_NOT_FOUND;
956
/* next (optional) token on the line is the subcode type */
959
cdrom_convert_subtype_string_to_track_info(token, &outtoc->tracks[trknum]);
961
else if (!strcmp(token, "START"))
967
frames = msf_to_frames( token );
969
outtoc->tracks[trknum].pregap = frames;
974
/* close the input TOC */
977
/* store the number of tracks found */
978
outtoc->numtrks = trknum + 1;