X-Git-Url: https://git.jtryba.com/browse/friidump.git/blobdiff_plain/d44030dfa440b1967ec7dcd3ccfe46fb69934486..1b1f91613a6976707cb92f47efa819c278c08d4f:/libfriidump/disc.c?ds=sidebyside diff --git a/libfriidump/disc.c b/libfriidump/disc.c index c132d59..6e90138 100644 --- a/libfriidump/disc.c +++ b/libfriidump/disc.c @@ -44,6 +44,8 @@ #include "byteorder.h" #include "disc.h" #include "dvd_drive.h" +#include "nintendo_disc_header.h" +#include "xbox_ref/xbox_ref_log.h" static void hlds_e7_visible_probe_log (const char *fmt, ...) { static bool started = false; @@ -122,12 +124,109 @@ struct disc_s { /* Read cache */ u_int32_t cache_size; //!< The number of blocks that will be cached when read. bool hlds_e7_read_schedule_logged; //!< True once the selected HLDS 0xE7 read schedule has been logged for this run. + bool seed_diagnostic_active; //!< True while the initial 20 Nintendo seed blocks are being acquired. + unsigned int seed_diagnostic_block; //!< Zero-based seed-block index currently being acquired. + disc_seed_diagnostic seed_diagnostic; //!< Last actionable seed/cache failure evidence. u_int8_t **raw_cache; //!< Memory area for raw sectors cache. u_int8_t **cache; //!< Memory area for unscrambled sectors cache. u_int32_t *cache_map; //!< Data structure used by the caching system to know which blocks are in memory. }; + +static void disc_seed_diag_clear (disc *d) { + if (!d) + return; + memset (&d -> seed_diagnostic, 0, sizeof (d -> seed_diagnostic)); +} + +static void disc_seed_diag_log (disc *d, const char *fmt, ...) { + va_list ap; + if (!d || !d -> seed_diagnostic_active || !fmt) + return; + xbox_ref_log_fprintf (stderr, "[SEED-DIAG] "); + va_start (ap, fmt); + /* xbox_ref_log_fprintf is variadic rather than vfprintf-based, so format + * into a bounded buffer before sending the line to both console and log. */ + { + char line[768]; + vsnprintf (line, sizeof (line), fmt, ap); + xbox_ref_log_fprintf (stderr, "%s\n", line); + } + va_end (ap); +} + +static void disc_seed_diag_fail ( + disc *d, + u_int32_t sector_start, + u_int32_t sector_end, + int retry, + const char *stage, + const char *detail, + int transport_result, + bool capture_command +) { + dvd_command_diagnostic command; + char cdb_text[3 * 12 + 1]; + int i; + int used; + + if (!d || !d -> seed_diagnostic_active) + return; + disc_seed_diag_clear (d); + d -> seed_diagnostic.valid = true; + d -> seed_diagnostic.seed_block_index = d -> seed_diagnostic_block; + d -> seed_diagnostic.sector_start = sector_start; + d -> seed_diagnostic.sector_end = sector_end; + d -> seed_diagnostic.retry = retry; + snprintf (d -> seed_diagnostic.stage, sizeof (d -> seed_diagnostic.stage), "%s", stage ? stage : "unknown"); + snprintf (d -> seed_diagnostic.detail, sizeof (d -> seed_diagnostic.detail), "%s", detail ? detail : ""); + d -> seed_diagnostic.transport_result = transport_result; + + memset (&command, 0, sizeof (command)); + if (capture_command && dvd_get_last_command_diagnostic (d -> dvd, &command)) { + d -> seed_diagnostic.transport_result = command.transport_result; + d -> seed_diagnostic.os_error = command.os_error; + d -> seed_diagnostic.scsi_status = command.scsi_status; + d -> seed_diagnostic.sense_key = command.sense_key; + d -> seed_diagnostic.asc = command.asc; + d -> seed_diagnostic.ascq = command.ascq; + d -> seed_diagnostic.cdb_length = command.cdb_length; + memcpy (d -> seed_diagnostic.cdb, command.cdb, sizeof (d -> seed_diagnostic.cdb)); + } + + cdb_text[0] = '\0'; + used = 0; + for (i = 0; i < d -> seed_diagnostic.cdb_length && i < 12; i++) { + int wrote = snprintf (cdb_text + used, sizeof (cdb_text) - (size_t) used, + "%s%02X", i ? " " : "", d -> seed_diagnostic.cdb[i]); + if (wrote < 0 || wrote >= (int) (sizeof (cdb_text) - (size_t) used)) + break; + used += wrote; + } + + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=%s result=FAIL rc=%d os=%d scsi=0x%02X sense=%02X/%02X/%02X cdb=[%s] detail=%s", + d -> seed_diagnostic.seed_block_index + 1, + sector_start, sector_end, retry, + d -> seed_diagnostic.stage, + d -> seed_diagnostic.transport_result, + d -> seed_diagnostic.os_error, + d -> seed_diagnostic.scsi_status & 0xff, + d -> seed_diagnostic.sense_key & 0xff, + d -> seed_diagnostic.asc & 0xff, + d -> seed_diagnostic.ascq & 0xff, + cdb_text[0] ? cdb_text : "none", + d -> seed_diagnostic.detail[0] ? d -> seed_diagnostic.detail : "none"); +} + +bool disc_get_seed_diagnostic (disc *d, disc_seed_diagnostic *out) { + if (!d || !out || !d -> seed_diagnostic.valid) + return false; + *out = d -> seed_diagnostic; + return true; +} + static void disc_cache_init (disc *d, u_int32_t size) { u_int32_t i; @@ -522,6 +621,8 @@ static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sec u_int8_t raw_block[RAW_BLOCK_SIZE]; u_int8_t iso_block[BLOCK_SIZE]; u_int8_t readbuf[BLOCK_SIZE]; + char detail[160]; + req_sense sense; block_no = block_sector / SECTORS_PER_BLOCK; @@ -530,22 +631,42 @@ static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sec memset (raw_block, 0, sizeof (raw_block)); memset (iso_block, 0, sizeof (iso_block)); out = true; + disc_seed_diag_clear (d); warning ("Method 8 split recovery: trying %d-sector chunks for sectors %u..%u", chunk_len, block_sector, block_sector + SECTORS_PER_BLOCK - 1); + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=split-profile result=BEGIN detail=chunk-sectors-%d", + d -> seed_diagnostic_block + 1, block_sector, + block_sector + SECTORS_PER_BLOCK - 1, c, chunk_len); for (chunk_start = 0; chunk_start < SECTORS_PER_BLOCK && out; chunk_start += chunk_len) { u_int32_t chunk_sector = block_sector + (u_int32_t) chunk_start; memset (readbuf, 0, sizeof (readbuf)); + memset (&sense, 0, sizeof (sense)); if (chunk_sector + 992 + 16 <= d -> sectors_no) - dvd_read_sector_dummy (d -> dvd, chunk_sector + 992, 16, NULL, NULL, 0); + ret = dvd_read_sector_dummy (d -> dvd, chunk_sector + 992, 16, &sense, NULL, 0); else if (chunk_sector >= 992) - dvd_read_sector_dummy (d -> dvd, chunk_sector - 992, 16, NULL, NULL, 0); + ret = dvd_read_sector_dummy (d -> dvd, chunk_sector - 992, 16, &sense, NULL, 0); else - dvd_flush_cache_READ12 (d -> dvd, chunk_sector, NULL); + ret = dvd_flush_cache_READ12 (d -> dvd, chunk_sector, &sense); + if (ret < 0) { + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=split-prefetch result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X detail=chunk-sectors-%d", + d -> seed_diagnostic_block + 1, chunk_sector, + chunk_sector + (u_int32_t) chunk_len - 1, c, ret, + sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff, chunk_len); + } - ret = dvd_read_streaming (d -> dvd, chunk_sector, (u_int32_t) chunk_len, NULL, readbuf, (size_t) chunk_len * SECTOR_SIZE); + memset (&sense, 0, sizeof (sense)); + ret = dvd_read_streaming (d -> dvd, chunk_sector, (u_int32_t) chunk_len, + &sense, readbuf, (size_t) chunk_len * SECTOR_SIZE); if (ret < 0) { + snprintf (detail, sizeof (detail), "chunk-sectors=%d sense=%02X/%02X/%02X", + chunk_len, sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff); + disc_seed_diag_fail (d, chunk_sector, + chunk_sector + (u_int32_t) chunk_len - 1, c, + "split-streaming-read", detail, ret, true); warning ("Method 8 split recovery: READ12 streaming failed for sectors %u..%u with %d", chunk_sector, chunk_sector + (u_int32_t) chunk_len - 1, ret); out = false; break; @@ -555,12 +676,24 @@ static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sec sect = &raw_block[(chunk_start + k) * RAW_SECTOR_SIZE]; ram_offset = (u_int32_t) k * RAW_SECTOR_SIZE; - if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { + ret = dvd_memdump (d -> dvd, ram_offset, 1, 12, sect); + if (ret < 0) { + snprintf (detail, sizeof (detail), "chunk-sectors=%d raw-offset=%u sector-index=%d", + chunk_len, ram_offset, k); + disc_seed_diag_fail (d, chunk_sector + (u_int32_t) k, + chunk_sector + (u_int32_t) k, c, + "split-header-memdump", detail, ret, true); warning ("Method 8 split recovery: header memdump failed at sector %u", chunk_sector + (u_int32_t) k); out = false; break; } - if (dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060) < 0) { + ret = dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060); + if (ret < 0) { + snprintf (detail, sizeof (detail), "chunk-sectors=%d raw-offset=%u sector-index=%d", + chunk_len, ram_offset + 2060, k); + disc_seed_diag_fail (d, chunk_sector + (u_int32_t) k, + chunk_sector + (u_int32_t) k, c, + "split-edc-memdump", detail, ret, true); warning ("Method 8 split recovery: EDC memdump failed at sector %u", chunk_sector + (u_int32_t) k); out = false; break; @@ -571,6 +704,10 @@ static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sec } if (out && !unscrambler_unscramble_16sectors (d -> u, block_sector, raw_block, iso_block)) { + snprintf (detail, sizeof (detail), "chunk-sectors=%d", chunk_len); + disc_seed_diag_fail (d, block_sector, + block_sector + SECTORS_PER_BLOCK - 1, c, + "split-edc-unscramble-validation", detail, -1, false); warning ("Method 8 split recovery: EDC/unscramble validation failed for %d-sector chunks at sectors %u..%u", chunk_len, block_sector, block_sector + SECTORS_PER_BLOCK - 1); out = false; } @@ -578,6 +715,10 @@ static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sec if (out) { disc_cache_add_block (d, block_no, iso_block, raw_block); warning ("Method 8 split recovery: recovered sectors %u..%u using %d-sector chunks", block_sector, block_sector + SECTORS_PER_BLOCK - 1, chunk_len); + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=split-profile result=PASS detail=chunk-sectors-%d", + d -> seed_diagnostic_block + 1, block_sector, + block_sector + SECTORS_PER_BLOCK - 1, c, chunk_len); return true; } } @@ -791,18 +932,22 @@ static int disc_read_sector_8_gdr8081n_search_guided (disc *d, u_int32_t sector_ } static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { - if (disc_hlds_type_is_gdr8081n_search_guided (dvd_get_hlds_e7_type (d -> dvd))) - return disc_read_sector_8_gdr8081n_search_guided (d, sector_no, data, rawdata); - bool out; u_int32_t ram_offset; + u_int32_t start_block; + u_int32_t profile_blocks; + u_int32_t prefetch_sector; int j, k, ret, retry; + int prefetch_ret; u_int8_t *sect, buf[5][RAW_BLOCK_SIZE]; u_int8_t readbuf[BLOCK_SIZE]; u_int8_t buf_unscrambled[5][BLOCK_SIZE]; - u_int32_t start_block; - u_int32_t profile_blocks; -//fprintf (stdout,"disc_read_sector_8"); + req_sense sense; + char detail[160]; + + if (disc_hlds_type_is_gdr8081n_search_guided (dvd_get_hlds_e7_type (d -> dvd))) + return disc_read_sector_8_gdr8081n_search_guided (d, sector_no, data, rawdata); + start_block = sector_no / SECTORS_PER_BLOCK; profile_blocks = dvd_get_hlds_e7_mem_blocks (d -> dvd); if (profile_blocks < 1 || profile_blocks > 5) @@ -810,88 +955,138 @@ static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_ out = false; for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { - /* Assume everything will turn out well */ out = true; + disc_seed_diag_clear (d); + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=BEGIN detail=windows-%u", + d -> seed_diagnostic_block + 1, sector_no, + sector_no + SECTORS_PER_BLOCK - 1, retry, profile_blocks); if (retry > 0) { warning ("Read retry %d for sector %u", retry, sector_no); - - /* Try to reset in-memory data by seeking to a distant sector */ -// if (sector_no > 1000) -// dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); -// else -// dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); - if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector - dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); - else if (sector_no -992 >= 0) //larger than first sector - dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); - else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + memset (&sense, 0, sizeof (sense)); + if (sector_no + 992 + 16 <= d -> sectors_no) + ret = dvd_read_sector_dummy (d -> dvd, sector_no + 992, 16, &sense, NULL, 0); + else if (sector_no >= 992) + ret = dvd_read_sector_dummy (d -> dvd, sector_no - 992, 16, &sense, NULL, 0); + else + ret = dvd_flush_cache_READ12 (d -> dvd, sector_no, &sense); + if (ret < 0) { + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=retry-cache-reset result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X", + d -> seed_diagnostic_block + 1, sector_no, + sector_no + SECTORS_PER_BLOCK - 1, retry, ret, + sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff); + } } - /* First READ command. Type3/Type4 drives can expose several 16-sector - * cache windows after a nearby READ. The modified GDR-8050L test firmware - * proved seed retrieval and the first data runs, but failed when we drove it - * with the normal five-window prefetch schedule. For that profile, avoid - * the distant prefetch and consume only the current 16-sector window. */ + /* Prime the volatile cache, then immediately consume it. */ + memset (&sense, 0, sizeof (sense)); if (disc_hlds_type_is_gdr8050l_no_prefetch (dvd_get_hlds_e7_type (d -> dvd))) { - /* GDR-8050L modified-firmware no-prefetch scheduling is selected/logged - * by the profile probe, not from this hot per-read path. Keeping - * logging here spams one line for every 16-sector read/cache probe. */ - dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); + prefetch_sector = sector_no; + prefetch_ret = dvd_flush_cache_READ12 (d -> dvd, sector_no, &sense); } else { if (sector_no > d -> sectors_no - 1000) - dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0); + prefetch_sector = sector_no - 16 * 5 * 2; else - dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0); + prefetch_sector = sector_no + 16 * 5; + prefetch_ret = dvd_read_sector_streaming (d -> dvd, prefetch_sector, &sense, NULL, 0); + } + if (prefetch_ret < 0) { + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=prefetch result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X detail=prefetch-lba-%u", + d -> seed_diagnostic_block + 1, sector_no, + sector_no + SECTORS_PER_BLOCK - 1, retry, prefetch_ret, + sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff, + prefetch_sector); } - if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, sizeof (readbuf))) >= 0) { + + memset (&sense, 0, sizeof (sense)); + ret = dvd_read_sector_streaming (d -> dvd, sector_no, &sense, readbuf, sizeof (readbuf)); + if (ret >= 0) { for (j = 0; j < (int) profile_blocks && sector_no + j * 16 < d -> sectors_no && out; j++) { - /* Reconstruct raw sectors */ for (k = 0; k < 16; k++) { sect = &buf[j][k * RAW_SECTOR_SIZE]; ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE; - /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */ - if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { + ret = dvd_memdump (d -> dvd, ram_offset, 1, 12, sect); + if (ret < 0) { + snprintf (detail, sizeof (detail), + "window=%d sector-index=%d raw-offset=%u", j, k, ram_offset); + disc_seed_diag_fail (d, sector_no + (u_int32_t) (j * 16 + k), + sector_no + (u_int32_t) (j * 16 + k), retry, + "header-memdump", detail, ret, true); error ("Memdump (1) failed"); out = false; - retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ - } else if (dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060) < 0) { /* Dumping in a single block is faster */ - error ("Memdump (2) failed"); - out = false; + retry = MAX_READ_RETRIES; + } else { + ret = dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060); + if (ret < 0) { + snprintf (detail, sizeof (detail), + "window=%d sector-index=%d raw-offset=%u", j, k, ram_offset + 2060); + disc_seed_diag_fail (d, sector_no + (u_int32_t) (j * 16 + k), + sector_no + (u_int32_t) (j * 16 + k), retry, + "edc-memdump", detail, ret, true); + error ("Memdump (2) failed"); + out = false; + } } } } - /* Now the same for remaining cached 16-sector blocks. Type1 drives only - * expose one validated cache window at their DIC-derived base address. */ for (j = 0; j < (int) profile_blocks && sector_no + j * 16 < d -> sectors_no && out; j++) { - if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, sizeof (readbuf))) >= 0) { - /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */ - for (k = 0; k < 16; k++) { - sect = &buf[j][k * RAW_SECTOR_SIZE]; - memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); + if (j != 0) { + memset (&sense, 0, sizeof (sense)); + ret = dvd_read_sector_streaming (d -> dvd, sector_no + (u_int32_t) j * 16, + &sense, readbuf, sizeof (readbuf)); + if (ret < 0) { + snprintf (detail, sizeof (detail), + "window=%d sense=%02X/%02X/%02X", j, + sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff); + disc_seed_diag_fail (d, sector_no + (u_int32_t) j * 16, + sector_no + (u_int32_t) j * 16 + SECTORS_PER_BLOCK - 1, + retry, "cached-window-streaming-read", detail, ret, true); + error ("dvd_read_sector_streaming() failed with %d", ret); + out = false; + break; } + } + + for (k = 0; k < 16; k++) { + sect = &buf[j][k * RAW_SECTOR_SIZE]; + memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); + } #ifdef DEBUG - if (d -> unscrambling) { + if (d -> unscrambling) { #endif - /* Try to unscramble all data to see if EDC fails */ - if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) - out = false; -#ifdef DEBUG + if (!unscrambler_unscramble_16sectors (d -> u, + sector_no + (u_int32_t) (j * 16), buf[j], buf_unscrambled[j])) { + snprintf (detail, sizeof (detail), "window=%d", j); + disc_seed_diag_fail (d, + sector_no + (u_int32_t) j * 16, + sector_no + (u_int32_t) j * 16 + SECTORS_PER_BLOCK - 1, + retry, "edc-unscramble-validation", detail, -1, false); + out = false; } -#endif - } else { - error ("dvd_read_sector_streaming() failed with %d", ret); - out = false; +#ifdef DEBUG } +#endif } if (out) { - /* It seems all data were unscrambled correctly, so cache them out */ for (j = 0; j < (int) profile_blocks && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++) disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); + disc_seed_diag_clear (d); + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=PASS detail=windows-%u", + d -> seed_diagnostic_block + 1, sector_no, + sector_no + SECTORS_PER_BLOCK - 1, retry, profile_blocks); } } else { + snprintf (detail, sizeof (detail), "sense=%02X/%02X/%02X", + sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff); + disc_seed_diag_fail (d, sector_no, + sector_no + SECTORS_PER_BLOCK - 1, retry, + "streaming-read", detail, ret, true); error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } @@ -912,13 +1107,17 @@ static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_ if (!out) { u_int32_t block_sector = start_block * SECTORS_PER_BLOCK; warning ("Method 8 normal profile read failed at sectors %u..%u; entering split recovery", block_sector, block_sector + SECTORS_PER_BLOCK - 1); + disc_seed_diag_log (d, + "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=EXHAUSTED detail=entering-split-recovery", + d -> seed_diagnostic_block + 1, block_sector, + block_sector + SECTORS_PER_BLOCK - 1, MAX_READ_RETRIES - 1); out = disc_read_sector_8_split_recover_block (d, block_sector); } if (!out) error ("Too many retries, giving up"); - return (out); + return out; } @@ -1139,10 +1338,30 @@ int disc_read_sector (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t ** } -static bool disc_analyze (disc *d) { +static const char *disc_detected_type_name (disc_type type) { + switch (type) { + case DISC_TYPE_GAMECUBE: + return "GameCube"; + case DISC_TYPE_WII: + return "Wii"; + case DISC_TYPE_WII_DL: + return "Wii_DL"; + case DISC_TYPE_DVD: + return "DVD"; + case DISC_TYPE_XBOX: + return "Xbox"; + default: + return "Unknown"; + } +} + + +static bool disc_analyze (disc *d, bool allow_type_correction, bool allow_size_correction) { u_int8_t *buf; char tmp[0x03E0 + 1]; bool unscramble_old, out; + nintendo_disc_header_type header_type; + disc_type previous_type; /* Force unscrambling for this read */ unscramble_old = d -> unscrambling; @@ -1151,16 +1370,41 @@ static bool disc_analyze (disc *d) { if (disc_read_sector (d, 0, &buf, NULL)) { /* System ID */ d -> system_id = buf[0]; -// if (d -> system_id == 'G') { -// d -> type = DISC_TYPE_GAMECUBE; -// d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; -// } else if (d -> system_id == 'R') { -// d -> type = DISC_TYPE_WII; -// d -> sectors_no = DISC_WII_SECTORS_NO; -// } else { -// error ("Unknown system ID: '%c'", d -> system_id); -// MY_ASSERT (false); -// } + + /* + * The legacy capacity probe can misclassify GameCube media on some + * HLDS drives because an out-of-range dummy read does not always + * return the expected ILLEGAL REQUEST / LBA OUT OF RANGE sense data. + * Once sector 0 has been unscrambled, its platform magic is the + * authoritative discriminator. + * + * Explicit -T selections remain authoritative and are not changed. + * Explicit -S geometry also remains authoritative. + */ + header_type = nintendo_disc_header_detect (buf, SECTOR_SIZE); + previous_type = d -> type; + + if (allow_type_correction && + header_type == NINTENDO_DISC_HEADER_GAMECUBE) { + d -> type = DISC_TYPE_GAMECUBE; + if (allow_size_correction) + d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; + } else if (allow_type_correction && + header_type == NINTENDO_DISC_HEADER_WII && + d -> type != DISC_TYPE_WII && + d -> type != DISC_TYPE_WII_DL) { + d -> type = DISC_TYPE_WII; + if (allow_size_correction) + d -> sectors_no = DISC_WII_SECTORS_NO_SL; + } + + if (allow_type_correction && + header_type != NINTENDO_DISC_HEADER_UNKNOWN && + previous_type != d -> type) { + warning ("Disc type corrected from %s to %s using sector-0 platform magic", + disc_detected_type_name (previous_type), + disc_detected_type_name (d -> type)); + } /* Game ID */ strncpy (d -> game_id, (char *) buf + 1, 2); @@ -2998,17 +3242,56 @@ static bool disc_probe_gdr8081n_e7_profile (disc *d) { static bool disc_crack_seeds (disc *d) { int i; - /* As a Nintendo GameCube/Wii disc should not have too many keys, 20 should be enough */ + /* As a Nintendo GameCube/Wii disc should not have too many keys, 20 should be enough. */ debug ("Retrieving all DVD seeds"); - if (!disc_probe_gdr8050l_e7_speed_profile (d)) + disc_seed_diag_clear (d); + d -> seed_diagnostic_active = true; + d -> seed_diagnostic_block = 0; + xbox_ref_log_fprintf (stderr, "\n"); + disc_seed_diag_log (d, + "campaign=begin blocks=20 method=%d profile=%s cache-base=0x%08X windows=%u", + d -> read_method, + dvd_get_hlds_e7_profile_name (d -> dvd), + dvd_get_hlds_e7_cache_base (d -> dvd), + dvd_get_hlds_e7_mem_blocks (d -> dvd)); + + if (!disc_probe_gdr8050l_e7_speed_profile (d)) { + disc_seed_diag_fail (d, 0, 15, 0, "profile-probe", + "GDR-8050L runtime profile probe failed", -1, false); + d -> seed_diagnostic_active = false; return false; - if (!disc_probe_gdr8081n_e7_profile (d)) + } + if (!disc_probe_gdr8081n_e7_profile (d)) { + disc_seed_diag_fail (d, 0, 15, 0, "profile-probe", + "GDR-8081N runtime profile probe failed", -1, false); + d -> seed_diagnostic_active = false; return false; + } + for (i = 0; i < 20 * 16; i += 16) { - if (!disc_read_sector (d, i, NULL, NULL)) + d -> seed_diagnostic_block = (unsigned int) (i / 16); + disc_seed_diag_clear (d); + disc_seed_diag_log (d, + "block=%u/20 sectors=%d..%d stage=seed-block result=BEGIN", + d -> seed_diagnostic_block + 1, i, i + 15); + if (!disc_read_sector (d, (u_int32_t) i, NULL, NULL)) { + if (!d -> seed_diagnostic.valid) + disc_seed_diag_fail (d, (u_int32_t) i, (u_int32_t) i + 15, + 0, "seed-block-read", "disc_read_sector returned failure without a classified sub-stage", -1, false); + disc_seed_diag_log (d, + "campaign=fail block=%u/20 sectors=%d..%d", + d -> seed_diagnostic_block + 1, i, i + 15); + d -> seed_diagnostic_active = false; return false; + } + disc_seed_diag_log (d, + "block=%u/20 sectors=%d..%d stage=seed-block result=PASS", + d -> seed_diagnostic_block + 1, i, i + 15); } + disc_seed_diag_clear (d); + disc_seed_diag_log (d, "campaign=pass blocks=20"); + d -> seed_diagnostic_active = false; return true; } @@ -3118,7 +3401,11 @@ bool disc_init (disc *d, u_int32_t disctype, u_int32_t sectors_no) { disc_set_unscrambling (d, false); out = true; } - else if (disc_analyze (d)) { + else if (disc_analyze ( + d, + disctype > 4, + sectors_no == (u_int32_t) -1 + )) { disc_check_update (d); out = true; } else { @@ -3228,10 +3515,14 @@ int disc_xbox_read_capacity_10 (disc *d, u_int32_t *sectors, u_int32_t *sector_s -int disc_xbox_recovery_kick (disc *d, bool auth_recovery) { +int disc_xbox_prepare_game_view (disc *d, u_int32_t *sectors, u_int32_t *sector_size) { + int out; if (!d || d -> type != DISC_TYPE_XBOX) return -1; - return dvd_xbox_recovery_kick (d -> dvd, auth_recovery); + out = dvd_xbox_prepare_game_view (d -> dvd, sectors, sector_size); + if (out == 0 && sectors) + d -> sectors_no = *sectors; + return out; } int disc_refresh_volume (disc *d) { @@ -3270,8 +3561,20 @@ int disc_xbox_wait_ready (disc *d, unsigned int timeout_ms) { return dvd_wait_ready (d -> dvd, timeout_ms); } +char *disc_get_drive_vendor (disc *d) { + return d ? dvd_get_vendor (d -> dvd) : NULL; +} + +char *disc_get_drive_product_id (disc *d) { + return d ? dvd_get_product_id (d -> dvd) : NULL; +} + +char *disc_get_drive_firmware_revision (disc *d) { + return d ? dvd_get_product_revision (d -> dvd) : NULL; +} + char *disc_get_drive_model_string (disc *d) { - return (dvd_get_model_string (d -> dvd)); + return d ? dvd_get_model_string (d -> dvd) : NULL; }