diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 12346a6686802..d0d03d3c3b5ba 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -73,26 +73,33 @@ void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2); ***************************************************************************/ /** @brief offset within sector. */ -const int SYNC_OFFSET = 0x000; +constexpr int SYNC_OFFSET = 0x000; /** @brief 12 bytes. */ -const int SYNC_NUM_BYTES = 12; +constexpr int SYNC_NUM_BYTES = 12; /** @brief offset within sector. */ -const int MODE_OFFSET = 0x00f; +constexpr int MODE_OFFSET = 0x00f; + +/** @brief EDC offsets */ +constexpr int EDC_MODE1_OFFSET = 16+2048; +/** @brief EDC CRC table size */ +constexpr int EDC_CRCTABLE_SIZE = 256; +/** @brief EDC polynomial as per ECMA 130 */ +constexpr uint32_t EDC_POLYNOMIAL = 0x8001801bL; /** @brief offset within sector. */ -const int ECC_P_OFFSET = 0x81c; +constexpr int ECC_P_OFFSET = 0x81c; /** @brief 2 lots of 86. */ -const int ECC_P_NUM_BYTES = 86; +constexpr int ECC_P_NUM_BYTES = 86; /** @brief 24 bytes each. */ -const int ECC_P_COMP = 24; +constexpr int ECC_P_COMP = 24; /** @brief The ECC q offset. */ -const int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES; +constexpr int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES; /** @brief 2 lots of 52. */ -const int ECC_Q_NUM_BYTES = 52; +constexpr int ECC_Q_NUM_BYTES = 52; /** @brief 43 bytes each. */ -const int ECC_Q_COMP = 43; +constexpr int ECC_Q_COMP = 43; @@ -1552,6 +1559,64 @@ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] = }; +/** + * @fn static constexpr std::array edc_crctable_gen + * + * @brief ------------------------------------------------- + * edc_crctable_gen - calculate CRC checksums as per + * ECMA 130 and return lookup table + * -------------------------------------------------. + */ + +static constexpr std::array edc_crctable_gen(void) +{ + std::array table = { 0 }; + + for (int index = 0; index < EDC_CRCTABLE_SIZE; index++) + { + uint32_t r = reverse32(index); + + for (int bit = 0; bit < 8; bit++) + { + if (r & 0x80000000L) + r = (r << 1) ^ EDC_POLYNOMIAL; + else + r <<= 1; + } + + table[index] = reverse32(r); + } + + return table; +} + + +/** + * @brief ------------------------------------------------- + * EDC_crctable - each value represents a CRC used + * in calculating the EDC for MODE1 sectors. the + * CRC polynomial is from ECMA 130 section 14.3 + * + * P(X) = (X^16 + x^15 + x^2 + 1) * (x^16 + x^2 + x + 1) + * + * where least significant parity bit (x^0) is the + * most significant bit of the checksum. the CRC + * table can be verified using these parameters in + * Rocksoft Model CRC Algorithm. + * + * Width : 4 bytes + * Poly : 0x8001801bL + * Reverse : TRUE + * + * the CRC in this table are generated at compile + * with edc_crctable_gen(). credit to cdrtools and + * Rocksoft for the code and technical details. + * -------------------------------------------------. + */ + +static const auto EDC_crctable = edc_crctable_gen(); + + //------------------------------------------------- // ecc_source_byte - return data from the sector // at the given offset, masking anything @@ -1625,6 +1690,33 @@ bool ecc_verify(const uint8_t *sector) return true; } +/** + * @fn void edc_generate(uint8_t *sector) + * + * @brief ------------------------------------------------- + * edc_generate - generate the EDC checksum for a sector, overwriting any + * existing checksum, code is only valid for MODE1 sectors + * -------------------------------------------------. + * + * @param [in,out] sector If non-null, the sector. + */ +void edc_generate(uint8_t *sector) +{ + uint32_t result = 0; + for (int byte = 0; byte < EDC_MODE1_OFFSET; byte += 4) + { + result = EDC_crctable[(result ^ sector[byte+0]) & 0xffL] ^ (result >> 8); + result = EDC_crctable[(result ^ sector[byte+1]) & 0xffL] ^ (result >> 8); + result = EDC_crctable[(result ^ sector[byte+2]) & 0xffL] ^ (result >> 8); + result = EDC_crctable[(result ^ sector[byte+3]) & 0xffL] ^ (result >> 8); + } + + sector[EDC_MODE1_OFFSET+0] = (result >> 0) & 0xff; + sector[EDC_MODE1_OFFSET+1] = (result >> 8) & 0xff; + sector[EDC_MODE1_OFFSET+2] = (result >> 16) & 0xff; + sector[EDC_MODE1_OFFSET+3] = (result >> 24) & 0xff; +} + /** * @fn void ecc_generate(uint8_t *sector) * diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h index a5b6beda806b7..1435e1518dd97 100644 --- a/src/lib/util/cdrom.h +++ b/src/lib/util/cdrom.h @@ -144,6 +144,8 @@ bool ecc_verify(const uint8_t *sector); void ecc_generate(uint8_t *sector); void ecc_clear(uint8_t *sector); +// EDC utilities +void edc_generate(uint8_t *sector); /*************************************************************************** @@ -169,6 +171,16 @@ static inline uint32_t lba_to_msf(uint32_t lba) ((f / 10) << 4) | ((f % 10) << 0); } +static constexpr inline uint32_t reverse32(uint32_t x) +{ + x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); + x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); + x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); + x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); + x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); + return x; +} + // segacd needs it like this.. investigate // Angelo also says PCE tracks often start playing at the // wrong address.. related? diff --git a/src/lib/util/chdcd.cpp b/src/lib/util/chdcd.cpp index ef013b41f48fb..cd4bf3a54c3a9 100644 --- a/src/lib/util/chdcd.cpp +++ b/src/lib/util/chdcd.cpp @@ -37,19 +37,6 @@ #define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) ); -enum gdi_area { - SINGLE_DENSITY, - HIGH_DENSITY -}; - -enum gdi_pattern { - TYPE_UNKNOWN = 0, - TYPE_I, - TYPE_II, - TYPE_III, - TYPE_III_SPLIT -}; - /*************************************************************************** GLOBAL VARIABLES @@ -1143,6 +1130,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i return CHDERR_NONE; } + /*--------------------------------------------------------------------------------------- chdcd_is_gdicue - determine if CUE contains Redump multi-CUE format for Dreamcast GDI ----------------------------------------------------------------------------------------*/ @@ -1190,6 +1178,46 @@ bool chdcd_is_gdicue(const char *tocfname) return has_rem_singledensity && has_rem_highdensity; } + +/*--------------------------------------------------------------------------------------- + gdrom_identify_pattern - Dreamcast has well-known standardised GDROM patterns +----------------------------------------------------------------------------------------*/ + +/** + * Dreamcast GDROM patterns are identified by track types and number of tracks + * + * Pattern I - (SD) DATA + AUDIO, (HD) DATA + * Pattern II - (SD) DATA + AUDIO, (HD) DATA + ... + AUDIO + * Pattern III - (SD) DATA + AUDIO, (HD) DATA + ... + DATA + * + * And a III variant when two HD DATA tracks are split by one or more AUDIO tracks. + */ + +enum gdrom_pattern gdrom_identify_pattern(const cdrom_toc *toc) +{ + if (toc->numtrks > 4 && toc->tracks[toc->numtrks-1].trktype == CD_TRACK_MODE1_RAW) + { + if (toc->tracks[toc->numtrks-2].trktype == CD_TRACK_AUDIO) + return GDROM_TYPE_III_SPLIT; + else + return GDROM_TYPE_III; + } + else if (toc->numtrks > 3) + { + if (toc->tracks[toc->numtrks-1].trktype == CD_TRACK_AUDIO) + return GDROM_TYPE_II; + else + return GDROM_TYPE_III; + } + else if (toc->numtrks == 3) + { + return GDROM_TYPE_I; + } + + return GDROM_TYPE_UNKNOWN; +} + + /*----------------------------------------------------------------- chdcd_parse_gdicue - parse a Redump multi-CUE for Dreamcast GDI ------------------------------------------------------------------*/ @@ -1226,8 +1254,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac std::string lastfname; uint32_t wavlen, wavoffs; std::string path = std::string(tocfname); - enum gdi_area current_area = SINGLE_DENSITY; - enum gdi_pattern disc_pattern = TYPE_UNKNOWN; + enum gdrom_area current_area = GDROM_SINGLE_DENSITY; infile = fopen(tocfname, "rt"); path = get_file_path(path); @@ -1256,14 +1283,14 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac /* single-density area starts LBA = 0 */ if (!strncmp(linebuffer, "REM SINGLE-DENSITY AREA", 23)) { - current_area = SINGLE_DENSITY; + current_area = GDROM_SINGLE_DENSITY; continue; } /* high-density area starts LBA = 45000 */ if (!strncmp(linebuffer, "REM HIGH-DENSITY AREA", 21)) { - current_area = HIGH_DENSITY; + current_area = GDROM_HIGH_DENSITY; continue; } @@ -1518,28 +1545,6 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac } } - /* - * Dreamcast patterns are identified by track types and number of tracks - */ - if (outtoc.numtrks > 4 && outtoc.tracks[outtoc.numtrks-1].pgtype == CD_TRACK_MODE1_RAW) - { - if (outtoc.tracks[outtoc.numtrks-2].pgtype == CD_TRACK_AUDIO) - disc_pattern = TYPE_III_SPLIT; - else - disc_pattern = TYPE_III; - } - else if (outtoc.numtrks > 3) - { - if (outtoc.tracks[outtoc.numtrks-1].pgtype == CD_TRACK_AUDIO) - disc_pattern = TYPE_II; - else - disc_pattern = TYPE_III; - } - else if (outtoc.numtrks == 3) - { - disc_pattern = TYPE_I; - } - /* * Strip pregaps from Redump tracks and adjust the LBA offset to match TOSEC layout */ @@ -1582,7 +1587,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac /* * Special handling for TYPE_III_SPLIT, pregap in last track contains 75 frames audio and 150 frames data */ - if (disc_pattern == TYPE_III_SPLIT) + if (gdrom_identify_pattern(&outtoc) == GDROM_TYPE_III_SPLIT) { assert(outtoc.tracks[outtoc.numtrks-1].pregap == 225); @@ -1602,7 +1607,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac */ for (trknum = 1; trknum < outtoc.numtrks; trknum++) { - if (outtoc.tracks[trknum].multicuearea == HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == SINGLE_DENSITY) + if (outtoc.tracks[trknum].multicuearea == GDROM_HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == GDROM_SINGLE_DENSITY) { outtoc.tracks[trknum].physframeofs = 45000; int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs); diff --git a/src/lib/util/chdcd.h b/src/lib/util/chdcd.h index 65e4b943964a0..7a0c7fee6355b 100644 --- a/src/lib/util/chdcd.h +++ b/src/lib/util/chdcd.h @@ -35,4 +35,32 @@ struct chdcd_track_input_info chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo); + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum gdrom_area +{ + GDROM_SINGLE_DENSITY = 0, + GDROM_HIGH_DENSITY +}; + +enum gdrom_pattern +{ + GDROM_TYPE_UNKNOWN = 0, + GDROM_TYPE_I, + GDROM_TYPE_II, + GDROM_TYPE_III, + GDROM_TYPE_III_SPLIT +}; + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* GDROM utilities */ +enum gdrom_pattern gdrom_identify_pattern(const cdrom_toc *toc); + + #endif // MAME_UTIL_CHDCD_H diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp index 6ea7648522c7e..672572df9689a 100644 --- a/src/tools/chdman.cpp +++ b/src/tools/chdman.cpp @@ -57,6 +57,7 @@ const uint32_t TEMP_BUFFER_SIZE = 32 * 1024 * 1024; const int MODE_NORMAL = 0; const int MODE_CUEBIN = 1; const int MODE_GDI = 2; +const int MODE_GDROM_CUEBIN = 3; // command modifier #define REQUIRED "~" @@ -1322,10 +1323,11 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const const char *const quotestr = needquote ? "\"" : ""; file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, mode, size, quotestr, filename, quotestr, discoffs); } - else if (mode == MODE_CUEBIN) + else if (mode == MODE_CUEBIN || mode == MODE_GDROM_CUEBIN) { // first track specifies the file - if (tracknum == 0) + // GDROM .cue/.bin has one file per track + if (tracknum == 0 || mode == MODE_GDROM_CUEBIN) file.printf("FILE \"%s\" BINARY\n", filename); // determine submode @@ -2376,6 +2378,112 @@ static void do_extract_raw(parameters_map ¶ms) } +//------------------------------------------------- +// gdrom_convert_toc() - Redump uses .cue/.bin +// for GDROM dumps. TOSEC uses .gdi/.bin instead. +// Redump and TOSEC also have slightly different +// TOC layouts. +// +// When creating CHDs from Redump GDROM dumps we +// adjust Redumps TOC so it matches TOSEC. This +// ensures there's only one TOC layout for GDROM +// stored in CHD. The TOSEC layout. +// +// Extractcd to .cue/.bin reverts the TOC to the +// Redump layout. If we didn't do this createcd +// followed by extractcd would corrupt the GDROM. +// This means .cue/.bin is always Redump layout. +// +// In summary: +// +// GDROM in .cue/.bin - Redump TOC +// GDROM in .gdi/.bin - TOSEC TOC +// GDROM in .chd - TOSEC TOC +//------------------------------------------------- +static void gdrom_convert_toc(cdrom_toc *toc) +{ + // GDROM should have at least 3 tracks + assert(toc->numtrks > 2); + + // restore the pregaps for Redump .cue/.bin export + for (int trknum = 0; trknum < toc->numtrks; trknum++) + { + toc->tracks[trknum].multicuearea = ((trknum < 2) ? GDROM_SINGLE_DENSITY : GDROM_HIGH_DENSITY); + toc->tracks[trknum].pgtype = toc->tracks[trknum].trktype; + toc->tracks[trknum].pregap = 150; + toc->tracks[trknum].pgdatasize = 2352; + } + + // first track in each DENSITY area has 0 pregap + toc->tracks[0].pregap = 0; + toc->tracks[2].pregap = 0; + + // strip the SINGLE density leadout (45000 LBA) + toc->tracks[1].frames -= toc->tracks[1].padframes; + toc->tracks[1].padframes = 0; + + // adjust padding and frames to match Redump layout + for (int trknum = 0; trknum < toc->numtrks - 1; trknum++) + { + if (toc->tracks[trknum].pgtype == CD_TRACK_MODE1_RAW) + { + // un-pad DATA track + toc->tracks[trknum].frames -= toc->tracks[trknum].padframes; + toc->tracks[trknum].padframes = 0; + } + + if (toc->tracks[trknum].pgtype == CD_TRACK_AUDIO && toc->tracks[trknum+1].pgtype == CD_TRACK_AUDIO) + { + // un-shift consecutive AUDIO tracks + toc->tracks[trknum].splitframes = toc->tracks[trknum].pregap; + } + + if (toc->tracks[trknum].pgtype == CD_TRACK_AUDIO && toc->tracks[trknum+1].pgtype == CD_TRACK_MODE1_RAW) + { + // un-shrink AUDIO track preceding DATA track + toc->tracks[trknum].frames += toc->tracks[trknum].pregap; + } + } + + // special handling for last tracks based on GDROM pattern + switch (gdrom_identify_pattern(toc)) + { + case GDROM_TYPE_I: + // do nothing + break; + + case GDROM_TYPE_II: + // un-shrink final AUDIO track + toc->tracks[toc->numtrks-1].frames += toc->tracks[toc->numtrks-1].pregap; + break; + + case GDROM_TYPE_III: + // second last DATA track extends 225 frames into last DATA tracks 225 pregap + toc->tracks[toc->numtrks-2].frames -= 225; + toc->tracks[toc->numtrks-2].splitframes = 225; + + // grow the last track by 225 pregap (225 frames data from end of previous track) + toc->tracks[toc->numtrks-1].frames += 225; + toc->tracks[toc->numtrks-1].pregap = 225; + break; + + case GDROM_TYPE_III_SPLIT: + // second last AUDIO track extends 75 frames into last DATA tracks 225 pregap + toc->tracks[toc->numtrks-2].frames -= 225; + toc->tracks[toc->numtrks-2].padframes = 0; + toc->tracks[toc->numtrks-2].splitframes = 75; + + // grow the last track by 225 pregap (75 frames actual-zeroes, 150 frames data-zeroes with PQ and sync) + toc->tracks[toc->numtrks-1].frames += 225; + toc->tracks[toc->numtrks-1].pregap = 225; + break; + + case GDROM_TYPE_UNKNOWN: + break; + } +} + + //------------------------------------------------- // do_extract_cd - extract a CD file from a // CHD image @@ -2392,7 +2500,7 @@ static void do_extract_cd(parameters_map ¶ms) cdrom_file *cdrom = cdrom_open(&input_chd); if (cdrom == nullptr) report_error(1, "Unable to recognize CHD file as a CD"); - const cdrom_toc *toc = cdrom_get_toc(cdrom); + cdrom_toc toc = *(cdrom_get_toc(cdrom)); // verify output file doesn't exist auto output_file_str = params.find(OPTION_OUTPUT); @@ -2437,13 +2545,19 @@ static void do_extract_cd(parameters_map ¶ms) mode = MODE_GDI; } + if (mode == MODE_CUEBIN && toc.flags == CD_FLAG_GDROM) + { + mode = MODE_GDROM_CUEBIN; + gdrom_convert_toc(&toc); + } + // process output file osd_file::error filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, output_toc_file); if (filerr != osd_file::error::NONE) report_error(1, "Unable to open file (%s)", output_file_str->second->c_str()); // process output BIN file - if (mode != MODE_GDI) + if (mode != MODE_GDI && mode != MODE_GDROM_CUEBIN) { filerr = util::core_file::open(*output_bin_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, output_bin_file); if (filerr != osd_file::error::NONE) @@ -2452,29 +2566,35 @@ static void do_extract_cd(parameters_map ¶ms) // determine total frames uint64_t total_bytes = 0; - for (int tracknum = 0; tracknum < toc->numtrks; tracknum++) - total_bytes += toc->tracks[tracknum].frames * (toc->tracks[tracknum].datasize + toc->tracks[tracknum].subsize); + for (int tracknum = 0; tracknum < toc.numtrks; tracknum++) + total_bytes += toc.tracks[tracknum].frames * (toc.tracks[tracknum].datasize + toc.tracks[tracknum].subsize); // GDI must start with the # of tracks if (mode == MODE_GDI) { - output_toc_file->printf("%d\n", toc->numtrks); + output_toc_file->printf("%d\n", toc.numtrks); + } + + // GDROM .cue/.bin starts with SINGLE-DENSITY marker + if (mode == MODE_GDROM_CUEBIN) + { + output_toc_file->printf("REM SINGLE-DENSITY AREA\n"); } // iterate over tracks and copy all data uint64_t outputoffs = 0; uint32_t discoffs = 0; std::vector buffer; - for (int tracknum = 0; tracknum < toc->numtrks; tracknum++) + for (int tracknum = 0; tracknum < toc.numtrks; tracknum++) { std::string trackbin_name(basename); - if (mode == MODE_GDI) + if (mode == MODE_GDI || mode == MODE_GDROM_CUEBIN) { char temp[11]; sprintf(temp, "%02d", tracknum+1); trackbin_name.append(temp); - if (toc->tracks[tracknum].trktype == CD_TRACK_AUDIO) + if (toc.tracks[tracknum].trktype == CD_TRACK_AUDIO) trackbin_name.append(".raw"); else trackbin_name.append(".bin"); @@ -2488,12 +2608,22 @@ static void do_extract_cd(parameters_map ¶ms) outputoffs = 0; } + // GDROM .cue/.bin HIGH-DENSITY marker when area changes + if (mode == MODE_GDROM_CUEBIN && tracknum > 1 && toc.tracks[tracknum].multicuearea != toc.tracks[tracknum-1].multicuearea) + { + output_toc_file->printf("REM HIGH-DENSITY AREA\n"); + } + // output the metadata about the track to the TOC file - const cdrom_track_info &trackinfo = toc->tracks[tracknum]; + cdrom_track_info &trackinfo = toc.tracks[tracknum]; if (mode == MODE_GDI) { output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(trackbin_name)), discoffs, outputoffs); } + else if (mode == MODE_GDROM_CUEBIN) + { + output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(trackbin_name)), 0, outputoffs); + } else { output_track_metadata(mode, *output_toc_file, tracknum, trackinfo, std::string(core_filename_extract_base(*output_bin_file_str)), discoffs, outputoffs); @@ -2502,7 +2632,7 @@ static void do_extract_cd(parameters_map ¶ms) // If this is bin/cue output and the CHD contains subdata, warn the user and don't include // the subdata size in the buffer calculation. uint32_t output_frame_size = trackinfo.datasize + ((trackinfo.subtype != CD_SUB_NONE) ? trackinfo.subsize : 0); - if (trackinfo.subtype != CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI))) + if (trackinfo.subtype != CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI) || (mode == MODE_GDROM_CUEBIN))) { printf("Warning: Track %d has subcode data. bin/cue and gdi formats cannot contain subcode data and it will be omitted.\n", tracknum+1); printf(" : This may affect usage of the output image. Use bin/toc output to keep all data.\n"); @@ -2512,6 +2642,49 @@ static void do_extract_cd(parameters_map ¶ms) // resize the buffer for the track buffer.resize((TEMP_BUFFER_SIZE / output_frame_size) * output_frame_size); + // GDROM .cue/.bin embeds pregap in output file + if (mode == MODE_GDROM_CUEBIN && (trackinfo.pregap > 0) && (trackinfo.pgdatasize > 0)) + { + if ((tracknum == toc.numtrks-1) && (gdrom_identify_pattern(&toc) >= GDROM_TYPE_III)) + { + // special case, 75 frames actual-zeroes, 150 frames data-zeroes with PQ and sync + outputoffs += 75 * trackinfo.pgdatasize; + trackinfo.frames -= 225; + discoffs += 225; + + uint8_t buffer[2352]; + static const uint8_t syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}; + uint32_t physframeofs = trackinfo.physframeofs; + + for (int frame=0; frame < 150; frame++) + { + uint32_t msf = lba_to_msf(physframeofs); + memcpy(&buffer[0], syncbytes, 12); + buffer[12] = msf>>16; + buffer[13] = msf>>8; + buffer[14] = msf&0xff; + buffer[15] = 1; // mode_1 + memset(&buffer[16], 0, 2048); + edc_generate(&buffer[0]); + ecc_generate(&buffer[0]); + + output_bin_file->seek(outputoffs, SEEK_SET); + uint32_t byteswritten = output_bin_file->write(&buffer[0], 2352); + if (byteswritten != 2352) + report_error(1, "Error writing pregap-frame %d to file (%s): %s\n", frame, output_file_str->second->c_str(), chd_file::error_string(CHDERR_WRITE_ERROR)); + + outputoffs += 2352; + physframeofs++; + } + } + else + { + // normal case, pregap contains actual-zeroes + outputoffs += trackinfo.pregap * trackinfo.pgdatasize; + trackinfo.frames -= trackinfo.pregap; + } + } + // now read and output the actual data uint32_t bufferoffs = 0; uint32_t actualframes = trackinfo.frames - trackinfo.padframes; @@ -2524,7 +2697,7 @@ static void do_extract_cd(parameters_map ¶ms) // for CDRWin and GDI audio tracks must be reversed // in the case of GDI and CHD version < 5 we assuming source CHD image is GDROM so audio tracks is already reversed - if (((mode == MODE_GDI && input_chd.version() > 4) || (mode == MODE_CUEBIN)) && (trackinfo.trktype == CD_TRACK_AUDIO)) + if (((mode == MODE_GDI && input_chd.version() > 4) || (mode == MODE_CUEBIN) || (mode == MODE_GDROM_CUEBIN)) && (trackinfo.trktype == CD_TRACK_AUDIO)) for (int swapindex = 0; swapindex < trackinfo.datasize; swapindex += 2) { uint8_t swaptemp = buffer[bufferoffs + swapindex];