X-Git-Url: https://git.jtryba.com/browse/friidump.git/blobdiff_plain/d44030dfa440b1967ec7dcd3ccfe46fb69934486..7564cb0e17b2d09c012ac9f2a6e62b4363fd563f:/src/friidump.c?ds=inline diff --git a/src/friidump.c b/src/friidump.c index f8b5292..292f658 100644 --- a/src/friidump.c +++ b/src/friidump.c @@ -28,6 +28,17 @@ #include "dumper.h" #include "unscrambler.h" #include "xbox_ref/xbox_ref_log.h" +#include "xbox_ref_bridge.h" +#include "redump_dat.h" + +#ifdef WIN32 +#include +#include +#include +#else +#include +#include +#endif #define printf xbox_ref_printf #define fprintf xbox_ref_log_fprintf @@ -41,6 +52,7 @@ typedef struct { bool active; bool dump_attempted; bool seed_ok; + bool seed_applicable; bool have_seed_duration; double seed_duration; bool dump_ok; @@ -62,6 +74,11 @@ typedef struct { } friidump_validation_summary; static friidump_validation_summary g_validation_summary; +static redump_verify_result g_redump_result; +static bool g_redump_attempted = false; +static bool g_operation_duration_override_valid = false; +static double g_operation_duration_override = 0.0; +static char g_executable_dir[1024]; static void friidump_summary_copy(char *dst, size_t dst_size, const char *src) { if (!dst || dst_size == 0) @@ -74,6 +91,9 @@ static void friidump_summary_copy(char *dst, size_t dst_size, const char *src) { static void friidump_summary_reset(void) { memset(&g_validation_summary, 0, sizeof(g_validation_summary)); g_validation_summary.fail_sector = 0xFFFFFFFFU; + g_validation_summary.seed_applicable = true; + g_operation_duration_override_valid = false; + g_operation_duration_override = 0.0; } static void friidump_format_hms(double seconds, char *buf, size_t buf_size) { @@ -109,8 +129,12 @@ static void friidump_summary_begin(disc *d) { static void friidump_print_validation_summary(double duration, bool have_duration) { double mib_total, mib_per_hour; + const char *seed_status; if (!g_validation_summary.active) return; + seed_status = g_validation_summary.seed_applicable + ? (g_validation_summary.seed_ok ? "OK" : "FAILED/NOT REACHED") + : "N/A (Xbox reference auth path)"; mib_total = (double) g_validation_summary.sectors * 2048.0 / 1024.0 / 1024.0; mib_per_hour = (have_duration && duration > 0.0) ? (mib_total / duration * 3600.0) : 0.0; fprintf(stderr, @@ -121,7 +145,7 @@ static void friidump_print_validation_summary(double duration, bool have_duratio " Cache base........: 0x%08x\n" " Memory windows....: %u\n" " Disc type.........: %s\n" - " Game ID...........: %s\n" + " Game/Media ID.....: %s\n" " Title.............: %s\n" " Output............: %s\n" " Seed read.........: %s\n" @@ -135,7 +159,7 @@ static void friidump_print_validation_summary(double duration, bool have_duratio g_validation_summary.game_id[0] ? g_validation_summary.game_id : "(unknown)", g_validation_summary.title[0] ? g_validation_summary.title : "(unknown)", g_validation_summary.output[0] ? g_validation_summary.output : "(none)", - g_validation_summary.seed_ok ? "OK" : "FAILED/NOT REACHED", + seed_status, g_validation_summary.dump_attempted ? (g_validation_summary.dump_ok ? "OK" : "FAILED") : "NOT ATTEMPTED", g_validation_summary.stop_attempted ? (g_validation_summary.stop_ok ? "OK" : "FAILED") : "NOT ATTEMPTED"); if (g_validation_summary.have_seed_duration) { @@ -151,8 +175,15 @@ static void friidump_print_validation_summary(double duration, bool have_duratio fprintf(stderr, " Duration..........: %.2f seconds\n", duration); if (have_duration && mib_per_hour > 0.0) fprintf(stderr, " Observed average..: %.2f MiB/h over %.2f MiB ISO payload\n", mib_per_hour, mib_total); - if (g_validation_summary.dump_ok) - fprintf(stderr, " Hash compare......: compare printed hashes against known-good target for this disc\n"); + if (g_validation_summary.dump_ok) { + if (g_redump_attempted) { + fprintf(stderr, " Redump verify.....: %s\n", redump_verify_overall_string(&g_redump_result)); + if (g_redump_result.status == REDUMP_VERIFY_MATCH) + fprintf(stderr, " Redump title......: %s\n", g_redump_result.game_name); + } else { + fprintf(stderr, " Redump verify.....: NOT RUN\n"); + } + } } @@ -166,7 +197,7 @@ static void friidump_print_validation_summary(double duration, bool have_duratio #define PACKAGE_NAME "FriiDump" /* Define to the version of this package. */ -#define PACKAGE_VERSION "0.5.3.5" +#define PACKAGE_VERSION "0.5.3.15" #ifdef WIN32 @@ -213,6 +244,13 @@ struct { char *hlds_e7_scan_log; char *hlds_e7_scan_dump_prefix; char *hlds_profile_report; + char *redump_dat_dir; + char *redump_report; + bool no_redump_verify; + bool xgd1_layout_probe; + char *xgd1_layout_probe_report; + bool xgd1_raw_id_probe; + char *xgd1_raw_id_probe_report; } options; @@ -235,6 +273,8 @@ static char friidump_drive_letter_from_device(const char *device) { static const char *friidump_requested_output_target(void) { + if (options.xgd1_raw_id_probe_report && options.xgd1_raw_id_probe_report[0]) return options.xgd1_raw_id_probe_report; + if (options.xgd1_layout_probe_report && options.xgd1_layout_probe_report[0]) return options.xgd1_layout_probe_report; if (options.iso_out && options.iso_out[0]) return options.iso_out; if (options.xiso_out && options.xiso_out[0]) return options.xiso_out; if (options.raw_out && options.raw_out[0]) return options.raw_out; @@ -453,6 +493,391 @@ void welcome (void) { } + +static void friidump_init_executable_dir(const char *argv0) { + char path[1024]; + size_t length = 0; + char *slash; + char *backslash; + char *separator; + + g_executable_dir[0] = '\0'; + path[0] = '\0'; + +#ifdef WIN32 + { + DWORD result = GetModuleFileNameA(NULL, path, (DWORD)sizeof(path)); + if (result > 0 && result < sizeof(path)) { + path[result] = '\0'; + length = (size_t)result; + } + } +#else +#if defined(__linux__) + { + ssize_t result = readlink("/proc/self/exe", path, sizeof(path) - 1); + if (result > 0 && (size_t)result < sizeof(path)) { + path[result] = '\0'; + length = (size_t)result; + } + } +#endif + if (length == 0 && argv0 && argv0[0] && + (strchr(argv0, '/') || strchr(argv0, '\\'))) { + char *resolved = realpath(argv0, path); + if (resolved) + length = strlen(path); + } +#endif + + if (length == 0) + return; + + slash = strrchr(path, '/'); + backslash = strrchr(path, '\\'); + separator = slash; + if (backslash && (!separator || backslash > separator)) + separator = backslash; + + if (!separator) + return; + + *separator = '\0'; + if (path[0]) + snprintf(g_executable_dir, sizeof(g_executable_dir), "%s", path); +} + +static const char *friidump_redump_dat_basename(disc_type type_id) { + switch (type_id) { + case DISC_TYPE_GAMECUBE: + return "Nintendo - GameCube.dat"; + case DISC_TYPE_WII: + case DISC_TYPE_WII_DL: + return "Nintendo - Wii.dat"; + case DISC_TYPE_XBOX: + return "Microsoft - Xbox.dat"; + default: + return NULL; + } +} + +static uint64_t friidump_file_size(const char *path) { + if (!path || !path[0]) + return 0; +#ifdef WIN32 + { + struct _stat64 st; + if (_stat64(path, &st) == 0) + return (uint64_t)st.st_size; + } +#else + { + struct stat st; + if (stat(path, &st) == 0) + return (uint64_t)st.st_size; + } +#endif + return 0; +} + +static bool friidump_sync_report_file(FILE *f) { + if (!f || fflush(f) != 0) + return false; +#ifdef WIN32 + return _commit(_fileno(f)) == 0; +#else + return fsync(fileno(f)) == 0; +#endif +} + +static bool friidump_atomic_replace(const char *temporary_path, const char *final_path) { + if (!temporary_path || !final_path) + return false; +#ifdef WIN32 + return MoveFileExA(temporary_path, final_path, + MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != 0; +#else + return rename(temporary_path, final_path) == 0; +#endif +} + +static void friidump_json_write_string_value(FILE *f, const char *prefix, + const char *value, const char *suffix) { + fputs(prefix, f); + friidump_json_string(f, value ? value : ""); + fputs(suffix, f); +} + +static void friidump_write_redump_report(const redump_verify_result *result, + const char *output_path, + uint64_t output_size, + const char *crc32, + const char *md5, + const char *sha1, + const char *sha256, + const char *hash_source, + const char *representation_note) { + FILE *f; + char *temporary_path; + char line[256]; + size_t temporary_path_size; + bool write_ok; + + if (!options.redump_report || !options.redump_report[0] || !result) + return; + + temporary_path_size = strlen(options.redump_report) + 5; + temporary_path = (char *)malloc(temporary_path_size); + if (!temporary_path) { + fprintf(stderr, "Redump report.......: memory allocation failed\n"); + return; + } + snprintf(temporary_path, temporary_path_size, "%s.tmp", options.redump_report); + remove(temporary_path); + + f = fopen(temporary_path, "wb"); + if (!f) { + fprintf(stderr, "Redump report.......: could not write temporary file %s\n", temporary_path); + free(temporary_path); + return; + } + + /* Use fputs/fputc rather than the logged fprintf wrapper. Report contents + * belong in the JSON artifact, not as a malformed partial mirror in the + * human-readable run log. */ + fputs("{\n", f); + fputs(" \"schema\": 2,\n", f); + friidump_json_write_string_value(f, " \"producer\": ", "friidump-" PACKAGE_VERSION, ",\n"); + friidump_json_write_string_value(f, " \"status\": ", redump_verify_status_string(result->status), ",\n"); + friidump_json_write_string_value(f, " \"overall\": ", redump_verify_overall_string(result), ",\n"); + friidump_json_write_string_value(f, " \"confidence\": ", redump_verify_confidence_string(result), ",\n"); + friidump_json_write_string_value(f, " \"output\": ", output_path ? output_path : "", ",\n"); + snprintf(line, sizeof(line), " \"output_size\": %llu,\n", (unsigned long long)output_size); + fputs(line, f); + friidump_json_write_string_value(f, " \"crc32\": ", crc32 ? crc32 : "", ",\n"); + friidump_json_write_string_value(f, " \"md5\": ", md5 ? md5 : "", ",\n"); + friidump_json_write_string_value(f, " \"sha1\": ", sha1 ? sha1 : "", ",\n"); + friidump_json_write_string_value(f, " \"sha256\": ", sha256 ? sha256 : "", ",\n"); + friidump_json_write_string_value(f, " \"hash_source\": ", hash_source ? hash_source : "", ",\n"); + friidump_json_write_string_value(f, " \"representation_note\": ", representation_note ? representation_note : "", ",\n"); + friidump_json_write_string_value(f, " \"dat_path\": ", result->dat_path, ",\n"); + snprintf(line, sizeof(line), " \"entries_scanned\": %lu,\n", result->entries_scanned); + fputs(line, f); + snprintf(line, sizeof(line), " \"exact_matches\": %lu,\n", result->exact_matches); + fputs(line, f); + friidump_json_write_string_value(f, " \"match_kind\": ", redump_verify_match_kind_string(result), ",\n"); + friidump_json_write_string_value(f, " \"game_name\": ", result->game_name, ",\n"); + friidump_json_write_string_value(f, " \"rom_name\": ", result->rom_name, ",\n"); + + fputs(" \"field_match_counts\": {\n", f); + snprintf(line, sizeof(line), " \"size\": %lu,\n", result->size_matches); fputs(line, f); + snprintf(line, sizeof(line), " \"crc32\": %lu,\n", result->crc32_matches); fputs(line, f); + snprintf(line, sizeof(line), " \"md5\": %lu,\n", result->md5_matches); fputs(line, f); + snprintf(line, sizeof(line), " \"sha1\": %lu\n", result->sha1_matches); fputs(line, f); + fputs(" },\n", f); + + fputs(" \"expected\": {\n", f); + friidump_json_write_string_value(f, " \"size\": ", result->expected_size, ",\n"); + friidump_json_write_string_value(f, " \"crc32\": ", result->expected_crc32, ",\n"); + friidump_json_write_string_value(f, " \"md5\": ", result->expected_md5, ",\n"); + friidump_json_write_string_value(f, " \"sha1\": ", result->expected_sha1, "\n"); + fputs(" },\n", f); + + fputs(" \"evidence\": {\n", f); + friidump_json_write_string_value(f, " \"size\": ", redump_field_status_string(result->size_status), ",\n"); + friidump_json_write_string_value(f, " \"crc32\": ", redump_field_status_string(result->crc32_status), ",\n"); + friidump_json_write_string_value(f, " \"md5\": ", redump_field_status_string(result->md5_status), ",\n"); + friidump_json_write_string_value(f, " \"sha1\": ", redump_field_status_string(result->sha1_status), "\n"); + fputs(" },\n", f); + friidump_json_write_string_value(f, " \"detail\": ", result->detail, "\n"); + fputs("}\n", f); + + write_ok = !ferror(f) && friidump_sync_report_file(f); + if (fclose(f) != 0) + write_ok = false; + + if (!write_ok) { + remove(temporary_path); + fprintf(stderr, "Redump report.......: write/flush failed; final report was not replaced\n"); + free(temporary_path); + return; + } + + if (!friidump_atomic_replace(temporary_path, options.redump_report)) { + fprintf(stderr, "Redump report.......: could not replace %s; complete temporary report retained at %s\n", + options.redump_report, temporary_path); + free(temporary_path); + return; + } + + fprintf(stderr, "Redump report.......: %s (atomic write)\n", options.redump_report); + free(temporary_path); +} + +static void friidump_print_redump_field(const char *label, redump_field_status status) { + fprintf(stderr, "%-20s %s\n", label, redump_field_status_string(status)); +} + +static void friidump_verify_redump_values(disc_type type_id, + const char *output_path, + uint64_t output_size, + const char *crc32, + const char *md5, + const char *sha1, + const char *sha256, + const char *hash_source) { + const char *basename; + const char *dat_dir; + const char *representation_note; + char dat_path[1024]; + + redump_verify_result_init(&g_redump_result); + g_redump_attempted = false; + representation_note = (type_id == DISC_TYPE_XBOX) + ? "XGD1 acquisition success and exact Redump hash identity are separate claims; unresolved zero-filled pregame/postgame content may prevent an exact match." + : ""; + + if (options.no_redump_verify) { + fprintf(stderr, "Redump verification: DISABLED (--no-redump-verify)\n"); + return; + } + if (options.no_hashing) { + fprintf(stderr, "Redump verification: SKIPPED (hashing disabled)\n"); + return; + } + if (!output_path || !output_path[0]) { + fprintf(stderr, "Redump verification: SKIPPED (final output path unavailable)\n"); + return; + } + if (!crc32 || !crc32[0] || !md5 || !md5[0] || !sha1 || !sha1[0]) { + fprintf(stderr, "Redump verification: SKIPPED (finalized CRC32/MD5/SHA-1 evidence incomplete)\n"); + return; + } + + basename = friidump_redump_dat_basename(type_id); + if (!basename) { + fprintf(stderr, "Redump verification: SKIPPED (no DAT mapping for this disc type)\n"); + return; + } + + dat_dir = (options.redump_dat_dir && options.redump_dat_dir[0]) + ? options.redump_dat_dir + : NULL; + redump_resolve_dat_path(dat_dir, g_executable_dir, basename, + dat_path, sizeof(dat_path)); + if (output_size == 0) + output_size = friidump_file_size(output_path); + + g_redump_attempted = true; + redump_verify_dat_file(dat_path, output_size, crc32, md5, sha1, &g_redump_result); + + fprintf(stderr, "\nRedump verification\n"); + fprintf(stderr, "------------------------------------------------------------\n"); + fprintf(stderr, "%-20s %s\n", "DAT", g_redump_result.dat_path); + fprintf(stderr, "%-20s %s\n", "Hash source", hash_source ? hash_source : "Finalized output hashes"); + fprintf(stderr, "%-20s %lu\n", "Entries scanned", g_redump_result.entries_scanned); + + if (g_redump_result.status == REDUMP_VERIFY_MATCH) { + fprintf(stderr, "%-20s %s\n", "Matched entry", g_redump_result.game_name); + fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name); + friidump_print_redump_field("Image size", g_redump_result.size_status); + friidump_print_redump_field("CRC32", g_redump_result.crc32_status); + friidump_print_redump_field("MD5", g_redump_result.md5_status); + friidump_print_redump_field("SHA-1", g_redump_result.sha1_status); + if (g_redump_result.exact_matches > 1) + fprintf(stderr, "%-20s %lu exact entries\n", "Duplicate matches", g_redump_result.exact_matches); + } else if (g_redump_result.candidate_available) { + fprintf(stderr, "%-20s %s\n", "Closest candidate", g_redump_result.game_name); + fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name); + friidump_print_redump_field("Image size", g_redump_result.size_status); + friidump_print_redump_field("CRC32", g_redump_result.crc32_status); + friidump_print_redump_field("MD5", g_redump_result.md5_status); + friidump_print_redump_field("SHA-1", g_redump_result.sha1_status); + } else { + fprintf(stderr, "%-20s %s\n", "Closest candidate", "None (no hash-correlated entry)"); + fprintf(stderr, "%-20s %lu entries\n", "Size matches", g_redump_result.size_matches); + fprintf(stderr, "%-20s %lu entries\n", "CRC32 matches", g_redump_result.crc32_matches); + fprintf(stderr, "%-20s %lu entries\n", "MD5 matches", g_redump_result.md5_matches); + fprintf(stderr, "%-20s %lu entries\n", "SHA-1 matches", g_redump_result.sha1_matches); + } + + fprintf(stderr, "%-20s %s\n", "Overall", redump_verify_overall_string(&g_redump_result)); + fprintf(stderr, "%-20s %s\n", "Confidence", redump_verify_confidence_string(&g_redump_result)); + if (g_redump_result.status != REDUMP_VERIFY_MATCH) + fprintf(stderr, "%-20s %s\n", "Detail", g_redump_result.detail); + if (type_id == DISC_TYPE_XBOX) + fprintf(stderr, "%-20s %s\n", "Representation note", representation_note); + + friidump_write_redump_report(&g_redump_result, output_path, output_size, + crc32, md5, sha1, sha256, hash_source, + representation_note); +} + +static void friidump_verify_redump_iso(dumper *dmp, disc_type type_id) { + const char *output_path; + + if (!dmp) + return; + + output_path = options.iso_out; + friidump_verify_redump_values(type_id, + output_path, + friidump_file_size(output_path), + dumper_get_iso_crc32(dmp), + dumper_get_iso_md5(dmp), + dumper_get_iso_sha1(dmp), + dumper_get_iso_sha2(dmp), + "FriiDump finalized ISO multihash"); +} + +static void friidump_verify_redump_xbox_reference(dumper *dmp) { + xbox_ref_dump_result result; + + xbox_ref_dump_result_init(&result); + if (!dumper_get_xbox_reference_result(dmp, &result)) { + fprintf(stderr, "Redump verification: SKIPPED (Xbox reference result handoff unavailable)\n"); + return; + } + if (result.mode != '1') { + fprintf(stderr, "Redump verification: SKIPPED (Xbox XISO is not a full Redump disc image)\n"); + return; + } + if (!result.dump_success) { + fprintf(stderr, "Redump verification: SKIPPED (Xbox reference dump did not complete)\n"); + return; + } + if (!result.hashes_complete) { + fprintf(stderr, "Redump verification: SKIPPED (Xbox finalized full-file hashes incomplete)\n"); + return; + } + + if (g_validation_summary.active) { + if (result.output_path[0]) + friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), result.output_path); + if (result.title[0]) + friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), result.title); + if (result.media_id[0]) + friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), result.media_id); + if (result.output_sectors) + g_validation_summary.sectors = result.output_sectors; + g_validation_summary.seed_applicable = false; + g_validation_summary.have_seed_duration = false; + } + if (result.elapsed_seconds > 0.0) { + g_operation_duration_override_valid = true; + g_operation_duration_override = result.elapsed_seconds; + } + + friidump_verify_redump_values(DISC_TYPE_XBOX, + result.output_path, + result.output_size, + result.crc32, + result.md5, + result.sha1, + result.sha256, + "GDR-8050L reference finalized full-file hashes"); +} + void help (void) { /* 80 cols guide: * |-------------------------------------------------------------------------------| @@ -539,6 +964,14 @@ void help (void) { " --scan-log JSON output path for --hlds-e7-scan\n" " --scan-dump-prefix Optional raw 0xE7 window dump prefix for --hlds-e7-scan\n" " --hlds-profile-report Write selected HLDS profile/evidence JSON\n" + " --redump-dat-dir Directory containing canonical Redump DAT files\n" + " (default: executable-relative redump_dat, then current directory)\n" + " --redump-report Write atomic Redump evidence JSON\n" + " --no-redump-verify Disable automatic post-dump DAT verification\n" + " --xgd1-layout-probe Read-only locked/unlocked XGD1 boundary probe;\n" + " writes atomic JSON and does not create an ISO\n" + " --xgd1-raw-id-probe Modified-firmware cache-flushed, block-aligned raw-ID probe;\n" + " maps logical LBAs to decoded physical sector IDs\n" " -A, --allmethods Try all known command/method combinations until\n" " one works. Reopens the drive for each command so\n" " command-specific vendor handlers are rebound.\n" @@ -594,6 +1027,11 @@ bool optparse (int argc, char **argv) { {"scan-log", 1, 0, 1001}, {"scan-dump-prefix", 1, 0, 1002}, {"hlds-profile-report", 1, 0, 1005}, + {"redump-dat-dir", 1, 0, 1006}, + {"redump-report", 1, 0, 1007}, + {"no-redump-verify", 0, 0, 1008}, + {"xgd1-layout-probe", 1, 0, 1009}, + {"xgd1-raw-id-probe", 1, 0, 1010}, #ifdef DEBUG /* We don't want newbies to generate and put into circulation bad dumps, so this options are disabled for releases */ {"donottunscramble", 0, 0, 'n'}, @@ -639,6 +1077,13 @@ bool optparse (int argc, char **argv) { options.hlds_e7_scan_log = NULL; options.hlds_e7_scan_dump_prefix = NULL; options.hlds_profile_report = NULL; + options.redump_dat_dir = NULL; + options.redump_report = NULL; + options.no_redump_verify = false; + options.xgd1_layout_probe = false; + options.xgd1_layout_probe_report = NULL; + options.xgd1_raw_id_probe = false; + options.xgd1_raw_id_probe_report = NULL; do { #ifdef DEBUG @@ -785,6 +1230,25 @@ bool optparse (int argc, char **argv) { case 1005: my_strdup (options.hlds_profile_report, optarg); break; + case 1006: + my_strdup (options.redump_dat_dir, optarg); + break; + case 1007: + my_strdup (options.redump_report, optarg); + break; + case 1008: + options.no_redump_verify = true; + break; + case 1009: + options.xgd1_layout_probe = true; + my_strdup (options.xgd1_layout_probe_report, optarg); + options.disctype = DISC_TYPE_XBOX; + break; + case 1010: + options.xgd1_raw_id_probe = true; + my_strdup (options.xgd1_raw_id_probe_report, optarg); + options.disctype = DISC_TYPE_XBOX; + break; case -1: break; default: @@ -799,6 +1263,9 @@ bool optparse (int argc, char **argv) { fprintf (stderr, "WARNING: Extra parameters ignored\n"); } + if (options.xgd1_layout_probe || options.xgd1_raw_id_probe) + options.disctype = DISC_TYPE_XBOX; + /* Sanity checks... */ out = false; if (!options.device && !options.raw_in) { @@ -812,6 +1279,13 @@ bool optparse (int argc, char **argv) { fprintf (stderr, "The -r, -i and -X options cannot be used together with -a.\n"); } else if (options.xiso_requested && (options.raw_out || options.iso_requested)) { fprintf (stderr, "The -X/--xiso option is a separate Xbox output mode and cannot be combined with -r or -i.\n"); + } else if ((options.xgd1_layout_probe || options.xgd1_raw_id_probe) && + (options.autodump || options.raw_in || options.raw_out || + options.iso_requested || options.xiso_requested || + options.allmethods || options.hlds_e7_scan || + options.hlds_e7_subcmd_sweep || options.hlds_e7_memrange_sweep || + (options.xgd1_layout_probe && options.xgd1_raw_id_probe))) { + fprintf (stderr, "XGD1 probe modes are mutually exclusive read-only diagnostics and cannot be combined with dump, conversion, all-methods, or HLDS 0xE7 probe options.\n"); } else { /* Specified options seem to make sense */ out = true; @@ -832,7 +1306,7 @@ int dologic (disc *d, progstats *stats) { u_int32_t current_sector = 0; xbox_forced = (options.disctype == DISC_TYPE_XBOX); - xbox_output_requested = xbox_forced || options.xiso_requested; + xbox_output_requested = xbox_forced || options.xiso_requested || options.xgd1_layout_probe || options.xgd1_raw_id_probe; dump_attempted = false; @@ -947,6 +1421,67 @@ int dologic (disc *d, progstats *stats) { fprintf (stderr, "OK\n"); } + if (options.xgd1_layout_probe || options.xgd1_raw_id_probe) { +#ifdef WIN32 + int media_rc; + int media_sense_key; + int media_asc; + int media_ascq; + int probe_status; + bool stop_ok; + const char *probe_name; + + probe_name = options.xgd1_raw_id_probe ? "XGD1 raw-sector ID" : "XGD1 logical-boundary"; + + if (!disc_is_xbox_challenge_drive (d)) { + fprintf (stderr, + "%s probe currently supports only the GDR-8050L challenge-handshake profile.\n", + probe_name); + return false; + } + + if (options.xgd1_raw_id_probe && + !(disc_get_hlds_e7_type (d) == 44 || + disc_get_hlds_e7_type (d) == 45 || + disc_get_hlds_e7_type (d) == 442 || + disc_get_hlds_e7_type (d) == 443 || + disc_get_hlds_e7_type (d) == 445)) { + fprintf (stderr, + "--xgd1-raw-id-probe requires the modified GDR-8050L HIT 0xE7 memdump profile.\n"); + return false; + } + + fprintf (stderr, "\nChecking for ready Xbox media before %s probing... ", probe_name); + media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq); + if (media_rc <= 0) { + fprintf (stderr, + "Failed (sense %02X/%02X/%02X). Insert the disc, wait for spin-up, close AutoPlay/File Explorer, and retry.\n", + media_sense_key, media_asc, media_ascq); + return false; + } + fprintf (stderr, "OK\n"); + + gettimeofday (&(stats -> start_time), NULL); + if (options.xgd1_raw_id_probe) + probe_status = xbox_ref_xgd1_raw_id_probe_with_handle ( + disc_get_native_handle (d), disc_get_device (d), options.xgd1_raw_id_probe_report); + else + probe_status = xbox_ref_xgd1_layout_probe_with_handle ( + disc_get_native_handle (d), disc_get_device (d), options.xgd1_layout_probe_report); + + fprintf (stderr, "Issuing STOP UNIT / spin-down after %s probe... ", probe_name); + stop_ok = disc_stop_unit (d, false); + fprintf (stderr, "%s\n", stop_ok ? "OK" : "Failed"); + gettimeofday (&(stats -> end_time), NULL); + fprintf (stderr, "%s probe status: %s\n", probe_name, probe_status == 0 ? "OK" : "FAILED"); + friidump_summary_reset(); + return probe_status == 0; +#else + fprintf (stderr, "XGD1 probe modes are available only in the Windows build.\n"); + return false; +#endif + } + if (options.hlds_e7_scan) { out = disc_hlds_e7_scan (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix); fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 scan... "); @@ -1194,6 +1729,11 @@ int dologic (disc *d, progstats *stats) { dumper_get_iso_sha1 (dmp), dumper_get_iso_sha2 (dmp)/*, dumper_get_iso_ed2k (dmp)*/ ); + if (type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d) && options.iso_requested) + friidump_verify_redump_xbox_reference(dmp); + else if (options.iso_out) + friidump_verify_redump_iso(dmp, type_id); + out = true; if (g_validation_summary.active) g_validation_summary.dump_ok = true; @@ -1294,6 +1834,7 @@ int main (int argc, char *argv[]) { /* First of all... */ drop_euid (); + friidump_init_executable_dir((argc > 0) ? argv[0] : NULL); xbox_ref_log_open_for_target(NULL, 0); welcome (); @@ -1356,6 +1897,8 @@ int main (int argc, char *argv[]) { duration += ((double) us / (double) USECS_PER_SEC); if (duration < 0) duration = 0; + if (g_operation_duration_override_valid) + duration = g_operation_duration_override; fprintf (stderr, "Operation took %.2f seconds\n", duration); friidump_print_validation_summary(duration, true); @@ -1371,6 +1914,8 @@ int main (int argc, char *argv[]) { my_free (options.raw_out); my_free (options.raw_in); my_free (options.hlds_profile_report); + my_free (options.xgd1_layout_probe_report); + my_free (options.xgd1_raw_id_probe_report); } if (xbox_ref_log_path())