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 ***************************************************************************/
30 #include "unscrambler.h"
31 #include "xbox_ref/xbox_ref_log.h"
32 #include "xbox_ref_bridge.h"
33 #include "redump_dat.h"
34 #include "native_report.h"
37 #include "linux_rawio.h"
49 #define printf xbox_ref_printf
50 #define fprintf xbox_ref_log_fprintf
52 #define USECS_PER_SEC 1000000
55 static const char *friidump_requested_output_target(void);
62 bool have_seed_duration;
76 u_int32_t source_sectors;
77 u_int32_t expected_output_sectors;
78 bool have_source_sectors;
79 bool have_expected_output_sectors;
80 u_int32_t fail_sector;
85 } friidump_validation_summary;
87 static friidump_validation_summary g_validation_summary;
88 static redump_verify_result g_redump_result;
89 static bool g_redump_attempted = false;
90 static bool g_operation_duration_override_valid = false;
91 static double g_operation_duration_override = 0.0;
92 static char g_executable_dir[1024];
93 static friidump_native_report g_native_report;
94 static bool g_native_report_dump_started = false;
95 static bool g_native_report_resumed = false;
96 static volatile sig_atomic_t g_native_cancel_requested = 0;
98 static void friidump_summary_copy(char *dst, size_t dst_size, const char *src) {
99 if (!dst || dst_size == 0)
103 snprintf(dst, dst_size, "%s", src);
106 static void friidump_summary_reset(void) {
107 memset(&g_validation_summary, 0, sizeof(g_validation_summary));
108 g_validation_summary.fail_sector = 0xFFFFFFFFU;
109 g_validation_summary.seed_applicable = true;
110 g_operation_duration_override_valid = false;
111 g_operation_duration_override = 0.0;
114 static void friidump_format_hms(double seconds, char *buf, size_t buf_size) {
115 long total, hours, minutes, secs;
116 if (!buf || buf_size == 0)
120 total = (long) (seconds + 0.5);
121 hours = total / 3600;
122 minutes = (total / 60) % 60;
124 snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hours, minutes, secs);
128 static bool friidump_capture_seed_diagnostic (disc *d) {
129 disc_seed_diagnostic diag;
130 char cdb_text[3 * 12 + 1];
135 memset (&diag, 0, sizeof (diag));
136 if (!disc_get_seed_diagnostic (d, &diag))
141 for (i = 0; i < diag.cdb_length && i < 12; i++) {
142 int wrote = snprintf (cdb_text + used, sizeof (cdb_text) - (size_t) used,
143 "%s%02X", i ? " " : "", diag.cdb[i]);
144 if (wrote < 0 || wrote >= (int) (sizeof (cdb_text) - (size_t) used))
150 "\nSeed diagnostic final failure:\n"
151 " Block.............: %u/20\n"
152 " Sectors...........: %u..%u\n"
153 " Retry.............: %d\n"
154 " Stage.............: %s\n"
155 " Detail............: %s\n"
156 " Transport result..: %d\n"
157 " OS error..........: %d\n"
158 " SCSI status.......: 0x%02X\n"
159 " Sense.............: %02X/%02X/%02X\n"
160 " CDB...............: %s\n",
161 diag.seed_block_index + 1,
162 diag.sector_start, diag.sector_end,
164 diag.stage[0] ? diag.stage : "unknown",
165 diag.detail[0] ? diag.detail : "none",
166 diag.transport_result,
168 diag.scsi_status & 0xff,
169 diag.sense_key & 0xff,
172 cdb_text[0] ? cdb_text : "none");
174 snprintf (note, sizeof (note),
175 "Seed diagnostic: block %u/20, sectors %u..%u, retry %d, stage %s, detail %s, transport_result %d, os_error %d, scsi_status 0x%02X, sense %02X/%02X/%02X, CDB [%s].",
176 diag.seed_block_index + 1,
177 diag.sector_start, diag.sector_end,
179 diag.stage[0] ? diag.stage : "unknown",
180 diag.detail[0] ? diag.detail : "none",
181 diag.transport_result,
183 diag.scsi_status & 0xff,
184 diag.sense_key & 0xff,
187 cdb_text[0] ? cdb_text : "none");
188 if (friidump_native_report_is_enabled (&g_native_report))
189 friidump_native_report_add_note (&g_native_report, note);
194 static bool friidump_linux_vendor_rawio_required(
196 bool xbox_output_requested,
197 bool drive_supported,
198 bool probe_requested) {
207 if (xbox_output_requested)
208 return disc_is_xbox_unlock_drive(d);
210 method = (int) disc_get_method(d);
211 return drive_supported && method >= 0 && method <= 9;
214 static void friidump_linux_record_rawio_failure(
216 bool xbox_output_requested,
220 if (friidump_native_report_is_enabled(&g_native_report)) {
221 friidump_native_report_set_seed(
224 xbox_output_requested ? "not_applicable" : "not_attempted",
227 friidump_native_report_set_dump(
241 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
242 friidump_native_report_add_note(&g_native_report, note);
245 stop_ok = disc_stop_unit(d, false);
247 "Issuing STOP UNIT / spin-down after Linux raw-I/O preflight failure... %s\n",
248 stop_ok ? "OK" : "Failed");
250 if (g_validation_summary.active) {
251 g_validation_summary.stop_attempted = true;
252 g_validation_summary.stop_ok = stop_ok;
255 if (friidump_native_report_is_enabled(&g_native_report)) {
256 friidump_native_report_add_note(
259 ? "STOP UNIT after Linux raw-I/O preflight failure succeeded."
260 : "STOP UNIT after Linux raw-I/O preflight failure failed.");
264 static bool friidump_linux_rawio_preflight(
266 bool xbox_output_requested,
267 bool drive_supported,
268 bool probe_requested) {
269 friidump_linux_rawio_state state;
270 unsigned long long effective_mask = 0;
271 char executable_path[1024];
273 const char *display_path;
275 if (!friidump_linux_vendor_rawio_required(
276 d, xbox_output_requested, drive_supported, probe_requested))
279 executable_path[0] = '\0';
280 if (!friidump_linux_executable_path(
281 executable_path, sizeof(executable_path))) {
282 const char *fallback_dir = g_executable_dir[0] ? g_executable_dir : ".";
283 const char *suffix = "friidump";
284 size_t used = strlen(fallback_dir);
285 size_t suffix_len = strlen(suffix);
287 if (used >= sizeof(executable_path))
288 used = sizeof(executable_path) - 1;
289 memcpy(executable_path, fallback_dir, used);
290 executable_path[used] = '\0';
292 if (used > 0 && executable_path[used - 1] != '/' &&
293 used + 1 < sizeof(executable_path)) {
294 executable_path[used++] = '/';
295 executable_path[used] = '\0';
298 if (suffix_len > sizeof(executable_path) - used - 1)
299 suffix_len = sizeof(executable_path) - used - 1;
300 memcpy(executable_path + used, suffix, suffix_len);
301 executable_path[used + suffix_len] = '\0';
303 display_path = executable_path;
305 if (geteuid() == 0) {
307 "\nERROR: FriiDump refuses this Linux vendor-command path while running as root.\n"
308 " Run FriiDump as your normal user and grant only CAP_SYS_RAWIO to the\n"
309 " exact validated executable. Do not use sudo to run the whole program.\n"
310 " Exact executable: %s\n"
311 " Install: sudo setcap cap_sys_rawio=ep '%s'\n"
312 " Verify: getcap '%s'\n"
313 " Rebuilding or replacing the executable clears file capabilities.\n\n",
314 display_path, display_path, display_path);
318 "Linux raw-I/O preflight failed: FriiDump was running as root. The supported least-privilege configuration is a normal user process with cap_sys_rawio=ep on the exact validated executable (%s).",
320 friidump_linux_record_rawio_failure(d, xbox_output_requested, note);
324 state = friidump_linux_rawio_effective(&effective_mask);
325 if (state == FRIIDUMP_LINUX_RAWIO_PRESENT) {
327 "Linux raw-I/O preflight: PASS (CAP_SYS_RAWIO effective; CapEff=0x%016llX).\n",
333 "\nERROR: Linux vendor-command authorization is unavailable.\n"
334 " This operation uses vendor-specific SCSI commands and requires effective\n"
335 " CAP_SYS_RAWIO. Device access through the cdrom group is necessary but is\n"
336 " not sufficient for commands such as HLDS 0xE7 memory reads.\n"
337 " Exact executable: %s\n"
338 " Install: sudo setcap cap_sys_rawio=ep '%s'\n"
339 " Verify: getcap '%s'\n"
340 " Expected: %s cap_sys_rawio=ep\n"
341 " Do not run the entire FriiDump process as root. Rebuilding or replacing\n"
342 " the executable clears file capabilities, so reapply and verify afterward.\n\n",
343 display_path, display_path, display_path, display_path);
348 "Linux raw-I/O preflight failed: effective CAP_SYS_RAWIO was %s for executable %s (CapEff=0x%016llX). Apply cap_sys_rawio=ep to the exact validated executable; do not run FriiDump as root. Rebuilds clear file capabilities.",
349 state == FRIIDUMP_LINUX_RAWIO_MISSING ? "missing" : "unavailable/undetectable",
352 friidump_linux_record_rawio_failure(d, xbox_output_requested, note);
357 static void friidump_summary_begin(disc *d) {
359 if (!d || disc_get_hlds_e7_type(d) == 0)
361 friidump_summary_reset();
362 g_validation_summary.active = true;
363 friidump_summary_copy(g_validation_summary.model, sizeof(g_validation_summary.model), disc_get_drive_model_string(d));
364 g_validation_summary.command = disc_get_command(d);
365 g_validation_summary.method = disc_get_method(d);
366 g_validation_summary.hlds_type = disc_get_hlds_e7_type(d);
367 g_validation_summary.profile = disc_get_hlds_e7_profile_name(d);
368 g_validation_summary.cache_base = disc_get_hlds_e7_cache_base(d);
369 g_validation_summary.mem_blocks = disc_get_hlds_e7_mem_blocks(d);
370 target = friidump_requested_output_target();
371 friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), target ? target : "(none)");
374 static void friidump_print_validation_summary(double duration, bool have_duration) {
375 double mib_completed, mib_per_hour;
376 bool have_completed_measurement;
377 const char *seed_status;
378 const char *dump_status;
379 if (!g_validation_summary.active)
381 seed_status = g_validation_summary.seed_applicable
382 ? (g_validation_summary.seed_ok ? "OK" : "FAILED/NOT REACHED")
383 : "N/A (Xbox reference auth path)";
384 dump_status = "NOT ATTEMPTED";
385 if (g_validation_summary.dump_attempted) {
386 if (g_validation_summary.dump_ok)
388 else if (g_native_cancel_requested && g_native_report.have_byte_count &&
389 g_native_report.byte_count > 0)
390 dump_status = "CANCELLED (PARTIAL)";
391 else if (g_native_cancel_requested)
392 dump_status = "CANCELLED";
393 else if (g_native_report.have_byte_count && g_native_report.byte_count > 0)
394 dump_status = "FAILED (PARTIAL)";
396 dump_status = "FAILED";
398 have_completed_measurement =
399 g_native_report.dump_attempted &&
400 g_native_report.have_byte_count &&
401 !g_native_report_resumed;
402 mib_completed = have_completed_measurement
403 ? (double) g_native_report.byte_count / 1024.0 / 1024.0
405 mib_per_hour = (have_completed_measurement && have_duration && duration > 0.0)
406 ? (mib_completed / duration * 3600.0)
409 "\nHLDS 0xE7 validation summary:\n"
410 " Model.............: %s\n"
411 " Profile...........: %s\n"
412 " Command/method....: %u / %d\n"
413 " Cache base........: 0x%08x\n"
414 " Memory windows....: %u\n"
415 " Disc type.........: %s\n"
416 " Game/Media ID.....: %s\n"
417 " Title.............: %s\n",
418 g_validation_summary.model[0] ? g_validation_summary.model : "(unknown)",
419 g_validation_summary.profile ? g_validation_summary.profile : "(unknown)",
420 g_validation_summary.command, g_validation_summary.method,
421 g_validation_summary.cache_base, g_validation_summary.mem_blocks,
422 g_validation_summary.disc_type[0] ? g_validation_summary.disc_type : "(unknown)",
423 g_validation_summary.game_id[0] ? g_validation_summary.game_id : "(unknown)",
424 g_validation_summary.title[0] ? g_validation_summary.title : "(unknown)");
425 if (g_validation_summary.region[0])
426 fprintf(stderr, " Region............: %s\n", g_validation_summary.region);
428 " Output............: %s\n"
429 " Seed read.........: %s\n"
430 " Dump status.......: %s\n"
431 " STOP UNIT.........: %s\n",
432 g_validation_summary.output[0] ? g_validation_summary.output : "(none)",
435 g_validation_summary.stop_attempted ? (g_validation_summary.stop_ok ? "OK" : "FAILED") : "NOT ATTEMPTED");
436 if (g_validation_summary.have_seed_duration) {
438 friidump_format_hms(g_validation_summary.seed_duration, seed_buf, sizeof(seed_buf));
439 fprintf(stderr, " Seed elapsed......: %s (%.2f seconds)\n", seed_buf, g_validation_summary.seed_duration);
441 if (g_validation_summary.dump_attempted && !g_validation_summary.dump_ok &&
442 g_validation_summary.fail_sector != 0xFFFFFFFFU) {
443 if (g_native_cancel_requested)
444 fprintf(stderr, " Cancelled at......: sector %u\n", g_validation_summary.fail_sector);
446 fprintf(stderr, " Failure sector....: %u..%u\n", g_validation_summary.fail_sector, g_validation_summary.fail_sector + 15);
448 if (g_validation_summary.have_source_sectors)
449 fprintf(stderr, " Source sectors....: %u\n", g_validation_summary.source_sectors);
450 if (g_validation_summary.have_expected_output_sectors)
451 fprintf(stderr, " Expected output...: %u sectors\n", g_validation_summary.expected_output_sectors);
452 else if (g_validation_summary.sectors)
453 fprintf(stderr, " Expected sectors..: %u\n", g_validation_summary.sectors);
454 if (g_native_report.have_sector_count)
455 fprintf(stderr, g_native_report_resumed
456 ? " Output sectors....: %llu (includes prior resumed data)\n"
457 : " Completed sectors.: %llu\n",
458 (unsigned long long) g_native_report.sector_count);
459 if (g_native_report.have_byte_count)
460 fprintf(stderr, g_native_report_resumed
461 ? " Output bytes......: %llu (%.2f MiB; includes prior resumed data)\n"
462 : " Completed bytes...: %llu (%.2f MiB)\n",
463 (unsigned long long) g_native_report.byte_count,
464 (double) g_native_report.byte_count / 1024.0 / 1024.0);
466 fprintf(stderr, " Duration..........: %.2f seconds\n", duration);
467 if (have_duration && mib_per_hour > 0.0)
468 fprintf(stderr, " Observed average..: %.2f MiB/h over %.2f MiB completed output\n",
469 mib_per_hour, mib_completed);
470 else if (g_native_report_resumed)
471 fprintf(stderr, " Observed average..: N/A (resumed output; measurement scope unknown)\n");
472 if (g_validation_summary.dump_ok) {
473 if (g_redump_attempted) {
474 fprintf(stderr, " Redump verify.....: %s\n", redump_verify_overall_string(&g_redump_result));
475 if (g_redump_result.status == REDUMP_VERIFY_MATCH)
476 fprintf(stderr, " Redump title......: %s\n", g_redump_result.game_name);
478 fprintf(stderr, " Redump verify.....: NOT RUN\n");
484 /* Name of package */
485 #define PACKAGE "friidump"
487 /* Define to the address where bug reports for this package should be sent. */
488 #define PACKAGE_BUGREPORT "arep@no.net"
490 /* Define to the full name of this package. */
491 #define PACKAGE_NAME "FriiDump"
493 /* Define to the version of this package. */
494 #define PACKAGE_VERSION "0.5.3.16-pf1"
499 #include "getopt-win32.h"
503 #include <sys/time.h>
508 /* Struct for program options */
519 bool xbox_filename_override;
520 bool xbox_auto_filename;
524 u_int32_t start_sector;
525 u_int32_t sectors_no;
531 bool no_unscrambling;
536 bool hlds_e7_subcmd_sweep;
537 bool hlds_e7_memrange_sweep;
538 char *hlds_e7_scan_log;
539 char *hlds_e7_scan_dump_prefix;
540 char *hlds_profile_report;
541 char *redump_dat_dir;
543 bool no_redump_verify;
544 bool xgd1_layout_probe;
545 char *xgd1_layout_probe_report;
546 bool xgd1_raw_id_probe;
547 char *xgd1_raw_id_probe_report;
548 char *native_report_json;
549 char *native_report_dir;
550 char *firmware_modified_note;
554 /* Struct for progress data */
556 struct timeval start_time;
557 struct timeval end_time;
559 double mb_total_real;
560 u_int32_t sectors_skipped;
564 static char friidump_drive_letter_from_device(const char *device) {
565 if (!device || !device[0]) return 0;
566 if (device[0] && device[1] == ':') return device[0];
567 if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
572 static const char *friidump_requested_output_target(void) {
573 if (options.xgd1_raw_id_probe_report && options.xgd1_raw_id_probe_report[0]) return options.xgd1_raw_id_probe_report;
574 if (options.xgd1_layout_probe_report && options.xgd1_layout_probe_report[0]) return options.xgd1_layout_probe_report;
575 if (options.iso_out && options.iso_out[0]) return options.iso_out;
576 if (options.xiso_out && options.xiso_out[0]) return options.xiso_out;
577 if (options.raw_out && options.raw_out[0]) return options.raw_out;
582 static void friidump_json_string(FILE *f, const char *s) {
583 const unsigned char *p;
586 for (p = (const unsigned char *) s; *p; p++) {
588 case '\\': fputs("\\\\", f); break;
589 case '"': fputs("\\\"", f); break;
590 case '\b': fputs("\\b", f); break;
591 case '\f': fputs("\\f", f); break;
592 case '\n': fputs("\\n", f); break;
593 case '\r': fputs("\\r", f); break;
594 case '\t': fputs("\\t", f); break;
598 snprintf(tmp, sizeof(tmp), "\\u%04x", (unsigned int) *p);
610 static void friidump_json_kv_string(FILE *f, const char *key, const char *value, bool comma) {
612 friidump_json_string(f, key);
614 friidump_json_string(f, value ? value : "");
615 fputs(comma ? ",\n" : "\n", f);
618 static void friidump_json_kv_u32_hex(FILE *f, const char *key, u_int32_t value, bool comma) {
620 snprintf(tmp, sizeof(tmp), "0x%08x", value);
621 friidump_json_kv_string(f, key, value ? tmp : "", comma);
624 static void friidump_json_kv_int(FILE *f, const char *key, int value, bool comma) {
627 friidump_json_string(f, key);
628 snprintf(tmp, sizeof(tmp), ": %d%s\n", value, comma ? "," : "");
632 static void friidump_json_kv_bool(FILE *f, const char *key, bool value, bool comma) {
634 friidump_json_string(f, key);
635 fputs(value ? ": true" : ": false", f);
636 fputs(comma ? ",\n" : "\n", f);
639 static bool friidump_write_hlds_profile_report(disc *d, const char *path) {
641 if (!d || !path || !path[0])
643 f = fopen(path, "wb");
645 fprintf(stderr, "WARNING: could not write HLDS profile report: %s\n", path);
649 friidump_json_kv_string(f, "schema", "friidump_hlds_profile_report_v1", true);
650 friidump_json_kv_string(f, "drive_model", disc_get_drive_model_string(d), true);
651 friidump_json_kv_string(f, "profile_name", disc_get_hlds_e7_profile_name(d), true);
652 friidump_json_kv_string(f, "support_tier", disc_get_hlds_e7_support_tier(d), true);
653 friidump_json_kv_string(f, "family", disc_get_hlds_e7_family(d), true);
654 friidump_json_kv_string(f, "tokens", disc_get_hlds_e7_tokens(d), true);
655 friidump_json_kv_string(f, "stage5b_record_id", disc_get_hlds_e7_record_id(d), true);
656 friidump_json_kv_u32_hex(f, "static_e7_cdb_base", disc_get_hlds_e7_static_cdb_base(d), true);
657 friidump_json_kv_u32_hex(f, "static_e7_gate", disc_get_hlds_e7_static_gate(d), true);
658 friidump_json_kv_int(f, "runtime_e7_type", (int) disc_get_hlds_e7_type(d), true);
659 friidump_json_kv_u32_hex(f, "runtime_cache_base", disc_get_hlds_e7_cache_base(d), true);
660 friidump_json_kv_int(f, "runtime_mem_windows", (int) disc_get_hlds_e7_mem_blocks(d), true);
661 friidump_json_kv_int(f, "preferred_method", disc_get_hlds_e7_preferred_method(d), true);
662 friidump_json_kv_int(f, "selected_method", (int) disc_get_method(d), true);
663 friidump_json_kv_int(f, "selected_command", (int) disc_get_command(d), true);
664 friidump_json_kv_bool(f, "live_safe_from_static_only", false, true);
665 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);
666 friidump_json_kv_string(f, "notes", disc_get_hlds_e7_notes(d), false);
673 static void friidump_retarget_log_from_options(void) {
674 const char *target = friidump_requested_output_target();
676 xbox_ref_log_retarget(target);
677 } else if (options.device && options.device[0]) {
678 xbox_ref_log_open_for_target(NULL, friidump_drive_letter_from_device(options.device));
683 void progress_for_guis (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
685 double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
692 gettimeofday (&(stats -> start_time), NULL);
693 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
694 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
695 stats -> sectors_skipped = sectors_done;
697 perc = (int) (100.0 * sectors_done / total_sectors);
698 gettimeofday (&now, NULL);
699 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
700 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
701 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
702 mb_hour = mb_done_real / elapsed * 60 * 60;
703 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
704 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
705 if (localtime_r (&eta, &etatm))
706 strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
708 sprintf (buf, "N/A");
710 /* This is the only thing we print to stdout, so that other programs can easily capture and parse our output */
711 fprintf (stdout, "%d%%|%u/%u sectors|%.2lf/%.0lf MB|%.0lf/%.0lf seconds|%.2lf MB/h|%s\n",
712 perc, sectors_done, total_sectors, mb_done, stats -> mb_total, elapsed, seconds_left, mb_hour, buf);
716 /* Save return time, in case this will be the last call */
717 gettimeofday (&(stats -> end_time), NULL);
723 void progress (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
725 double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
732 gettimeofday (&(stats -> start_time), NULL);
733 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
734 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
735 stats -> sectors_skipped = sectors_done;
737 perc = (int) (100.0 * sectors_done / total_sectors);
738 gettimeofday (&now, NULL);
739 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
740 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
741 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
742 mb_hour = mb_done_real / elapsed * 60 * 60;
743 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
744 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
745 if (localtime_r (&eta, &etatm))
746 strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
748 sprintf (buf, "N/A");
750 fprintf (stdout, "\r%3d%% ", perc);
751 fprintf (stdout, "|");
752 for (i = 0; i < 100 / 3; i++) {
754 fprintf (stdout, "*");
756 fprintf (stdout, "-");
758 fprintf (stdout, "| ");
759 fprintf (stdout, "%.2lf MB/h, ETA: %s", mb_hour, buf);
763 if (sectors_done == total_sectors)
766 /* Save return time, in case this will be the last call */
767 gettimeofday (&(stats -> end_time), NULL);
774 void welcome (void) {
777 "FriiDump " PACKAGE_VERSION " - Copyright (C) 2007 Arep\n"
778 "This software comes with ABSOLUTELY NO WARRANTY.\n"
779 "This is free software, and you are welcome to redistribute it\n"
780 "under certain conditions; see COPYING for details.\n"
782 "Official support forum: http://wii.console-tribe.com\n"
784 "Forum for this UNOFFICIAL VERSION: http://forum.redump.org\n"
794 static void friidump_init_executable_dir(const char *argv0) {
801 g_executable_dir[0] = '\0';
806 DWORD result = GetModuleFileNameA(NULL, path, (DWORD)sizeof(path));
807 if (result > 0 && result < sizeof(path)) {
809 length = (size_t)result;
813 #if defined(__linux__)
815 ssize_t result = readlink("/proc/self/exe", path, sizeof(path) - 1);
816 if (result > 0 && (size_t)result < sizeof(path)) {
818 length = (size_t)result;
822 if (length == 0 && argv0 && argv0[0] &&
823 (strchr(argv0, '/') || strchr(argv0, '\\'))) {
824 char *resolved = realpath(argv0, path);
826 length = strlen(path);
833 slash = strrchr(path, '/');
834 backslash = strrchr(path, '\\');
836 if (backslash && (!separator || backslash > separator))
837 separator = backslash;
844 snprintf(g_executable_dir, sizeof(g_executable_dir), "%s", path);
847 static const char *friidump_redump_dat_basename(disc_type type_id) {
849 case DISC_TYPE_GAMECUBE:
850 return "Nintendo - GameCube.dat";
852 case DISC_TYPE_WII_DL:
853 return "Nintendo - Wii.dat";
855 return "Microsoft - Xbox.dat";
861 static uint64_t friidump_file_size(const char *path) {
862 if (!path || !path[0])
867 if (_stat64(path, &st) == 0)
868 return (uint64_t)st.st_size;
873 if (stat(path, &st) == 0)
874 return (uint64_t)st.st_size;
880 static bool friidump_sync_report_file(FILE *f) {
881 if (!f || fflush(f) != 0)
884 return _commit(_fileno(f)) == 0;
886 return fsync(fileno(f)) == 0;
890 static bool friidump_atomic_replace(const char *temporary_path, const char *final_path) {
891 if (!temporary_path || !final_path)
894 return MoveFileExA(temporary_path, final_path,
895 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != 0;
897 return rename(temporary_path, final_path) == 0;
901 static void friidump_json_write_string_value(FILE *f, const char *prefix,
902 const char *value, const char *suffix) {
904 friidump_json_string(f, value ? value : "");
908 static void friidump_write_redump_report(const redump_verify_result *result,
909 const char *output_path,
910 uint64_t output_size,
915 const char *hash_source,
916 const char *representation_note) {
918 char *temporary_path;
920 size_t temporary_path_size;
923 if (!options.redump_report || !options.redump_report[0] || !result)
926 temporary_path_size = strlen(options.redump_report) + 5;
927 temporary_path = (char *)malloc(temporary_path_size);
928 if (!temporary_path) {
929 fprintf(stderr, "Redump report.......: memory allocation failed\n");
932 snprintf(temporary_path, temporary_path_size, "%s.tmp", options.redump_report);
933 remove(temporary_path);
935 f = fopen(temporary_path, "wb");
937 fprintf(stderr, "Redump report.......: could not write temporary file %s\n", temporary_path);
938 free(temporary_path);
942 /* Use fputs/fputc rather than the logged fprintf wrapper. Report contents
943 * belong in the JSON artifact, not as a malformed partial mirror in the
944 * human-readable run log. */
946 fputs(" \"schema\": 2,\n", f);
947 friidump_json_write_string_value(f, " \"producer\": ", "friidump-" PACKAGE_VERSION, ",\n");
948 friidump_json_write_string_value(f, " \"status\": ", redump_verify_status_string(result->status), ",\n");
949 friidump_json_write_string_value(f, " \"overall\": ", redump_verify_overall_string(result), ",\n");
950 friidump_json_write_string_value(f, " \"confidence\": ", redump_verify_confidence_string(result), ",\n");
951 friidump_json_write_string_value(f, " \"output\": ", output_path ? output_path : "", ",\n");
952 snprintf(line, sizeof(line), " \"output_size\": %llu,\n", (unsigned long long)output_size);
954 friidump_json_write_string_value(f, " \"crc32\": ", crc32 ? crc32 : "", ",\n");
955 friidump_json_write_string_value(f, " \"md5\": ", md5 ? md5 : "", ",\n");
956 friidump_json_write_string_value(f, " \"sha1\": ", sha1 ? sha1 : "", ",\n");
957 friidump_json_write_string_value(f, " \"sha256\": ", sha256 ? sha256 : "", ",\n");
958 friidump_json_write_string_value(f, " \"hash_source\": ", hash_source ? hash_source : "", ",\n");
959 friidump_json_write_string_value(f, " \"representation_note\": ", representation_note ? representation_note : "", ",\n");
960 friidump_json_write_string_value(f, " \"dat_path\": ", result->dat_path, ",\n");
961 snprintf(line, sizeof(line), " \"entries_scanned\": %lu,\n", result->entries_scanned);
963 snprintf(line, sizeof(line), " \"exact_matches\": %lu,\n", result->exact_matches);
965 friidump_json_write_string_value(f, " \"match_kind\": ", redump_verify_match_kind_string(result), ",\n");
966 friidump_json_write_string_value(f, " \"game_name\": ", result->game_name, ",\n");
967 friidump_json_write_string_value(f, " \"rom_name\": ", result->rom_name, ",\n");
969 fputs(" \"field_match_counts\": {\n", f);
970 snprintf(line, sizeof(line), " \"size\": %lu,\n", result->size_matches); fputs(line, f);
971 snprintf(line, sizeof(line), " \"crc32\": %lu,\n", result->crc32_matches); fputs(line, f);
972 snprintf(line, sizeof(line), " \"md5\": %lu,\n", result->md5_matches); fputs(line, f);
973 snprintf(line, sizeof(line), " \"sha1\": %lu\n", result->sha1_matches); fputs(line, f);
976 fputs(" \"expected\": {\n", f);
977 friidump_json_write_string_value(f, " \"size\": ", result->expected_size, ",\n");
978 friidump_json_write_string_value(f, " \"crc32\": ", result->expected_crc32, ",\n");
979 friidump_json_write_string_value(f, " \"md5\": ", result->expected_md5, ",\n");
980 friidump_json_write_string_value(f, " \"sha1\": ", result->expected_sha1, "\n");
983 fputs(" \"evidence\": {\n", f);
984 friidump_json_write_string_value(f, " \"size\": ", redump_field_status_string(result->size_status), ",\n");
985 friidump_json_write_string_value(f, " \"crc32\": ", redump_field_status_string(result->crc32_status), ",\n");
986 friidump_json_write_string_value(f, " \"md5\": ", redump_field_status_string(result->md5_status), ",\n");
987 friidump_json_write_string_value(f, " \"sha1\": ", redump_field_status_string(result->sha1_status), "\n");
989 friidump_json_write_string_value(f, " \"detail\": ", result->detail, "\n");
992 write_ok = !ferror(f) && friidump_sync_report_file(f);
997 remove(temporary_path);
998 fprintf(stderr, "Redump report.......: write/flush failed; final report was not replaced\n");
999 free(temporary_path);
1003 if (!friidump_atomic_replace(temporary_path, options.redump_report)) {
1004 fprintf(stderr, "Redump report.......: could not replace %s; complete temporary report retained at %s\n",
1005 options.redump_report, temporary_path);
1006 free(temporary_path);
1010 fprintf(stderr, "Redump report.......: %s (atomic write)\n", options.redump_report);
1011 free(temporary_path);
1014 static void friidump_print_redump_field(const char *label, redump_field_status status) {
1015 fprintf(stderr, "%-20s %s\n", label, redump_field_status_string(status));
1018 static void friidump_verify_redump_values(disc_type type_id,
1019 const char *output_path,
1020 uint64_t output_size,
1025 const char *hash_source) {
1026 const char *basename;
1027 const char *dat_dir;
1028 const char *representation_note;
1029 char dat_path[1024];
1031 redump_verify_result_init(&g_redump_result);
1032 g_redump_attempted = false;
1033 representation_note = (type_id == DISC_TYPE_XBOX)
1034 ? "XGD1 acquisition success and exact Redump hash identity are separate claims; unresolved zero-filled pregame/postgame content may prevent an exact match."
1037 if (options.no_redump_verify) {
1038 fprintf(stderr, "Redump verification: DISABLED (--no-redump-verify)\n");
1041 if (options.no_hashing) {
1042 fprintf(stderr, "Redump verification: SKIPPED (hashing disabled)\n");
1045 if (!output_path || !output_path[0]) {
1046 fprintf(stderr, "Redump verification: SKIPPED (final output path unavailable)\n");
1049 if (!crc32 || !crc32[0] || !md5 || !md5[0] || !sha1 || !sha1[0]) {
1050 fprintf(stderr, "Redump verification: SKIPPED (finalized CRC32/MD5/SHA-1 evidence incomplete)\n");
1054 basename = friidump_redump_dat_basename(type_id);
1056 fprintf(stderr, "Redump verification: SKIPPED (no DAT mapping for this disc type)\n");
1060 dat_dir = (options.redump_dat_dir && options.redump_dat_dir[0])
1061 ? options.redump_dat_dir
1063 redump_resolve_dat_path(dat_dir, g_executable_dir, basename,
1064 dat_path, sizeof(dat_path));
1065 if (output_size == 0)
1066 output_size = friidump_file_size(output_path);
1068 g_redump_attempted = true;
1069 redump_verify_dat_file(dat_path, output_size, crc32, md5, sha1, &g_redump_result);
1071 fprintf(stderr, "\nRedump verification\n");
1072 fprintf(stderr, "------------------------------------------------------------\n");
1073 fprintf(stderr, "%-20s %s\n", "DAT", g_redump_result.dat_path);
1074 fprintf(stderr, "%-20s %s\n", "Hash source", hash_source ? hash_source : "Finalized output hashes");
1075 fprintf(stderr, "%-20s %lu\n", "Entries scanned", g_redump_result.entries_scanned);
1077 if (g_redump_result.status == REDUMP_VERIFY_MATCH) {
1078 fprintf(stderr, "%-20s %s\n", "Matched entry", g_redump_result.game_name);
1079 fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name);
1080 friidump_print_redump_field("Image size", g_redump_result.size_status);
1081 friidump_print_redump_field("CRC32", g_redump_result.crc32_status);
1082 friidump_print_redump_field("MD5", g_redump_result.md5_status);
1083 friidump_print_redump_field("SHA-1", g_redump_result.sha1_status);
1084 if (g_redump_result.exact_matches > 1)
1085 fprintf(stderr, "%-20s %lu exact entries\n", "Duplicate matches", g_redump_result.exact_matches);
1086 } else if (g_redump_result.candidate_available) {
1087 fprintf(stderr, "%-20s %s\n", "Closest candidate", g_redump_result.game_name);
1088 fprintf(stderr, "%-20s %s\n", "ROM", g_redump_result.rom_name);
1089 friidump_print_redump_field("Image size", g_redump_result.size_status);
1090 friidump_print_redump_field("CRC32", g_redump_result.crc32_status);
1091 friidump_print_redump_field("MD5", g_redump_result.md5_status);
1092 friidump_print_redump_field("SHA-1", g_redump_result.sha1_status);
1094 fprintf(stderr, "%-20s %s\n", "Closest candidate", "None (no hash-correlated entry)");
1095 fprintf(stderr, "%-20s %lu entries\n", "Size matches", g_redump_result.size_matches);
1096 fprintf(stderr, "%-20s %lu entries\n", "CRC32 matches", g_redump_result.crc32_matches);
1097 fprintf(stderr, "%-20s %lu entries\n", "MD5 matches", g_redump_result.md5_matches);
1098 fprintf(stderr, "%-20s %lu entries\n", "SHA-1 matches", g_redump_result.sha1_matches);
1101 fprintf(stderr, "%-20s %s\n", "Overall", redump_verify_overall_string(&g_redump_result));
1102 fprintf(stderr, "%-20s %s\n", "Confidence", redump_verify_confidence_string(&g_redump_result));
1103 if (g_redump_result.status != REDUMP_VERIFY_MATCH)
1104 fprintf(stderr, "%-20s %s\n", "Detail", g_redump_result.detail);
1105 if (type_id == DISC_TYPE_XBOX)
1106 fprintf(stderr, "%-20s %s\n", "Representation note", representation_note);
1108 friidump_write_redump_report(&g_redump_result, output_path, output_size,
1109 crc32, md5, sha1, sha256, hash_source,
1110 representation_note);
1113 static void friidump_verify_redump_iso(dumper *dmp, disc_type type_id) {
1114 const char *output_path;
1119 output_path = options.iso_out;
1120 friidump_verify_redump_values(type_id,
1122 friidump_file_size(output_path),
1123 dumper_get_iso_crc32(dmp),
1124 dumper_get_iso_md5(dmp),
1125 dumper_get_iso_sha1(dmp),
1126 dumper_get_iso_sha2(dmp),
1127 "FriiDump finalized ISO multihash");
1130 static bool friidump_uses_windows_xbox_reference_path(disc *d, disc_type type_id) {
1132 return type_id == DISC_TYPE_XBOX && d && disc_is_xbox_challenge_drive(d);
1140 static void friidump_verify_redump_xbox_reference(dumper *dmp) {
1141 xbox_ref_dump_result result;
1143 xbox_ref_dump_result_init(&result);
1144 if (!dumper_get_xbox_reference_result(dmp, &result)) {
1145 fprintf(stderr, "Redump verification: SKIPPED (Xbox reference result handoff unavailable)\n");
1148 if (result.mode != '1') {
1149 fprintf(stderr, "Redump verification: SKIPPED (Xbox XISO is not a full Redump disc image)\n");
1152 if (!result.dump_success) {
1153 fprintf(stderr, "Redump verification: SKIPPED (Xbox reference dump did not complete)\n");
1156 if (!result.hashes_complete) {
1157 fprintf(stderr, "Redump verification: SKIPPED (Xbox finalized full-file hashes incomplete)\n");
1161 if (g_validation_summary.active) {
1162 if (result.output_path[0])
1163 friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), result.output_path);
1164 if (result.title[0])
1165 friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), result.title);
1166 if (result.media_id[0])
1167 friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), result.media_id);
1168 if (result.output_sectors)
1169 g_validation_summary.sectors = result.output_sectors;
1170 g_validation_summary.seed_applicable = false;
1171 g_validation_summary.have_seed_duration = false;
1173 if (result.elapsed_seconds > 0.0) {
1174 g_operation_duration_override_valid = true;
1175 g_operation_duration_override = result.elapsed_seconds;
1178 friidump_verify_redump_values(DISC_TYPE_XBOX,
1185 "Xbox finalized full-file hashes");
1189 static void friidump_native_signal_handler(int signal_number) {
1190 (void) signal_number;
1191 g_native_cancel_requested = 1;
1194 static bool friidump_native_cancel_callback(void *unused) {
1196 return g_native_cancel_requested != 0;
1199 static const char *friidump_native_platform(disc_type type_id) {
1201 case DISC_TYPE_GAMECUBE:
1204 case DISC_TYPE_WII_DL:
1206 case DISC_TYPE_XBOX:
1215 static bool friidump_native_report_operation_supported(void) {
1216 return options.device &&
1218 !options.allmethods &&
1219 !options.stop_unit &&
1220 !options.hlds_e7_scan &&
1221 !options.hlds_e7_subcmd_sweep &&
1222 !options.hlds_e7_memrange_sweep &&
1223 !options.xgd1_layout_probe &&
1224 !options.xgd1_raw_id_probe &&
1225 !(options.raw_out && options.iso_requested) &&
1226 (options.autodump || options.raw_out ||
1227 options.iso_requested || options.xiso_requested);
1230 static bool friidump_native_report_option_requested(void) {
1231 return options.native_report_json ||
1232 options.native_report_dir ||
1233 options.firmware_modified_note;
1236 static void friidump_native_configure(void) {
1237 if (!friidump_native_report_operation_supported())
1240 friidump_native_report_enable_default(&g_native_report);
1242 if (options.native_report_json)
1243 friidump_native_report_enable_path(
1244 &g_native_report, options.native_report_json);
1245 if (options.native_report_dir)
1246 friidump_native_report_enable_dir(
1247 &g_native_report, options.native_report_dir);
1249 #ifdef FRIIDUMP_BUILD_COMMIT
1250 friidump_native_report_set_build_commit(&g_native_report, FRIIDUMP_BUILD_COMMIT);
1253 if (options.firmware_modified_note)
1254 friidump_native_report_set_firmware_modified(
1255 &g_native_report, true, options.firmware_modified_note);
1257 if (signal(SIGINT, friidump_native_signal_handler) == SIG_ERR) {
1258 fprintf(stderr, "WARNING: could not install the native-report Ctrl+C handler.\n");
1262 static void friidump_native_set_drive(disc *d) {
1263 if (!friidump_native_report_is_enabled(&g_native_report) || !d)
1266 friidump_native_report_set_drive(
1268 disc_get_drive_vendor(d),
1269 disc_get_drive_product_id(d),
1270 disc_get_drive_firmware_revision(d),
1272 disc_get_device(d));
1275 static void friidump_native_set_profile(disc *d, bool xbox_output_requested) {
1276 (void) xbox_output_requested;
1278 if (!friidump_native_report_is_enabled(&g_native_report) || !d)
1281 friidump_native_report_set_profile(
1283 disc_get_hlds_e7_support_tier(d),
1284 disc_get_hlds_e7_static_cdb_base(d),
1285 disc_get_hlds_e7_static_gate(d));
1288 static void friidump_native_set_preflight_failure(int media_rc,
1294 if (!friidump_native_report_is_enabled(&g_native_report))
1297 friidump_native_report_set_seed(
1298 &g_native_report, false, "not_attempted", false, 0.0);
1299 friidump_native_report_set_dump(
1300 &g_native_report, false, "not_attempted",
1301 false, 0, false, 0, false, 0.0,
1302 NULL, false, 0, NULL);
1303 friidump_native_report_set_reference(
1304 &g_native_report, NULL, "not_applicable", NULL);
1306 if (media_rc == 0) {
1307 friidump_native_report_set_outcome(
1308 &g_native_report, "diagnostic_no_media", "not_applicable");
1309 snprintf(note, sizeof(note),
1310 "Media preflight found no readable disc (sense %02X/%02X/%02X).",
1311 sense_key & 0xff, asc & 0xff, ascq & 0xff);
1313 friidump_native_report_set_outcome(
1314 &g_native_report, "other", "fail");
1315 snprintf(note, sizeof(note),
1316 "Drive or media did not become ready (sense %02X/%02X/%02X).",
1317 sense_key & 0xff, asc & 0xff, ascq & 0xff);
1319 friidump_native_report_add_note(&g_native_report, note);
1322 static void friidump_native_set_media(disc_type type_id,
1325 const char *disc_id) {
1326 if (!friidump_native_report_is_enabled(&g_native_report))
1328 friidump_native_report_set_media(
1330 friidump_native_platform(type_id),
1336 static void friidump_native_add_measurement_scope(const char *scope) {
1338 if (!friidump_native_report_is_enabled(&g_native_report) || !scope)
1340 snprintf(note, sizeof(note), "Measurement scope: %s.", scope);
1341 friidump_native_report_add_note(&g_native_report, note);
1344 static void friidump_native_capture_output(dumper *dmp,
1349 u_int32_t failure_sector,
1350 u_int32_t expected_sectors) {
1351 xbox_ref_dump_result xbox_result;
1352 bool have_xbox_result;
1360 uint64_t sector_size;
1362 const char *failure_stage;
1364 if (!friidump_native_report_is_enabled(&g_native_report) || !dmp)
1367 g_native_report_dump_started = true;
1368 g_native_report_resumed = dumper_get_start_sector(dmp) > 0;
1370 xbox_ref_dump_result_init(&xbox_result);
1371 have_xbox_result = dumper_get_xbox_reference_result(dmp, &xbox_result);
1373 crc32 = md5 = sha1 = sha256 = NULL;
1378 if (have_xbox_result) {
1379 if (xbox_result.cancelled)
1381 path = xbox_result.output_path[0] ? xbox_result.output_path : NULL;
1382 bytes = xbox_result.output_size;
1383 sectors = xbox_result.output_sectors;
1384 if (g_validation_summary.active) {
1385 if (xbox_result.output_path[0])
1386 friidump_summary_copy(
1387 g_validation_summary.output,
1388 sizeof(g_validation_summary.output),
1389 xbox_result.output_path);
1390 if (xbox_result.title[0])
1391 friidump_summary_copy(
1392 g_validation_summary.title,
1393 sizeof(g_validation_summary.title),
1395 if (xbox_result.media_id[0])
1396 friidump_summary_copy(
1397 g_validation_summary.game_id,
1398 sizeof(g_validation_summary.game_id),
1399 xbox_result.media_id);
1400 if (xbox_result.region[0])
1401 friidump_summary_copy(
1402 g_validation_summary.region,
1403 sizeof(g_validation_summary.region),
1404 xbox_result.region);
1405 if (expected_sectors > 0) {
1406 g_validation_summary.source_sectors = expected_sectors;
1407 g_validation_summary.have_source_sectors = true;
1409 if (xbox_result.output_sectors > 0) {
1410 g_validation_summary.expected_output_sectors =
1411 xbox_result.output_sectors;
1412 g_validation_summary.have_expected_output_sectors = true;
1414 g_validation_summary.seed_applicable = false;
1415 g_validation_summary.have_seed_duration = false;
1417 if (xbox_result.hashes_complete) {
1418 crc32 = xbox_result.crc32;
1419 md5 = xbox_result.md5;
1420 sha1 = xbox_result.sha1;
1421 sha256 = xbox_result.sha256;
1423 if (xbox_result.title[0] || xbox_result.media_id[0] || xbox_result.region[0]) {
1424 friidump_native_report_set_media(
1427 xbox_result.title[0] ? xbox_result.title :
1428 (g_native_report.have_title ? g_native_report.title : NULL),
1429 xbox_result.region[0] ? xbox_result.region : NULL,
1430 xbox_result.media_id[0] ? xbox_result.media_id :
1431 (g_native_report.have_disc_id ? g_native_report.disc_id : NULL));
1433 if (xbox_result.have_game_region) {
1434 char region_note[96];
1435 snprintf(region_note, sizeof(region_note),
1436 "Xbox XBE GameRegion mask: 0x%08x.",
1437 xbox_result.game_region);
1438 friidump_native_report_add_note(&g_native_report, region_note);
1440 if (xbox_result.elapsed_seconds > 0.0) {
1441 g_native_report.have_dump_duration = true;
1442 g_native_report.dump_duration_seconds = xbox_result.elapsed_seconds;
1444 } else if (xiso_mode) {
1445 path = (options.xiso_out && options.xiso_out[0]) ? options.xiso_out : NULL;
1447 bytes = friidump_file_size(path);
1448 sectors = bytes / 2048;
1449 if (!options.no_hashing) {
1450 crc32 = dumper_get_xiso_crc32(dmp);
1451 md5 = dumper_get_xiso_md5(dmp);
1452 sha1 = dumper_get_xiso_sha1(dmp);
1453 sha256 = dumper_get_xiso_sha2(dmp);
1455 } else if (options.iso_out && options.iso_out[0]) {
1456 path = options.iso_out;
1457 bytes = friidump_file_size(path);
1458 sectors = bytes / 2048;
1459 if (!options.no_hashing) {
1460 crc32 = dumper_get_iso_crc32(dmp);
1461 md5 = dumper_get_iso_md5(dmp);
1462 sha1 = dumper_get_iso_sha1(dmp);
1463 sha256 = dumper_get_iso_sha2(dmp);
1465 } else if (options.raw_out && options.raw_out[0]) {
1466 path = options.raw_out;
1468 bytes = friidump_file_size(path);
1469 sectors = bytes / sector_size;
1470 if (!options.no_hashing) {
1471 crc32 = dumper_get_raw_crc32(dmp);
1472 md5 = dumper_get_raw_md5(dmp);
1473 sha1 = dumper_get_raw_sha1(dmp);
1474 sha256 = dumper_get_raw_sha2(dmp);
1478 if (path && (!success || bytes == 0)) {
1479 uint64_t observed_bytes = friidump_file_size(path);
1480 if (observed_bytes > 0 || !success)
1481 bytes = observed_bytes;
1483 sectors = bytes / sector_size;
1486 if (success && sectors == 0 && type_id != DISC_TYPE_XBOX) {
1487 sectors = expected_sectors;
1488 bytes = sectors * sector_size;
1491 if (g_native_report_resumed) {
1492 /* FriiDump's streaming hashes cover only bytes produced by this
1493 * invocation when a file is resumed, not the complete output file. */
1494 crc32 = md5 = sha1 = sha256 = NULL;
1497 partial = !success && (bytes > 0 ||
1498 (failure_sector != 0 && failure_sector != 0xFFFFFFFFU));
1499 failure_stage = NULL;
1502 failure_stage = "user_cancelled";
1504 failure_stage = (failure_sector == 0xFFFFFFFFU)
1505 ? "xbox_reference_dump"
1506 : "raw_sector_read_or_output_write";
1509 friidump_native_report_set_dump(
1512 success ? "pass" : (partial ? "partial" : "fail"),
1513 success || sectors > 0,
1515 success || bytes > 0,
1517 g_native_report.have_dump_duration,
1518 g_native_report.dump_duration_seconds,
1520 !success && failure_sector != 0xFFFFFFFFU,
1524 friidump_native_report_set_hashes(
1525 &g_native_report, crc32, md5, sha1, sha256);
1528 friidump_native_report_add_artifact(
1538 friidump_native_report_set_outcome(
1539 &g_native_report, "full_dump", "pass");
1541 friidump_native_report_set_outcome(
1542 &g_native_report, "partial_dump", "partial");
1544 friidump_native_report_set_outcome(
1545 &g_native_report, "other", "fail");
1547 friidump_native_report_set_outcome(
1548 &g_native_report, "full_dump", "fail");
1551 friidump_native_report_add_note(
1553 "The invocation was cancelled by the user; only completed output bytes are represented.");
1555 if (g_native_report_resumed) {
1556 friidump_native_add_measurement_scope("unknown");
1557 friidump_native_report_add_note(
1559 "Resumed output: duration is not used for throughput because the output byte count includes data from an earlier invocation.");
1560 friidump_native_report_add_note(
1562 "Resumed output: streaming hashes were suppressed because they do not describe the complete output file.");
1563 } else if (!success && partial) {
1564 friidump_native_add_measurement_scope("partial_progress");
1565 } else if (type_id == DISC_TYPE_XBOX || xiso_mode) {
1566 friidump_native_add_measurement_scope("assembled_output");
1567 friidump_native_report_add_note(
1569 "Xbox output throughput describes the produced logical or assembled output and is not directly comparable to a full optical-payload read.");
1570 } else if (success) {
1571 friidump_native_add_measurement_scope("full_optical_payload");
1573 friidump_native_add_measurement_scope("unknown");
1576 if (options.no_hashing)
1577 friidump_native_report_add_note(
1579 "Output hashing was disabled for this invocation.");
1582 static bool friidump_elapsed_from_stats(const progstats *stats, double *duration) {
1586 if (!stats || !duration || stats->start_time.tv_sec == 0 ||
1587 stats->end_time.tv_sec == 0)
1590 value = (double) (stats->end_time.tv_sec - stats->start_time.tv_sec);
1591 if (stats->end_time.tv_usec >= stats->start_time.tv_usec) {
1592 usecs = stats->end_time.tv_usec - stats->start_time.tv_usec;
1595 usecs = USECS_PER_SEC + stats->end_time.tv_usec - stats->start_time.tv_usec;
1597 value += (double) usecs / (double) USECS_PER_SEC;
1604 static void friidump_native_finalize_reference(void) {
1605 if (!friidump_native_report_is_enabled(&g_native_report))
1608 if (strcmp(g_native_report.run_result, "not_applicable") == 0) {
1609 friidump_native_report_set_reference(
1610 &g_native_report, NULL, "not_applicable", NULL);
1611 } else if (g_redump_attempted) {
1612 if (g_redump_result.status == REDUMP_VERIFY_MATCH) {
1613 friidump_native_report_set_reference(
1617 g_redump_result.rom_name[0]
1618 ? g_redump_result.rom_name
1619 : g_redump_result.game_name);
1620 } else if (g_redump_result.status == REDUMP_VERIFY_NO_MATCH) {
1621 friidump_native_report_set_reference(
1622 &g_native_report, "redump", "mismatch", NULL);
1624 friidump_native_report_set_reference(
1625 &g_native_report, "redump", "not_checked", NULL);
1627 } else if (options.xiso_requested) {
1628 friidump_native_report_set_reference(
1629 &g_native_report, NULL, "not_applicable", NULL);
1631 friidump_native_report_set_reference(
1632 &g_native_report, NULL, "not_checked", NULL);
1636 static void friidump_native_publish(double duration, bool have_duration) {
1637 char path[FRIIDUMP_REPORT_PATH_MAX];
1638 char error_text[256];
1639 const char *name_basis;
1641 if (!friidump_native_report_is_enabled(&g_native_report))
1644 name_basis = xbox_ref_log_path();
1646 name_basis = friidump_requested_output_target();
1648 name_basis = "friidump.log";
1649 friidump_native_report_set_name_basis(&g_native_report, name_basis);
1651 if (g_native_report_dump_started && !g_native_report_resumed &&
1652 !g_native_report.have_dump_duration && have_duration) {
1653 g_native_report.have_dump_duration = true;
1654 g_native_report.dump_duration_seconds = duration;
1657 friidump_native_finalize_reference();
1659 if (friidump_native_report_write(
1664 sizeof(error_text))) {
1665 fprintf(stderr, "Native report.......: %s (atomic write)\n", path);
1667 fprintf(stderr, "WARNING: native report was not written: %s\n", error_text);
1673 * |-------------------------------------------------------------------------------|
1675 fprintf (stderr, "\n"
1676 "Available command line options:\n"
1678 " -h, --help Show this help\n"
1679 " -a, --autodump Dump the disc to an ISO file with an\n"
1680 " automatically-generated name, resuming the dump\n"
1682 " -g, --gui Use more verbose output that can be easily\n"
1683 " parsed by a GUI frontend\n"
1684 " -d, --device <device> Dump disc from device <device>\n"
1685 " -p, --stop Instruct device to stop disc rotation\n"
1686 " -D, --dvd Force standard DVD-ROM mode for any drive\n"
1687 " (equivalent to -T 3; uses FriiDump original\n"
1688 " DVD/raw/ISO paths, not Xbox mode)\n"
1689 " -c, --command <nr> Force memory dump command:\n"
1690 " 0 - vanilla 2064\n"
1691 " 1 - vanilla 2384\n"
1695 " -x, --speed <x> Set streaming speed (1, 24, 32, 64, etc.,\n"
1696 " where 1 = 150 KiB/s and so on)\n"
1697 " -T, --type <nr> Force disc type:\n"
1702 " 4 - Xbox/XGD 2048-byte-sector mode\n"
1703 " Native profiles: GDR-8050L and GDR-3120L.\n"
1704 " Other drives keep normal FriiDump behavior\n"
1705 " unless Xbox mode is explicitly forced.\n"
1706 " -S, --size <sectors> Force disc size\n"
1707 " -r, --raw <file> Output to file <file> in raw format (2064-byte\n"
1709 " -i, --iso[=<file>] Output to file <file> in ISO format (2048-byte\n"
1710 " sectors). For Xbox/GDR-8050L, omitting <file>\n"
1711 " derives Title[MediaID].iso from the XBE/DMI;\n"
1712 " providing <file> is an explicit override. For\n"
1713 " Xbox/XGD this reconstructs the redump-style\n"
1714 " XGD1 layout and writes .pfi.bin, .dmi.bin, and\n"
1715 " .redump.json metadata when possible\n"
1716 " -X, --xiso[=<file>] Output Xbox/XGD game partition as XISO (.xiso).\n"
1717 " For Xbox/GDR-8050L, omitting <file> derives\n"
1718 " Title[MediaID].xiso from the XBE/DMI; providing\n"
1719 " <file> is an explicit override. Attempts to read\n"
1720 " the 32-sector game lead-in from drive-readable\n"
1721 " sectors and zero-fills only unreadable sectors.\n"
1722 " -u, --unscramble <file> Convert (unscramble) raw image contained in\n"
1723 " <file> to ISO format\n"
1724 " -H, --nohash Do not compute CRC32/MD5/SHA-1/SHA-256 hashes\n"
1725 " for generated files\n"
1726 " -s, --resume Resume partial dump\n"
1727 " - General -----------------------------------\n"
1728 " -0, --method0[=<req>,<exp>] Use dumping method 0 (Optional argument\n"
1729 " specifies how many sectors to request from disc\n"
1730 " and read from cache at a time. Values should be\n"
1731 " separated with a comma. Default 16,16)\n"
1732 " - Non-Streaming -----------------------------\n"
1733 " -1, --method1[=<req>,<exp>] Use dumping method 1 (Default 16,16)\n"
1734 " -2, --method2[=<req>,<exp>] Use dumping method 2 (Default 16,16)\n"
1735 " -3, --method3[=<req>,<exp>] Use dumping method 3 (Default 16,16)\n"
1736 " - Streaming ---------------------------------\n"
1737 " -4, --method4[=<req>,<exp>] Use dumping method 4 (Default 27,27)\n"
1738 " -5, --method5[=<req>,<exp>] Use dumping method 5 (Default 27,27)\n"
1739 " -6, --method6[=<req>,<exp>] Use dumping method 6 (Default 27,27)\n"
1740 " - Hitachi -----------------------------------\n"
1741 " -7, --method7 Use dumping method 7 (Read and dump 5 blocks\n"
1742 " at a time, using streaming read)\n"
1743 " -8, --method8 Use dumping method 8 (Read and dump 5 blocks\n"
1744 " at a time, using streaming read, using DMA)\n"
1745 " -9, --method9 Use dumping method 9 (Read and dump 5 blocks\n"
1746 " at a time, using streaming read, using DMA and\n"
1747 " some speed tricks)\n"
1748 " --hlds-e7-scan Probe HLDS HIT 0xE7 cache/memdump bases only;\n"
1749 " writes JSON and does not crack seeds or dump data\n"
1750 " --hlds-e7-subcmd-sweep Probe HIT 0xE7 subcommands/address candidates only;\n"
1751 " writes JSON and does not crack seeds or dump data\n"
1752 " --hlds-e7-memrange-sweep Sweep wider HIT 0xE7 subcmd 0x01 address ranges;\n"
1753 " writes JSON and does not crack seeds or dump data\n"
1754 " --scan-log <file> JSON output path for --hlds-e7-scan\n"
1755 " --scan-dump-prefix <prefix> Optional raw 0xE7 window dump prefix for --hlds-e7-scan\n"
1756 " --hlds-profile-report <file> Write selected HLDS profile/evidence JSON\n"
1757 " --redump-dat-dir <dir> Directory containing canonical Redump DAT files\n"
1758 " (default: bundled executable data, installed share data, then current directory)\n"
1759 " Environment override: FRIIDUMP_REDUMP_DAT_DIR\n"
1760 " --redump-report <file> Write atomic Redump evidence JSON\n"
1761 " --no-redump-verify Disable automatic post-dump DAT verification\n"
1762 " --xgd1-layout-probe <file> Read-only locked/unlocked XGD1 boundary probe;\n"
1763 " writes atomic JSON and does not create an ISO\n"
1764 " --xgd1-raw-id-probe <file> Modified-firmware cache-flushed, block-aligned raw-ID probe;\n"
1765 " maps logical LBAs to decoded physical sector IDs\n"
1766 " Native .friidump.json reports are created by default\n"
1767 " beside the final log using its filename\n"
1768 " --report-json <file> Override the complete native report pathname\n"
1769 " --report-dir <directory> Override only the report destination directory\n"
1770 " (may be combined with --report-json)\n"
1771 " --firmware-modified <note> Mark firmware modified; omission assumes stock\n"
1772 " -A, --allmethods Try all known command/method combinations until\n"
1773 " one works. Reopens the drive for each command so\n"
1774 " command-specific vendor handlers are rebound.\n"
1776 " -n, --donottunscramble Do not try unscrambling to check EDC. Only\n"
1777 " useful for testing the raw performance of the\n"
1778 " different methods\n"
1779 " -f, --donottflush Do not call fflush() after every fwrite()\n"
1784 "\nLinux raw-I/O requirement:\n"
1785 " GC/Wii memory-dump methods and Xbox vendor-unlock paths require effective\n"
1786 " CAP_SYS_RAWIO. Apply cap_sys_rawio=ep to the exact validated executable;\n"
1787 " do not run FriiDump as root. Rebuilds and replacements clear capabilities.\n"
1788 " See docs/LINUX.md and validation/friidump-linux-rawio-capability.sh.\n");
1795 bool optparse (int argc, char **argv) {
1797 char *result = NULL;
1799 int option_index = 0;
1800 static struct option long_options[] = {
1801 {"help", 0, 0, 'h'}, //0 - no_argument
1802 {"autodump", 0, 0, 'a'},
1804 {"device", 1, 0, 'd'}, //1 - required_argument
1807 {"xiso", 2, 0, 'X'},
1808 {"unscramble", 1, 0, 'u'},
1809 {"nohash", 0, 0, 'H'},
1810 {"resume", 0, 0, 's'},
1811 {"method0", 2, 0, '0'}, //2 - optional_argument
1812 {"method1", 2, 0, '1'},
1813 {"method2", 2, 0, '2'},
1814 {"method3", 2, 0, '3'},
1815 {"method4", 2, 0, '4'},
1816 {"method5", 2, 0, '5'},
1817 {"method6", 2, 0, '6'},
1818 {"method7", 0, 0, '7'},
1819 {"method8", 0, 0, '8'},
1820 {"method9", 0, 0, '9'},
1821 {"stop", 0, 0, 'p'},
1823 {"command", 1, 0, 'c'},
1824 {"startsector", 1, 0, 't'},
1825 {"size", 1, 0, 'S'},
1826 {"speed", 1, 0, 'x'},
1827 {"type", 1, 0, 'T'},
1828 {"allmethods", 0, 0, 'A'},
1829 {"hlds-e7-scan", 0, 0, 1000},
1830 {"hlds-e7-subcmd-sweep", 0, 0, 1003},
1831 {"hlds-e7-memrange-sweep", 0, 0, 1004},
1832 {"scan-log", 1, 0, 1001},
1833 {"scan-dump-prefix", 1, 0, 1002},
1834 {"hlds-profile-report", 1, 0, 1005},
1835 {"redump-dat-dir", 1, 0, 1006},
1836 {"redump-report", 1, 0, 1007},
1837 {"no-redump-verify", 0, 0, 1008},
1838 {"xgd1-layout-probe", 1, 0, 1009},
1839 {"xgd1-raw-id-probe", 1, 0, 1010},
1840 {"report-json", 1, 0, 1011},
1841 {"report-dir", 1, 0, 1012},
1842 {"firmware-modified", 1, 0, 1013},
1844 /* We don't want newbies to generate and put into circulation bad dumps, so this options are disabled for releases */
1845 {"donottunscramble", 0, 0, 'n'},
1846 {"donottflush", 0, 0, 'f'},
1856 /* Init options to default values */
1857 options.device = NULL;
1858 options.autodump = false;
1859 options.gui = false;
1860 options.raw_in = NULL;
1861 options.raw_out = NULL;
1862 options.iso_out = NULL;
1863 options.xiso_out = NULL;
1864 options.iso_requested = false;
1865 options.xiso_requested = false;
1866 options.xbox_filename_override = false;
1867 options.xbox_auto_filename = false;
1868 options.no_hashing = false;
1869 options.resume = false;
1870 options.dump_method = -1;
1871 options.command = -1;
1872 options.start_sector = -1;
1873 options.sectors_no = -1;
1875 options.disctype = -1;
1876 options.sec_disc = -1;
1877 options.sec_mem = -1;
1878 options.no_unscrambling = false;
1879 options.no_flushing = false;
1880 options.stop_unit = false;
1881 options.allmethods = false;
1882 options.hlds_e7_scan = false;
1883 options.hlds_e7_subcmd_sweep = false;
1884 options.hlds_e7_memrange_sweep = false;
1885 options.hlds_e7_scan_log = NULL;
1886 options.hlds_e7_scan_dump_prefix = NULL;
1887 options.hlds_profile_report = NULL;
1888 options.redump_dat_dir = NULL;
1889 options.redump_report = NULL;
1890 options.no_redump_verify = false;
1891 options.xgd1_layout_probe = false;
1892 options.xgd1_layout_probe_report = NULL;
1893 options.xgd1_raw_id_probe = false;
1894 options.xgd1_raw_id_probe_report = NULL;
1895 options.native_report_json = NULL;
1896 options.native_report_dir = NULL;
1897 options.firmware_modified_note = NULL;
1901 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);
1903 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);
1912 options.stop_unit = true;
1915 options.autodump = true;
1916 options.resume = true;
1922 my_strdup (options.device, optarg);
1925 my_strdup (options.raw_out, optarg);
1928 options.iso_requested = true;
1929 /* Preserve the old `-i file.iso` syntax even though Xbox now also
1930 * supports bare `-i` for XBE/DMI-derived names. */
1931 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
1932 optarg = argv[optind++];
1934 my_strdup (options.iso_out, optarg);
1935 options.xbox_filename_override = true;
1937 options.xbox_auto_filename = true;
1941 options.xiso_requested = true;
1942 /* Preserve the old `-X file.xiso` syntax while allowing bare `-X`. */
1943 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
1944 optarg = argv[optind++];
1946 my_strdup (options.xiso_out, optarg);
1947 options.xbox_filename_override = true;
1949 options.xbox_auto_filename = true;
1953 my_strdup (options.raw_in, optarg);
1956 options.no_hashing = true;
1959 options.resume = true;
1968 options.dump_method = c - '0';
1970 result = strtok(optarg, ",");
1971 result = strtok(NULL, ",");
1972 options.sec_disc = atol(strpbrk(optarg,"1234567890"));
1973 if (result) options.sec_mem = atol(result);
1983 options.dump_method = c - '0';
1986 options.disctype = DISC_TYPE_DVD;
1987 unscrambler_set_disctype (DISC_TYPE_DVD);
1990 options.command = atol (optarg);
1991 if (options.command > 4) {
1997 options.start_sector = atol (optarg);
2000 options.sectors_no = atol (optarg);
2003 options.speed = atol (optarg);
2006 options.disctype = atol (optarg);
2007 if (options.disctype > 4) {
2011 if (options.disctype <= DISC_TYPE_DVD)
2012 unscrambler_set_disctype (options.disctype);
2015 options.allmethods = true;
2016 options.resume = true;
2020 options.no_unscrambling = true;
2023 options.no_flushing = true;
2027 options.hlds_e7_scan = true;
2030 options.hlds_e7_subcmd_sweep = true;
2033 options.hlds_e7_memrange_sweep = true;
2036 my_strdup (options.hlds_e7_scan_log, optarg);
2039 my_strdup (options.hlds_e7_scan_dump_prefix, optarg);
2042 my_strdup (options.hlds_profile_report, optarg);
2045 my_strdup (options.redump_dat_dir, optarg);
2048 my_strdup (options.redump_report, optarg);
2051 options.no_redump_verify = true;
2054 options.xgd1_layout_probe = true;
2055 my_strdup (options.xgd1_layout_probe_report, optarg);
2056 options.disctype = DISC_TYPE_XBOX;
2059 options.xgd1_raw_id_probe = true;
2060 my_strdup (options.xgd1_raw_id_probe_report, optarg);
2061 options.disctype = DISC_TYPE_XBOX;
2064 my_strdup (options.native_report_json, optarg);
2067 my_strdup (options.native_report_dir, optarg);
2070 my_strdup (options.firmware_modified_note, optarg);
2075 // fprintf (stderr, "?? getopt returned character code 0%o ??\n", c);
2081 if (optind < argc) {
2082 /* Command-line arguments remaining. Ignore them, warning the user. */
2083 fprintf (stderr, "WARNING: Extra parameters ignored\n");
2086 if (options.xgd1_layout_probe || options.xgd1_raw_id_probe)
2087 options.disctype = DISC_TYPE_XBOX;
2089 /* Sanity checks... */
2091 if (!options.device && !options.raw_in) {
2092 fprintf (stderr, "No operation specified. Please use the -d or -u options.\n");
2093 } else if (options.raw_in && options.raw_out) {
2095 "Are you sure you want to convert a raw image to another raw image? ;)\n"
2096 "Take a look at the -i and -a options!\n"
2098 } else if (options.autodump && (options.raw_out || options.iso_requested || options.xiso_requested)) {
2099 fprintf (stderr, "The -r, -i and -X options cannot be used together with -a.\n");
2100 } else if (options.xiso_requested && (options.raw_out || options.iso_requested)) {
2101 fprintf (stderr, "The -X/--xiso option is a separate Xbox output mode and cannot be combined with -r or -i.\n");
2102 } else if ((options.xgd1_layout_probe || options.xgd1_raw_id_probe) &&
2103 (options.autodump || options.raw_in || options.raw_out ||
2104 options.iso_requested || options.xiso_requested ||
2105 options.allmethods || options.hlds_e7_scan ||
2106 options.hlds_e7_subcmd_sweep || options.hlds_e7_memrange_sweep ||
2107 (options.xgd1_layout_probe && options.xgd1_raw_id_probe))) {
2108 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");
2109 } else if ((options.native_report_json && !options.native_report_json[0]) ||
2110 (options.native_report_dir && !options.native_report_dir[0])) {
2111 fprintf (stderr, "Native report file and directory arguments must not be empty.\n");
2112 } else if ((options.native_report_json && strlen(options.native_report_json) >= FRIIDUMP_REPORT_PATH_MAX) ||
2113 (options.native_report_dir && strlen(options.native_report_dir) >= FRIIDUMP_REPORT_PATH_MAX)) {
2114 fprintf (stderr, "Native report path is too long.\n");
2115 } else if (options.firmware_modified_note &&
2116 !options.firmware_modified_note[0]) {
2117 fprintf (stderr, "--firmware-modified requires a non-empty explanation.\n");
2118 } else if (options.firmware_modified_note &&
2119 strlen(options.firmware_modified_note) > 1000) {
2120 fprintf (stderr, "--firmware-modified note exceeds the 1000-byte report limit.\n");
2121 } else if (friidump_native_report_option_requested() &&
2122 !friidump_native_report_operation_supported()) {
2123 fprintf (stderr, "Native compatibility report options require one normal device dump invocation with exactly one primary output and cannot be combined with conversion, all-methods, stop-only, or research probe modes.\n");
2124 } else if (options.device &&
2125 !options.allmethods && !options.stop_unit &&
2126 !options.hlds_e7_scan && !options.hlds_e7_subcmd_sweep &&
2127 !options.hlds_e7_memrange_sweep && !options.xgd1_layout_probe &&
2128 !options.xgd1_raw_id_probe &&
2129 options.raw_out && options.iso_requested) {
2130 fprintf (stderr, "Native compatibility reports are created by default and require exactly one primary output; do not combine -r and -i.\n");
2132 /* Specified options seem to make sense */
2139 int dologic (disc *d, progstats *stats) {
2141 char *type, *game_id, *region, *maker_id, *maker, *version, *title, tmp[0x03E0 + 4 + 1];
2142 bool drive_supported;
2144 bool xbox_output_requested;
2145 bool dump_attempted;
2148 u_int32_t current_sector = 0;
2150 xbox_forced = (options.disctype == DISC_TYPE_XBOX);
2151 xbox_output_requested = xbox_forced || options.xiso_requested || options.xgd1_layout_probe || options.xgd1_raw_id_probe;
2152 dump_attempted = false;
2154 g_native_cancel_requested = 0;
2158 if (options.stop_unit) { //stop rotation, if requested
2159 fprintf (stderr, "Issuing STOP command... %s\n", (disc_stop_unit (d, false)) ? "OK" : "Failed");
2162 else disc_stop_unit(d, true); //else start rotation
2164 drive_supported = disc_get_drive_support_status (d);
2167 "Drive information:\n"
2168 "----------------------------------------------------------------------\n"
2169 "Drive model........: %s\n"
2170 "Supported..........: %s\n", disc_get_drive_model_string (d), drive_supported ? "Yes" : "No"
2173 if (xbox_output_requested && !disc_is_xbox_unlock_drive (d)) {
2174 if (friidump_native_report_is_enabled(&g_native_report))
2175 friidump_native_report_add_note(&g_native_report, "The selected drive does not expose a supported Xbox unlock profile; dumping was not attempted.");
2177 "Xbox/XGD output is limited to the supported Xbox unlock profiles "
2178 "currently wired into this branch: GDR-8050L, GDR-3120L, "
2179 "and known Samsung/Kreon-style vendor-unlock drives.\n"
2180 "Refusing to fall back to FriiDump GC/Wii methods for Xbox mode on this drive.\n");
2184 /* Xbox/XGD (-T 4 or -X) is a direct MMC/SCSI READ(10) path.
2185 * Do not let the detected GC/Wii vendor memdump method (for example
2186 * Hitachi command 2 / method 9 on GDR-8050L) drive sector reads. */
2187 if (xbox_output_requested && options.dump_method == -1)
2188 options.dump_method = 10;
2190 init_range(d, options.sec_disc, options.sec_mem);
2192 if (!(disc_set_read_method (d, options.dump_method))) {
2193 if (friidump_native_report_is_enabled(&g_native_report))
2194 friidump_native_report_add_note(&g_native_report, "FriiDump could not select the requested read method; dumping was not attempted.");
2198 if (xbox_output_requested && disc_get_method(d) == 10) {
2199 fprintf (stderr, "Command............: Xbox direct MMC/SCSI path\n");
2200 fprintf (stderr, "Method.............: 10 (Xbox READ(10))\n");
2202 if (options.command!=-1) fprintf (stderr,
2203 "Command............: %d (forced)\n", disc_get_command(d));
2204 else fprintf (stderr,
2205 "Command............: %d\n", disc_get_command(d));
2206 if (disc_get_def_method(d)!=disc_get_method(d)) fprintf (stderr,
2207 "Method.............: %d (forced)\n", disc_get_method(d));
2208 else fprintf (stderr,
2209 "Method.............: %d\n", disc_get_method(d));
2210 if (disc_get_hlds_e7_type (d) != 0) {
2211 fprintf (stderr, "HLDS 0xE7 profile..: %s\n", disc_get_hlds_e7_profile_name (d));
2212 fprintf (stderr, "HLDS support tier..: %s\n", disc_get_hlds_e7_support_tier (d));
2213 fprintf (stderr, "HLDS family........: %s\n", disc_get_hlds_e7_family (d));
2214 fprintf (stderr, "HLDS E7 tokens.....: %s\n", disc_get_hlds_e7_tokens (d));
2215 fprintf (stderr, "HLDS Stage5B row...: %s\n", disc_get_hlds_e7_record_id (d));
2216 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));
2217 fprintf (stderr, "Cache base.........: 0x%08x\n", disc_get_hlds_e7_cache_base (d));
2218 fprintf (stderr, "Memory windows.....: %u\n", disc_get_hlds_e7_mem_blocks (d));
2219 if (disc_get_hlds_e7_notes (d) && disc_get_hlds_e7_notes (d)[0])
2220 fprintf (stderr, "HLDS notes.........: %s\n", disc_get_hlds_e7_notes (d));
2223 if (options.hlds_profile_report) {
2224 if (friidump_write_hlds_profile_report(d, options.hlds_profile_report))
2225 fprintf(stderr, "HLDS profile report: %s\n", options.hlds_profile_report);
2227 options.dump_method=disc_get_method(d);
2228 friidump_summary_begin(d);
2229 friidump_native_set_profile(d, xbox_output_requested);
2231 if (!friidump_linux_rawio_preflight(
2233 xbox_output_requested,
2235 options.hlds_e7_scan ||
2236 options.hlds_e7_subcmd_sweep ||
2237 options.hlds_e7_memrange_sweep))
2240 if ((options.dump_method==0)
2241 || (options.dump_method==1) || (options.dump_method==2) || (options.dump_method==3)
2242 || (options.dump_method==4) || (options.dump_method==5) || (options.dump_method==6)
2245 "Requested sectors..: %d\n", disc_get_sec_disc(d));
2247 "Expected sectors...: %d\n", disc_get_sec_mem(d));
2250 fprintf (stderr, "\nPress Ctrl+C at any time to terminate\n");
2252 //set speed for 1st time
2253 if (options.speed != -1) disc_set_speed(d, options.speed * 177);
2254 if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
2255 // disc_set_speed(d, 0xffff);
2257 /* Windows may attach filesystem/autoplay polling to odd GC/Wii discs and
2258 * steal the drive during the HLDS 0xE7 seed phase, especially on Type1
2259 * GCC-4160N/GCC-4240N. Reuse the same volume guard mechanism that the
2260 * Xbox path already uses, but apply it before disc_init()/seed reads for
2261 * all HLDS 0xE7 GC/Wii profiles. Failure is warning-only because Explorer
2262 * may already have a transient handle; the read path itself remains the
2264 if (!xbox_output_requested && disc_get_hlds_e7_type (d) != 0) {
2265 if (disc_get_hlds_e7_type (d) == 44 || disc_get_hlds_e7_type (d) == 45) {
2267 "\nGDR-8050L modified-firmware warning:\n"
2268 " This GC/Wii 0xE7 path assumes a cross-flashed or modified GDR-8050L firmware with 0xE7 memdump support added.\n"
2269 " Stock GDR-8050L firmware is still supported for Xbox ripping, but it is not expected to dump GC/Wii discs through this path.\n");
2273 "\nWindows AutoPlay warning:\n"
2274 " HLDS 0xE7 GC/Wii seed reads are sensitive to Windows polling.\n"
2275 " Disable AutoPlay for this drive/media and close File Explorer or any \"insert a disc\" dialogs before dumping.\n"
2276 " FriiDump will try to lock the volume, but AutoPlay can still interfere before or during seed retrieval.\n");
2277 fprintf (stderr, "Applying Windows volume lock guard for HLDS 0xE7 seed reads... ");
2280 "\nLinux media-polling warning:\n"
2281 " HLDS 0xE7 GC/Wii seed reads can be disturbed by desktop automount and media polling.\n"
2282 " Unmount the disc and close file managers or media players before dumping.\n");
2284 disc_refresh_volume (d);
2286 int volume_lock_result = disc_lock_volume (d);
2288 if (volume_lock_result < 0)
2289 fprintf (stderr, "Warning: failed; disable AutoPlay, close File Explorer/AutoPlay dialogs for this drive, then rerun.\n");
2291 fprintf (stderr, "OK\n");
2293 if (volume_lock_result > 0)
2294 fprintf (stderr, "Linux exclusive optical-device lock: unavailable; continuing without one.\n");
2295 else if (volume_lock_result < 0)
2296 fprintf (stderr, "Linux exclusive optical-device lock: failed; continuing without one.\n");
2298 fprintf (stderr, "Linux exclusive optical-device lock: acquired.\n");
2303 if (options.xgd1_layout_probe || options.xgd1_raw_id_probe) {
2306 int media_sense_key;
2311 const char *probe_name;
2313 probe_name = options.xgd1_raw_id_probe ? "XGD1 raw-sector ID" : "XGD1 logical-boundary";
2315 if (!disc_is_xbox_challenge_drive (d)) {
2317 "%s probe currently supports only the GDR-8050L challenge-handshake profile.\n",
2322 if (options.xgd1_raw_id_probe &&
2323 !(disc_get_hlds_e7_type (d) == 44 ||
2324 disc_get_hlds_e7_type (d) == 45 ||
2325 disc_get_hlds_e7_type (d) == 442 ||
2326 disc_get_hlds_e7_type (d) == 443 ||
2327 disc_get_hlds_e7_type (d) == 445)) {
2329 "--xgd1-raw-id-probe requires the modified GDR-8050L HIT 0xE7 memdump profile.\n");
2333 fprintf (stderr, "\nChecking for ready Xbox media before %s probing... ", probe_name);
2334 media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq);
2335 if (media_rc <= 0) {
2337 "Failed (sense %02X/%02X/%02X). Insert the disc, wait for spin-up, close AutoPlay/File Explorer, and retry.\n",
2338 media_sense_key, media_asc, media_ascq);
2341 fprintf (stderr, "OK\n");
2343 gettimeofday (&(stats -> start_time), NULL);
2344 if (options.xgd1_raw_id_probe)
2345 probe_status = xbox_ref_xgd1_raw_id_probe_with_handle (
2346 disc_get_native_handle (d), disc_get_device (d), options.xgd1_raw_id_probe_report);
2348 probe_status = xbox_ref_xgd1_layout_probe_with_handle (
2349 disc_get_native_handle (d), disc_get_device (d), options.xgd1_layout_probe_report);
2351 fprintf (stderr, "Issuing STOP UNIT / spin-down after %s probe... ", probe_name);
2352 stop_ok = disc_stop_unit (d, false);
2353 fprintf (stderr, "%s\n", stop_ok ? "OK" : "Failed");
2354 gettimeofday (&(stats -> end_time), NULL);
2355 fprintf (stderr, "%s probe status: %s\n", probe_name, probe_status == 0 ? "OK" : "FAILED");
2356 friidump_summary_reset();
2357 return probe_status == 0;
2359 fprintf (stderr, "XGD1 probe modes are available only in the Windows build.\n");
2364 if (options.hlds_e7_scan) {
2365 out = disc_hlds_e7_scan (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
2366 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 scan... ");
2367 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
2371 if (options.hlds_e7_subcmd_sweep) {
2372 out = disc_hlds_e7_subcmd_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
2373 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 subcommand sweep... ");
2374 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
2378 if (options.hlds_e7_memrange_sweep) {
2379 out = disc_hlds_e7_memrange_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
2380 fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 memory-range sweep... ");
2381 fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
2386 int media_rc, media_sense_key, media_asc, media_ascq;
2387 fprintf (stderr, "\nChecking for ready media before disc seed retrieval... ");
2388 media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq);
2389 if (media_rc <= 0) {
2390 friidump_native_set_preflight_failure(
2391 media_rc, media_sense_key, media_asc, media_ascq);
2393 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);
2395 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);
2398 fprintf (stderr, "OK\n");
2400 struct timeval seed_start, seed_end;
2401 double seed_elapsed;
2402 char seed_elapsed_buf[32];
2404 if (xbox_output_requested)
2405 fprintf (stderr, "\nInitializing Xbox/XGD disc state... ");
2407 fprintf (stderr, "\nRetrieving disc seeds, this might take a while... ");
2409 gettimeofday(&seed_start, NULL);
2410 if (!disc_init (d, options.disctype, options.sectors_no)) {
2411 gettimeofday(&seed_end, NULL);
2412 seed_elapsed = (double)(seed_end.tv_sec - seed_start.tv_sec) +
2413 (double)(seed_end.tv_usec - seed_start.tv_usec) / (double)USECS_PER_SEC;
2414 if (seed_elapsed < 0.0) seed_elapsed = 0.0;
2415 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
2416 if (!xbox_output_requested)
2417 fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
2418 if (g_validation_summary.active) {
2419 g_validation_summary.have_seed_duration = true;
2420 g_validation_summary.seed_duration = seed_elapsed;
2422 if (friidump_native_report_is_enabled(&g_native_report)) {
2423 if (g_native_cancel_requested) {
2424 friidump_native_report_set_seed(&g_native_report, true, "fail", true, seed_elapsed);
2425 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
2426 friidump_native_report_add_note(&g_native_report, "The invocation was cancelled by the user during media initialization or seed retrieval.");
2427 } else if (xbox_output_requested) {
2428 friidump_native_report_set_seed(&g_native_report, false, "not_applicable", false, 0.0);
2429 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
2430 friidump_native_report_add_note(&g_native_report, "Xbox/media initialization failed before dumping.");
2432 friidump_native_report_set_seed(&g_native_report, true, "fail", true, seed_elapsed);
2433 friidump_native_report_set_outcome(&g_native_report, "seed_only", "fail");
2434 friidump_native_report_add_note(&g_native_report, "Disc initialization or seed retrieval failed.");
2436 friidump_native_report_set_dump(&g_native_report, false, "not_attempted", false, 0, false, 0, false, 0.0, NULL, false, 0, NULL);
2438 if (!xbox_output_requested) {
2440 friidump_capture_seed_diagnostic (d);
2441 stop_ok = disc_stop_unit (d, false);
2442 fprintf (stderr, "\nIssuing STOP UNIT / spin-down after seed failure... %s\n", stop_ok ? "OK" : "Failed");
2443 if (g_validation_summary.active) {
2444 g_validation_summary.stop_attempted = true;
2445 g_validation_summary.stop_ok = stop_ok;
2447 if (friidump_native_report_is_enabled(&g_native_report))
2448 friidump_native_report_add_note(&g_native_report,
2449 stop_ok ? "STOP UNIT after seed failure succeeded." : "STOP UNIT after seed failure failed.");
2451 fprintf (stderr, "Failed\n");
2454 gettimeofday(&seed_end, NULL);
2455 seed_elapsed = (double)(seed_end.tv_sec - seed_start.tv_sec) +
2456 (double)(seed_end.tv_usec - seed_start.tv_usec) / (double)USECS_PER_SEC;
2457 if (seed_elapsed < 0.0) seed_elapsed = 0.0;
2458 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
2459 if (!xbox_output_requested)
2460 fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
2461 fprintf (stderr, "OK\n");
2462 if (g_validation_summary.active) {
2463 g_validation_summary.seed_ok = true;
2464 g_validation_summary.have_seed_duration = true;
2465 g_validation_summary.seed_duration = seed_elapsed;
2467 disc_get_type (d, &type_id, &type);
2468 disc_get_gameid (d, &game_id);
2469 disc_get_region (d, NULL, ®ion);
2470 disc_get_maker (d, &maker_id, &maker);
2471 disc_get_version (d, NULL, &version);
2472 disc_get_title (d, &title);
2473 if (g_validation_summary.active) {
2474 friidump_summary_copy(g_validation_summary.disc_type, sizeof(g_validation_summary.disc_type), type);
2475 friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), game_id);
2476 friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), title);
2477 g_validation_summary.sectors = disc_get_sectors_no(d);
2479 friidump_native_set_media(type_id, title,
2480 type_id == DISC_TYPE_XBOX ? NULL : region, game_id);
2481 if (friidump_native_report_is_enabled(&g_native_report)) {
2482 if (type_id == DISC_TYPE_GAMECUBE || type_id == DISC_TYPE_WII || type_id == DISC_TYPE_WII_DL)
2483 friidump_native_report_set_seed(&g_native_report, true, "pass", true, seed_elapsed);
2485 friidump_native_report_set_seed(&g_native_report, false, "not_applicable", false, 0.0);
2487 if (g_native_cancel_requested) {
2488 friidump_native_report_set_dump(&g_native_report, false, "not_attempted", false, 0, false, 0, false, 0.0, NULL, false, 0, NULL);
2489 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
2490 friidump_native_report_add_note(&g_native_report, "The invocation was cancelled by the user before dumping began.");
2495 "Disc information:\n"
2496 "----------------------------------------------------------------------\n");
2498 if (options.disctype!=-1) fprintf (stderr,
2499 "Disc type..........: %s (forced)\n", type);
2500 else fprintf (stderr,
2501 "Disc type..........: %s\n", type);
2502 if (options.sectors_no!=-1) fprintf (stderr,
2503 "Disc size..........: %d (forced)\n", disc_get_sectors_no(d));
2504 else fprintf (stderr,
2505 "Disc size..........: %d\n", disc_get_sectors_no(d));
2507 if (disc_get_layerbreak(d)>0 && type_id==DISC_TYPE_DVD) fprintf (stderr,
2508 "Layer break........: %d\n", disc_get_layerbreak(d));
2510 if ((type_id==DISC_TYPE_GAMECUBE) || (type_id==DISC_TYPE_WII) || (type_id==DISC_TYPE_WII_DL)) fprintf (stderr,
2511 "Game ID............: %s\n"
2512 "Region.............: %s\n"
2513 "Maker..............: %s - %s\n"
2514 "Version............: %s\n"
2515 "Game title.........: %s\n", game_id, region, maker_id, maker, version, title
2518 if (type_id == DISC_TYPE_WII || type_id == DISC_TYPE_WII_DL)
2519 fprintf (stderr, "Contains update....: %s\n" , disc_get_update (d) ? "Yes" : "No");
2520 fprintf (stderr, "\n");
2522 disc_set_unscrambling (d, !options.no_unscrambling);
2524 if (type_id <= DISC_TYPE_DVD)
2525 unscrambler_set_disctype (type_id);
2527 if (options.autodump) {
2528 snprintf (tmp, sizeof (tmp), "%s.iso", title);
2529 my_strdup (options.iso_out, tmp);
2530 options.iso_requested = true;
2533 /* Xbox/GDR-8050L supports default XBE/DMI-derived names.
2534 * Use an empty-string placeholder so the copied reference path can
2535 * derive Title[MediaID].iso/.xiso after the first unlock/XBE probe. */
2536 if (type_id == DISC_TYPE_XBOX && options.iso_requested && !options.iso_out)
2537 my_strdup (options.iso_out, "");
2538 if (type_id == DISC_TYPE_XBOX && options.xiso_requested && !options.xiso_out)
2539 my_strdup (options.xiso_out, "");
2541 //set speed 2nd time after rotation is started and some sectors read
2542 if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
2543 if (options.speed != -1) disc_set_speed(d, options.speed * 177);
2545 /* If at least an output file was specified, proceed dumping, otherwise stop here */
2546 if (options.xiso_requested) {
2547 if (type_id != DISC_TYPE_XBOX) {
2548 fprintf (stderr, "Xbox XISO output requires Xbox/XGD disc type. Use -T 4 or an Xbox-capable drive/disc.\n");
2551 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 : "");
2553 dmp = dumper_new (d);
2554 if (friidump_native_report_is_enabled(&g_native_report))
2555 dumper_set_cancel_callback(dmp, friidump_native_cancel_callback, NULL);
2556 dumper_set_hashing (dmp, !options.no_hashing);
2557 dumper_set_flushing (dmp, !options.no_flushing);
2559 if (!dumper_set_xiso_output_file (dmp, options.xiso_out, options.resume)) {
2560 fprintf (stderr, "Cannot setup Xbox XISO output file\n");
2561 } else if (!dumper_prepare_xiso (dmp)) {
2562 fprintf (stderr, "Cannot prepare Xbox XISO dumper");
2565 dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
2567 dumper_set_progress_callback (dmp, (progress_func) progress, stats);
2569 dump_attempted = true;
2570 if (g_validation_summary.active)
2571 g_validation_summary.dump_attempted = true;
2572 out = dumper_dump_xiso (dmp, ¤t_sector);
2573 gettimeofday(&(stats -> end_time), NULL);
2575 fprintf (stderr, "Xbox XISO dump completed successfully!\n");
2576 if (!options.no_hashing && !friidump_uses_windows_xbox_reference_path(d, type_id))
2578 "XISO image hashes:\n"
2583 dumper_get_xiso_crc32 (dmp), dumper_get_xiso_md5 (dmp),
2584 dumper_get_xiso_sha1 (dmp), dumper_get_xiso_sha2 (dmp)
2587 if (g_validation_summary.active)
2588 g_validation_summary.dump_ok = true;
2590 if (dumper_was_cancelled(dmp))
2591 fprintf (stderr, "\nXbox XISO dump cancelled at output sector: %u\n", current_sector);
2593 fprintf (stderr, "\nXbox XISO dump failed at output sector: %u\n", current_sector);
2595 if (g_validation_summary.active) {
2596 g_validation_summary.dump_ok = false;
2597 g_validation_summary.fail_sector = current_sector;
2603 friidump_native_capture_output(dmp, type_id, true, out, dumper_was_cancelled(dmp), current_sector, disc_get_sectors_no(d));
2604 else if (friidump_native_report_is_enabled(&g_native_report)) {
2605 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
2606 friidump_native_report_add_note(&g_native_report, "Xbox XISO output could not be prepared; dumping was not attempted.");
2608 dmp = dumper_destroy (dmp);
2610 } else if (options.raw_out || options.iso_requested) {
2611 if (type_id == DISC_TYPE_XBOX && options.raw_out)
2612 fprintf (stderr, "Xbox/XGD output does not support -r/raw together with the redump-style ISO path. Use -i or -X.\n");
2613 else if (options.raw_out)
2614 fprintf (stderr, "Writing to file \"%s\" in raw format\n", options.raw_out);
2615 if (options.iso_out) {
2616 if (type_id == DISC_TYPE_XBOX)
2617 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 : "");
2619 fprintf (stderr, "Writing to file \"%s\" in ISO format\n", options.iso_out);
2621 fprintf (stderr, "\n");
2623 dmp = dumper_new (d);
2624 if (friidump_native_report_is_enabled(&g_native_report))
2625 dumper_set_cancel_callback(dmp, friidump_native_cancel_callback, NULL);
2627 dumper_set_hashing (dmp, !options.no_hashing);
2628 dumper_set_flushing (dmp, !options.no_flushing);
2630 if (!dumper_set_raw_output_file (dmp, options.raw_out, options.resume)) {
2631 fprintf (stderr, "Cannot setup raw output file\n");
2632 } else if (!dumper_set_iso_output_file (dmp, options.iso_out, options.resume)) {
2633 fprintf (stderr, "Cannot setup ISO output file\n");
2634 } else if (!dumper_prepare (dmp)) {
2635 fprintf (stderr, "Cannot prepare dumper");
2637 // fprintf (stderr, "Starting dump process from sector %u...\n", dmp -> start_sector);
2638 // opdd.start_sector = options.start_sector;
2641 dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
2643 dumper_set_progress_callback (dmp, (progress_func) progress, stats);
2645 dump_attempted = true;
2646 if (g_validation_summary.active)
2647 g_validation_summary.dump_attempted = true;
2648 out = dumper_dump (dmp, ¤t_sector);
2649 gettimeofday(&(stats -> end_time), NULL);
2651 fprintf (stderr, "Dump completed successfully!\n");
2652 if (!options.no_hashing && options.raw_out)
2654 "Raw image hashes:\n"
2660 /*"ED2K....: %s\n"*/,
2661 dumper_get_raw_crc32 (dmp), /*dumper_get_raw_md4 (dmp),*/ dumper_get_raw_md5 (dmp),
2662 dumper_get_raw_sha1 (dmp), dumper_get_raw_sha2 (dmp)/*, dumper_get_raw_ed2k (dmp)*/
2664 if (!options.no_hashing && options.iso_out && !friidump_uses_windows_xbox_reference_path(d, type_id))
2666 "ISO image hashes:\n"
2672 /*"ED2K....: %s\n"*/,
2673 dumper_get_iso_crc32 (dmp), /*dumper_get_iso_md4 (dmp),*/ dumper_get_iso_md5 (dmp),
2674 dumper_get_iso_sha1 (dmp), dumper_get_iso_sha2 (dmp)/*, dumper_get_iso_ed2k (dmp)*/
2677 if (type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d) && options.iso_requested)
2678 friidump_verify_redump_xbox_reference(dmp);
2679 else if (options.iso_out)
2680 friidump_verify_redump_iso(dmp, type_id);
2683 if (g_validation_summary.active)
2684 g_validation_summary.dump_ok = true;
2686 if (g_validation_summary.active) {
2687 g_validation_summary.dump_ok = false;
2688 g_validation_summary.fail_sector = current_sector;
2690 if (dumper_was_cancelled(dmp))
2691 fprintf (stderr, "\nDump cancelled at sector: %u\n", current_sector);
2692 else if (current_sector == 0xFFFFFFFFU)
2693 fprintf (stderr, "\nXbox reference dumper failed; see the Xbox-specific message above.\n");
2695 fprintf (stderr, "\nDump failed at sectors: %u..%u\n", current_sector, current_sector+15);
2697 //disc_stop_unit (d, 0);
2702 friidump_native_capture_output(dmp, type_id, false, out, dumper_was_cancelled(dmp), current_sector, disc_get_sectors_no(d));
2703 else if (friidump_native_report_is_enabled(&g_native_report)) {
2704 friidump_native_report_set_outcome(&g_native_report, "other", "fail");
2705 friidump_native_report_add_note(&g_native_report, "Dump output could not be prepared; dumping was not attempted.");
2707 dmp = dumper_destroy (dmp);
2709 fprintf (stderr, "No output file for dumping specified, please take a look at the -i, -r, -X and -a options\n");
2713 if (dump_attempted) {
2714 bool stop_ok = disc_stop_unit (d, false);
2715 fprintf (stderr, "\nIssuing STOP UNIT / spin-down after dump attempt... %s\n", stop_ok ? "OK" : "Failed");
2716 if (g_validation_summary.active) {
2717 g_validation_summary.stop_attempted = true;
2718 g_validation_summary.stop_ok = stop_ok;
2726 static int try_all_methods (progstats *stats) {
2727 u_int32_t saved_command;
2734 saved_command = options.command;
2735 saved_method = options.dump_method;
2738 fprintf (stderr, "Trying all command/method combinations... This will take a LOOOONG time and generate an insanely long console output :p\n");
2740 for (command = 0; command <= 4 && !out; command++) {
2741 for (method = 0; method <= 10 && !out; method++) {
2742 options.command = command;
2743 options.dump_method = method;
2744 memset (stats, 0, sizeof (*stats));
2746 fprintf (stderr, "\nTrying with command %u, method %d\n", command, method);
2747 fprintf (stderr, "Initializing DVD drive... ");
2749 d = disc_new (options.device, options.command);
2751 fprintf (stderr, "Failed\n");
2754 "Probably you do not have access to the DVD device. Ask the administrator\n"
2755 "to add you to the proper device-access group. Do not run FriiDump as root.\n"
2761 fprintf (stderr, "OK\n");
2762 out = dologic (d, stats);
2763 d = disc_destroy (d);
2766 fprintf (stderr, "Command %u and method %d combination worked!\n", command, method);
2771 options.command = saved_command;
2772 options.dump_method = saved_method;
2778 int main (int argc, char *argv[]) {
2785 unscrambler_progress_func pfunc;
2786 u_int32_t current_sector;
2788 /* First of all... */
2790 friidump_native_report_init(&g_native_report, PACKAGE_VERSION);
2791 friidump_init_executable_dir((argc > 0) ? argv[0] : NULL);
2792 xbox_ref_log_open_for_target(NULL, 0);
2796 memset (&stats, 0, sizeof (stats));
2800 if (optparse (argc, argv)) {
2801 friidump_native_configure();
2802 friidump_retarget_log_from_options();
2803 if (options.device) {
2804 if (options.allmethods) {
2805 out = try_all_methods (&stats);
2807 /* Dump DVD to file */
2808 fprintf (stderr, "Initializing DVD drive... ");
2810 if (!(d = disc_new (options.device, options.command))) {
2811 fprintf (stderr, "Failed\n");
2814 "Probably you do not have access to the DVD device. Ask the administrator\n"
2815 "to add you to the proper device-access group. Do not run FriiDump as root.\n"
2819 fprintf (stderr, "OK\n");
2820 friidump_native_set_drive(d);
2821 out = dologic (d, &stats);
2822 d = disc_destroy (d);
2825 } else if (options.raw_in) {
2826 /* Convert raw image to ISO format */
2827 u = unscrambler_new ();
2830 pfunc = (unscrambler_progress_func) progress_for_guis;
2832 pfunc = (unscrambler_progress_func) progress;
2834 if ((out = unscrambler_unscramble_file (u, options.raw_in, options.iso_out, pfunc, &stats, ¤t_sector)))
2835 fprintf (stderr, "Unscrambling completed successfully!\n");
2837 fprintf (stderr, "\nUnscrambling failed at sectors: %u..%u\n", current_sector, current_sector+15);
2839 u = unscrambler_destroy (u);
2845 have_duration = friidump_elapsed_from_stats(&stats, &duration);
2846 if (g_operation_duration_override_valid) {
2847 duration = g_operation_duration_override;
2848 have_duration = true;
2853 fprintf (stderr, "Operation took %.2f seconds\n", duration);
2854 friidump_print_validation_summary(duration, have_duration);
2857 friidump_print_validation_summary(duration, have_duration);
2861 friidump_native_publish(duration, have_duration);
2863 my_free (options.device);
2864 my_free (options.iso_out);
2865 my_free (options.xiso_out);
2866 my_free (options.raw_out);
2867 my_free (options.raw_in);
2868 my_free (options.hlds_profile_report);
2869 my_free (options.xgd1_layout_probe_report);
2870 my_free (options.xgd1_raw_id_probe_report);
2871 my_free (options.native_report_json);
2872 my_free (options.native_report_dir);
2873 my_free (options.firmware_modified_note);
2876 if (xbox_ref_log_path())
2877 fprintf (stderr, "Log file complete: %s\n", xbox_ref_log_path());
2878 xbox_ref_log_close();