chdman: Correct processing of GDI files, add ability to extractcd to .gdi, verified perfect checksum round-tripping on .GDI files. [R. Belmont]

This commit is contained in:
R. Belmont 2012-03-04 02:17:35 +00:00
parent b89d7c94c7
commit bb993d1174
6 changed files with 289 additions and 143 deletions

View File

@ -702,11 +702,11 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
/* start with no tracks */
for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
{
int tracknum = -1, frames = 0, pregap, postgap;
int tracknum = -1, frames = 0, pregap, postgap, padframes;
char type[16], subtype[16], pgtype[16], pgsub[16];
cdrom_track_info *track;
pregap = postgap = 0;
pregap = postgap = padframes = 0;
/* fetch the metadata for this track */
err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata);
@ -723,16 +723,37 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
else
{
err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata);
if (err != CHDERR_NONE)
break;
/* parse the metadata */
type[0] = subtype[0] = 0;
pregap = postgap = 0;
if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
return CHDERR_INVALID_DATA;
if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
return CHDERR_INVALID_DATA;
track = &toc->tracks[tracknum - 1];
if (err == CHDERR_NONE)
{
/* parse the metadata */
type[0] = subtype[0] = 0;
pregap = postgap = 0;
if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
return CHDERR_INVALID_DATA;
if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
return CHDERR_INVALID_DATA;
track = &toc->tracks[tracknum - 1];
}
else
{
err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata);
if (err == CHDERR_NONE)
{
/* parse the metadata */
type[0] = subtype[0] = 0;
pregap = postgap = 0;
if (sscanf(metadata, GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9)
return CHDERR_INVALID_DATA;
if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
return CHDERR_INVALID_DATA;
track = &toc->tracks[tracknum - 1];
}
else
{
break;
}
}
}
/* extract the track type and determine the data size */
@ -749,6 +770,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
/* set the frames and extra frames data */
track->frames = frames;
track->padframes = padframes;
int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
track->extraframes = padded * CD_TRACK_PADDING - frames;
@ -769,6 +791,8 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
if (toc->numtrks > 0)
return CHDERR_NONE;
printf("toc->numtrks = %d?!\n", toc->numtrks);
/* look for old-style metadata */
dynamic_buffer oldmetadata;
err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata);
@ -806,6 +830,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
toc->tracks[i].datasize = FLIPENDIAN_INT32(toc->tracks[i].datasize);
toc->tracks[i].subsize = FLIPENDIAN_INT32(toc->tracks[i].subsize);
toc->tracks[i].frames = FLIPENDIAN_INT32(toc->tracks[i].frames);
toc->tracks[i].padframes = FLIPENDIAN_INT32(toc->tracks[i].padframes);
toc->tracks[i].extraframes = FLIPENDIAN_INT32(toc->tracks[i].extraframes);
}
}
@ -826,13 +851,25 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
/* write the metadata */
for (i = 0; i < toc->numtrks; i++)
{
astring metadata;
metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap,
cdrom_get_type_string(toc->tracks[i].pgtype), cdrom_get_subtype_string(toc->tracks[i].pgsub),
toc->tracks[i].postgap);
astring metadata;
if (!(toc->flags & CD_FLAG_GDROM))
{
metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap,
cdrom_get_type_string(toc->tracks[i].pgtype), cdrom_get_subtype_string(toc->tracks[i].pgsub),
toc->tracks[i].postgap);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
}
else
{
metadata.format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes,
toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype),
cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap);
err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
}
if (err != CHDERR_NONE)
return err;
}

View File

@ -84,7 +84,7 @@ enum
CD_SUB_NONE /* no subcode data stored */
};
#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
/***************************************************************************
TYPE DEFINITIONS
@ -109,6 +109,9 @@ struct cdrom_track_info
UINT32 pgdatasize; /* size of data in each sector of the pregap */
UINT32 pgsubsize; /* size of subchannel data in each sector of the pregap */
/* fields used in CHDMAN only */
UINT32 padframes; /* number of frames of padding to add to the end of the track; needed for GDI */
/* fields used in MAME only */
UINT32 physframeofs; /* frame number on the real CD this track starts at */
UINT32 chdframeofs; /* frame number this track starts at on the CHD */
@ -118,6 +121,7 @@ struct cdrom_track_info
struct cdrom_toc
{
UINT32 numtrks; /* number of tracks */
UINT32 flags; /* see FLAG_ above */
cdrom_track_info tracks[CD_MAX_TRACKS];
};

