1 /***************************************************************************
2 * Copyright (C) 2007 by Arep *
3 * Support is provided through the forums at *
4 * http://wii.console-tribe.com *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20 ***************************************************************************/
29 #include "unscrambler.h"
30 #include "xbox_ref/xbox_ref_log.h"
31 #include "xbox_ref_bridge.h"
32 #include "redump_dat.h"
43 #define printf xbox_ref_printf
44 #define fprintf xbox_ref_log_fprintf
46 #define USECS_PER_SEC 1000000
49 static const char *friidump_requested_output_target(void);
56 bool have_seed_duration;
69 u_int32_t fail_sector;
74 } friidump_validation_summary;
76 static friidump_validation_summary g_validation_summary;
77 static redump_verify_result g_redump_result;
78 static bool g_redump_attempted = false;
79 static bool g_operation_duration_override_valid = false;
80 static double g_operation_duration_override = 0.0;
81 static char g_executable_dir[1024];
83 static void friidump_summary_copy(char *dst, size_t dst_size, const char *src) {
84 if (!dst || dst_size == 0)
88 snprintf(dst, dst_size, "%s", src);
91 static void friidump_summary_reset(void) {
92 memset(&g_validation_summary, 0, sizeof(g_validation_summary));
93 g_validation_summary.fail_sector = 0xFFFFFFFFU;
94 g_validation_summary.seed_applicable = true;
95 g_operation_duration_override_valid = false;
96 g_operation_duration_override = 0.0;
99 static void friidump_format_hms(double seconds, char *buf, size_t buf_size) {
100 long total, hours, minutes, secs;
101 if (!buf || buf_size == 0)
105 total = (long) (seconds + 0.5);
106 hours = total / 3600;
107 minutes = (total / 60) % 60;
109 snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hours, minutes, secs);
113 static void friidump_summary_begin(disc *d) {
115 if (!d || disc_get_hlds_e7_type(d) == 0)
117 friidump_summary_reset();
118 g_validation_summary.active = true;
119 friidump_summary_copy(g_validation_summary.model, sizeof(g_validation_summary.model), disc_get_drive_model_string(d));
120 g_validation_summary.command = disc_get_command(d);
121 g_validation_summary.method = disc_get_method(d);
122 g_validation_summary.hlds_type = disc_get_hlds_e7_type(d);
123 g_validation_summary.profile = disc_get_hlds_e7_profile_name(d);
124 g_validation_summary.cache_base = disc_get_hlds_e7_cache_base(d);
125 g_validation_summary.mem_blocks = disc_get_hlds_e7_mem_blocks(d);
126 target = friidump_requested_output_target();
127 friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), target ? target : "(none)");
130 static void friidump_print_validation_summary(double duration, bool have_duration) {
131 double mib_total, mib_per_hour;
132 const char *seed_status;
133 if (!g_validation_summary.active)
135 seed_status = g_validation_summary.seed_applicable
136 ? (g_validation_summary.seed_ok ? "OK" : "FAILED/NOT REACHED")
137 : "N/A (Xbox reference auth path)";
138 mib_total = (double) g_validation_summary.sectors * 2048.0 / 1024.0 / 1024.0;
139 mib_per_hour = (have_duration && duration > 0.0) ? (mib_total / duration * 3600.0) : 0.0;
141 "\nHLDS 0xE7 validation summary:\n"
142 " Model.............: %s\n"
143 " Profile...........: %s\n"
144 " Command/method....: %u / %d\n"
145 " Cache base........: 0x%08x\n"
146 " Memory windows....: %u\n"
147 " Disc type.........: %s\n"
148 " Game/Media ID.....: %s\n"
149 " Title.............: %s\n"
150 " Output............: %s\n"
151 " Seed read.........: %s\n"
152 " Dump status.......: %s\n"
153 " STOP UNIT.........: %s\n",
154 g_validation_summary.model[0] ? g_validation_summary.model : "(unknown)",
155 g_validation_summary.profile ? g_validation_summary.profile : "(unknown)",
156 g_validation_summary.command, g_validation_summary.method,
157 g_validation_summary.cache_base, g_validation_summary.mem_blocks,
158 g_validation_summary.disc_type[0] ? g_validation_summary.disc_type : "(unknown)",
159 g_validation_summary.game_id[0] ? g_validation_summary.game_id : "(unknown)",
160 g_validation_summary.title[0] ? g_validation_summary.title : "(unknown)",
161 g_validation_summary.output[0] ? g_validation_summary.output : "(none)",
163 g_validation_summary.dump_attempted ? (g_validation_summary.dump_ok ? "OK" : "FAILED") : "NOT ATTEMPTED",
164 g_validation_summary.stop_attempted ? (g_validation_summary.stop_ok ? "OK" : "FAILED") : "NOT ATTEMPTED");
165 if (g_validation_summary.have_seed_duration) {
167 friidump_format_hms(g_validation_summary.seed_duration, seed_buf, sizeof(seed_buf));
168 fprintf(stderr, " Seed elapsed......: %s (%.2f seconds)\n", seed_buf, g_validation_summary.seed_duration);
170 if (g_validation_summary.dump_attempted && !g_validation_summary.dump_ok && g_validation_summary.fail_sector != 0xFFFFFFFFU)
171 fprintf(stderr, " Failure sector....: %u..%u\n", g_validation_summary.fail_sector, g_validation_summary.fail_sector + 15);
172 if (g_validation_summary.sectors)
173 fprintf(stderr, " Sectors...........: %u\n", g_validation_summary.sectors);
175 fprintf(stderr, " Duration..........: %.2f seconds\n", duration);
176 if (have_duration && mib_per_hour > 0.0)
177 fprintf(stderr, " Observed average..: %.2f MiB/h over %.2f MiB ISO payload\n", mib_per_hour, mib_total);
178 if (g_validation_summary.dump_ok) {
179 if (g_redump_attempted) {
180 fprintf(stderr, " Redump verify.....: %s\n", redump_verify_overall_string(&g_redump_result));
181 if (g_redump_result.status == REDUMP_VERIFY_MATCH)
182 fprintf(stderr, " Redump title......: %s\n", g_redump_result.game_name);
184 fprintf(stderr, " Redump verify.....: NOT RUN\n");
190 /* Name of package */
191 #define PACKAGE "friidump"
193 /* Define to the address where bug reports for this package should be sent. */
194 #define PACKAGE_BUGREPORT "arep@no.net"
196 /* Define to the full name of this package. */
197 #define PACKAGE_NAME "FriiDump"
199 /* Define to the version of this package. */
200 #define PACKAGE_VERSION "0.5.3.15"
205 #include "getopt-win32.h"
209 #include <sys/time.h>
214 /* Struct for program options */
225 bool xbox_filename_override;
226 bool xbox_auto_filename;
230 u_int32_t start_sector;
231 u_int32_t sectors_no;
237 bool no_unscrambling;
242 bool hlds_e7_subcmd_sweep;
243 bool hlds_e7_memrange_sweep;
244 char *hlds_e7_scan_log;
245 char *hlds_e7_scan_dump_prefix;
246 char *hlds_profile_report;
247 char *redump_dat_dir;
249 bool no_redump_verify;
250 bool xgd1_layout_probe;
251 char *xgd1_layout_probe_report;
252 bool xgd1_raw_id_probe;
253 char *xgd1_raw_id_probe_report;
257 /* Struct for progress data */
259 struct timeval start_time;
260 struct timeval end_time;
262 double mb_total_real;
263 u_int32_t sectors_skipped;
267 static char friidump_drive_letter_from_device(const char *device) {
268 if (!device || !device[0]) return 0;
269 if (device[0] && device[1] == ':') return device[0];
270 if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
275 static const char *friidump_requested_output_target(void) {
276 if (options.xgd1_raw_id_probe_report && options.xgd1_raw_id_probe_report[0]) return options.xgd1_raw_id_probe_report;
277 if (options.xgd1_layout_probe_report && options.xgd1_layout_probe_report[0]) return options.xgd1_layout_probe_report;
278 if (options.iso_out && options.iso_out[0]) return options.iso_out;
279 if (options.xiso_out && options.xiso_out[0]) return options.xiso_out;
280 if (options.raw_out && options.raw_out[0]) return options.raw_out;
285 static void friidump_json_string(FILE *f, const char *s) {
286 const unsigned char *p;
289 for (p = (const unsigned char *) s; *p; p++) {
291 case '\\': fputs("\\\\", f); break;
292 case '"': fputs("\\\"", f); break;
293 case '\b': fputs("\\b", f); break;
294 case '\f': fputs("\\f", f); break;
295 case '\n': fputs("\\n", f); break;
296 case '\r': fputs("\\r", f); break;
297 case '\t': fputs("\\t", f); break;
301 snprintf(tmp, sizeof(tmp), "\\u%04x", (unsigned int) *p);
313 static void friidump_json_kv_string(FILE *f, const char *key, const char *value, bool comma) {
315 friidump_json_string(f, key);
317 friidump_json_string(f, value ? value : "");
318 fputs(comma ? ",\n" : "\n", f);
321 static void friidump_json_kv_u32_hex(FILE *f, const char *key, u_int32_t value, bool comma) {
323 snprintf(tmp, sizeof(tmp), "0x%08x", value);
324 friidump_json_kv_string(f, key, value ? tmp : "", comma);
327 static void friidump_json_kv_int(FILE *f, const char *key, int value, bool comma) {
330 friidump_json_string(f, key);
331 snprintf(tmp, sizeof(tmp), ": %d%s\n", value, comma ? "," : "");
335 static void friidump_json_kv_bool(FILE *f, const char *key, bool value, bool comma) {
337 friidump_json_string(f, key);
338 fputs(value ? ": true" : ": false", f);
339 fputs(comma ? ",\n" : "\n", f);
342 static bool friidump_write_hlds_profile_report(disc *d, const char *path) {
344 if (!d || !path || !path[0])
346 f = fopen(path, "wb");
348 fprintf(stderr, "WARNING: could not write HLDS profile report: %s\n", path);
352 friidump_json_kv_string(f, "schema", "friidump_hlds_profile_report_v1", true);
353 friidump_json_kv_string(f, "drive_model", disc_get_drive_model_string(d), true);
354 friidump_json_kv_string(f, "profile_name", disc_get_hlds_e7_profile_name(d), true);
355 friidump_json_kv_string(f, "support_tier", disc_get_hlds_e7_support_tier(d), true);
356 friidump_json_kv_string(f, "family", disc_get_hlds_e7_family(d), true);
357 friidump_json_kv_string(f, "tokens", disc_get_hlds_e7_tokens(d), true);
358 friidump_json_kv_string(f, "stage5b_record_id", disc_get_hlds_e7_record_id(d), true);
359 friidump_json_kv_u32_hex(f, "static_e7_cdb_base", disc_get_hlds_e7_static_cdb_base(d), true);
360 friidump_json_kv_u32_hex(f, "static_e7_gate", disc_get_hlds_e7_static_gate(d), true);
361 friidump_json_kv_int(f, "runtime_e7_type", (int) disc_get_hlds_e7_type(d), true);
362 friidump_json_kv_u32_hex(f, "runtime_cache_base", disc_get_hlds_e7_cache_base(d), true);
363 friidump_json_kv_int(f, "runtime_mem_windows", (int) disc_get_hlds_e7_mem_blocks(d), true);
364 friidump_json_kv_int(f, "preferred_method", disc_get_hlds_e7_preferred_method(d), true);
365 friidump_json_kv_int(f, "selected_method", (int) disc_get_method(d), true);
366 friidump_json_kv_int(f, "selected_command", (int) disc_get_command(d), true);
367 friidump_json_kv_bool(f, "live_safe_from_static_only", false, true);
368 friidump_json_kv_string(f, "safety_note", "Static firmware addresses are evidence only; FriiDump does not use them as host-side commands and does not emit flash/write/update CDBs.", true);
369 friidump_json_kv_string(f, "notes", disc_get_hlds_e7_notes(d), false);
376 static void friidump_retarget_log_from_options(void) {
377 const char *target = friidump_requested_output_target();
379 xbox_ref_log_retarget(target);
380 } else if (options.device && options.device[0]) {
381 xbox_ref_log_open_for_target(NULL, friidump_drive_letter_from_device(options.device));
386 void progress_for_guis (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
388 double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
395 gettimeofday (&(stats -> start_time), NULL);
396 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
397 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
398 stats -> sectors_skipped = sectors_done;
400 perc = (int) (100.0 * sectors_done / total_sectors);
401 gettimeofday (&now, NULL);
402 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
403 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
404 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
405 mb_hour = mb_done_real / elapsed * 60 * 60;
406 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
407 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
408 if (localtime_r (&eta, &etatm))
409 strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
411 sprintf (buf, "N/A");
413 /* This is the only thing we print to stdout, so that other programs can easily capture and parse our output */
414 fprintf (stdout, "%d%%|%u/%u sectors|%.2lf/%.0lf MB|%.0lf/%.0lf seconds|%.2lf MB/h|%s\n",
415 perc, sectors_done, total_sectors, mb_done, stats -> mb_total, elapsed, seconds_left, mb_hour, buf);
419 /* Save return time, in case this will be the last call */
420 gettimeofday (&(stats -> end_time), NULL);
426 void progress (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
428 double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
435 gettimeofday (&(stats -> start_time), NULL);
436 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
437 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
438 stats -> sectors_skipped = sectors_done;
440 perc = (int) (100.0 * sectors_done / total_sectors);
441 gettimeofday (&now, NULL);
442 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
443 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
444 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
445 mb_hour = mb_done_real / elapsed * 60 * 60;
446 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
447 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
448 if (localtime_r (&eta, &etatm))
449 strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
451 sprintf (buf, "N/A");
453 fprintf (stdout, "\r%3d%% ", perc);
454 fprintf (stdout, "|");
455 for (i = 0; i < 100 / 3; i++) {
457 fprintf (stdout, "*");
459 fprintf (stdout, "-");
461 fprintf (stdout, "| ");
462 fprintf (stdout, "%.2lf MB/h, ETA: %s", mb_hour, buf);
466 if (sectors_done == total_sectors)
469 /* Save return time, in case this will be the last call */
470 gettimeofday (&(stats -> end_time), NULL);
477 void welcome (void) {
480 "FriiDump " PACKAGE_VERSION " - Copyright (C) 2007 Arep\n"
481 "This software comes with ABSOLUTELY NO WARRANTY.\n"
482 "This is free software, and you are welcome to redistribute it\n"
483 "under certain conditions; see COPYING for details.\n"
485 "Official support forum: http://wii.console-tribe.com\n"
487 "Forum for this UNOFFICIAL VERSION: http://forum.redump.org\n"
497 static void friidump_init_executable_dir(const char *argv0) {
504 g_executable_dir[0] = '\0';
509 DWORD result = GetModuleFileNameA(NULL, path, (DWORD)sizeof(path));
510 if (result > 0 && result < sizeof(path)) {
512 length = (size_t)result;
516 #if defined(__linux__)
518 ssize_t result = readlink("/proc/self/exe", path, sizeof(path) - 1);
519 if (result > 0 && (size_t)result < sizeof(path)) {
521 length = (size_t)result;
525 if (length == 0 && argv0 && argv0[0] &&
526 (strchr(argv0, '/') || strchr(argv0, '\\'))) {
527 char *resolved = realpath(argv0, path);
529 length = strlen(path);
536 slash = strrchr(path, '/');
537 backslash = strrchr(path, '\\');
539 if (backslash && (!separator || backslash > separator))
540 separator = backslash;
547 snprintf(g_executable_dir, sizeof(g_executable_dir), "%s", path);
550 static const char *friidump_redump_dat_basename(disc_type type_id) {
552 case DISC_TYPE_GAMECUBE:
553 return "Nintendo - GameCube.dat";
555 case DISC_TYPE_WII_DL:
556 return "Nintendo - Wii.dat";
558 return "Microsoft - Xbox.dat";
564 static uint64_t friidump_file_size(const char *path) {
565 if (!path || !path[0])
570 if (_stat64(path, &st) == 0)
571 return (uint64_t)st.st_size;
576 if (stat(path, &st) == 0)
577 return (uint64_t)st.st_size;
583 static bool friidump_sync_report_file(FILE *f) {
584 if (!f || fflush(f) != 0)
587 return _commit(_fileno(f)) == 0;
589 return fsync(fileno(f)) == 0;
593 static bool friidump_atomic_replace(const char *temporary_path, const char *final_path) {
594 if (!temporary_path || !final_path)
597 return MoveFileExA(temporary_path, final_path,
598 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != 0;
600 return rename(temporary_path, final_path) == 0;
604 static void friidump_json_write_string_value(FILE *f, const char *prefix,
605 const char *value, const char *suffix) {
607 friidump_json_string(f, value ? value : "");
611 static void friidump_write_redump_report(const redump_verify_result *result,
612 const char *output_path,
613 uint64_t output_size,
618 const char *hash_source,
619 const char *representation_note) {
621 char *temporary_path;
623 size_t temporary_path_size;
626 if (!options.redump_report || !options.redump_report[0] || !result)
629 temporary_path_size = strlen(options.redump_report) + 5;
630 temporary_path = (char *)malloc(temporary_path_size);
631 if (!temporary_path) {
632 fprintf(stderr, "Redump report.......: memory allocation failed\n");
635 snprintf(temporary_path, temporary_path_size, "%s.tmp", options.redump_report);
636 remove(temporary_path);
638 f = fopen(temporary_path, "wb");
640 fprintf(stderr, "Redump report.......: could not write temporary file %s\n", temporary_path);
641 free(temporary_path);
645 /* Use fputs/fputc rather than the logged fprintf wrapper. Report contents
646 * belong in the JSON artifact, not as a malformed partial mirror in the
647 * human-readable run log. */
649 fputs(" \"schema\": 2,\n", f);
650 friidump_json_write_string_value(f, " \"producer\": ", "friidump-" PACKAGE_VERSION, ",\n");
651 friidump_json_write_string_value(f, " \"status\": ", redump_verify_status_string(result->status), ",\n");
652 friidump_json_write_string_value(f, " \"overall\": ", redump_verify_overall_string(result), ",\n");
653 friidump_json_write_string_value(f, " \"confidence\": ", redump_verify_confidence_string(result), ",\n");
654 friidump_json_write_string_value(f, " \"output\": ", output_path ? output_path : "", ",\n");
655 snprintf(line, sizeof(line), " \"output_size\": %llu,\n", (unsigned long long)output_size);
657 friidump_json_write_string_value(f, " \"crc32\": ", crc32 ? crc32 : "", ",\n");
658 friidump_json_write_string_value(f, " \"md5\": ", md5 ? md5 : "", ",\n");
659 friidump_json_write_string_value(f, " \"sha1\": ", sha1 ? sha1 : "", ",\n");
660 friidump_json_write_string_value(f, " \"sha256\": ", sha256 ? sha256 : "", ",\n");
661 friidump_json_write_string_value(f, " \"hash_source\": ", hash_source ? hash_source : "", ",\n");
662 friidump_json_write_string_value(f, " \"representation_note\": ", representation_note ? representation_note : "", ",\n");
663 friidump_json_write_string_value(f, " \"dat_path\": ", result->dat_path, ",\n");
664 snprintf(line, sizeof(line), " \"entries_scanned\": %lu,\n", result->entries_scanned);
666 snprintf(line, sizeof(line), " \"exact_matches\": %lu,\n", result->exact_matches);
668 friidump_json_write_string_value(f, " \"match_kind\": ", redump_verify_match_kind_string(result), ",\n");
669 friidump_json_write_string_value(f, " \"game_name\": ", result->game_name, ",\n");
670 friidump_json_write_string_value(f, " \"rom_name\": ", result->rom_name, ",\n");
672 fputs(" \"field_match_counts\": {\n", f);
673 snprintf(line, sizeof(line), " \"size\": %lu,\n", result->size_matches); fputs(line, f);
674 snprintf(line, sizeof(line), " \"crc32\": %lu,\n", result->crc32_matches); fputs(line, f);
675 snprintf(line, sizeof(line), " \"md5\": %lu,\n", result->md5_matches); fputs(line, f);
676 snprintf(line, sizeof(line), " \"sha1\": %lu\n", result->sha1_matches); fputs(line, f);
679 fputs(" \"expected\": {\n", f);
680 friidump_json_write_string_value(f, " \"size\": ", result->expected_size, ",\n");
681 friidump_json_write_string_value(f, " \"crc32\": ", result->expected_crc32, ",\n");
682 friidump_json_write_string_value(f, " \"md5\": ", result->expected_md5, ",\n");
683 friidump_json_write_string_value(f, " \"sha1\": ", result->expected_sha1, "\n");
686 fputs(" \"evidence\": {\n", f);
687 friidump_json_write_string_value(f, " \"size\": ", redump_field_status_string(result->size_status), ",\n");
688 friidump_json_write_string_value(f, " \"crc32\": ", redump_field_status_string(result->crc32_status), ",\n");
689 friidump_json_write_string_value(f, " \"md5\": ", redump_field_status_string(result->md5_status), ",\n");
690 friidump_json_write_string_value(f, " \"sha1\": ", redump_field_status_string(result->sha1_status), "\n");
692 friidump_json_write_string_value(f, " \"detail\": ", result->detail, "\n");
695 write_ok = !ferror(f) && friidump_sync_report_file(f);
700 remove(temporary_path);
701 fprintf(stderr, "Redump report.......: write/flush failed; final report was not replaced\n");
702 free(temporary_path);
706 if (!friidump_atomic_replace(temporary_path, options.redump_report)) {
707 fprintf(stderr, "Redump report.......: could not replace %s; complete temporary report retained at %s\n",
708 options.redump_report, temporary_path);
709 free(temporary_path);
713 fprintf(stderr, "Redump report.......: %s (atomic write)\n", options.redump_report);
714 free(temporary_path);
717 static void friidump_print_redump_field(const char *label, redump_field_status status) {
718 fprintf(stderr, "%-20s %s\n", label, redump_field_status_string(status));
721 static void friidump_verify_redump_values(disc_type type_id,
722 const char *output_path,
723 uint64_t output_size,
728 const char *hash_source) {
729 const char *basename;
731 const char *representation_note;
734 redump_verify_result_init(&g_redump_result);
735 g_redump_attempted = false;
736 representation_note = (type_id == DISC_TYPE_XBOX)
737 ? "XGD1 acquisition success and exact Redump hash identity are separate claims; unresolved zero-filled pregame/postgame content may prevent an exact match."
740 if (options.no_redump_verify) {
741 fprintf(stderr, "Redump verification: DISABLED (--no-redump-verify)\n");
744 if (options.no_hashing) {
745 fprintf(stderr, "Redump verification: SKIPPED (hashing disabled)\n");
748 if (!output_path || !output_path[0]) {
749 fprintf(stderr, "Redump verification: SKIPPED (final output path unavailable)\n");
752 if (!crc32 || !crc32[0] || !md5 || !md5[0] || !sha1 || !sha1[0]) {
753 fprintf(stderr, "Redump verification: SKIPPED (finalized CRC32/MD5/SHA-1 evidence incomplete)\n");
757 basename = friidump_redump_dat_basename(type_id);
759 fprintf(stderr, "Redump verification: SKIPPED (no DAT mapping for this disc type)\n");
763 dat_dir = (options.redump_dat_dir && options.redump_dat_dir[0])
764 ? options.redump_dat_dir
766 redump_resolve_dat_path(dat_dir, g_executable_dir, basename,
767 dat_path, sizeof(dat_path));
768 if (output_size == 0)
769 output_size = friidump_file_size(output_path);
771 g_redump_attempted = true;
772 redump_verify_dat_file(dat_path, output_size, crc32, md5, sha1, &g_redump_result);
774 fprintf(stderr, "\nRedump verification\n");
775 fprintf(stderr, "------------------------------------------------------------\n");
776 fprintf(stderr, "%-20s %s\n", "DAT", g_redump_result.dat_path);
777 fprintf(stderr, "%-20s %s\n", "Hash source", hash_source ? hash_source : "Finalized output hashes");
778 fprintf(stderr, "%-20s %lu\n", "Entries scanned", g_redump_result.entries_scanned);
780 if (g_redump_result.status == REDUMP_VERIFY_MATCH) {
781 fprintf(stderr, "%-20s %s\n", "Matched entry", g_redump_result.game_name);
782 fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name);
783 friidump_print_redump_field("Image size", g_redump_result.size_status);
784 friidump_print_redump_field("CRC32", g_redump_result.crc32_status);
785 friidump_print_redump_field("MD5", g_redump_result.md5_status);
786 friidump_print_redump_field("SHA-1", g_redump_result.sha1_status);
787 if (g_redump_result.exact_matches > 1)
788 fprintf(stderr, "%-20s %lu exact entries\n", "Duplicate matches", g_redump_result.exact_matches);
789 } else if (g_redump_result.candidate_available) {
790 fprintf(stderr, "%-20s %s\n", "Closest candidate", g_redump_result.game_name);
791 fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name);
792 friidump_print_redump_field("Image size", g_redump_result.size_status);
793 friidump_print_redump_field("CRC32", g_redump_result.crc32_status);
794 friidump_print_redump_field("MD5", g_redump_result.md5_status);
795 friidump_print_redump_field("SHA-1", g_redump_result.sha1_status);
797 fprintf(stderr, "%-20s %s\n", "Closest candidate", "None (no hash-correlated entry)");
798 fprintf(stderr, "%-20s %lu entries\n", "Size matches", g_redump_result.size_matches);
799 fprintf(stderr, "%-20s %lu entries\n", "CRC32 matches", g_redump_result.crc32_matches);
800 fprintf(stderr, "%-20s %lu entries\n", "MD5 matches", g_redump_result.md5_matches);
801 fprintf(stderr, "%-20s %lu entries\n", "SHA-1 matches", g_redump_result.sha1_matches);
804 fprintf(stderr, "%-20s %s\n", "Overall", redump_verify_overall_string(&g_redump_result));
805 fprintf(stderr, "%-20s %s\n", "Confidence", redump_verify_confidence_string(&g_redump_result));
806 if (g_redump_result.status != REDUMP_VERIFY_MATCH)
807 fprintf(stderr, "%-20s %s\n", "Detail", g_redump_result.detail);
808 if (type_id == DISC_TYPE_XBOX)
809 fprintf(stderr, "%-20s %s\n", "Representation note", representation_note);
811 friidump_write_redump_report(&g_redump_result, output_path, output_size,
812 crc32, md5, sha1, sha256, hash_source,
813 representation_note);
816 static void friidump_verify_redump_iso(dumper *dmp, disc_type type_id) {
817 const char *output_path;
822 output_path = options.iso_out;
823 friidump_verify_redump_values(type_id,
825 friidump_file_size(output_path),
826 dumper_get_iso_crc32(dmp),
827 dumper_get_iso_md5(dmp),
828 dumper_get_iso_sha1(dmp),
829 dumper_get_iso_sha2(dmp),
830 "FriiDump finalized ISO multihash");
833 static void friidump_verify_redump_xbox_reference(dumper *dmp) {
834 xbox_ref_dump_result result;
836 xbox_ref_dump_result_init(&result);
837 if (!dumper_get_xbox_reference_result(dmp, &result)) {
838 fprintf(stderr, "Redump verification: SKIPPED (Xbox reference result handoff unavailable)\n");
841 if (result.mode != '1') {
842 fprintf(stderr, "Redump verification: SKIPPED (Xbox XISO is not a full Redump disc image)\n");
845 if (!result.dump_success) {
846 fprintf(stderr, "Redump verification: SKIPPED (Xbox reference dump did not complete)\n");
849 if (!result.hashes_complete) {
850 fprintf(stderr, "Redump verification: SKIPPED (Xbox finalized full-file hashes incomplete)\n");
854 if (g_validation_summary.active) {
855 if (result.output_path[0])
856 friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), result.output_path);
858 friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), result.title);
859 if (result.media_id[0])
860 friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), result.media_id);
861 if (result.output_sectors)
862 g_validation_summary.sectors = result.output_sectors;
863 g_validation_summary.seed_applicable = false;
864 g_validation_summary.have_seed_duration = false;
866 if (result.elapsed_seconds > 0.0) {
867 g_operation_duration_override_valid = true;
868 g_operation_duration_override = result.elapsed_seconds;
871 friidump_verify_redump_values(DISC_TYPE_XBOX,
878 "GDR-8050L reference finalized full-file hashes");
883 * |-------------------------------------------------------------------------------|
885 fprintf (stderr, "\n"
886 "Available command line options:\n"
888 " -h, --help Show this help\n"
889 " -a, --autodump Dump the disc to an ISO file with an\n"
890 " automatically-generated name, resuming the dump\n"
892 " -g, --gui Use more verbose output that can be easily\n"
893 " parsed by a GUI frontend\n"
894 " -d, --device <device> Dump disc from device <device>\n"
895 " -p, --stop Instruct device to stop disc rotation\n"
896 " -D, --dvd Force standard DVD-ROM mode for any drive\n"
897 " (equivalent to -T 3; uses FriiDump original\n"
898 " DVD/raw/ISO paths, not Xbox mode)\n"
899 " -c, --command <nr> Force memory dump command:\n"
900 " 0 - vanilla 2064\n"
901 " 1 - vanilla 2384\n"
905 " -x, --speed <x> Set streaming speed (1, 24, 32, 64, etc.,\n"
906 " where 1 = 150 KiB/s and so on)\n"
907 " -T, --type <nr> Force disc type:\n"
912 " 4 - Xbox/XGD 2048-byte-sector mode\n"
913 " Native profiles: GDR-8050L and GDR-3120L.\n"
914 " Other drives keep normal FriiDump behavior\n"
915 " unless Xbox mode is explicitly forced.\n"
916 " -S, --size <sectors> Force disc size\n"
917 " -r, --raw <file> Output to file <file> in raw format (2064-byte\n"
919 " -i, --iso[=<file>] Output to file <file> in ISO format (2048-byte\n"
920 " sectors). For Xbox/GDR-8050L, omitting <file>\n"
921 " derives Title[MediaID].iso from the XBE/DMI;\n"
922 " providing <file> is an explicit override. For\n"
923 " Xbox/XGD this reconstructs the redump-style\n"
924 " XGD1 layout and writes .pfi.bin, .dmi.bin, and\n"
925 " .redump.json metadata when possible\n"
926 " -X, --xiso[=<file>] Output Xbox/XGD game partition as XISO (.xiso).\n"
927 " For Xbox/GDR-8050L, omitting <file> derives\n"
928 " Title[MediaID].xiso from the XBE/DMI; providing\n"
929 " <file> is an explicit override. Attempts to read\n"
930 " the 32-sector game lead-in from drive-readable\n"
931 " sectors and zero-fills only unreadable sectors.\n"
932 " -u, --unscramble <file> Convert (unscramble) raw image contained in\n"
933 " <file> to ISO format\n"
934 " -H, --nohash Do not compute CRC32/MD5/SHA-1/SHA-256 hashes\n"
935 " for generated files\n"
936 " -s, --resume Resume partial dump\n"
937 " - General -----------------------------------\n"
938 " -0, --method0[=<req>,<exp>] Use dumping method 0 (Optional argument\n"
939 " specifies how many sectors to request from disc\n"
940 " and read from cache at a time. Values should be\n"
941 " separated with a comma. Default 16,16)\n"
942 " - Non-Streaming -----------------------------\n"
943 " -1, --method1[=<req>,<exp>] Use dumping method 1 (Default 16,16)\n"
944 " -2, --method2[=<req>,<exp>] Use dumping method 2 (Default 16,16)\n"
945 " -3, --method3[=<req>,<exp>] Use dumping method 3 (Default 16,16)\n"
946 " - Streaming ---------------------------------\n"
947 " -4, --method4[=<req>,<exp>] Use dumping method 4 (Default 27,27)\n"
948 " -5, --method5[=<req>,<exp>] Use dumping method 5 (Default 27,27)\n"
949 " -6, --method6[=<req>,<exp>] Use dumping method 6 (Default 27,27)\n"
950 " - Hitachi -----------------------------------\n"
951 " -7, --method7 Use dumping method 7 (Read and dump 5 blocks\n"
952 " at a time, using streaming read)\n"
953 " -8, --method8 Use dumping method 8 (Read and dump 5 blocks\n"
954 " at a time, using streaming read, using DMA)\n"
955 " -9, --method9 Use dumping method 9 (Read and dump 5 blocks\n"
956 " at a time, using streaming read, using DMA and\n"
957 " some speed tricks)\n"
958 " --hlds-e7-scan Probe HLDS HIT 0xE7 cache/memdump bases only;\n"
959 " writes JSON and does not crack seeds or dump data\n"
960 " --hlds-e7-subcmd-sweep Probe HIT 0xE7 subcommands/address candidates only;\n"
961 " writes JSON and does not crack seeds or dump data\n"
962 " --hlds-e7-memrange-sweep Sweep wider HIT 0xE7 subcmd 0x01 address ranges;\n"
963 " writes JSON and does not crack seeds or dump data\n"
964 " --scan-log <file> JSON output path for --hlds-e7-scan\n"
965 " --scan-dump-prefix <prefix> Optional raw 0xE7 window dump prefix for --hlds-e7-scan\n"
966 " --hlds-profile-report <file> Write selected HLDS profile/evidence JSON\n"
967 " --redump-dat-dir <dir> Directory containing canonical Redump DAT files\n"
968 " (default: executable-relative redump_dat, then current directory)\n"
969 " --redump-report <file> Write atomic Redump evidence JSON\n"
970 " --no-redump-verify Disable automatic post-dump DAT verification\n"
971 " --xgd1-layout-probe <file> Read-only locked/unlocked XGD1 boundary probe;\n"
972 " writes atomic JSON and does not create an ISO\n"
973 " --xgd1-raw-id-probe <file> Modified-firmware cache-flushed, block-aligned raw-ID probe;\n"
974 " maps logical LBAs to decoded physical sector IDs\n"
975 " -A, --allmethods Try all known command/method combinations until\n"
976 " one works. Reopens the drive for each command so\n"
977 " command-specific vendor handlers are rebound.\n"
979 " -n, --donottunscramble Do not try unscrambling to check EDC. Only\n"
980 " useful for testing the raw performance of the\n"
981 " different methods\n"
982 " -f, --donottflush Do not call fflush() after every fwrite()\n"
990 bool optparse (int argc, char **argv) {
994 int option_index = 0;
995 static struct option long_options[] = {
996 {"help", 0, 0, 'h'}, //0 - no_argument
997 {"autodump", 0, 0, 'a'},
999 {"device", 1, 0, 'd'}, //1 - required_argument
1002 {"xiso", 2, 0, 'X'},
1003 {"unscramble", 1, 0, 'u'},
1004 {"nohash", 0, 0, 'H'},
1005 {"resume", 0, 0, 's'},
1006 {"method0", 2, 0, '0'}, //2 - optional_argument
1007 {"method1", 2, 0, '1'},
1008 {"method2", 2, 0, '2'},
1009 {"method3", 2, 0, '3'},
1010 {"method4", 2, 0, '4'},
1011 {"method5", 2, 0, '5'},
1012 {"method6", 2, 0, '6'},
1013 {"method7", 0, 0, '7'},
1014 {"method8", 0, 0, '8'},
1015 {"method9", 0, 0, '9'},
1016 {"stop", 0, 0, 'p'},
1018 {"command", 1, 0, 'c'},
1019 {"startsector", 1, 0, 't'},
1020 {"size", 1, 0, 'S'},
1021 {"speed", 1, 0, 'x'},
1022 {"type", 1, 0, 'T'},
1023 {"allmethods", 0, 0, 'A'},
1024 {"hlds-e7-scan", 0, 0, 1000},
1025 {"hlds-e7-subcmd-sweep", 0, 0, 1003},
1026 {"hlds-e7-memrange-sweep", 0, 0, 1004},
1027 {"scan-log", 1, 0, 1001},
1028 {"scan-dump-prefix", 1, 0, 1002},
1029 {"hlds-profile-report", 1, 0, 1005},
1030 {"redump-dat-dir", 1, 0, 1006},
1031 {"redump-report", 1, 0, 1007},
1032 {"no-redump-verify", 0, 0, 1008},
1033 {"xgd1-layout-probe", 1, 0, 1009},
1034 {"xgd1-raw-id-probe", 1, 0, 1010},
1036 /* We don't want newbies to generate and put into circulation bad dumps, so this options are disabled for releases */
1037 {"donottunscramble", 0, 0, 'n'},
1038 {"donottflush", 0, 0, 'f'},
1048 /* Init options to default values */
1049 options.device = NULL;
1050 options.autodump = false;
1051 options.gui = false;
1052 options.raw_in = NULL;
1053 options.raw_out = NULL;
1054 options.iso_out = NULL;
1055 options.xiso_out = NULL;
1056 options.iso_requested = false;
1057 options.xiso_requested = false;
1058 options.xbox_filename_override = false;
1059 options.xbox_auto_filename = false;
1060 options.no_hashing = false;
1061 options.resume = false;
1062 options.dump_method = -1;
1063 options.command = -1;
1064 options.start_sector = -1;
1065 options.sectors_no = -1;
1067 options.disctype = -1;
1068 options.sec_disc = -1;
1069 options.sec_mem = -1;
1070 options.no_unscrambling = false;
1071 options.no_flushing = false;
1072 options.stop_unit = false;
1073 options.allmethods = false;
1074 options.hlds_e7_scan = false;
1075 options.hlds_e7_subcmd_sweep = false;
1076 options.hlds_e7_memrange_sweep = false;
1077 options.hlds_e7_scan_log = NULL;
1078 options.hlds_e7_scan_dump_prefix = NULL;
1079 options.hlds_profile_report = NULL;
1080 options.redump_dat_dir = NULL;
1081 options.redump_report = NULL;
1082 options.no_redump_verify = false;
1083 options.xgd1_layout_probe = false;
1084 options.xgd1_layout_probe_report = NULL;
1085 options.xgd1_raw_id_probe = false;
1086 options.xgd1_raw_id_probe_report = NULL;
1090 c = getopt_long (argc, argv, "hpagd:r:i::X::u:Hs0::1::2::3::4::5::6::789Dc:t:S:x:T:Anf", long_options, &option_index);
1092 c = getopt_long (argc, argv, "hpagd:r:i::X::u:Hs0::1::2::3::4::5::6::789Dc:t:S:x:T:A", long_options, &option_index);
1101 options.stop_unit = true;
1104 options.autodump = true;
1105 options.resume = true;
1111 my_strdup (options.device, optarg);
1114 my_strdup (options.raw_out, optarg);
1117 options.iso_requested = true;
1118 /* Preserve the old `-i file.iso` syntax even though Xbox now also
1119 * supports bare `-i` for XBE/DMI-derived names. */
1120 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
1121 optarg = argv[optind++];
1123 my_strdup (options.iso_out, optarg);
1124 options.xbox_filename_override = true;
1126 options.xbox_auto_filename = true;
1130 options.xiso_requested = true;
1131 /* Preserve the old `-X file.xiso` syntax while allowing bare `-X`. */
1132 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
1133 optarg = argv[optind++];
1135 my_strdup (options.xiso_out, optarg);
1136 options.xbox_filename_override = true;
1138 options.xbox_auto_filename = true;
1142 my_strdup (options.raw_in, optarg);
1145 options.no_hashing = true;
1148 options.resume = true;
1157 options.dump_method = c - '0';
1159 result = strtok(optarg, ",");
1160 result = strtok(NULL, ",");
1161 options.sec_disc = atol(strpbrk(optarg,"1234567890"));
1162 if (result) options.sec_mem = atol(result);
1172 options.dump_method = c - '0';
1175 options.disctype = DISC_TYPE_DVD;
1176 unscrambler_set_disctype (DISC_TYPE_DVD);
1179 options.command = atol (optarg);
1180 if (options.command > 4) {
1186 options.start_sector = atol (optarg);
1189 options.sectors_no = atol (optarg);
1192 options.speed = atol (optarg);
1195 options.disctype = atol (optarg);
1196 if (options.disctype > 4) {
1200 if (options.disctype <= DISC_TYPE_DVD)
1201 unscrambler_set_disctype (options.disctype);
1204 options.allmethods = true;
1205 options.resume = true;
1209 options.no_unscrambling = true;
1212 options.no_flushing = true;
1216 options.hlds_e7_scan = true;
1219 options.hlds_e7_subcmd_sweep = true;
1222 options.hlds_e7_memrange_sweep = true;
1225 my_strdup (options.hlds_e7_scan_log, optarg);
1228 my_strdup (options.hlds_e7_scan_dump_prefix, optarg);
1231 my_strdup (options.hlds_profile_report, optarg);
1234 my_strdup (options.redump_dat_dir, optarg);
1237 my_strdup (options.redump_report, optarg);
1240 options.no_redump_verify = true;
1243 options.xgd1_layout_probe = true;
1244 my_strdup (options.xgd1_layout_probe_report, optarg);
1245 options.disctype = DISC_TYPE_XBOX;
1248 options.xgd1_raw_id_probe = true;
1249 my_strdup (options.xgd1_raw_id_probe_report, optarg);
1250 options.disctype = DISC_TYPE_XBOX;
1255 // fprintf (stderr, "?? getopt returned character code 0%o ??\n", c);
1261 if (optind < argc) {
1262 /* Command-line arguments remaining. Ignore them, warning the user. */
1263 fprintf (stderr, "WARNING: Extra parameters ignored\n");
1266 if (options.xgd1_layout_probe || options.xgd1_raw_id_probe)
1267 options.disctype = DISC_TYPE_XBOX;
1269 /* Sanity checks... */
1271 if (!options.device && !options.raw_in) {
1272 fprintf (stderr, "No operation specified. Please use the -d or -u options.\n");
1273 } else if (options.raw_in && options.raw_out) {
1275 "Are you sure you want to convert a raw image to another raw image? ;)\n"
1276 "Take a look at the -i and -a options!\n"
1278 } else if (options.autodump && (options.raw_out || options.iso_requested || options.xiso_requested)) {
1279 fprintf (stderr, "The -r, -i and -X options cannot be used together with -a.\n");
1280 } else if (options.xiso_requested && (options.raw_out || options.iso_requested)) {
1281 fprintf (stderr, "The -X/--xiso option is a separate Xbox output mode and cannot be combined with -r or -i.\n");
1282 } else if ((options.xgd1_layout_probe || options.xgd1_raw_id_probe) &&
1283 (options.autodump || options.raw_in || options.raw_out ||
1284 options.iso_requested || options.xiso_requested ||
1285 options.allmethods || options.hlds_e7_scan ||
1286 options.hlds_e7_subcmd_sweep || options.hlds_e7_memrange_sweep ||
1287 (options.xgd1_layout_probe && options.xgd1_raw_id_probe))) {
1288 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");
1290 /* Specified options seem to make sense */
1297 int dologic (disc *d, progstats *stats) {
1299 char *type, *game_id, *region, *maker_id, *maker, *version, *title, tmp[0x03E0 + 4 + 1];
1300 bool drive_supported;
1302 bool xbox_output_requested;
1303 bool dump_attempted;
1306 u_int32_t current_sector = 0;
1308 xbox_forced = (options.disctype == DISC_TYPE_XBOX);
1309 xbox_output_requested = xbox_forced || options.xiso_requested || options.xgd1_layout_probe || options.xgd1_raw_id_probe;
1310 dump_attempted = false;
1314 if (options.stop_unit) { //stop rotation, if requested
1315 fprintf (stderr, "Issuing STOP command... %s\n", (disc_stop_unit (d, false)) ? "OK" : "Failed");
1318 else disc_stop_unit(d, true); //else start rotation
1320 drive_supported = disc_get_drive_support_status (d);
1323 "Drive information:\n"
1324 "----------------------------------------------------------------------\n"
1325 "Drive model........: %s\n"
1326 "Supported..........: %s\n", disc_get_drive_model_string (d), drive_supported ? "Yes" : "No"
1329 if (xbox_output_requested && !disc_is_xbox_unlock_drive (d)) {
1331 "Xbox/XGD output is limited to the supported Xbox unlock profiles "
1332 "currently wired into this branch: GDR-8050L, GDR-3120L, "
1333 "and known Samsung/Kreon-style vendor-unlock drives.\n"
1334 "Refusing to fall back to FriiDump GC/Wii methods for Xbox mode on this drive.\n");
1338 /* Xbox/XGD (-T 4 or -X) is a direct MMC/SCSI READ(10) path.
1339 * Do not let the detected GC/Wii vendor memdump method (for example
1340 * Hitachi command 2 / method 9 on GDR-8050L) drive sector reads. */
1341 if (xbox_output_requested && options.dump_method == -1)
1342 options.dump_method = 10;
1344 init_range(d, options.sec_disc, options.sec_mem);
1346 if (!(disc_set_read_method (d, options.dump_method)))
1349 if (xbox_output_requested && disc_get_method(d) == 10) {
1350 fprintf (stderr, "Command............: Xbox direct MMC/SCSI path\n");
1351 fprintf (stderr, "Method.............: 10 (Xbox READ(10))\n");
1353 if (options.command!=-1) fprintf (stderr,
1354 "Command............: %d (forced)\n", disc_get_command(d));
1355 else fprintf (stderr,
1356 "Command............: %d\n", disc_get_command(d));
1357 if (disc_get_def_method(d)!=disc_get_method(d)) fprintf (stderr,
1358 "Method.............: %d (forced)\n", disc_get_method(d));
1359 else fprintf (stderr,
1360 "Method.............: %d\n", disc_get_method(d));
1361 if (disc_get_hlds_e7_type (d) != 0) {
1362 fprintf (stderr, "HLDS 0xE7 profile..: %s\n", disc_get_hlds_e7_profile_name (d));
1363 fprintf (stderr, "HLDS support tier..: %s\n", disc_get_hlds_e7_support_tier (d));
1364 fprintf (stderr, "HLDS family........: %s\n", disc_get_hlds_e7_family (d));
1365 fprintf (stderr, "HLDS E7 tokens.....: %s\n", disc_get_hlds_e7_tokens (d));
1366 fprintf (stderr, "HLDS Stage5B row...: %s\n", disc_get_hlds_e7_record_id (d));
1367 fprintf (stderr, "Static CDB/gate....: 0x%03x / 0x%08x\n", disc_get_hlds_e7_static_cdb_base (d), disc_get_hlds_e7_static_gate (d));
1368 fprintf (stderr, "Cache base.........: 0x%08x\n", disc_get_hlds_e7_cache_base (d));
1369 fprintf (stderr, "Memory windows.....: %u\n", disc_get_hlds_e7_mem_blocks (d));
1370 if (disc_get_hlds_e7_notes (d) && disc_get_hlds_e7_notes (d)[0])
1371 fprintf (stderr, "HLDS notes.........: %s\n", disc_get_hlds_e7_notes (d));
1374 if (options.hlds_profile_report) {
1375 if (friidump_write_hlds_profile_report(d, options.hlds_profile_report))
1376 fprintf(stderr, "HLDS profile report: %s\n", options.hlds_profile_report);
1378 options.dump_method=disc_get_method(d);
1379 friidump_summary_begin(d);
1380 if ((options.dump_method==0)
1381 || (options.dump_method==1) || (options.dump_method==2) || (options.dump_method==3)
1382 || (options.dump_method==4) || (options.dump_method==5) || (options.dump_method==6)
1385 "Requested sectors..: %d\n", disc_get_sec_disc(d));
1387 "Expected sectors...: %d\n", disc_get_sec_mem(d));
1390 fprintf (stderr, "\nPress Ctrl+C at any time to terminate\n");
1392 //set speed for 1st time
1393 if (options.speed != -1) disc_set_speed(d, options.speed * 177);
1394 if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
1395 // disc_set_speed(d, 0xffff);
1397 /* Windows may attach filesystem/autoplay polling to odd GC/Wii discs and
1398 * steal the drive during the HLDS 0xE7 seed phase, especially on Type1
1399 * GCC-4160N/GCC-4240N. Reuse the same volume guard mechanism that the
1400 * Xbox path already uses, but apply it before disc_init()/seed reads for
1401 * all HLDS 0xE7 GC/Wii profiles. Failure is warning-only because Explorer
1402 * may already have a transient handle; the read path itself remains the
1404 if (!xbox_output_requested && disc_get_hlds_e7_type (d) != 0) {
1405 if (disc_get_hlds_e7_type (d) == 44 || disc_get_hlds_e7_type (d) == 45) {
1407 "\nGDR-8050L modified-firmware warning:\n"
1408 " This GC/Wii 0xE7 path assumes a cross-flashed or modified GDR-8050L firmware with 0xE7 memdump support added.\n"
1409 " Stock GDR-8050L firmware is still supported for Xbox ripping, but it is not expected to dump GC/Wii discs through this path.\n");
1412 "\nWindows AutoPlay warning:\n"
1413 " HLDS 0xE7 GC/Wii seed reads are sensitive to Windows polling.\n"
1414 " Disable AutoPlay for this drive/media and close File Explorer or any \"insert a disc\" dialogs before dumping.\n"
1415 " FriiDump will try to lock the volume, but AutoPlay can still interfere before or during seed retrieval.\n");
1416 fprintf (stderr, "Applying Windows volume lock guard for HLDS 0xE7 seed reads... ");
1417 disc_refresh_volume (d);
1418 if (disc_lock_volume (d) < 0)
1419 fprintf (stderr, "Warning: failed; disable AutoPlay, close File Explorer/AutoPlay dialogs for this drive, then rerun.\n");
1421 fprintf (stderr, "OK\n");
1424 if (options.xgd1_layout_probe || options.xgd1_raw_id_probe) {
1427 int media_sense_key;
1432 const char *probe_name;
1434 probe_name = options.xgd1_raw_id_probe ? "XGD1 raw-sector ID" : "XGD1 logical-boundary";
1436 if (!disc_is_xbox_challenge_drive (d)) {
1438 "%s probe currently supports only the GDR-8050L challenge-handshake profile.\n",
1443 if (options.xgd1_raw_id_probe &&
1444 !(disc_get_hlds_e7_type (d) == 44 ||
1445 disc_get_hlds_e7_type (d) == 45 ||
1446 disc_get_hlds_e7_type (d) == 442 ||
1447 disc_get_hlds_e7_type (d) == 443 ||
1448 disc_get_hlds_e7_type (d) == 445)) {
1450 "--xgd1-raw-id-probe requires the modified GDR-8050L HIT 0xE7 memdump profile.\n");
1454 fprintf (stderr, "\nChecking for ready Xbox media before %s probing... ", probe_name);
1455 media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq);
1456 if (media_rc <= 0) {
1458 "Failed (sense %02X/%02X/%02X). Insert the disc, wait for spin-up, close AutoPlay/File Explorer, and retry.\n",
1459 media_sense_key, media_asc, media_ascq);
1462 fprintf (stderr, "OK\n");
1464 gettimeofday (&(stats -> start_time), NULL);
1465 if (options.xgd1_raw_id_probe)
1466 probe_status = xbox_ref_xgd1_raw_id_probe_with_handle (
1467 disc_get_native_handle (d), disc_get_device (d), options.xgd1_raw_id_probe_report);
1469 probe_status = xbox_ref_xgd1_layout_probe_with_handle (
1470 disc_get_native_handle (d), disc_get_device (d), options.xgd1_layout_probe_report);
1472 fprintf (stderr, "Issuing STOP UNIT / spin-down after %s probe... ", probe_name);
1473 stop_ok = disc_stop_unit (d, false);
1474 fprintf (stderr, "%s\n", stop_ok ? "OK" : "Failed");
1475 gettimeofday (&(stats -> end_time), NULL);
1476 fprintf (stderr, "%s probe status: %s\n", probe_name, probe_status == 0 ? "OK" : "FAILED");
1477 friidump_summary_reset();
1478 return probe_status == 0;
1480 fprintf (stderr, "XGD1 probe modes are available only in the Windows build.\n");
1485 if (options.hlds_e7_scan) {
1486 out = disc_hlds_e7_scan (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
1487 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 scan... ");
1488 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
1492 if (options.hlds_e7_subcmd_sweep) {
1493 out = disc_hlds_e7_subcmd_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
1494 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 subcommand sweep... ");
1495 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
1499 if (options.hlds_e7_memrange_sweep) {
1500 out = disc_hlds_e7_memrange_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
1501 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 memory-range sweep... ");
1502 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
1507 int media_rc, media_sense_key, media_asc, media_ascq;
1508 fprintf (stderr, "\nChecking for ready media before disc seed retrieval... ");
1509 media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq);
1510 if (media_rc <= 0) {
1512 fprintf (stderr, "No readable disc present (sense %02X/%02X/%02X). Insert a disc, wait for spin-up, and retry.\n", media_sense_key, media_asc, media_ascq);
1514 fprintf (stderr, "Drive/media not ready (sense %02X/%02X/%02X). Wait for spin-up, close AutoPlay/File Explorer, and retry.\n", media_sense_key, media_asc, media_ascq);
1517 fprintf (stderr, "OK\n");
1519 time_t seed_start, seed_end;
1520 double seed_elapsed;
1521 char seed_elapsed_buf[32];
1523 if (xbox_output_requested)
1524 fprintf (stderr, "\nInitializing Xbox/XGD disc state... ");
1526 fprintf (stderr, "\nRetrieving disc seeds, this might take a while... ");
1528 seed_start = time(NULL);
1529 if (!disc_init (d, options.disctype, options.sectors_no)) {
1530 seed_end = time(NULL);
1531 seed_elapsed = difftime(seed_end, seed_start);
1532 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
1533 if (!xbox_output_requested)
1534 fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
1535 if (g_validation_summary.active) {
1536 g_validation_summary.have_seed_duration = true;
1537 g_validation_summary.seed_duration = seed_elapsed;
1539 fprintf (stderr, "Failed\n");
1542 seed_end = time(NULL);
1543 seed_elapsed = difftime(seed_end, seed_start);
1544 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
1545 if (!xbox_output_requested)
1546 fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
1547 fprintf (stderr, "OK\n");
1548 if (g_validation_summary.active) {
1549 g_validation_summary.seed_ok = true;
1550 g_validation_summary.have_seed_duration = true;
1551 g_validation_summary.seed_duration = seed_elapsed;
1553 disc_get_type (d, &type_id, &type);
1554 disc_get_gameid (d, &game_id);
1555 disc_get_region (d, NULL, ®ion);
1556 disc_get_maker (d, &maker_id, &maker);
1557 disc_get_version (d, NULL, &version);
1558 disc_get_title (d, &title);
1559 if (g_validation_summary.active) {
1560 friidump_summary_copy(g_validation_summary.disc_type, sizeof(g_validation_summary.disc_type), type);
1561 friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), game_id);
1562 friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), title);
1563 g_validation_summary.sectors = disc_get_sectors_no(d);
1567 "Disc information:\n"
1568 "----------------------------------------------------------------------\n");
1570 if (options.disctype!=-1) fprintf (stderr,
1571 "Disc type..........: %s (forced)\n", type);
1572 else fprintf (stderr,
1573 "Disc type..........: %s\n", type);
1574 if (options.sectors_no!=-1) fprintf (stderr,
1575 "Disc size..........: %d (forced)\n", disc_get_sectors_no(d));
1576 else fprintf (stderr,
1577 "Disc size..........: %d\n", disc_get_sectors_no(d));
1579 if (disc_get_layerbreak(d)>0 && type_id==DISC_TYPE_DVD) fprintf (stderr,
1580 "Layer break........: %d\n", disc_get_layerbreak(d));
1582 if ((type_id==DISC_TYPE_GAMECUBE) || (type_id==DISC_TYPE_WII) || (type_id==DISC_TYPE_WII_DL)) fprintf (stderr,
1583 "Game ID............: %s\n"
1584 "Region.............: %s\n"
1585 "Maker..............: %s - %s\n"
1586 "Version............: %s\n"
1587 "Game title.........: %s\n", game_id, region, maker_id, maker, version, title
1590 if (type_id == DISC_TYPE_WII || type_id == DISC_TYPE_WII_DL)
1591 fprintf (stderr, "Contains update....: %s\n" , disc_get_update (d) ? "Yes" : "No");
1592 fprintf (stderr, "\n");
1594 disc_set_unscrambling (d, !options.no_unscrambling);
1596 if (type_id <= DISC_TYPE_DVD)
1597 unscrambler_set_disctype (type_id);
1599 if (options.autodump) {
1600 snprintf (tmp, sizeof (tmp), "%s.iso", title);
1601 my_strdup (options.iso_out, tmp);
1602 options.iso_requested = true;
1605 /* Xbox/GDR-8050L supports default XBE/DMI-derived names.
1606 * Use an empty-string placeholder so the copied reference path can
1607 * derive Title[MediaID].iso/.xiso after the first unlock/XBE probe. */
1608 if (type_id == DISC_TYPE_XBOX && options.iso_requested && !options.iso_out)
1609 my_strdup (options.iso_out, "");
1610 if (type_id == DISC_TYPE_XBOX && options.xiso_requested && !options.xiso_out)
1611 my_strdup (options.xiso_out, "");
1613 //set speed 2nd time after rotation is started and some sectors read
1614 if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
1615 if (options.speed != -1) disc_set_speed(d, options.speed * 177);
1617 /* If at least an output file was specified, proceed dumping, otherwise stop here */
1618 if (options.xiso_requested) {
1619 if (type_id != DISC_TYPE_XBOX) {
1620 fprintf (stderr, "Xbox XISO output requires Xbox/XGD disc type. Use -T 4 or an Xbox-capable drive/disc.\n");
1623 fprintf (stderr, options.xiso_out && options.xiso_out[0] ? "Writing to file \"%s\" in Xbox XISO format\n\n" : "Writing Xbox XISO using XBE-derived filename (override with -X <file>)\n\n", (options.xiso_out && options.xiso_out[0]) ? options.xiso_out : "");
1625 dmp = dumper_new (d);
1626 dumper_set_hashing (dmp, !options.no_hashing);
1627 dumper_set_flushing (dmp, !options.no_flushing);
1629 if (!dumper_set_xiso_output_file (dmp, options.xiso_out, options.resume)) {
1630 fprintf (stderr, "Cannot setup Xbox XISO output file\n");
1631 } else if (!dumper_prepare_xiso (dmp)) {
1632 fprintf (stderr, "Cannot prepare Xbox XISO dumper");
1635 dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
1637 dumper_set_progress_callback (dmp, (progress_func) progress, stats);
1639 dump_attempted = true;
1640 if (g_validation_summary.active)
1641 g_validation_summary.dump_attempted = true;
1642 if (dumper_dump_xiso (dmp, ¤t_sector)) {
1643 fprintf (stderr, "Xbox XISO dump completed successfully!\n");
1644 if (!options.no_hashing && !(type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d)))
1646 "XISO image hashes:\n"
1651 dumper_get_xiso_crc32 (dmp), dumper_get_xiso_md5 (dmp),
1652 dumper_get_xiso_sha1 (dmp), dumper_get_xiso_sha2 (dmp)
1655 if (g_validation_summary.active)
1656 g_validation_summary.dump_ok = true;
1658 fprintf (stderr, "\nXbox XISO dump failed at output sector: %u\n", current_sector);
1660 if (g_validation_summary.active) {
1661 g_validation_summary.dump_ok = false;
1662 g_validation_summary.fail_sector = current_sector;
1667 dmp = dumper_destroy (dmp);
1669 } else if (options.raw_out || options.iso_requested) {
1670 if (type_id == DISC_TYPE_XBOX && options.raw_out)
1671 fprintf (stderr, "Xbox/XGD output does not support -r/raw together with the redump-style ISO path. Use -i or -X.\n");
1672 else if (options.raw_out)
1673 fprintf (stderr, "Writing to file \"%s\" in raw format\n", options.raw_out);
1674 if (options.iso_out) {
1675 if (type_id == DISC_TYPE_XBOX)
1676 fprintf (stderr, options.iso_out && options.iso_out[0] ? "Writing to file \"%s\" in Xbox/XGD redump-style ISO format\n" : "Writing Xbox/XGD redump-style ISO using XBE-derived filename (override with -i <file>)\n", (options.iso_out && options.iso_out[0]) ? options.iso_out : "");
1678 fprintf (stderr, "Writing to file \"%s\" in ISO format\n", options.iso_out);
1680 fprintf (stderr, "\n");
1682 dmp = dumper_new (d);
1684 dumper_set_hashing (dmp, !options.no_hashing);
1685 dumper_set_flushing (dmp, !options.no_flushing);
1687 if (!dumper_set_raw_output_file (dmp, options.raw_out, options.resume)) {
1688 fprintf (stderr, "Cannot setup raw output file\n");
1689 } else if (!dumper_set_iso_output_file (dmp, options.iso_out, options.resume)) {
1690 fprintf (stderr, "Cannot setup ISO output file\n");
1691 } else if (!dumper_prepare (dmp)) {
1692 fprintf (stderr, "Cannot prepare dumper");
1694 // fprintf (stderr, "Starting dump process from sector %u...\n", dmp -> start_sector);
1695 // opdd.start_sector = options.start_sector;
1698 dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
1700 dumper_set_progress_callback (dmp, (progress_func) progress, stats);
1702 dump_attempted = true;
1703 if (g_validation_summary.active)
1704 g_validation_summary.dump_attempted = true;
1705 if (dumper_dump (dmp, ¤t_sector)) {
1706 fprintf (stderr, "Dump completed successfully!\n");
1707 if (!options.no_hashing && options.raw_out)
1709 "Raw image hashes:\n"
1715 /*"ED2K....: %s\n"*/,
1716 dumper_get_raw_crc32 (dmp), /*dumper_get_raw_md4 (dmp),*/ dumper_get_raw_md5 (dmp),
1717 dumper_get_raw_sha1 (dmp), dumper_get_raw_sha2 (dmp)/*, dumper_get_raw_ed2k (dmp)*/
1719 if (!options.no_hashing && options.iso_out && !(type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d)))
1721 "ISO image hashes:\n"
1727 /*"ED2K....: %s\n"*/,
1728 dumper_get_iso_crc32 (dmp), /*dumper_get_iso_md4 (dmp),*/ dumper_get_iso_md5 (dmp),
1729 dumper_get_iso_sha1 (dmp), dumper_get_iso_sha2 (dmp)/*, dumper_get_iso_ed2k (dmp)*/
1732 if (type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d) && options.iso_requested)
1733 friidump_verify_redump_xbox_reference(dmp);
1734 else if (options.iso_out)
1735 friidump_verify_redump_iso(dmp, type_id);
1738 if (g_validation_summary.active)
1739 g_validation_summary.dump_ok = true;
1741 if (g_validation_summary.active) {
1742 g_validation_summary.dump_ok = false;
1743 g_validation_summary.fail_sector = current_sector;
1745 if (current_sector == 0xFFFFFFFFU)
1746 fprintf (stderr, "\nXbox reference dumper failed; see the Xbox-specific message above.\n");
1748 fprintf (stderr, "\nDump failed at sectors: %u..%u\n", current_sector, current_sector+15);
1750 //disc_stop_unit (d, 0);
1754 dmp = dumper_destroy (dmp);
1756 fprintf (stderr, "No output file for dumping specified, please take a look at the -i, -r, -X and -a options\n");
1760 if (dump_attempted) {
1761 bool stop_ok = disc_stop_unit (d, false);
1762 fprintf (stderr, "\nIssuing STOP UNIT / spin-down after dump attempt... %s\n", stop_ok ? "OK" : "Failed");
1763 if (g_validation_summary.active) {
1764 g_validation_summary.stop_attempted = true;
1765 g_validation_summary.stop_ok = stop_ok;
1773 static int try_all_methods (progstats *stats) {
1774 u_int32_t saved_command;
1781 saved_command = options.command;
1782 saved_method = options.dump_method;
1785 fprintf (stderr, "Trying all command/method combinations... This will take a LOOOONG time and generate an insanely long console output :p\n");
1787 for (command = 0; command <= 4 && !out; command++) {
1788 for (method = 0; method <= 10 && !out; method++) {
1789 options.command = command;
1790 options.dump_method = method;
1791 memset (stats, 0, sizeof (*stats));
1793 fprintf (stderr, "\nTrying with command %u, method %d\n", command, method);
1794 fprintf (stderr, "Initializing DVD drive... ");
1796 d = disc_new (options.device, options.command);
1798 fprintf (stderr, "Failed\n");
1801 "Probably you do not have access to the DVD device. Ask the administrator\n"
1802 "to add you to the proper group, or use 'sudo'.\n"
1808 fprintf (stderr, "OK\n");
1809 out = dologic (d, stats);
1810 d = disc_destroy (d);
1813 fprintf (stderr, "Command %u and method %d combination worked!\n", command, method);
1818 options.command = saved_command;
1819 options.dump_method = saved_method;
1825 int main (int argc, char *argv[]) {
1832 unscrambler_progress_func pfunc;
1833 u_int32_t current_sector;
1835 /* First of all... */
1837 friidump_init_executable_dir((argc > 0) ? argv[0] : NULL);
1838 xbox_ref_log_open_for_target(NULL, 0);
1842 memset (&stats, 0, sizeof (stats));
1846 if (optparse (argc, argv)) {
1847 friidump_retarget_log_from_options();
1848 if (options.device) {
1849 if (options.allmethods) {
1850 out = try_all_methods (&stats);
1852 /* Dump DVD to file */
1853 fprintf (stderr, "Initializing DVD drive... ");
1855 if (!(d = disc_new (options.device, options.command))) {
1856 fprintf (stderr, "Failed\n");
1859 "Probably you do not have access to the DVD device. Ask the administrator\n"
1860 "to add you to the proper group, or use 'sudo'.\n"
1864 fprintf (stderr, "OK\n");
1865 out = dologic (d, &stats);
1866 d = disc_destroy (d);
1869 } else if (options.raw_in) {
1870 /* Convert raw image to ISO format */
1871 u = unscrambler_new ();
1874 pfunc = (unscrambler_progress_func) progress_for_guis;
1876 pfunc = (unscrambler_progress_func) progress;
1878 if ((out = unscrambler_unscramble_file (u, options.raw_in, options.iso_out, pfunc, &stats, ¤t_sector)))
1879 fprintf (stderr, "Unscrambling completed successfully!\n");
1881 fprintf (stderr, "\nUnscrambling failed at sectors: %u..%u\n", current_sector, current_sector+15);
1883 u = unscrambler_destroy (u);
1889 duration = stats.end_time.tv_sec - stats.start_time.tv_sec;
1890 if (stats.end_time.tv_usec >= stats.start_time.tv_usec) {
1891 us = stats.end_time.tv_usec - stats.start_time.tv_usec;
1895 us = USECS_PER_SEC + stats.end_time.tv_usec - stats.start_time.tv_usec;
1897 duration += ((double) us / (double) USECS_PER_SEC);
1900 if (g_operation_duration_override_valid)
1901 duration = g_operation_duration_override;
1902 fprintf (stderr, "Operation took %.2f seconds\n", duration);
1903 friidump_print_validation_summary(duration, true);
1907 friidump_print_validation_summary(0.0, false);
1911 my_free (options.device);
1912 my_free (options.iso_out);
1913 my_free (options.xiso_out);
1914 my_free (options.raw_out);
1915 my_free (options.raw_in);
1916 my_free (options.hlds_profile_report);
1917 my_free (options.xgd1_layout_probe_report);
1918 my_free (options.xgd1_raw_id_probe_report);
1921 if (xbox_ref_log_path())
1922 fprintf (stderr, "Log file complete: %s\n", xbox_ref_log_path());
1923 xbox_ref_log_close();