/*************************************************************************** TOC parser for CHD compression frontend Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. ***************************************************************************/ #include #include #include "osdcore.h" #include "chd.h" #include "chdcd.h" #include "corefile.h" /*************************************************************************** CONSTANTS & DEFINES ***************************************************************************/ #define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) ); /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ static char linebuffer[512]; /*************************************************************************** IMPLEMENTATION ***************************************************************************/ static astring get_file_path(astring &path) { int pos = path.rchr( 0, '\\'); if (pos!=-1) { path = path.substr(0,pos+1); } else { pos = path.rchr( 0, '/'); path = path.substr(0,pos+1); } return path; } /*------------------------------------------------- get_file_size - get the size of a file -------------------------------------------------*/ static UINT64 get_file_size(const char *filename) { osd_file *file; UINT64 filesize = 0; file_error filerr; filerr = osd_open(filename, OPEN_FLAG_READ, &file, &filesize); if (filerr == FILERR_NONE) osd_close(file); return filesize; } /*------------------------------------------------- tokenize - get a token from the line buffer -------------------------------------------------*/ static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize ) { int j = 0; int singlequote = 0; int doublequote = 0; while ((i < linebuffersize) && isspace((UINT8)linebuffer[i])) { i++; } while ((i < linebuffersize) && (j < tokensize)) { if (!singlequote && linebuffer[i] == '"' ) { doublequote = !doublequote; } else if (!doublequote && linebuffer[i] == '\'') { singlequote = !singlequote; } else if (!singlequote && !doublequote && isspace((UINT8)linebuffer[i])) { break; } else { token[j] = linebuffer[i]; j++; } i++; } token[j] = '\0'; return i; } /*------------------------------------------------- msf_to_frames - convert m:s:f into a number of frames -------------------------------------------------*/ static int msf_to_frames( char *token ) { int m = 0; int s = 0; int f = 0; if( sscanf( token, "%d:%d:%d", &m, &s, &f ) == 1 ) { f = m; } else { /* convert to just frames */ s += (m * 60); f += (s * 75); } return f; } /*------------------------------------------------- parse_wav_sample - takes a .WAV file, verifies that the file is 16 bits, and returns the length in bytes of the data and the offset in bytes to where the data starts in the file. -------------------------------------------------*/ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) { unsigned long offset = 0; UINT32 length, rate, filesize; UINT16 bits, temp16; char buf[32]; osd_file *file; file_error filerr; UINT64 fsize = 0; UINT32 actual; filerr = osd_open(filename, OPEN_FLAG_READ, &file, &fsize); if (filerr != FILERR_NONE) { osd_close(file); return 0; } /* read the core header and make sure it's a WAVE file */ osd_read(file, buf, 0, 4, &actual); offset += actual; if (offset < 4) return 0; if (memcmp(&buf[0], "RIFF", 4) != 0) return 0; /* get the total size */ osd_read(file, &filesize, offset, 4, &actual); offset += actual; if (offset < 8) return 0; filesize = LITTLE_ENDIANIZE_INT32(filesize); /* read the RIFF file type and make sure it's a WAVE file */ osd_read(file, buf, offset, 4, &actual); offset += actual; if (offset < 12) return 0; if (memcmp(&buf[0], "WAVE", 4) != 0) return 0; /* seek until we find a format tag */ while (1) { osd_read(file, buf, offset, 4, &actual); offset += actual; osd_read(file, &length, offset, 4, &actual); offset += actual; length = LITTLE_ENDIANIZE_INT32(length); if (memcmp(&buf[0], "fmt ", 4) == 0) break; /* seek to the next block */ offset += length; if (offset >= filesize) return 0; } /* read the format -- make sure it is PCM */ osd_read(file, &temp16, offset, 2, &actual); offset += actual; temp16 = LITTLE_ENDIANIZE_INT16(temp16); if (temp16 != 1) return 0; /* number of channels -- only mono is supported */ osd_read(file, &temp16, offset, 2, &actual); offset += actual; temp16 = LITTLE_ENDIANIZE_INT16(temp16); if (temp16 != 2) return 0; /* sample rate */ osd_read(file, &rate, offset, 4, &actual); offset += actual; rate = LITTLE_ENDIANIZE_INT32(rate); if (rate != 44100) return 0; /* bytes/second and block alignment are ignored */ osd_read(file, buf, offset, 6, &actual); offset += actual; /* bits/sample */ osd_read(file, &bits, offset, 2, &actual); offset += actual; if (bits != 16) return 0; /* seek past any extra data */ offset += length - 16; /* seek until we find a data tag */ while (1) { osd_read(file, buf, offset, 4, &actual); offset += actual; osd_read(file, &length, offset, 4, &actual); offset += actual; length = LITTLE_ENDIANIZE_INT32(length); if (memcmp(&buf[0], "data", 4) == 0) break; /* seek to the next block */ offset += length; if (offset >= filesize) return 0; } /* if there was a 0 length data block, we're done */ if (length == 0) return 0; osd_close(file); *dataoffs = offset; return length; } UINT16 read_uint16(FILE *infile) { UINT16 res = 0; unsigned char buffer[2]; fread(buffer, 2, 1, infile); res = buffer[1] | buffer[0]<<8; return res; } UINT32 read_uint32(FILE *infile) { UINT32 res = 0; unsigned char buffer[4]; fread(buffer, 4, 1, infile); res = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24; return res; } UINT64 read_uint64(FILE *infile) { UINT64 res0 = U64(0), res1 = U64(0); UINT64 res; unsigned char buffer[8]; fread(buffer, 8, 1, infile); res0 = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24; res1 = buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24; res = res0<<32 | res1; return res; } /*------------------------------------------------- chdcd_parse_nero - parse a Nero .NRG file -------------------------------------------------*/ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { FILE *infile; unsigned char buffer[12]; UINT32 chain_offs, chunk_size; int done = 0; astring path = astring(tocfname); infile = fopen(tocfname, "rt"); path = get_file_path(path); if (infile == (FILE *)NULL) { return CHDERR_FILE_NOT_FOUND; } /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); // seek to 12 bytes before the end fseek(infile, -12, SEEK_END); fread(buffer, 12, 1, infile); if (memcmp(buffer, "NER5", 4)) { printf("ERROR: Not a Nero 5.5 or later image!\n"); fclose(infile); return CHDERR_FILE_NOT_FOUND; } chain_offs = buffer[11] | (buffer[10]<<8) | (buffer[9]<<16) | (buffer[8]<<24); if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0)) { printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it."); fclose(infile); return CHDERR_FILE_NOT_FOUND; } // printf("NER5 detected, chain offset: %x\n", chain_offs); while (!done) { UINT32 offset; UINT8 start, end; int track; fseek(infile, chain_offs, SEEK_SET); fread(buffer, 8, 1, infile); chunk_size = (buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24); // printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size); // we want the DAOX chunk, which has the TOC information if (!memcmp(buffer, "DAOX", 4)) { // skip second chunk size and UPC code fseek(infile, 16, SEEK_CUR); read_uint32(infile); fread(&start, 1, 1, infile); fread(&end, 1, 1, infile); // printf("TOC type: %08x. Start track %d End track: %d\n", toc_type, start, end); outtoc.numtrks = (end-start) + 1; offset = 0; for (track = start; track <= end; track++) { UINT32 size, mode; UINT64 index0, index1, index2; fseek(infile, 12, SEEK_CUR); // skip ISRC code size = read_uint16(infile); mode = read_uint32(infile); index0 = read_uint64(infile); index1 = read_uint64(infile); index2 = read_uint64(infile); // 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); outinfo.track[track-1].fname.cpy(path).cat(tocfname); outinfo.track[track-1].offset = offset + (UINT32)(index1-index0); outinfo.track[track-1].idx0offs = 0; outinfo.track[track-1].idx1offs = 0; switch (mode>>24) { case 0x00: // 2048 byte data outtoc.tracks[track-1].trktype = CD_TRACK_MODE1; outinfo.track[track-1].swap = false; break; case 0x06: // 2352 byte mode 2 raw outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW; outinfo.track[track-1].swap = false; break; case 0x07: // 2352 byte audio outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO; outinfo.track[track-1].swap = true; break; default: printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode>>24); break; } outtoc.tracks[track-1].datasize = size; outtoc.tracks[track-1].subtype = CD_SUB_NONE; outtoc.tracks[track-1].subsize = 0; outtoc.tracks[track-1].pregap = (UINT32)(index1-index0)/size; outtoc.tracks[track-1].frames = (UINT32)(index2-index1)/size; outtoc.tracks[track-1].postgap = 0; outtoc.tracks[track-1].pgtype = 0; outtoc.tracks[track-1].pgsub = CD_SUB_NONE; outtoc.tracks[track-1].pgdatasize = 0; outtoc.tracks[track-1].pgsubsize = 0; outtoc.tracks[track-1].padframes = 0; offset += (UINT32)index2-index1; } } if (!memcmp(buffer, "END!", 4)) { done = 1; } else { chain_offs += chunk_size + 8; } } fclose(infile); return CHDERR_NONE; } /*------------------------------------------------- chdcd_parse_gdi - parse a Sega GD-ROM rip -------------------------------------------------*/ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { FILE *infile; int i, numtracks; astring path = astring(tocfname); infile = fopen(tocfname, "rt"); path = get_file_path(path); if (infile == (FILE *)NULL) { return CHDERR_FILE_NOT_FOUND; } /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); outtoc.flags = CD_FLAG_GDROM; fgets(linebuffer,511,infile); numtracks=atoi(linebuffer); for(i=0;i