Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 101 additions & 9 deletions src/lib/util/cdrom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;



Expand Down Expand Up @@ -1552,6 +1559,64 @@ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
};


/**
* @fn static constexpr std::array<uint32_t, EDC_CRCTABLE_SIZE> edc_crctable_gen
*
* @brief -------------------------------------------------
* edc_crctable_gen - calculate CRC checksums as per
* ECMA 130 and return lookup table
* -------------------------------------------------.
*/

static constexpr std::array<uint32_t, EDC_CRCTABLE_SIZE> edc_crctable_gen(void)
{
std::array<uint32_t, EDC_CRCTABLE_SIZE> 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
Expand Down Expand Up @@ -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)
*
Expand Down
12 changes: 12 additions & 0 deletions src/lib/util/cdrom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);


/***************************************************************************
Expand All @@ -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?
Expand Down
87 changes: 46 additions & 41 deletions src/lib/util/chdcd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
----------------------------------------------------------------------------------------*/
Expand Down Expand Up @@ -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
------------------------------------------------------------------*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -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);

Expand All @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions src/lib/util/chdcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading