Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support to extractcd for GDROM .cue/.bin #7717

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f0e2bbd
refactoring for GDROM .cue/.bin extract
nathanhand Dec 28, 2020
40d4dc0
initial support for GDROM .cue/.bin extract
nathanhand Dec 28, 2020
867460d
successful extract GDROM Type I
nathanhand Dec 28, 2020
d0d9d03
successful extract GDROM Type II
nathanhand Dec 28, 2020
15ed072
successful extract GDROM Type III
nathanhand Dec 29, 2020
b77029d
added EDC support, required for TYPE_III GDROM
nathanhand Jan 27, 2021
9faaf32
use constexpr to resolve at compile time
nathanhand Jan 29, 2021
eb23b45
added technical details for the EDC polynomial
nathanhand Jan 29, 2021
e79599f
lowercase hex values to match existing style
nathanhand Jan 29, 2021
38a7fd7
gdrom classifications dont belong in cdrom.h or cdrom.cpp
nathanhand Jan 29, 2021
e8d8825
more information on the CRC polynomial
nathanhand Jan 29, 2021
82a9b11
resolve conflict with upstream master
nathanhand Jan 29, 2021
0780c51
Merge branch 'master' into extract_gdrom_cue
Jan 29, 2021
f57a477
use std::string same as upstream master
nathanhand Jan 29, 2021
2f5b224
Merge branch 'extract_gdrom_cue' of https://github.com/nhand42/mame i…
nathanhand Jan 29, 2021
dde21b1
generate CRC lookup table for EDC at runtime
nathanhand Jan 30, 2021
b08d627
use bit shifts rather than casts to avoid endian issues
nathanhand Jan 30, 2021
0d4bb49
gdrom should be internal to the CHD code
nathanhand Jan 30, 2021
2e90c84
control the scope of table
nathanhand Feb 2, 2021
d55e9bf
init at compile time to avoid separate init func
nathanhand Feb 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 144 additions & 0 deletions src/lib/util/cdrom.cpp
Expand Up @@ -80,6 +80,9 @@ const int SYNC_NUM_BYTES = 12;
/** @brief offset within sector. */
const int MODE_OFFSET = 0x00f;

/** @brief EDC offsets */
const int EDC_MODE1_OFFSET = 16+2048;
This conversation was marked as resolved.
Show resolved Hide resolved

/** @brief offset within sector. */
const int ECC_P_OFFSET = 0x81c;
/** @brief 2 lots of 86. */
Expand Down Expand Up @@ -1552,6 +1555,81 @@ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
};


/**
* @brief -------------------------------------------------
* EDC_crctable - each value represents a CRC used
* in calculating the EDC for MODE1 sectors
* -------------------------------------------------.
*/

static const uint32_t EDC_crctable[256] =
{
0x00000000, 0x90910101, 0x91210201, 0x01B00300,
This conversation was marked as resolved.
Show resolved Hide resolved
0x92410401, 0x02D00500, 0x03600600, 0x93F10701,
0x94810801, 0x04100900, 0x05A00A00, 0x95310B01,
0x06C00C00, 0x96510D01, 0x97E10E01, 0x07700F00,
0x99011001, 0x09901100, 0x08201200, 0x98B11301,
0x0B401400, 0x9BD11501, 0x9A611601, 0x0AF01700,
0x0D801800, 0x9D111901, 0x9CA11A01, 0x0C301B00,
0x9FC11C01, 0x0F501D00, 0x0EE01E00, 0x9E711F01,
0x82012001, 0x12902100, 0x13202200, 0x83B12301,
0x10402400, 0x80D12501, 0x81612601, 0x11F02700,
0x16802800, 0x86112901, 0x87A12A01, 0x17302B00,
0x84C12C01, 0x14502D00, 0x15E02E00, 0x85712F01,
0x1B003000, 0x8B913101, 0x8A213201, 0x1AB03300,
0x89413401, 0x19D03500, 0x18603600, 0x88F13701,
0x8F813801, 0x1F103900, 0x1EA03A00, 0x8E313B01,
0x1DC03C00, 0x8D513D01, 0x8CE13E01, 0x1C703F00,
0xB4014001, 0x24904100, 0x25204200, 0xB5B14301,
0x26404400, 0xB6D14501, 0xB7614601, 0x27F04700,
0x20804800, 0xB0114901, 0xB1A14A01, 0x21304B00,
0xB2C14C01, 0x22504D00, 0x23E04E00, 0xB3714F01,
0x2D005000, 0xBD915101, 0xBC215201, 0x2CB05300,
0xBF415401, 0x2FD05500, 0x2E605600, 0xBEF15701,
0xB9815801, 0x29105900, 0x28A05A00, 0xB8315B01,
0x2BC05C00, 0xBB515D01, 0xBAE15E01, 0x2A705F00,
0x36006000, 0xA6916101, 0xA7216201, 0x37B06300,
0xA4416401, 0x34D06500, 0x35606600, 0xA5F16701,
0xA2816801, 0x32106900, 0x33A06A00, 0xA3316B01,
0x30C06C00, 0xA0516D01, 0xA1E16E01, 0x31706F00,
0xAF017001, 0x3F907100, 0x3E207200, 0xAEB17301,
0x3D407400, 0xADD17501, 0xAC617601, 0x3CF07700,
0x3B807800, 0xAB117901, 0xAAA17A01, 0x3A307B00,
0xA9C17C01, 0x39507D00, 0x38E07E00, 0xA8717F01,
0xD8018001, 0x48908100, 0x49208200, 0xD9B18301,
0x4A408400, 0xDAD18501, 0xDB618601, 0x4BF08700,
0x4C808800, 0xDC118901, 0xDDA18A01, 0x4D308B00,
0xDEC18C01, 0x4E508D00, 0x4FE08E00, 0xDF718F01,
0x41009000, 0xD1919101, 0xD0219201, 0x40B09300,
0xD3419401, 0x43D09500, 0x42609600, 0xD2F19701,
0xD5819801, 0x45109900, 0x44A09A00, 0xD4319B01,
0x47C09C00, 0xD7519D01, 0xD6E19E01, 0x46709F00,
0x5A00A000, 0xCA91A101, 0xCB21A201, 0x5BB0A300,
0xC841A401, 0x58D0A500, 0x5960A600, 0xC9F1A701,
0xCE81A801, 0x5E10A900, 0x5FA0AA00, 0xCF31AB01,
0x5CC0AC00, 0xCC51AD01, 0xCDE1AE01, 0x5D70AF00,
0xC301B001, 0x5390B100, 0x5220B200, 0xC2B1B301,
0x5140B400, 0xC1D1B501, 0xC061B601, 0x50F0B700,
0x5780B800, 0xC711B901, 0xC6A1BA01, 0x5630BB00,
0xC5C1BC01, 0x5550BD00, 0x54E0BE00, 0xC471BF01,
0x6C00C000, 0xFC91C101, 0xFD21C201, 0x6DB0C300,
0xFE41C401, 0x6ED0C500, 0x6F60C600, 0xFFF1C701,
0xF881C801, 0x6810C900, 0x69A0CA00, 0xF931CB01,
0x6AC0CC00, 0xFA51CD01, 0xFBE1CE01, 0x6B70CF00,
0xF501D001, 0x6590D100, 0x6420D200, 0xF4B1D301,
0x6740D400, 0xF7D1D501, 0xF661D601, 0x66F0D700,
0x6180D800, 0xF111D901, 0xF0A1DA01, 0x6030DB00,
0xF3C1DC01, 0x6350DD00, 0x62E0DE00, 0xF271DF01,
0xEE01E001, 0x7E90E100, 0x7F20E200, 0xEFB1E301,
0x7C40E400, 0xECD1E501, 0xED61E601, 0x7DF0E700,
0x7A80E800, 0xEA11E901, 0xEBA1EA01, 0x7B30EB00,
0xE8C1EC01, 0x7850ED00, 0x79E0EE00, 0xE971EF01,
0x7700F000, 0xE791F101, 0xE621F201, 0x76B0F300,
0xE541F401, 0x75D0F500, 0x7460F600, 0xE4F1F701,
0xE381F801, 0x7310F900, 0x72A0FA00, 0xE231FB01,
0x71C0FC00, 0xE151FD01, 0xE0E1FE01, 0x7070FF00,
};

