From 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Sun, 8 Nov 2015 12:56:12 +0100 Subject: Rename *.c -> *.cpp in our source (nw) --- src/lib/util/chdcd.cpp | 1339 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1339 insertions(+) create mode 100644 src/lib/util/chdcd.cpp (limited to 'src/lib/util/chdcd.cpp') diff --git a/src/lib/util/chdcd.cpp b/src/lib/util/chdcd.cpp new file mode 100644 index 00000000000..e267c613d1d --- /dev/null +++ b/src/lib/util/chdcd.cpp @@ -0,0 +1,1339 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/*************************************************************************** + + TOC parser for CHD compression frontend + Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi + +***************************************************************************/ + +#include +#include +#include +#include "osdcore.h" +#include "chd.h" +#include "chdcd.h" +#include "corefile.h" + + + +/*************************************************************************** + CONSTANTS & DEFINES +***************************************************************************/ + +/** + * @def TOKENIZE(); + * + * @brief A macro that defines tokenize. + * + * @param linebuffer The linebuffer. + * @param i Zero-based index of the. + * @param sizeof(linebuffer) The sizeof(linebuffer) + * @param token The token. + * @param sizeof(token) The sizeof(token) + */ + +#define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) ); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/** @brief The linebuffer[ 512]. */ +static char linebuffer[512]; + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/** + * @fn static std::string get_file_path(std::string &path) + * + * @brief Gets file path. + * + * @param [in,out] path Full pathname of the file. + * + * @return The file path. + */ + +static std::string get_file_path(std::string &path) +{ + int pos = path.find_last_of('\\'); + if (pos!=-1) { + path = path.substr(0,pos+1); + } else { + pos = path.find_last_of('/'); + path = path.substr(0,pos+1); + } + return path; +} +/*------------------------------------------------- + get_file_size - get the size of a file +-------------------------------------------------*/ + +/** + * @fn static UINT64 get_file_size(const char *filename) + * + * @brief Gets file size. + * + * @param filename Filename of the file. + * + * @return The file size. + */ + +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 +-------------------------------------------------*/ + +/** + * @fn static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize ) + * + * @brief Tokenizes. + * + * @param linebuffer The linebuffer. + * @param i Zero-based index of the. + * @param linebuffersize The linebuffersize. + * @param [in,out] token If non-null, the token. + * @param tokensize The tokensize. + * + * @return An int. + */ + +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 +-------------------------------------------------*/ + +/** + * @fn static int msf_to_frames( char *token ) + * + * @brief Msf to frames. + * + * @param [in,out] token If non-null, the token. + * + * @return An int. + */ + +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. +-------------------------------------------------*/ + +/** + * @fn static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) + * + * @brief Parse WAV sample. + * + * @param filename Filename of the file. + * @param [in,out] dataoffs If non-null, the dataoffs. + * + * @return An UINT32. + */ + +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) + { + printf("ERROR: could not open (%s)\n", filename); + 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) + { + osd_close(file); + printf("ERROR: unexpected RIFF offset %lu (%s)\n", offset, filename); + return 0; + } + if (memcmp(&buf[0], "RIFF", 4) != 0) + { + osd_close(file); + printf("ERROR: could not find RIFF header (%s)\n", filename); + return 0; + } + + /* get the total size */ + osd_read(file, &filesize, offset, 4, &actual); + offset += actual; + if (offset < 8) + { + osd_close(file); + printf("ERROR: unexpected size offset %lu (%s)\n", offset, filename); + 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) + { + osd_close(file); + printf("ERROR: unexpected WAVE offset %lu (%s)\n", offset, filename); + return 0; + } + if (memcmp(&buf[0], "WAVE", 4) != 0) + { + osd_close(file); + printf("ERROR: could not find WAVE header (%s)\n", filename); + 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) + { + osd_close(file); + printf("ERROR: could not find fmt tag (%s)\n", filename); + 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) + { + osd_close(file); + printf("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, filename); + return 0; + } + + /* number of channels -- only stereo is supported */ + osd_read(file, &temp16, offset, 2, &actual); + offset += actual; + temp16 = LITTLE_ENDIANIZE_INT16(temp16); + if (temp16 != 2) + { + osd_close(file); + printf("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, filename); + return 0; + } + + /* sample rate */ + osd_read(file, &rate, offset, 4, &actual); + offset += actual; + rate = LITTLE_ENDIANIZE_INT32(rate); + if (rate != 44100) + { + osd_close(file); + printf("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, filename); + 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; + bits = LITTLE_ENDIANIZE_INT16(bits); + if (bits != 16) + { + osd_close(file); + printf("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, filename); + 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) + { + osd_close(file); + printf("ERROR: could not find data tag (%s)\n", filename); + return 0; + } + } + + osd_close(file); + + /* if there was a 0 length data block, we're done */ + if (length == 0) + { + printf("ERROR: empty data block (%s)\n", filename); + return 0; + } + + *dataoffs = offset; + + return length; +} + +/** + * @fn UINT16 read_uint16(FILE *infile) + * + * @brief Reads uint 16. + * + * @param [in,out] infile If non-null, the infile. + * + * @return The uint 16. + */ + +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; +} + +/** + * @fn UINT32 read_uint32(FILE *infile) + * + * @brief Reads uint 32. + * + * @param [in,out] infile If non-null, the infile. + * + * @return The uint 32. + */ + +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; +} + +/** + * @fn UINT64 read_uint64(FILE *infile) + * + * @brief Reads uint 64. + * + * @param [in,out] infile If non-null, the infile. + * + * @return The uint 64. + */ + +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 +-------------------------------------------------*/ + +/** + * @fn chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * + * @brief Chdcd parse nero. + * + * @param tocfname The tocfname. + * @param [in,out] outtoc The outtoc. + * @param [in,out] outinfo The outinfo. + * + * @return A chd_error. + */ + +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; + + std::string path = std::string(tocfname); + + infile = fopen(tocfname, "rb"); + 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_UNSUPPORTED_VERSION; + } + + 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_UNSUPPORTED_FORMAT; + } + +// 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, 20, SEEK_CUR); + + fread(&start, 1, 1, infile); + fread(&end, 1, 1, infile); + +// printf("Start track %d End track: %d\n", start, end); + + outtoc.numtrks = (end-start) + 1; + + offset = 0; + for (track = start; track <= end; track++) + { + UINT32 size, mode; + UINT64 index0, index1, track_end; + + fseek(infile, 12, SEEK_CUR); // skip ISRC code + size = read_uint16(infile); + mode = read_uint16(infile); + fseek(infile, 2, SEEK_CUR); + index0 = read_uint64(infile); + index1 = read_uint64(infile); + track_end = read_uint64(infile); + +// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx track_end %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, track_end, (UINT32)(index1-index0)/size, (UINT32)(track_end-index1)/size); + outinfo.track[track-1].fname.assign(tocfname); + outinfo.track[track-1].offset = offset + (UINT32)(index1-index0); + outinfo.track[track-1].idx0offs = 0; + outinfo.track[track-1].idx1offs = 0; + + switch (mode) + { + case 0x0000: // 2048 byte data + outtoc.tracks[track-1].trktype = CD_TRACK_MODE1; + outinfo.track[track-1].swap = false; + break; + + case 0x0300: // Mode 2 Form 1 + printf("ERROR: Mode 2 Form 1 tracks not supported\n"); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + + case 0x0500: // raw data + printf("ERROR: Raw data tracks not supported\n"); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + + case 0x0600: // 2352 byte mode 2 raw + outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW; + outinfo.track[track-1].swap = false; + break; + + case 0x0700: // 2352 byte audio + outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO; + outinfo.track[track-1].swap = true; + break; + + case 0x0f00: // raw data with sub-channel + printf("ERROR: Raw data tracks with sub-channel not supported\n"); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + + case 0x1000: // audio with sub-channel + printf("ERROR: Audio tracks with sub-channel not supported\n"); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + + case 0x1100: // raw Mode 2 Form 1 with sub-channel + printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n"); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + + default: + printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode); + fclose(infile); + return CHDERR_UNSUPPORTED_FORMAT; + } + + 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)(track_end-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)track_end-index1; + } + } + + if (!memcmp(buffer, "END!", 4)) + { + done = 1; + } + else + { + chain_offs += chunk_size + 8; + } + } + + fclose(infile); + + return CHDERR_NONE; +} + +/*------------------------------------------------- + chdcd_parse_iso - parse a .ISO file +-------------------------------------------------*/ + +/** + * @fn chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * + * @brief Chdcd parse ISO. + * + * @param tocfname The tocfname. + * @param [in,out] outtoc The outtoc. + * @param [in,out] outinfo The outinfo. + * + * @return A chd_error. + */ + +chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +{ + FILE *infile; + std::string path = std::string(tocfname); + + infile = fopen(tocfname, "rb"); + path = get_file_path(path); + + if (infile == (FILE *)NULL) + { + return CHDERR_FILE_NOT_FOUND; + } + + /* clear structures */ + memset(&outtoc, 0, sizeof(outtoc)); + outinfo.reset(); + + fseek(infile, 0, SEEK_END); + long size = ftell(infile); + fclose(infile); + + + outtoc.numtrks = 1; + + outinfo.track[0].fname = tocfname; + outinfo.track[0].offset = 0; + outinfo.track[0].idx0offs = 0; + outinfo.track[0].idx1offs = 0; + + if ((size % 2048)==0 ) { + outtoc.tracks[0].trktype = CD_TRACK_MODE1; + outtoc.tracks[0].frames = size / 2048; + outtoc.tracks[0].datasize = 2048; + outinfo.track[0].swap = false; + } else if ((size % 2352)==0 ) { + // 2352 byte mode 2 raw + outtoc.tracks[0].trktype = CD_TRACK_MODE2_RAW; + outtoc.tracks[0].frames = size / 2352; + outtoc.tracks[0].datasize = 2352; + outinfo.track[0].swap = false; + } else { + printf("ERROR: Unrecognized track type\n"); + return CHDERR_UNSUPPORTED_FORMAT; + } + + outtoc.tracks[0].subtype = CD_SUB_NONE; + outtoc.tracks[0].subsize = 0; + + outtoc.tracks[0].pregap = 0; + + outtoc.tracks[0].postgap = 0; + outtoc.tracks[0].pgtype = 0; + outtoc.tracks[0].pgsub = CD_SUB_NONE; + outtoc.tracks[0].pgdatasize = 0; + outtoc.tracks[0].pgsubsize = 0; + outtoc.tracks[0].padframes = 0; + + + return CHDERR_NONE; +} + +/*------------------------------------------------- + chdcd_parse_gdi - parse a Sega GD-ROM rip +-------------------------------------------------*/ + +/** + * @fn static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * + * @brief Chdcd parse GDI. + * + * @param tocfname The tocfname. + * @param [in,out] outtoc The outtoc. + * @param [in,out] outinfo The outinfo. + * + * @return A chd_error. + */ + +static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +{ + FILE *infile; + int i, numtracks; + + std::string path = std::string(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