util/cdrom.cpp: Refactored parse_cue and parse_gdicue (should fix GitHub #12081). (#12087)

This should greatly improve data integrity when creating and extracting GD-ROM images.

* util/cdrom.cpp: Refactored parse_cue to handle GD-ROMs.
* util/cdrom.cpp: Don't discard any data from GD-ROM cue/bin input including pre-gap data.
* tools/chdman.cpp: Fixed splitframes handling.
* tools/chdman.cpp: Added warning when extracting GD-ROM CHDs to cue/bin format.
This commit is contained in:
987123879113 2024-03-07 00:15:18 +09:00 committed by GitHub
parent 7b047641fe
commit 853db181d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 196 additions and 478 deletions

View File

@ -192,7 +192,7 @@ cdrom_file::cdrom_file(std::string_view inputfile)
logofs += track.frames;
if (EXTRA_VERBOSE)
printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d\n", i+1,
printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", i+1,
track.trktype,
track.subtype,
track.datasize,
@ -206,7 +206,8 @@ cdrom_file::cdrom_file(std::string_view inputfile)
track.logframeofs,
track.physframeofs,
track.chdframeofs,
track.logframes);
track.logframes,
track.padframes);
}
// fill out dummy entries for the last track to help our search
@ -289,7 +290,7 @@ cdrom_file::cdrom_file(chd_file *_chd)
logofs += track.frames;
if (EXTRA_VERBOSE)
printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d\n", i+1,
printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n", i+1,
track.trktype,
track.subtype,
track.datasize,
@ -303,7 +304,8 @@ cdrom_file::cdrom_file(chd_file *_chd)
track.logframeofs,
track.physframeofs,
track.chdframeofs,
track.logframes);
track.logframes,
track.padframes);
}
// fill out dummy entries for the last track to help our search
@ -1412,14 +1414,14 @@ void cdrom_file::ecc_clear(uint8_t *sector)
*
* @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)
* @param linebuffer The linebuffer.
* @param i Zero-based index of the.
* @param std::size(linebuffer) The std::size(linebuffer)
* @param token The token.
* @param std::size(token) The std::size(token)
*/
#define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) );
#define TOKENIZE i = tokenize( linebuffer, i, std::size(linebuffer), token, std::size(token) );
/***************************************************************************
@ -1493,17 +1495,17 @@ uint64_t cdrom_file::get_file_size(std::string_view filename)
int cdrom_file::tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
{
int j = 0;
int singlequote = 0;
int doublequote = 0;
bool singlequote = false;
bool doublequote = false;
while ((i < linebuffersize) && isspace((uint8_t)linebuffer[i]))
{
i++;
}
while ((i < linebuffersize) && (j < tokensize))
while ((i < linebuffersize) && (j < tokensize) && (linebuffer[i] != '\0'))
{
if (!singlequote && linebuffer[i] == '"' )
if (!singlequote && linebuffer[i] == '"')
{
doublequote = !doublequote;
}
@ -2094,6 +2096,8 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
outtoc.flags = CD_FLAG_GDROM;
char linebuffer[512];
memset(linebuffer, 0, sizeof(linebuffer));
fgets(linebuffer,511,infile);
numtracks=atoi(linebuffer);
@ -2189,7 +2193,7 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
}
/*-------------------------------------------------
parse_cue - parse a CDRWin format CUE file
parse_cue - parse a .CUE file
-------------------------------------------------*/
/**
@ -2202,15 +2206,21 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
* @param [in,out] outinfo The outinfo.
*
* @return A std::error_condition.
*
* Redump multi-CUE for Dreamcast GDI:
* Dreamcast discs have two images on a single disc. The first image is SINGLE-DENSITY and the second image
* is HIGH-DENSITY. The SINGLE-DENSITY area starts 0 LBA and HIGH-DENSITY area starts 45000 LBA.
*/
std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
int i, trknum;
static char token[512];
char token[512];
std::string lastfname;
uint32_t wavlen, wavoffs;
std::string path = std::string(tocfname);
const bool is_gdrom = is_gdicue(tocfname);
enum gdi_area current_area = SINGLE_DENSITY;
FILE *infile = fopen(path.c_str(), "rt");
if (!infile)
@ -2227,11 +2237,19 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
trknum = -1;
wavoffs = wavlen = 0;
if (is_gdrom)
{
outtoc.flags = CD_FLAG_GDROM;
}
char linebuffer[512];
memset(linebuffer, 0, sizeof(linebuffer));
while (!feof(infile))
{
/* get the next line */
fgets(linebuffer, 511, infile);
if (!fgets(linebuffer, 511, infile))
break;
/* if EOF didn't hit, keep going */
if (!feof(infile))
@ -2240,7 +2258,26 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
TOKENIZE
if (!strcmp(token, "FILE"))
if (!strcmp(token, "REM"))
{
/* TODO: sessions are notated using REM commands: "REM SESSION 01" */
/* skip to actual data of REM command */
while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
i++;
if (is_gdrom && !strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
{
/* single-density area starts LBA = 0 */
current_area = SINGLE_DENSITY;
}
else if (is_gdrom && !strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
{
/* high-density area starts LBA = 45000 */
current_area = HIGH_DENSITY;
}
}
else if (!strcmp(token, "FILE"))
{
/* found the data file for a track */
TOKENIZE
@ -2285,30 +2322,37 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
/* next token on the line is the track type */
TOKENIZE
if (wavlen != 0)
{
outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
outtoc.tracks[trknum].frames = wavlen/2352;
outinfo.track[trknum].offset = wavoffs;
wavoffs = wavlen = 0;
}
else
{
outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outinfo.track[trknum].offset = 0;
}
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
outtoc.tracks[trknum].pregap = 0;
outtoc.tracks[trknum].padframes = 0;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].multicuearea = is_gdrom ? current_area : 0;
outinfo.track[trknum].idx0offs = -1;
outinfo.track[trknum].idx1offs = 0;
outinfo.track[trknum].offset = 0;
if (wavlen != 0)
{
outtoc.tracks[trknum].frames = wavlen/2352;
outinfo.track[trknum].offset = wavoffs;
wavoffs = wavlen = 0;
}
outinfo.track[trknum].fname.assign(lastfname); // default filename to the last one
// printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset);
if (EXTRA_VERBOSE)
{
if (is_gdrom)
{
printf("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea);
}
else
{
printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset);
}
}
convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
if (outtoc.tracks[trknum].datasize == 0)
@ -2346,28 +2390,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
{
outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
switch (outtoc.tracks[trknum].pgtype)
{
case CD_TRACK_MODE1:
case CD_TRACK_MODE2_FORM1:
outtoc.tracks[trknum].pgdatasize = 2048;
break;
case CD_TRACK_MODE1_RAW:
case CD_TRACK_MODE2_RAW:
case CD_TRACK_AUDIO:
outtoc.tracks[trknum].pgdatasize = 2352;
break;
case CD_TRACK_MODE2:
case CD_TRACK_MODE2_FORM_MIX:
outtoc.tracks[trknum].pgdatasize = 2336;
break;
case CD_TRACK_MODE2_FORM2:
outtoc.tracks[trknum].pgdatasize = 2324;
break;
}
outtoc.tracks[trknum].pgdatasize = outtoc.tracks[trknum].datasize;
}
else // pregap sectors not in file, but we're always using idx0ofs for track length calc now
{
@ -2482,9 +2505,67 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
}
}
}
//printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset);
}
if (is_gdrom)
{
/*
* Strip pregaps from Redump tracks and adjust the LBA offset to match TOSEC layout
*/
for (trknum = 1; trknum < outtoc.numtrks; trknum++)
{
uint32_t this_pregap = outtoc.tracks[trknum].pregap;
uint32_t this_offset = this_pregap * (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outtoc.tracks[trknum-1].frames += this_pregap;
outtoc.tracks[trknum-1].splitframes += this_pregap;
outinfo.track[trknum].offset += this_offset;
outtoc.tracks[trknum].frames -= this_pregap;
outinfo.track[trknum].idx1offs -= this_pregap;
outtoc.tracks[trknum].pregap = 0;
outtoc.tracks[trknum].pgtype = 0;
}
/*
* TOC now matches TOSEC layout, set LBA for every track with HIGH-DENSITY area @ LBA 45000
*/
for (trknum = 1; trknum < outtoc.numtrks; trknum++)
{
if (outtoc.tracks[trknum].multicuearea == HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == SINGLE_DENSITY)
{
outtoc.tracks[trknum].physframeofs = 45000;
int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
outtoc.tracks[trknum-1].frames += dif;
outtoc.tracks[trknum-1].padframes = dif;
}
else
{
outtoc.tracks[trknum].physframeofs = outtoc.tracks[trknum-1].physframeofs + outtoc.tracks[trknum-1].frames;
}
}
}
if (EXTRA_VERBOSE)
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
printf("trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, pgdatasize=%d, idx0=%d, idx1=%d, dataframes=%d\n",
trknum+1,
outtoc.tracks[trknum].frames,
outinfo.track[trknum].offset,
outtoc.tracks[trknum].padframes,
outtoc.tracks[trknum].splitframes,
outtoc.tracks[trknum].multicuearea,
outtoc.tracks[trknum].physframeofs,
outtoc.tracks[trknum].pregap,
outtoc.tracks[trknum].pgtype,
outtoc.tracks[trknum].pgdatasize,
outinfo.track[trknum].idx0offs,
outinfo.track[trknum].idx1offs,
outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
}
return std::error_condition();
}
@ -2506,6 +2587,7 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
bool cdrom_file::is_gdicue(std::string_view tocfname)
{
char token[512];
bool has_rem_singledensity = false;
bool has_rem_highdensity = false;
std::string path = std::string(tocfname);
@ -2519,15 +2601,31 @@ bool cdrom_file::is_gdicue(std::string_view tocfname)
path = get_file_path(path);
char linebuffer[512];
memset(linebuffer, 0, sizeof(linebuffer));
while (!feof(infile))
{
fgets(linebuffer, 511, infile);
if (!fgets(linebuffer, 511, infile))
break;
/* if EOF didn't hit, keep going */
if (!feof(infile))
{
has_rem_singledensity = has_rem_singledensity || !strncmp(linebuffer, "REM SINGLE-DENSITY AREA", 23);
has_rem_highdensity = has_rem_highdensity || !strncmp(linebuffer, "REM HIGH-DENSITY AREA", 21);
int i = 0;
TOKENIZE
if (!strcmp(token, "REM"))
{
/* skip to actual data of REM command */
while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
i++;
if (!strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
has_rem_singledensity = true;
else if (!strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
has_rem_highdensity = true;
}
}
}
@ -2536,413 +2634,6 @@ bool cdrom_file::is_gdicue(std::string_view tocfname)
return has_rem_singledensity && has_rem_highdensity;
}
/*-----------------------------------------------------------------
parse_gdicue - parse a Redump multi-CUE for Dreamcast GDI
------------------------------------------------------------------*/
/**
* @fn std::error_condition parse_gdicue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
*
* @brief Chdcd parse cue.
*
* @param tocfname The tocfname.
* @param [in,out] outtoc The outtoc.
* @param [in,out] outinfo The outinfo.
*
* @return A std::error_condition.
*
* Dreamcast discs have two images on a single disc. The first image is SINGLE-DENSITY and the second image
* is HIGH-DENSITY. The SINGLE-DENSITY area starts 0 LBA and HIGH-DENSITY area starts 45000 LBA.
*
* There are three Dreamcast disc patterns.
*
* Pattern I - (SD) DATA + AUDIO, (HD) DATA
* Pattern II - (SD) DATA + AUDIO, (HD) DATA + ... + AUDIO
* Pattern III - (SD) DATA + AUDIO, (HD) DATA + ... + DATA
*
* TOSEC layout is preferred and this code adjusts the TOC and INFO generated by a Redump .cue to match the
* layout from a TOSEC .gdi.
*/
std::error_condition cdrom_file::parse_gdicue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
int i, trknum;
static char token[512];
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;
FILE *infile = fopen(path.c_str(), "rt");
if (!infile)
{
return std::error_condition(errno, std::generic_category());
}
path = get_file_path(path);
/* clear structures */
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
trknum = -1;
wavoffs = wavlen = 0;
outtoc.flags = CD_FLAG_GDROM;
char linebuffer[512];
while (!feof(infile))
{
/* get the next line */
fgets(linebuffer, 511, infile);
/* if EOF didn't hit, keep going */
if (!feof(infile))
{
/* single-density area starts LBA = 0 */
if (!strncmp(linebuffer, "REM SINGLE-DENSITY AREA", 23))
{
current_area = SINGLE_DENSITY;
continue;
}
/* high-density area starts LBA = 45000 */
if (!strncmp(linebuffer, "REM HIGH-DENSITY AREA", 21))
{
current_area = HIGH_DENSITY;
continue;
}
i = 0;
TOKENIZE
if (!strcmp(token, "FILE"))
{
/* found the data file for a track */
TOKENIZE
/* keep the filename */
lastfname.assign(path).append(token);
/* get the file type */
TOKENIZE
if (!strcmp(token, "BINARY"))
{
outinfo.track[trknum+1].swap = false;
}
else if (!strcmp(token, "MOTOROLA"))
{
outinfo.track[trknum+1].swap = true;
}
else if (!strcmp(token, "WAVE"))
{
wavlen = parse_wav_sample(lastfname, &wavoffs);
if (!wavlen)
{
fclose(infile);
printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str());
return chd_file::error::INVALID_DATA;
}
}
else
{
fclose(infile);
printf("ERROR: Unhandled track type %s\n", token);
return chd_file::error::UNSUPPORTED_FORMAT;
}
}
else if (!strcmp(token, "TRACK"))
{
/* get the track number */
TOKENIZE
trknum = strtoul(token, nullptr, 10) - 1;
/* next token on the line is the track type */
TOKENIZE
if (wavlen != 0)
{
outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
outtoc.tracks[trknum].frames = wavlen/2352;
outinfo.track[trknum].offset = wavoffs;
wavoffs = wavlen = 0;
}
else
{
outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outinfo.track[trknum].offset = 0;
}
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
outtoc.tracks[trknum].pregap = 0;
outtoc.tracks[trknum].padframes = 0;
outtoc.tracks[trknum].multicuearea = current_area;
outinfo.track[trknum].idx0offs = -1;
outinfo.track[trknum].idx1offs = 0;
outinfo.track[trknum].fname.assign(lastfname); // default filename to the last one
if (EXTRA_VERBOSE)
printf("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea);
convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
if (outtoc.tracks[trknum].datasize == 0)
{
fclose(infile);
printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
return chd_file::error::UNSUPPORTED_FORMAT;
}
/* next (optional) token on the line is the subcode type */
TOKENIZE
convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
}
else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
{
int idx, frames;
/* get index number */
TOKENIZE
idx = strtoul(token, nullptr, 10);
/* get index */
TOKENIZE
frames = msf_to_frames( token );
if (idx == 0)
{
outinfo.track[trknum].idx0offs = frames;
}
else if (idx == 1)
{
outinfo.track[trknum].idx1offs = frames;
if ((outtoc.tracks[trknum].pregap == 0) && (outinfo.track[trknum].idx0offs != -1))
{
outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
switch (outtoc.tracks[trknum].pgtype)
{
case CD_TRACK_MODE1:
case CD_TRACK_MODE2_FORM1:
outtoc.tracks[trknum].pgdatasize = 2048;
break;
case CD_TRACK_MODE1_RAW:
case CD_TRACK_MODE2_RAW:
case CD_TRACK_AUDIO:
outtoc.tracks[trknum].pgdatasize = 2352;
break;
case CD_TRACK_MODE2:
case CD_TRACK_MODE2_FORM_MIX:
outtoc.tracks[trknum].pgdatasize = 2336;
break;
case CD_TRACK_MODE2_FORM2:
outtoc.tracks[trknum].pgdatasize = 2324;
break;
}
}
else // pregap sectors not in file, but we're always using idx0ofs for track length calc now
{
outinfo.track[trknum].idx0offs = frames;
}
}
}
else if (!strcmp(token, "PREGAP"))
{
int frames;
/* get index */
TOKENIZE
frames = msf_to_frames( token );
outtoc.tracks[trknum].pregap = frames;
}
else if (!strcmp(token, "POSTGAP"))
{
int frames;
/* get index */
TOKENIZE
frames = msf_to_frames( token );
outtoc.tracks[trknum].postgap = frames;
}
}
}
/* close the input CUE */
fclose(infile);
/* store the number of tracks found */
outtoc.numtrks = trknum + 1;
/* now go over the files again and set the lengths */
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
uint64_t tlen = 0;
// this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
{
outinfo.track[trknum].swap = true;
}
// don't do this for .WAV tracks, we already have their length and offset filled out
if (outinfo.track[trknum].offset == 0)
{
// is this the last track?
if (trknum == (outtoc.numtrks-1))
{
/* if we have the same filename as the last track, do it that way */
if (trknum != 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname)==0))
{
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
return std::errc::no_such_file_or_directory;
}
outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
}
else /* data files are different */
{
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
return std::errc::no_such_file_or_directory;
}
tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outtoc.tracks[trknum].frames = tlen;
outinfo.track[trknum].offset = 0;
}
}
else
{
/* if we have the same filename as the next track, do it that way */
if (outinfo.track[trknum].fname.compare(outinfo.track[trknum+1].fname)==0)
{
outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx0offs - outinfo.track[trknum].idx0offs;
if (trknum == 0) // track 0 offset is 0
{
outinfo.track[trknum].offset = 0;
}
else
{
outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
}
if (!outtoc.tracks[trknum].frames)
{
printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
return chd_file::error::INVALID_DATA;
}
}
else /* data files are different */
{
tlen = get_file_size(outinfo.track[trknum].fname);
if (tlen == 0)
{
printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str());
return std::errc::no_such_file_or_directory;
}
tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
outtoc.tracks[trknum].frames = tlen;
outinfo.track[trknum].offset = 0;
}
}
}
}
/*
* 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;
}
/*
* Special handling for TYPE_III_SPLIT, pregap in last track contains 75 frames audio and 150 frames data
*/
if (disc_pattern == TYPE_III_SPLIT)
{
assert(outtoc.tracks[outtoc.numtrks-1].pregap == 225);
// grow the AUDIO track into DATA track by 75 frames as per Pattern III
outtoc.tracks[outtoc.numtrks-2].frames += 225;
outtoc.tracks[outtoc.numtrks-2].padframes += 150;
outinfo.track[outtoc.numtrks-2].offset = 150 * (outtoc.tracks[outtoc.numtrks-2].datasize+outtoc.tracks[outtoc.numtrks-2].subsize);
outtoc.tracks[outtoc.numtrks-2].splitframes = 75;
// skip the pregap when reading the DATA track
outtoc.tracks[outtoc.numtrks-1].frames -= 225;
outinfo.track[outtoc.numtrks-1].offset += 225 * (outtoc.tracks[outtoc.numtrks-1].datasize+outtoc.tracks[outtoc.numtrks-1].subsize);
}
/*
* Set LBA for every track with HIGH-DENSITY area @ LBA 45000
*/
for (trknum = 1; trknum < outtoc.numtrks; trknum++)
{
if (outtoc.tracks[trknum].multicuearea == HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == SINGLE_DENSITY)
{
outtoc.tracks[trknum].physframeofs = 45000;
int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
outtoc.tracks[trknum-1].frames += dif;
outtoc.tracks[trknum-1].padframes = dif;
}
else
{
outtoc.tracks[trknum].physframeofs = outtoc.tracks[trknum-1].physframeofs + outtoc.tracks[trknum-1].frames;
}
}
if (EXTRA_VERBOSE)
for (trknum = 0; trknum < outtoc.numtrks; trknum++)
{
printf("trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, idx0=%d, idx1=%d, (true %d)\n",
trknum+1,
outtoc.tracks[trknum].frames,
outinfo.track[trknum].offset,
outtoc.tracks[trknum].padframes,
outtoc.tracks[trknum].splitframes,
outtoc.tracks[trknum].multicuearea,
outtoc.tracks[trknum].physframeofs,
outtoc.tracks[trknum].pregap,
outtoc.tracks[trknum].pgtype,
outinfo.track[trknum].idx0offs,
outinfo.track[trknum].idx1offs,
outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
}
return std::error_condition();
}
/*-------------------------------------------------
parse_toc - parse a CDRDAO format TOC file
-------------------------------------------------*/
@ -2961,7 +2652,7 @@ std::error_condition cdrom_file::parse_gdicue(std::string_view tocfname, toc &ou
std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
static char token[512];
char token[512];
auto pos = tocfname.rfind('.');
std::string tocfext = pos == std::string_view::npos ? std::string() : strmakelower(tocfname.substr(pos + 1));
@ -2973,10 +2664,7 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
if (tocfext == "cue")
{
if (is_gdicue(tocfname))
return parse_gdicue(tocfname, outtoc, outinfo);
else
return parse_cue(tocfname, outtoc, outinfo);
return parse_cue(tocfname, outtoc, outinfo);
}
if (tocfext == "nrg")
@ -3006,10 +2694,13 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
int trknum = -1;
char linebuffer[512];
memset(linebuffer, 0, sizeof(linebuffer));
while (!feof(infile))
{
/* get the next line */
fgets(linebuffer, 511, infile);
if (!fgets(linebuffer, 511, infile))
break;
/* if EOF didn't hit, keep going */
if (!feof(infile))

View File

@ -75,7 +75,7 @@ public:
/* fields used in CHDMAN only */
uint32_t padframes; /* number of frames of padding to add to the end of the track; needed for GDI */
uint32_t splitframes; /* number of frames to read from the next file; needed for Redump split-bin GDI */
uint32_t splitframes; /* number of frames from the next file to add to the end of the current track after padding; needed for Redump split-bin GDI */
/* fields used in MAME/MESS only */
uint32_t logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */
@ -135,7 +135,6 @@ public:
static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
static std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
static bool is_gdicue(std::string_view tocfname);
static std::error_condition parse_gdicue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
static std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
int get_last_track() const { return cdtoc.numtrks; }
int get_adr_control(int track) const { return track == 0xaa || cdtoc.tracks[track].trktype == CD_TRACK_AUDIO ? 0x10 : 0x14; }

View File

@ -404,6 +404,7 @@ public:
{
const cdrom_file::track_info &trackinfo = m_toc.tracks[tracknum];
uint64_t endoffs = startoffs + (uint64_t)(trackinfo.frames + trackinfo.extraframes) * cdrom_file::FRAME_SIZE;
if (offset >= startoffs && offset < endoffs)
{
// if we don't already have this file open, open it now
@ -420,11 +421,11 @@ public:
uint64_t bytesperframe = trackinfo.datasize + trackinfo.subsize;
uint64_t src_track_start = m_info.track[tracknum].offset;
uint64_t src_track_end = src_track_start + bytesperframe * (uint64_t)trackinfo.frames;
uint64_t pad_track_start = src_track_end - ((uint64_t)m_toc.tracks[tracknum].padframes * bytesperframe);
uint64_t split_track_start = pad_track_start - ((uint64_t)m_toc.tracks[tracknum].splitframes * bytesperframe);
uint64_t split_track_start = src_track_end - ((uint64_t)trackinfo.splitframes * bytesperframe);
uint64_t pad_track_start = split_track_start - ((uint64_t)trackinfo.padframes * bytesperframe);
// dont split when split-bin read not required
if ((uint64_t)m_toc.tracks[tracknum].splitframes == 0L)
if ((uint64_t)trackinfo.splitframes == 0L)
split_track_start = UINT64_MAX;
while (length_remaining != 0 && offset < endoffs)
@ -433,7 +434,7 @@ public:
uint64_t src_frame_start = src_track_start + ((offset - startoffs) / cdrom_file::FRAME_SIZE) * bytesperframe;
// auto-advance next track for split-bin read
if (src_frame_start == split_track_start && m_lastfile.compare(m_info.track[tracknum+1].fname)!=0)
if (src_frame_start >= split_track_start && src_frame_start < src_track_end && m_lastfile.compare(m_info.track[tracknum+1].fname)!=0)
{
m_file.reset();
m_lastfile = m_info.track[tracknum+1].fname;
@ -445,7 +446,7 @@ public:
if (src_frame_start < src_track_end)
{
// read it in, or pad if we're into the padframes
if (src_frame_start >= pad_track_start)
if (src_frame_start >= pad_track_start && src_frame_start < split_track_start)
{
memset(dest, 0, bytesperframe);
}
@ -2627,6 +2628,11 @@ static void do_extract_cd(parameters_map &params)
mode = MODE_GDI;
}
if (cdrom->is_gdrom() && (mode == MODE_CUEBIN))
{
util::stream_format(std::cout, "Warning: extracting GD-ROM CHDs as bin/cue is not fully supported and will result in an unusable CD-ROM cue file.\n");
}
// process output file
std::error_condition filerr = util::core_file::open(*output_file_str->second, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, output_toc_file);
if (filerr)
@ -2777,6 +2783,28 @@ static void do_extract_cd(parameters_map &params)
}
}
if (cdrom->is_gdrom() && mode == MODE_CUEBIN && trackinfo.padframes > 0)
{
uint32_t padframes = trackinfo.padframes;
// don't write the pad frames between the end of the single density area and start of the high density area
if (tracknum+1 < toc.numtrks && toc.tracks[tracknum+1].physframeofs == 45000)
padframes = 0;
bufferoffs = 0;
std::fill(buffer.begin(), buffer.end(), 0);
while (bufferoffs < padframes)
{
auto const [writerr, byteswritten] = write(*output_bin_file, &buffer[0], output_frame_size);
if (writerr)
report_error(1, "Error writing pad data to file (%s): %s\n", *output_file_str->second, "Write error");
bufferoffs++;
}
outputoffs += output_frame_size * padframes;
}
discoffs += trackinfo.padframes;
}