View File

@ -58,6 +58,7 @@
const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d";
const char *CDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d";
const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
const char *GDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d";
const char *AV_METADATA_FORMAT = "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d";
static const UINT32 METADATA_HEADER_SIZE = 16; // metadata header size
@ -1440,7 +1441,8 @@ UINT32 chd_file::guess_unitbytes()
// look for CD-ROM metadata; if found, then the unit size == CD frame size
if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE)
read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE ||
read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
return CD_FRAME_SIZE;
// otherwise, just map 1:1 with the hunk size

View File

@ -254,6 +254,8 @@ const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R');
extern const char *CDROM_TRACK_METADATA_FORMAT;
const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2');
extern const char *CDROM_TRACK_METADATA2_FORMAT;
const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T');
extern const char *GDROM_TRACK_METADATA_FORMAT;
// standard A/V metadata
const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V');

View File

@ -1,7 +1,7 @@
/***************************************************************************
TOC parser for CHD compression frontend
Handles CDRDAO .toc, CDRWIN .cue, and Sega GDROM .gdi
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.
@ -301,7 +301,7 @@ UINT64 read_uint64(FILE *infile)
}
/*-------------------------------------------------
chdcd_parse_nero - parse a Nero format image file
chdcd_parse_nero - parse a Nero .NRG file
-------------------------------------------------*/
chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
@ -427,6 +427,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_
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;
}
@ -455,7 +456,6 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
{
FILE *infile;
int i, numtracks;
//int chdpos=0;
astring path = astring(tocfname);
@ -471,6 +471,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
memset(&outtoc, 0, sizeof(outtoc));
outinfo.reset();
outtoc.flags = CD_FLAG_GDROM;
fgets(linebuffer,511,infile);
numtracks=atoi(linebuffer);
@ -481,8 +482,6 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
int trknum;
int trksize,trktype;
int sz;
int hunks;
fgets(linebuffer,511,infile);
@ -493,8 +492,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
outinfo.track[trknum].swap=false;
outinfo.track[trknum].offset=0;
//outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
@ -519,7 +517,6 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
}
if(trktype==0)
{
//assert(trksize==2352);
outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO;
outtoc.tracks[trknum].datasize=2352;
}
@ -540,35 +537,26 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
}
outinfo.track[trknum].fname.cpy(path).cat(name);
sz=get_file_size(outinfo.track[trknum].fname);
sz = get_file_size(outinfo.track[trknum].fname);
outtoc.tracks[trknum].frames=sz/trksize;
outtoc.tracks[trknum].extraframes=0;
outtoc.tracks[trknum].frames = sz/trksize;
outtoc.tracks[trknum].padframes = 0;
if(trknum!=0)
if (trknum != 0)
{
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].frames += dif;
outtoc.tracks[trknum-1].padframes = dif;
}
/*
if(trknum!=0)
{
outtoc.tracks[trknum-1].extraframes=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
}
*/
hunks = (outtoc.tracks[trknum].frames+CD_FRAMES_PER_HUNK - 1) / CD_FRAMES_PER_HUNK;
outtoc.tracks[trknum].extraframes = hunks * CD_FRAMES_PER_HUNK - outtoc.tracks[trknum].frames;
//chdpos+=outtoc.tracks[trknum].frames+outtoc.tracks[trknum].extraframes;
}
/*
for(i=0;i<numtracks;++i)
#if 0
for(i=0; i < numtracks; i++)
{
printf("%s %d %d %d\n",outinfo.track[i].fname,outtoc.tracks[i].frames,outtoc.tracks[i].extraframes,outtoc.tracks[i].physframeofs);
printf("%s %d %d %d (true %d)\n", outinfo.track[i].fname.cstr(), outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes);
}
*/
#endif
/* close the input TOC */
fclose(infile);
@ -579,7 +567,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_
}
/*-------------------------------------------------
chdcd_parse_cue - parse a CDRWin format CUE file
chdcd_parse_toc - parse a CDRWin format CUE file
-------------------------------------------------*/
chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
@ -683,6 +671,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].pregap = 0;
outtoc.tracks[trknum].padframes = 0;
outinfo.track[trknum].idx0offs = -1;
outinfo.track[trknum].idx1offs = 0;
@ -988,6 +977,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i
outtoc.tracks[trknum].datasize = 0;
outtoc.tracks[trknum].subtype = CD_SUB_NONE;
outtoc.tracks[trknum].subsize = 0;
outtoc.tracks[trknum].padframes = 0;
cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
if (outtoc.tracks[trknum].datasize == 0)