//-------------------------------------------------
// ecc_source_byte - return data from the sector
// at the given offset, masking anything
Expand Down Expand Up @@ -1625,6 +1703,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] = ((uint8_t *) &result)[0];
sector[EDC_MODE1_OFFSET+1] = ((uint8_t *) &result)[1];
sector[EDC_MODE1_OFFSET+2] = ((uint8_t *) &result)[2];
sector[EDC_MODE1_OFFSET+3] = ((uint8_t *) &result)[3];
This conversation was marked as resolved.
Show resolved Hide resolved
}

/**
* @fn void ecc_generate(uint8_t *sector)
*
Expand Down Expand Up @@ -1662,3 +1767,42 @@ void ecc_clear(uint8_t *sector)
memset(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
}


/*---------------------------------------------------------------------------------------
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;
}
22 changes: 22 additions & 0 deletions src/lib/util/cdrom.h
Expand Up @@ -58,6 +58,22 @@ enum
#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
#define CD_FLAG_GDROMLE 0x00000002 // legacy GD-ROM, with little-endian CDDA data

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
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said before, this is an artificial classification based on observation of pressed discs, not something inherent to the format. It doesn’t belong here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've split the GDROM routines out to gdrom.h and gdrom.cpp and updated build files. 38a7fd7

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn’t belong in a public header at all. It should be internal to the CHD conversion code. It’s an artificial classification and not something inherent to the disk format.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in two source files chdcd.cpp and chdman.cpp. It could go in chdcd.h although that's also a public header. If no header is acceptable what's the alternative?

Copy link
Author

@ghost ghost Jan 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to chdcd.cpp and chdcd.h in 0d4bb49. The function is used in chdman.cpp:do_extract_cd() so the prototype and enums need to be in chdcd.h. If that's wrong there is a more disruptive option that involves moving the bulk of do_extract_cd from chdman.cpp to chdcd.cpp. However that will affect existing code that's unrelated to this pull request so the least-disruptive option is on the table first.



/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
Expand Down Expand Up @@ -139,11 +155,17 @@ const char *cdrom_get_subtype_string(uint32_t subtype);
chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc);
chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc);

/* GDROM utilities */
enum gdrom_pattern gdrom_identify_pattern(const cdrom_toc *toc);

// ECC utilities
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);



/***************************************************************************
Expand Down
46 changes: 5 additions & 41 deletions src/lib/util/chdcd.cpp
Expand Up @@ -36,19 +36,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
Expand Down Expand Up @@ -1225,8 +1212,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);
Expand Down Expand Up @@ -1255,14 +1241,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;
}

Expand Down Expand Up @@ -1517,28 +1503,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
*/
Expand Down Expand Up @@ -1581,7 +1545,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);

Expand All @@ -1601,7 +1565,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);
Expand Down