View File

@ -64,6 +64,11 @@ const UINT32 IDE_SECTOR_SIZE = 512;
// temporary input buffer size
const UINT32 TEMP_BUFFER_SIZE = 32 * 1024 * 1024;
// modes
const int MODE_NORMAL = 0;
const int MODE_CUEBIN = 1;
const int MODE_GDI = 2;
// command modifier
#define REQUIRED "~"
@ -113,7 +118,6 @@ const UINT32 TEMP_BUFFER_SIZE = 32 * 1024 * 1024;
#define OPTION_NUMPROCESSORS "numprocessors"
//**************************************************************************
// FUNCTION PROTOTYPES
//**************************************************************************
@ -318,17 +322,25 @@ public:
UINT32 bytesperframe = trackinfo.datasize + trackinfo.subsize;
UINT64 src_track_start = m_info.track[tracknum].offset;
UINT64 src_track_end = src_track_start + bytesperframe * trackinfo.frames;
while (length_remaining != 0 && offset < endoffs)
UINT64 pad_track_start = src_track_end - (m_toc.tracks[tracknum].padframes * bytesperframe);
while (length_remaining != 0 && offset < endoffs)
{
// determine start of current frame
UINT64 src_frame_start = src_track_start + ((offset - startoffs) / CD_FRAME_SIZE) * bytesperframe;
if (src_frame_start < src_track_end)
{
// read it in
core_fseek(m_file, src_frame_start, SEEK_SET);
UINT32 count = core_fread(m_file, dest, bytesperframe);
if (count != bytesperframe)
report_error(1, "Error reading input file (%s)'", m_lastfile.cstr());
// read it in, or pad if we're into the padframes
if (src_frame_start >= pad_track_start)
{
memset(dest, 0, bytesperframe);
}
else
{
core_fseek(m_file, src_frame_start, SEEK_SET);
UINT32 count = core_fread(m_file, dest, bytesperframe);
if (count != bytesperframe)
report_error(1, "Error reading input file (%s)'", m_lastfile.cstr());
}
// swap if appropriate
if (m_info.track[tracknum].swap)
@ -1156,93 +1168,139 @@ static void compress_common(chd_file_compressor &chd)
// to a CUE file
//-------------------------------------------------
void output_track_metadata(bool cuemode, core_file *file, int tracknum, const cdrom_track_info &info, const char *filename, UINT32 frameoffs, UINT64 discoffs)
void output_track_metadata(int mode, core_file *file, int tracknum, const cdrom_track_info &info, const char *filename, UINT32 frameoffs, UINT64 discoffs)
{
// CUE mode?
if (cuemode)
if (mode == MODE_GDI)
{
// first track specifies the file
if (tracknum == 0)
core_fprintf(file, "FILE \"%s\" BINARY\n", filename);
int mode = 0, size = 2048;
// determine submode
astring tempstr;
switch (info.trktype)
{
case CD_TRACK_MODE1:
case CD_TRACK_MODE1_RAW:
tempstr.format("MODE1/%04d", info.datasize);
break;
switch (info.trktype)
{
case CD_TRACK_MODE1:
mode = 0;
size = 2048;
break;
case CD_TRACK_MODE2:
case CD_TRACK_MODE2_FORM1:
case CD_TRACK_MODE2_FORM2:
case CD_TRACK_MODE2_FORM_MIX:
case CD_TRACK_MODE2_RAW:
tempstr.format("MODE2/%04d", info.datasize);
break;
case CD_TRACK_MODE1_RAW:
mode = 4;
size = 2352;
break;
case CD_TRACK_AUDIO:
tempstr.cpy("AUDIO");
break;
}
case CD_TRACK_MODE2:
mode = 4;
size = 2336;
break;
// output TRACK entry
core_fprintf(file, " TRACK %02d %s\n", tracknum + 1, tempstr.cstr());
case CD_TRACK_MODE2_FORM1:
mode = 4;
size = 2048;
break;
// output PREGAP
if (info.pregap > 0)
core_fprintf(file, " PREGAP %s\n", msf_string_from_frames(tempstr, info.pregap));
case CD_TRACK_MODE2_FORM2:
mode = 4;
size = 2324;
break;
// output track data
core_fprintf(file, " INDEX 01 %s\n", msf_string_from_frames(tempstr, frameoffs));
case CD_TRACK_MODE2_FORM_MIX:
mode = 4;
size = 2336;
break;
// output POSTGAP
if (info.postgap > 0)
core_fprintf(file, " POSTGAP %s\n", msf_string_from_frames(tempstr, info.postgap));
case CD_TRACK_MODE2_RAW:
mode = 4;
size = 2352;
break;
case CD_TRACK_AUDIO:
mode = 0;
size = 2352;
break;
}
core_fprintf(file, "%d %d %d %d %s %lld\n", tracknum+1, frameoffs, mode, size, filename, discoffs);
}
else if (mode == MODE_CUEBIN)
{
// first track specifies the file
if (tracknum == 0)
core_fprintf(file, "FILE \"%s\" BINARY\n", filename);
// non-CUE mode
else
{
// header on the first track
if (tracknum == 0)
core_fprintf(file, "CD_ROM\n\n\n");
core_fprintf(file, "// Track %d\n", tracknum + 1);
// determine submode
astring tempstr;
switch (info.trktype)
{
case CD_TRACK_MODE1:
case CD_TRACK_MODE1_RAW:
tempstr.format("MODE1/%04d", info.datasize);
break;
// write out the track type
astring modesubmode;
if (info.subtype != CD_SUB_NONE)
modesubmode.format("%s %s", cdrom_get_type_string(info.trktype), cdrom_get_subtype_string(info.subtype));
else
modesubmode.format("%s", cdrom_get_type_string(info.trktype));
core_fprintf(file, "TRACK %s\n", modesubmode.cstr());
case CD_TRACK_MODE2:
case CD_TRACK_MODE2_FORM1:
case CD_TRACK_MODE2_FORM2:
case CD_TRACK_MODE2_FORM_MIX:
case CD_TRACK_MODE2_RAW:
tempstr.format("MODE2/%04d", info.datasize);
break;
// write out the attributes
core_fprintf(file, "NO COPY\n");
if (info.trktype == CD_TRACK_AUDIO)
{
core_fprintf(file, "NO PRE_EMPHASIS\n");
core_fprintf(file, "TWO_CHANNEL_AUDIO\n");
}
case CD_TRACK_AUDIO:
tempstr.cpy("AUDIO");
break;
}
// output pregap
astring tempstr;
if (info.pregap > 0)
core_fprintf(file, "ZERO %s %s\n", modesubmode.cstr(), msf_string_from_frames(tempstr, info.pregap));
// output TRACK entry
core_fprintf(file, " TRACK %02d %s\n", tracknum + 1, tempstr.cstr());
// all tracks but the first one have a file offset
if (tracknum > 0)
core_fprintf(file, "DATAFILE \"%s\" #%d %s // length in bytes: %d\n", filename, UINT32(discoffs), msf_string_from_frames(tempstr, info.frames), info.frames * (info.datasize + info.subsize));
else
core_fprintf(file, "DATAFILE \"%s\" %s // length in bytes: %d\n", filename, msf_string_from_frames(tempstr, info.frames), info.frames * (info.datasize + info.subsize));
// output PREGAP
if (info.pregap > 0)
core_fprintf(file, " PREGAP %s\n", msf_string_from_frames(tempstr, info.pregap));
// tracks with pregaps get a START marker too
if (info.pregap > 0)
core_fprintf(file, "START %s\n", msf_string_from_frames(tempstr, info.pregap));
// output track data
core_fprintf(file, " INDEX 01 %s\n", msf_string_from_frames(tempstr, frameoffs));
core_fprintf(file, "\n\n");
}
// output POSTGAP
if (info.postgap > 0)
core_fprintf(file, " POSTGAP %s\n", msf_string_from_frames(tempstr, info.postgap));
}
// non-CUE mode
else if (mode == MODE_NORMAL)
{
// header on the first track
if (tracknum == 0)
core_fprintf(file, "CD_ROM\n\n\n");
core_fprintf(file, "// Track %d\n", tracknum + 1);
// write out the track type
astring modesubmode;
if (info.subtype != CD_SUB_NONE)
modesubmode.format("%s %s", cdrom_get_type_string(info.trktype), cdrom_get_subtype_string(info.subtype));
else
modesubmode.format("%s", cdrom_get_type_string(info.trktype));
core_fprintf(file, "TRACK %s\n", modesubmode.cstr());
// write out the attributes
core_fprintf(file, "NO COPY\n");
if (info.trktype == CD_TRACK_AUDIO)
{
core_fprintf(file, "NO PRE_EMPHASIS\n");
core_fprintf(file, "TWO_CHANNEL_AUDIO\n");
}
// output pregap
astring tempstr;
if (info.pregap > 0)
core_fprintf(file, "ZERO %s %s\n", modesubmode.cstr(), msf_string_from_frames(tempstr, info.pregap));
// all tracks but the first one have a file offset
if (tracknum > 0)
core_fprintf(file, "DATAFILE \"%s\" #%d %s // length in bytes: %d\n", filename, UINT32(discoffs), msf_string_from_frames(tempstr, info.frames), info.frames * (info.datasize + info.subsize));
else
core_fprintf(file, "DATAFILE \"%s\" %s // length in bytes: %d\n", filename, msf_string_from_frames(tempstr, info.frames), info.frames * (info.datasize + info.subsize));
// tracks with pregaps get a START marker too
if (info.pregap > 0)
core_fprintf(file, "START %s\n", msf_string_from_frames(tempstr, info.pregap));
core_fprintf(file, "\n\n");
}
}
@ -2194,6 +2252,8 @@ static void do_extract_cd(parameters_t &params)
int chop = default_name.rchr(0, '.');
if (chop != -1)
default_name.substr(0, chop);
char basename[128];
strncpy(basename, default_name.cstr(), 127);
default_name.cat(".bin");
if (output_bin_file_str == NULL)
output_bin_file_str = &default_name;
@ -2210,38 +2270,86 @@ static void do_extract_cd(parameters_t &params)
core_file *output_toc_file = NULL;
try
{
// process output file
file_error filerr = core_fopen(*output_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, &output_toc_file);
if (filerr != FILERR_NONE)
report_error(1, "Unable to open file (%s)", output_file_str->cstr());
bool cuemode = (output_file_str->find(".cue") != -1);
int mode = MODE_NORMAL;
if (output_file_str->find(".cue") != -1)
{
mode = MODE_CUEBIN;
}
else if (output_file_str->find(".gdi") != -1)
{
mode = MODE_GDI;
}
// process output file
file_error filerr = core_fopen(*output_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_NO_BOM, &output_toc_file);
if (filerr != FILERR_NONE)
report_error(1, "Unable to open file (%s)", output_file_str->cstr());
// process output BIN file
filerr = core_fopen(*output_bin_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &output_bin_file);
if (filerr != FILERR_NONE)
report_error(1, "Unable to open file (%s)", output_bin_file_str->cstr());
if (mode != MODE_GDI)
{
filerr = core_fopen(*output_bin_file_str, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &output_bin_file);
if (filerr != FILERR_NONE)
report_error(1, "Unable to open file (%s)", output_bin_file_str->cstr());
}
// determine total frames
UINT64 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);
// GDI must start with the # of tracks
if (mode == MODE_GDI)
{
core_fprintf(output_toc_file, "%d\n", toc->numtrks);
}
// iterate over tracks and copy all data
UINT64 outputoffs = 0;
UINT32 discoffs = 0;
dynamic_buffer buffer;
for (int tracknum = 0; tracknum < toc->numtrks; tracknum++)
{
astring trackbin_name(basename);
if (mode == MODE_GDI)
{
char temp[8];
sprintf(temp, "%02d", tracknum+1);
trackbin_name.cat(temp);
trackbin_name.cat(".bin");
if (output_bin_file)
{
core_fclose(output_bin_file);
output_bin_file = NULL;
}
filerr = core_fopen(trackbin_name, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &output_bin_file);
if (filerr != FILERR_NONE)
report_error(1, "Unable to open file (%s)", trackbin_name.cstr());
outputoffs = 0;
}
// output the metadata about the track to the TOC file
const cdrom_track_info &trackinfo = toc->tracks[tracknum];
output_track_metadata(cuemode, output_toc_file, tracknum, trackinfo, *output_bin_file_str, discoffs, outputoffs);
if (mode == MODE_GDI)
{
output_track_metadata(mode, output_toc_file, tracknum, trackinfo, trackbin_name, discoffs, outputoffs);
}
else
{
output_track_metadata(mode, output_toc_file, tracknum, trackinfo, *output_bin_file_str, discoffs, outputoffs);
}
// 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 output_frame_size = trackinfo.datasize + ((trackinfo.subtype != CD_SUB_NONE) ? trackinfo.subsize : 0);
if (trackinfo.subtype != CD_SUB_NONE && cuemode)
if (trackinfo.subtype != CD_SUB_NONE && ((mode == MODE_CUEBIN) || (mode == MODE_GDI)))
{
printf("Warning: Track %d has subcode data. bin/cue format cannot contain subcode data and it will be omitted.\n", tracknum+1);
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");
output_frame_size = trackinfo.datasize;
}
@ -2251,7 +2359,8 @@ static void do_extract_cd(parameters_t &params)
// now read and output the actual data
UINT32 bufferoffs = 0;
for (int frame = 0; frame < trackinfo.frames; frame++)
UINT32 actualframes = trackinfo.frames - trackinfo.padframes;
for (UINT32 frame = 0; frame < actualframes; frame++)
{
progress(false, "Extracting, %.1f%% complete... \r", 100.0 * double(outputoffs) / double(total_bytes));
@ -2259,7 +2368,7 @@ static void do_extract_cd(parameters_t &params)
cdrom_read_data(cdrom, cdrom_get_track_start(cdrom, tracknum) + frame, &buffer[bufferoffs], trackinfo.trktype);
// for CDRWin, audio tracks must be reversed
if (cuemode && (trackinfo.trktype == CD_TRACK_AUDIO))
if ((mode == MODE_CUEBIN) && (trackinfo.trktype == CD_TRACK_AUDIO))
for (int swapindex = 0; swapindex < trackinfo.datasize; swapindex += 2)
{
UINT8 swaptemp = buffer[bufferoffs + swapindex];
@ -2270,14 +2379,14 @@ static void do_extract_cd(parameters_t &params)
discoffs++;
// read the subcode data
if (trackinfo.subtype != CD_SUB_NONE && !cuemode)
if (trackinfo.subtype != CD_SUB_NONE && (mode == MODE_NORMAL))
{
cdrom_read_subcode(cdrom, cdrom_get_track_start(cdrom, tracknum) + frame, &buffer[bufferoffs]);
bufferoffs += trackinfo.subsize;
}
// write it out if we need to
if (bufferoffs == buffer.count() || frame == trackinfo.frames - 1)
if (bufferoffs == buffer.count() || frame == actualframes - 1)
{
core_fseek(output_bin_file, outputoffs, SEEK_SET);
UINT32 byteswritten = core_fwrite(output_bin_file, buffer, bufferoffs);
@ -2287,10 +2396,12 @@ static void do_extract_cd(parameters_t &params)
bufferoffs = 0;
}
}
discoffs += trackinfo.padframes;
}
// finish up
core_fclose(output_bin_file);
core_fclose(output_bin_file);
core_fclose(output_toc_file);
printf("Extraction complete \n");
}