]> FriiDump Source - friidump.git/blob - src/friidump.c
f8b529240c07eeff281a356b5c858081171f6f68
[friidump.git] / src / friidump.c
1 /***************************************************************************
2  *   Copyright (C) 2007 by Arep                                            *
3  *   Support is provided through the forums at                             *
4  *   http://wii.console-tribe.com                                          *
5  *                                                                         *
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.                                   *
10  *                                                                         *
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.                          *
15  *                                                                         *
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  ***************************************************************************/
21
22 #include "misc.h"
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <time.h>
26 #include <string.h>
27 #include "disc.h"
28 #include "dumper.h"
29 #include "unscrambler.h"
30 #include "xbox_ref/xbox_ref_log.h"
31
32 #define printf xbox_ref_printf
33 #define fprintf xbox_ref_log_fprintf
34
35 #define USECS_PER_SEC   1000000
36
37
38 static const char *friidump_requested_output_target(void);
39
40 typedef struct {
41         bool active;
42         bool dump_attempted;
43         bool seed_ok;
44         bool have_seed_duration;
45         double seed_duration;
46         bool dump_ok;
47         bool stop_attempted;
48         bool stop_ok;
49         char model[256];
50         char disc_type[64];
51         char game_id[64];
52         char title[256];
53         char output[512];
54         u_int32_t command;
55         int method;
56         u_int32_t sectors;
57         u_int32_t fail_sector;
58         u_int32_t hlds_type;
59         u_int32_t cache_base;
60         u_int32_t mem_blocks;
61         const char *profile;
62 } friidump_validation_summary;
63
64 static friidump_validation_summary g_validation_summary;
65
66 static void friidump_summary_copy(char *dst, size_t dst_size, const char *src) {
67         if (!dst || dst_size == 0)
68                 return;
69         if (!src)
70                 src = "";
71         snprintf(dst, dst_size, "%s", src);
72 }
73
74 static void friidump_summary_reset(void) {
75         memset(&g_validation_summary, 0, sizeof(g_validation_summary));
76         g_validation_summary.fail_sector = 0xFFFFFFFFU;
77 }
78
79 static void friidump_format_hms(double seconds, char *buf, size_t buf_size) {
80         long total, hours, minutes, secs;
81         if (!buf || buf_size == 0)
82                 return;
83         if (seconds < 0.0)
84                 seconds = 0.0;
85         total = (long) (seconds + 0.5);
86         hours = total / 3600;
87         minutes = (total / 60) % 60;
88         secs = total % 60;
89         snprintf(buf, buf_size, "%02ld:%02ld:%02ld", hours, minutes, secs);
90 }
91
92
93 static void friidump_summary_begin(disc *d) {
94         const char *target;
95         if (!d || disc_get_hlds_e7_type(d) == 0)
96                 return;
97         friidump_summary_reset();
98         g_validation_summary.active = true;
99         friidump_summary_copy(g_validation_summary.model, sizeof(g_validation_summary.model), disc_get_drive_model_string(d));
100         g_validation_summary.command = disc_get_command(d);
101         g_validation_summary.method = disc_get_method(d);
102         g_validation_summary.hlds_type = disc_get_hlds_e7_type(d);
103         g_validation_summary.profile = disc_get_hlds_e7_profile_name(d);
104         g_validation_summary.cache_base = disc_get_hlds_e7_cache_base(d);
105         g_validation_summary.mem_blocks = disc_get_hlds_e7_mem_blocks(d);
106         target = friidump_requested_output_target();
107         friidump_summary_copy(g_validation_summary.output, sizeof(g_validation_summary.output), target ? target : "(none)");
108 }
109
110 static void friidump_print_validation_summary(double duration, bool have_duration) {
111         double mib_total, mib_per_hour;
112         if (!g_validation_summary.active)
113                 return;
114         mib_total = (double) g_validation_summary.sectors * 2048.0 / 1024.0 / 1024.0;
115         mib_per_hour = (have_duration && duration > 0.0) ? (mib_total / duration * 3600.0) : 0.0;
116         fprintf(stderr,
117                 "\nHLDS 0xE7 validation summary:\n"
118                 "  Model.............: %s\n"
119                 "  Profile...........: %s\n"
120                 "  Command/method....: %u / %d\n"
121                 "  Cache base........: 0x%08x\n"
122                 "  Memory windows....: %u\n"
123                 "  Disc type.........: %s\n"
124                 "  Game ID...........: %s\n"
125                 "  Title.............: %s\n"
126                 "  Output............: %s\n"
127                 "  Seed read.........: %s\n"
128                 "  Dump status.......: %s\n"
129                 "  STOP UNIT.........: %s\n",
130                 g_validation_summary.model[0] ? g_validation_summary.model : "(unknown)",
131                 g_validation_summary.profile ? g_validation_summary.profile : "(unknown)",
132                 g_validation_summary.command, g_validation_summary.method,
133                 g_validation_summary.cache_base, g_validation_summary.mem_blocks,
134                 g_validation_summary.disc_type[0] ? g_validation_summary.disc_type : "(unknown)",
135                 g_validation_summary.game_id[0] ? g_validation_summary.game_id : "(unknown)",
136                 g_validation_summary.title[0] ? g_validation_summary.title : "(unknown)",
137                 g_validation_summary.output[0] ? g_validation_summary.output : "(none)",
138                 g_validation_summary.seed_ok ? "OK" : "FAILED/NOT REACHED",
139                 g_validation_summary.dump_attempted ? (g_validation_summary.dump_ok ? "OK" : "FAILED") : "NOT ATTEMPTED",
140                 g_validation_summary.stop_attempted ? (g_validation_summary.stop_ok ? "OK" : "FAILED") : "NOT ATTEMPTED");
141         if (g_validation_summary.have_seed_duration) {
142                 char seed_buf[32];
143                 friidump_format_hms(g_validation_summary.seed_duration, seed_buf, sizeof(seed_buf));
144                 fprintf(stderr, "  Seed elapsed......: %s (%.2f seconds)\n", seed_buf, g_validation_summary.seed_duration);
145         }
146         if (g_validation_summary.dump_attempted && !g_validation_summary.dump_ok && g_validation_summary.fail_sector != 0xFFFFFFFFU)
147                 fprintf(stderr, "  Failure sector....: %u..%u\n", g_validation_summary.fail_sector, g_validation_summary.fail_sector + 15);
148         if (g_validation_summary.sectors)
149                 fprintf(stderr, "  Sectors...........: %u\n", g_validation_summary.sectors);
150         if (have_duration)
151                 fprintf(stderr, "  Duration..........: %.2f seconds\n", duration);
152         if (have_duration && mib_per_hour > 0.0)
153                 fprintf(stderr, "  Observed average..: %.2f MiB/h over %.2f MiB ISO payload\n", mib_per_hour, mib_total);
154         if (g_validation_summary.dump_ok)
155                 fprintf(stderr, "  Hash compare......: compare printed hashes against known-good target for this disc\n");
156 }
157
158
159 /* Name of package */
160 #define PACKAGE "friidump"
161
162 /* Define to the address where bug reports for this package should be sent. */
163 #define PACKAGE_BUGREPORT "arep@no.net"
164
165 /* Define to the full name of this package. */
166 #define PACKAGE_NAME "FriiDump"
167
168 /* Define to the version of this package. */
169 #define PACKAGE_VERSION "0.5.3.5"
170
171
172 #ifdef WIN32
173
174 #include "getopt-win32.h"
175
176
177 #else
178 #include <sys/time.h>
179 #include <getopt.h>
180 #endif
181
182
183 /* Struct for program options */
184 struct {
185         char *device;
186         bool autodump;
187         bool gui;
188         char *raw_in;
189         char *raw_out;
190         char *iso_out;
191         char *xiso_out;
192         bool iso_requested;
193         bool xiso_requested;
194         bool xbox_filename_override;
195         bool xbox_auto_filename;
196         bool resume;
197         int dump_method;
198         u_int32_t command;
199         u_int32_t start_sector;
200         u_int32_t sectors_no;
201         u_int32_t speed;
202         u_int32_t disctype;
203         u_int32_t sec_disc;
204         u_int32_t sec_mem;
205         bool no_hashing;
206         bool no_unscrambling;
207         bool no_flushing;
208         bool stop_unit;
209         bool allmethods;
210         bool hlds_e7_scan;
211         bool hlds_e7_subcmd_sweep;
212         bool hlds_e7_memrange_sweep;
213         char *hlds_e7_scan_log;
214         char *hlds_e7_scan_dump_prefix;
215         char *hlds_profile_report;
216 } options;
217
218
219 /* Struct for progress data */
220 typedef struct {
221         struct timeval start_time;
222         struct timeval end_time;
223         double mb_total;
224         double mb_total_real;
225         u_int32_t sectors_skipped;
226 } progstats;
227
228
229 static char friidump_drive_letter_from_device(const char *device) {
230         if (!device || !device[0]) return 0;
231         if (device[0] && device[1] == ':') return device[0];
232         if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
233         return device[0];
234 }
235
236
237 static const char *friidump_requested_output_target(void) {
238         if (options.iso_out && options.iso_out[0]) return options.iso_out;
239         if (options.xiso_out && options.xiso_out[0]) return options.xiso_out;
240         if (options.raw_out && options.raw_out[0]) return options.raw_out;
241         return NULL;
242 }
243
244
245 static void friidump_json_string(FILE *f, const char *s) {
246         const unsigned char *p;
247         fputc('"', f);
248         if (s) {
249                 for (p = (const unsigned char *) s; *p; p++) {
250                         switch (*p) {
251                                 case '\\': fputs("\\\\", f); break;
252                                 case '"': fputs("\\\"", f); break;
253                                 case '\b': fputs("\\b", f); break;
254                                 case '\f': fputs("\\f", f); break;
255                                 case '\n': fputs("\\n", f); break;
256                                 case '\r': fputs("\\r", f); break;
257                                 case '\t': fputs("\\t", f); break;
258                                 default:
259                                         if (*p < 0x20) {
260                                                 char tmp[8];
261                                                 snprintf(tmp, sizeof(tmp), "\\u%04x", (unsigned int) *p);
262                                                 fputs(tmp, f);
263                                         } else {
264                                                 fputc(*p, f);
265                                         }
266                                         break;
267                         }
268                 }
269         }
270         fputc('"', f);
271 }
272
273 static void friidump_json_kv_string(FILE *f, const char *key, const char *value, bool comma) {
274         fputs("  ", f);
275         friidump_json_string(f, key);
276         fputs(": ", f);
277         friidump_json_string(f, value ? value : "");
278         fputs(comma ? ",\n" : "\n", f);
279 }
280
281 static void friidump_json_kv_u32_hex(FILE *f, const char *key, u_int32_t value, bool comma) {
282         char tmp[32];
283         snprintf(tmp, sizeof(tmp), "0x%08x", value);
284         friidump_json_kv_string(f, key, value ? tmp : "", comma);
285 }
286
287 static void friidump_json_kv_int(FILE *f, const char *key, int value, bool comma) {
288         char tmp[32];
289         fputs("  ", f);
290         friidump_json_string(f, key);
291         snprintf(tmp, sizeof(tmp), ": %d%s\n", value, comma ? "," : "");
292         fputs(tmp, f);
293 }
294
295 static void friidump_json_kv_bool(FILE *f, const char *key, bool value, bool comma) {
296         fputs("  ", f);
297         friidump_json_string(f, key);
298         fputs(value ? ": true" : ": false", f);
299         fputs(comma ? ",\n" : "\n", f);
300 }
301
302 static bool friidump_write_hlds_profile_report(disc *d, const char *path) {
303         FILE *f;
304         if (!d || !path || !path[0])
305                 return false;
306         f = fopen(path, "wb");
307         if (!f) {
308                 fprintf(stderr, "WARNING: could not write HLDS profile report: %s\n", path);
309                 return false;
310         }
311         fputs("{\n", f);
312         friidump_json_kv_string(f, "schema", "friidump_hlds_profile_report_v1", true);
313         friidump_json_kv_string(f, "drive_model", disc_get_drive_model_string(d), true);
314         friidump_json_kv_string(f, "profile_name", disc_get_hlds_e7_profile_name(d), true);
315         friidump_json_kv_string(f, "support_tier", disc_get_hlds_e7_support_tier(d), true);
316         friidump_json_kv_string(f, "family", disc_get_hlds_e7_family(d), true);
317         friidump_json_kv_string(f, "tokens", disc_get_hlds_e7_tokens(d), true);
318         friidump_json_kv_string(f, "stage5b_record_id", disc_get_hlds_e7_record_id(d), true);
319         friidump_json_kv_u32_hex(f, "static_e7_cdb_base", disc_get_hlds_e7_static_cdb_base(d), true);
320         friidump_json_kv_u32_hex(f, "static_e7_gate", disc_get_hlds_e7_static_gate(d), true);
321         friidump_json_kv_int(f, "runtime_e7_type", (int) disc_get_hlds_e7_type(d), true);
322         friidump_json_kv_u32_hex(f, "runtime_cache_base", disc_get_hlds_e7_cache_base(d), true);
323         friidump_json_kv_int(f, "runtime_mem_windows", (int) disc_get_hlds_e7_mem_blocks(d), true);
324         friidump_json_kv_int(f, "preferred_method", disc_get_hlds_e7_preferred_method(d), true);
325         friidump_json_kv_int(f, "selected_method", (int) disc_get_method(d), true);
326         friidump_json_kv_int(f, "selected_command", (int) disc_get_command(d), true);
327         friidump_json_kv_bool(f, "live_safe_from_static_only", false, true);
328         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);
329         friidump_json_kv_string(f, "notes", disc_get_hlds_e7_notes(d), false);
330         fputs("}\n", f);
331         fclose(f);
332         return true;
333 }
334
335
336 static void friidump_retarget_log_from_options(void) {
337         const char *target = friidump_requested_output_target();
338         if (target) {
339                 xbox_ref_log_retarget(target);
340         } else if (options.device && options.device[0]) {
341                 xbox_ref_log_open_for_target(NULL, friidump_drive_letter_from_device(options.device));
342         }
343 }
344
345
346 void progress_for_guis (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
347         int perc;
348         double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
349         struct timeval now;
350         time_t eta;
351         struct tm etatm;
352         char buf[50];
353         
354         if (start) {
355                 gettimeofday (&(stats -> start_time), NULL);
356                 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
357                 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
358                 stats -> sectors_skipped = sectors_done;
359         } else {
360                 perc = (int) (100.0 * sectors_done / total_sectors);
361                 gettimeofday (&now, NULL);
362                 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
363                 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
364                 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
365                 mb_hour = mb_done_real / elapsed * 60 * 60;
366                 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
367                 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
368                 if (localtime_r (&eta, &etatm))
369                         strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
370                 else
371                         sprintf (buf, "N/A");
372
373                 /* This is the only thing we print to stdout, so that other programs can easily capture and parse our output */
374                 fprintf (stdout, "%d%%|%u/%u sectors|%.2lf/%.0lf MB|%.0lf/%.0lf seconds|%.2lf MB/h|%s\n",
375                          perc, sectors_done, total_sectors, mb_done, stats -> mb_total, elapsed, seconds_left, mb_hour, buf);
376                 fflush (stdout);
377         }
378
379         /* Save return time, in case this will be the last call */
380         gettimeofday (&(stats -> end_time), NULL);
381
382         return;
383 }
384
385
386 void progress (bool start, u_int32_t sectors_done, u_int32_t total_sectors, progstats *stats) {
387         int perc, i;
388         double elapsed, mb_done, mb_done_real, mb_hour, seconds_left;
389         struct timeval now;
390         time_t eta;
391         struct tm etatm;
392         char buf[50];
393         
394         if (start) {
395                 gettimeofday (&(stats -> start_time), NULL);
396                 stats -> mb_total = (double) total_sectors * 2064 / 1024 / 1024;
397                 stats -> mb_total_real = (double) (total_sectors - sectors_done) * 2064 / 1024 / 1024;
398                 stats -> sectors_skipped = sectors_done;
399         } else {
400                 perc = (int) (100.0 * sectors_done / total_sectors);
401                 gettimeofday (&now, NULL);
402                 elapsed = difftime (now.tv_sec, (stats -> start_time).tv_sec);
403                 mb_done = (double) sectors_done * 2064 / 1024 / 1024;
404                 mb_done_real = (double) (sectors_done - stats -> sectors_skipped) * 2064 / 1024 / 1024;
405                 mb_hour = mb_done_real / elapsed * 60 * 60;
406                 seconds_left = stats -> mb_total_real / mb_hour * 60 * 60;
407                 eta = (time_t) ((stats -> start_time).tv_sec + seconds_left);
408                 if (localtime_r (&eta, &etatm))
409                         strftime (buf, 50, "%d/%m/%Y %H:%M:%S", &etatm);
410                 else
411                         sprintf (buf, "N/A");
412
413                 fprintf (stdout, "\r%3d%% ", perc);
414                 fprintf (stdout, "|");
415                 for (i = 0; i < 100 / 3; i++) {
416                         if (i == perc / 3)
417                                 fprintf (stdout, "*");
418                         else
419                                 fprintf (stdout, "-");
420                 }
421                 fprintf (stdout, "| ");
422                 fprintf (stdout, "%.2lf MB/h, ETA: %s", mb_hour, buf);
423                 fflush (stdout);
424         }
425
426         if (sectors_done == total_sectors)
427                 printf ("\n");
428
429         /* Save return time, in case this will be the last call */
430         gettimeofday (&(stats -> end_time), NULL);
431
432         return;
433 }
434
435
436
437 void welcome (void) {
438         /* Welcome text */
439         fprintf (stderr,
440                 "FriiDump " PACKAGE_VERSION " - Copyright (C) 2007 Arep\n"
441                 "This software comes with ABSOLUTELY NO WARRANTY.\n"
442                 "This is free software, and you are welcome to redistribute it\n"
443                 "under certain conditions; see COPYING for details.\n"
444                 "\n"
445                 "Official support forum: http://wii.console-tribe.com\n"
446                 "\n"
447                 "Forum for this UNOFFICIAL VERSION: http://forum.redump.org\n"
448                 "\n"
449                 );
450         fflush (stderr);
451
452         return;
453 }
454
455
456 void help (void) {
457         /* 80 cols guide:
458          *      |-------------------------------------------------------------------------------|
459          */
460         fprintf (stderr, "\n"
461                 "Available command line options:\n"
462                 "\n"
463                 " -h, --help                    Show this help\n"
464                 " -a, --autodump                        Dump the disc to an ISO file with an\n"
465                 "                               automatically-generated name, resuming the dump\n"
466                 "                               if possible\n"
467                 " -g, --gui                     Use more verbose output that can be easily\n"
468                 "                               parsed by a GUI frontend\n"
469                 " -d, --device <device>         Dump disc from device <device>\n"
470                 " -p, --stop                    Instruct device to stop disc rotation\n"
471                 " -D, --dvd                     Force standard DVD-ROM mode for any drive\n"
472                 "                               (equivalent to -T 3; uses FriiDump original\n"
473                 "                               DVD/raw/ISO paths, not Xbox mode)\n"
474                 " -c, --command <nr>            Force memory dump command:\n"
475                 "                               0 - vanilla 2064\n"
476                 "                               1 - vanilla 2384\n"
477                 "                               2 - Hitachi\n"
478                 "                               3 - Lite-On\n"
479                 "                               4 - Renesas\n"
480                 " -x, --speed <x>               Set streaming speed (1, 24, 32, 64, etc.,\n"
481                 "                               where 1 = 150 KiB/s and so on)\n"
482                 " -T, --type <nr>               Force disc type:\n"
483                 "                               0 - GameCube\n"
484                 "                               1 - Wii\n"
485                 "                               2 - Wii_DL\n"
486                 "                               3 - DVD\n"
487                 "                               4 - Xbox/XGD 2048-byte-sector mode\n"
488                 "                                   Native profiles: GDR-8050L and GDR-3120L.\n"
489                 "                                   Other drives keep normal FriiDump behavior\n"
490                 "                                   unless Xbox mode is explicitly forced.\n"
491                 " -S, --size <sectors>          Force disc size\n"
492                 " -r, --raw <file>              Output to file <file> in raw format (2064-byte\n"
493                 "                               sectors)\n"
494                 " -i, --iso[=<file>]            Output to file <file> in ISO format (2048-byte\n"
495                 "                               sectors). For Xbox/GDR-8050L, omitting <file>\n"
496                 "                               derives Title[MediaID].iso from the XBE/DMI;\n"
497                 "                               providing <file> is an explicit override. For\n"
498                 "                               Xbox/XGD this reconstructs the redump-style\n"
499                 "                               XGD1 layout and writes .pfi.bin, .dmi.bin, and\n"
500                 "                               .redump.json metadata when possible\n"
501                 " -X, --xiso[=<file>]   Output Xbox/XGD game partition as XISO (.xiso).\n"
502                 "                               For Xbox/GDR-8050L, omitting <file> derives\n"
503                 "                               Title[MediaID].xiso from the XBE/DMI; providing\n"
504                 "                               <file> is an explicit override. Attempts to read\n"
505                 "                               the 32-sector game lead-in from drive-readable\n"
506                 "                               sectors and zero-fills only unreadable sectors.\n"
507                 " -u, --unscramble <file>       Convert (unscramble) raw image contained in\n"
508                 "                               <file> to ISO format\n"
509                 " -H, --nohash                  Do not compute CRC32/MD5/SHA-1/SHA-256 hashes\n"
510                 "                               for generated files\n"
511                 " -s, --resume                  Resume partial dump\n"
512                 "                               -  General  -----------------------------------\n"
513                 " -0, --method0[=<req>,<exp>]   Use dumping method 0 (Optional argument\n"
514                 "                               specifies how many sectors to request from disc\n"
515                 "                               and read from cache at a time. Values should be\n"
516                 "                               separated with a comma. Default 16,16)\n"
517                 "                               -  Non-Streaming  -----------------------------\n"
518                 " -1, --method1[=<req>,<exp>]   Use dumping method 1 (Default 16,16)\n"
519                 " -2, --method2[=<req>,<exp>]   Use dumping method 2 (Default 16,16)\n"
520                 " -3, --method3[=<req>,<exp>]   Use dumping method 3 (Default 16,16)\n"
521                 "                               -  Streaming  ---------------------------------\n"
522                 " -4, --method4[=<req>,<exp>]   Use dumping method 4 (Default 27,27)\n"
523                 " -5, --method5[=<req>,<exp>]   Use dumping method 5 (Default 27,27)\n"
524                 " -6, --method6[=<req>,<exp>]   Use dumping method 6 (Default 27,27)\n"
525                 "                               -  Hitachi  -----------------------------------\n"
526                 " -7, --method7                 Use dumping method 7 (Read and dump 5 blocks\n"
527                 "                               at a time, using streaming read)\n"
528                 " -8, --method8                 Use dumping method 8 (Read and dump 5 blocks\n"
529                 "                               at a time, using streaming read, using DMA)\n"
530                 " -9, --method9                 Use dumping method 9 (Read and dump 5 blocks\n"
531                 "                               at a time, using streaming read, using DMA and\n"
532                 "                               some speed tricks)\n"
533                 "     --hlds-e7-scan            Probe HLDS HIT 0xE7 cache/memdump bases only;\n"
534                 "                               writes JSON and does not crack seeds or dump data\n"
535                 "     --hlds-e7-subcmd-sweep    Probe HIT 0xE7 subcommands/address candidates only;\n"
536                 "                               writes JSON and does not crack seeds or dump data\n"
537                 "     --hlds-e7-memrange-sweep  Sweep wider HIT 0xE7 subcmd 0x01 address ranges;\n"
538                 "                               writes JSON and does not crack seeds or dump data\n"
539                 "     --scan-log <file>         JSON output path for --hlds-e7-scan\n"
540                 "     --scan-dump-prefix <prefix>       Optional raw 0xE7 window dump prefix for --hlds-e7-scan\n"
541                 "     --hlds-profile-report <file> Write selected HLDS profile/evidence JSON\n"
542                 " -A, --allmethods              Try all known command/method combinations until\n"
543                 "                               one works. Reopens the drive for each command so\n"
544                 "                               command-specific vendor handlers are rebound.\n"
545 #ifdef DEBUG
546                 " -n, --donottunscramble                Do not try unscrambling to check EDC. Only\n"
547                 "                               useful for testing the raw performance of the\n"
548                 "                               different methods\n"
549                 " -f, --donottflush             Do not call fflush() after every fwrite()\n"
550 #endif
551         );
552
553         return;
554 }
555
556
557 bool optparse (int argc, char **argv) {
558         bool out;
559         char *result = NULL;
560         int c;
561         int option_index = 0;
562         static struct option long_options[] = {
563                 {"help", 0, 0, 'h'},    //0 - no_argument
564                 {"autodump", 0, 0, 'a'},
565                 {"gui", 0, 0, 'g'},
566                 {"device", 1, 0, 'd'},  //1 - required_argument
567                 {"raw", 1, 0, 'r'},
568                 {"iso", 2, 0, 'i'},
569                 {"xiso", 2, 0, 'X'},
570                 {"unscramble", 1, 0, 'u'},
571                 {"nohash", 0, 0, 'H'},
572                 {"resume", 0, 0, 's'},
573                 {"method0", 2, 0, '0'}, //2 - optional_argument
574                 {"method1", 2, 0, '1'},
575                 {"method2", 2, 0, '2'},
576                 {"method3", 2, 0, '3'},
577                 {"method4", 2, 0, '4'},
578                 {"method5", 2, 0, '5'},
579                 {"method6", 2, 0, '6'},
580                 {"method7", 0, 0, '7'},
581                 {"method8", 0, 0, '8'},
582                 {"method9", 0, 0, '9'},
583                 {"stop", 0, 0, 'p'},
584                 {"dvd", 0, 0, 'D'},
585                 {"command", 1, 0, 'c'},
586                 {"startsector", 1, 0, 't'},
587                 {"size", 1, 0, 'S'},
588                 {"speed", 1, 0, 'x'},
589                 {"type", 1, 0, 'T'},
590                 {"allmethods", 0, 0, 'A'},
591                 {"hlds-e7-scan", 0, 0, 1000},
592                 {"hlds-e7-subcmd-sweep", 0, 0, 1003},
593                 {"hlds-e7-memrange-sweep", 0, 0, 1004},
594                 {"scan-log", 1, 0, 1001},
595                 {"scan-dump-prefix", 1, 0, 1002},
596                 {"hlds-profile-report", 1, 0, 1005},
597 #ifdef DEBUG
598                 /* We don't want newbies to generate and put into circulation bad dumps, so this options are disabled for releases */
599                 {"donottunscramble", 0, 0, 'n'},
600                 {"donottflush", 0, 0, 'f'},
601 #endif
602                 {0, 0, 0, 0}
603         };
604
605         if (argc == 1) {
606                 help ();
607                 exit (1);
608         }
609         
610         /* Init options to default values */
611         options.device = NULL;
612         options.autodump = false;
613         options.gui = false;
614         options.raw_in = NULL;
615         options.raw_out = NULL;
616         options.iso_out = NULL;
617         options.xiso_out = NULL;
618         options.iso_requested = false;
619         options.xiso_requested = false;
620         options.xbox_filename_override = false;
621         options.xbox_auto_filename = false;
622         options.no_hashing = false;
623         options.resume = false;
624         options.dump_method = -1;
625         options.command = -1;
626         options.start_sector = -1;
627         options.sectors_no = -1;
628         options.speed = -1;
629         options.disctype = -1;
630         options.sec_disc = -1;
631         options.sec_mem = -1;
632         options.no_unscrambling = false;
633         options.no_flushing = false;
634         options.stop_unit = false;
635         options.allmethods = false;
636         options.hlds_e7_scan = false;
637         options.hlds_e7_subcmd_sweep = false;
638         options.hlds_e7_memrange_sweep = false;
639         options.hlds_e7_scan_log = NULL;
640         options.hlds_e7_scan_dump_prefix = NULL;
641         options.hlds_profile_report = NULL;
642
643         do {
644 #ifdef DEBUG
645                 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);
646 #else
647                 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);
648 #endif
649
650                 switch (c) {
651                         case 'h':
652                                 help ();
653                                 exit (1);
654                                 break;
655                         case 'p':
656                                 options.stop_unit = true;
657                                 break;
658                         case 'a':
659                                 options.autodump = true;
660                                 options.resume = true;
661                                 break;
662                         case 'g':
663                                 options.gui = true;
664                                 break;
665                         case 'd':
666                                 my_strdup (options.device, optarg);
667                                 break;
668                         case 'r':
669                                 my_strdup (options.raw_out, optarg);
670                                 break;
671                         case 'i':
672                                 options.iso_requested = true;
673                                 /* Preserve the old `-i file.iso` syntax even though Xbox now also
674                                  * supports bare `-i` for XBE/DMI-derived names. */
675                                 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
676                                         optarg = argv[optind++];
677                                 if (optarg) {
678                                         my_strdup (options.iso_out, optarg);
679                                         options.xbox_filename_override = true;
680                                 } else {
681                                         options.xbox_auto_filename = true;
682                                 }
683                                 break;
684                         case 'X':
685                                 options.xiso_requested = true;
686                                 /* Preserve the old `-X file.xiso` syntax while allowing bare `-X`. */
687                                 if (!optarg && optind < argc && argv[optind] && argv[optind][0] != '-')
688                                         optarg = argv[optind++];
689                                 if (optarg) {
690                                         my_strdup (options.xiso_out, optarg);
691                                         options.xbox_filename_override = true;
692                                 } else {
693                                         options.xbox_auto_filename = true;
694                                 }
695                                 break;
696                         case 'u':
697                                 my_strdup (options.raw_in, optarg);
698                                 break;
699                         case 'H':
700                                 options.no_hashing = true;
701                                 break;
702                         case 's':
703                                 options.resume = true;
704                                 break;
705                         case '0':
706                         case '1':
707                         case '2':
708                         case '3':
709                         case '4':
710                         case '5':
711                         case '6':
712                                 options.dump_method = c - '0';
713                                 if (optarg) {
714                                         result = strtok(optarg, ",");
715                                         result = strtok(NULL, ",");
716                                         options.sec_disc = atol(strpbrk(optarg,"1234567890"));
717                                         if (result) options.sec_mem = atol(result);
718                                         else {
719                                                 help ();
720                                                 exit (1);
721                                         }
722                                 }
723                                 break;
724                         case '7':
725                         case '8':
726                         case '9':
727                                 options.dump_method = c - '0';
728                                 break;
729                         case 'D':
730                                 options.disctype = DISC_TYPE_DVD;
731                                 unscrambler_set_disctype (DISC_TYPE_DVD);
732                                 break;
733                         case 'c':
734                                 options.command = atol (optarg);
735                                 if (options.command > 4) {
736                                         help ();
737                                         exit (1);
738                                 };
739                                 break;
740                         case 't':
741                                 options.start_sector = atol (optarg);
742                                 break;
743                         case 'S':
744                                 options.sectors_no = atol (optarg);
745                                 break;
746                         case 'x':
747                                 options.speed = atol (optarg);
748                                 break;
749                         case 'T':
750                                 options.disctype = atol (optarg);
751                                 if (options.disctype > 4) {
752                                         help ();
753                                         exit (1);
754                                 };
755                                 if (options.disctype <= DISC_TYPE_DVD)
756                                         unscrambler_set_disctype (options.disctype);
757                                 break;
758                         case 'A':
759                                 options.allmethods = true;
760                                 options.resume = true;
761                                 break;
762 #ifdef DEBUG
763                         case 'n':
764                                 options.no_unscrambling = true;
765                                 break;
766                         case 'f':
767                                 options.no_flushing = true;
768                                 break;
769 #endif
770                         case 1000:
771                                 options.hlds_e7_scan = true;
772                                 break;
773                         case 1003:
774                                 options.hlds_e7_subcmd_sweep = true;
775                                 break;
776                         case 1004:
777                                 options.hlds_e7_memrange_sweep = true;
778                                 break;
779                         case 1001:
780                                 my_strdup (options.hlds_e7_scan_log, optarg);
781                                 break;
782                         case 1002:
783                                 my_strdup (options.hlds_e7_scan_dump_prefix, optarg);
784                                 break;
785                         case 1005:
786                                 my_strdup (options.hlds_profile_report, optarg);
787                                 break;
788                         case -1:
789                                 break;
790                         default:
791 //                              fprintf (stderr, "?? getopt returned character code 0%o ??\n", c);
792                                 exit (7);
793                                 break;
794                 }
795         } while (c != -1);
796
797         if (optind < argc) {
798                 /* Command-line arguments remaining. Ignore them, warning the user. */
799                 fprintf (stderr, "WARNING: Extra parameters ignored\n");
800         }
801
802         /* Sanity checks... */
803         out = false;
804         if (!options.device && !options.raw_in) {
805                 fprintf (stderr, "No operation specified. Please use the -d or -u options.\n");
806         } else if (options.raw_in && options.raw_out) {
807                 fprintf (stderr,
808                         "Are you sure you want to convert a raw image to another raw image? ;)\n"
809                         "Take a look at the -i and -a options!\n"
810                 );
811         } else if (options.autodump && (options.raw_out || options.iso_requested || options.xiso_requested)) {
812                 fprintf (stderr, "The -r, -i and -X options cannot be used together with -a.\n");
813         } else if (options.xiso_requested && (options.raw_out || options.iso_requested)) {
814                 fprintf (stderr, "The -X/--xiso option is a separate Xbox output mode and cannot be combined with -r or -i.\n");
815         } else {
816                 /* Specified options seem to make sense */
817                 out = true;
818         }
819                 
820         return (out);
821 }
822
823 int dologic (disc *d, progstats *stats) {
824         disc_type type_id;
825         char *type, *game_id, *region, *maker_id, *maker, *version, *title, tmp[0x03E0 + 4 + 1];
826         bool drive_supported;
827         bool xbox_forced;
828         bool xbox_output_requested;
829         bool dump_attempted;
830         int out;
831         dumper *dmp;
832         u_int32_t current_sector = 0;
833         
834         xbox_forced = (options.disctype == DISC_TYPE_XBOX);
835         xbox_output_requested = xbox_forced || options.xiso_requested;
836         dump_attempted = false;
837         
838         
839
840                                 if (options.stop_unit) { //stop rotation, if requested
841                                         fprintf (stderr, "Issuing STOP command... %s\n", (disc_stop_unit (d, false)) ? "OK" : "Failed");
842                                         exit (1);
843                                 }
844                                 else disc_stop_unit(d, true); //else start rotation
845
846                                 drive_supported = disc_get_drive_support_status (d);
847                                 fprintf (stderr,
848                                         "\n"
849                                         "Drive information:\n"
850                                         "----------------------------------------------------------------------\n"
851                                         "Drive model........: %s\n"
852                                         "Supported..........: %s\n", disc_get_drive_model_string (d), drive_supported ? "Yes" : "No"
853                                 );
854
855                                         if (xbox_output_requested && !disc_is_xbox_unlock_drive (d)) {
856                                                 fprintf (stderr,
857                                                         "Xbox/XGD output is limited to the supported Xbox unlock profiles "
858                                                         "currently wired into this branch: GDR-8050L, GDR-3120L, "
859                                                         "and known Samsung/Kreon-style vendor-unlock drives.\n"
860                                                         "Refusing to fall back to FriiDump GC/Wii methods for Xbox mode on this drive.\n");
861                                                 return false;
862                                         }
863
864                                         /* Xbox/XGD (-T 4 or -X) is a direct MMC/SCSI READ(10) path.
865                                          * Do not let the detected GC/Wii vendor memdump method (for example
866                                          * Hitachi command 2 / method 9 on GDR-8050L) drive sector reads. */
867                                         if (xbox_output_requested && options.dump_method == -1)
868                                                 options.dump_method = 10;
869
870                                         init_range(d, options.sec_disc, options.sec_mem);
871
872                                         if (!(disc_set_read_method (d, options.dump_method)))
873                                                 exit (2);
874
875                                         if (xbox_output_requested && disc_get_method(d) == 10) {
876                                                 fprintf (stderr, "Command............: Xbox direct MMC/SCSI path\n");
877                                                 fprintf (stderr, "Method.............: 10 (Xbox READ(10))\n");
878                                         } else {
879                                                 if (options.command!=-1) fprintf (stderr, 
880                                                         "Command............: %d (forced)\n", disc_get_command(d));
881                                                 else fprintf (stderr, 
882                                                         "Command............: %d\n", disc_get_command(d));
883                                                 if (disc_get_def_method(d)!=disc_get_method(d)) fprintf (stderr, 
884                                                         "Method.............: %d (forced)\n", disc_get_method(d));
885                                                 else fprintf (stderr, 
886                                                         "Method.............: %d\n", disc_get_method(d));
887                                                 if (disc_get_hlds_e7_type (d) != 0) {
888                                                         fprintf (stderr, "HLDS 0xE7 profile..: %s\n", disc_get_hlds_e7_profile_name (d));
889                                                         fprintf (stderr, "HLDS support tier..: %s\n", disc_get_hlds_e7_support_tier (d));
890                                                         fprintf (stderr, "HLDS family........: %s\n", disc_get_hlds_e7_family (d));
891                                                         fprintf (stderr, "HLDS E7 tokens.....: %s\n", disc_get_hlds_e7_tokens (d));
892                                                         fprintf (stderr, "HLDS Stage5B row...: %s\n", disc_get_hlds_e7_record_id (d));
893                                                         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));
894                                                         fprintf (stderr, "Cache base.........: 0x%08x\n", disc_get_hlds_e7_cache_base (d));
895                                                         fprintf (stderr, "Memory windows.....: %u\n", disc_get_hlds_e7_mem_blocks (d));
896                                                         if (disc_get_hlds_e7_notes (d) && disc_get_hlds_e7_notes (d)[0])
897                                                                 fprintf (stderr, "HLDS notes.........: %s\n", disc_get_hlds_e7_notes (d));
898                                                 }
899                                         }
900                                         if (options.hlds_profile_report) {
901                                                 if (friidump_write_hlds_profile_report(d, options.hlds_profile_report))
902                                                         fprintf(stderr, "HLDS profile report: %s\n", options.hlds_profile_report);
903                                         }
904                                         options.dump_method=disc_get_method(d);
905                                         friidump_summary_begin(d);
906                                 if ((options.dump_method==0) 
907                                 || (options.dump_method==1) || (options.dump_method==2) || (options.dump_method==3)
908                                 || (options.dump_method==4) || (options.dump_method==5) || (options.dump_method==6)
909                                 ){
910                                         fprintf (stderr, 
911                                         "Requested sectors..: %d\n", disc_get_sec_disc(d));
912                                         fprintf (stderr, 
913                                         "Expected sectors...: %d\n", disc_get_sec_mem(d));
914                                 }
915
916                                 fprintf (stderr, "\nPress Ctrl+C at any time to terminate\n");
917
918                                 //set speed for 1st time
919                                 if (options.speed != -1) disc_set_speed(d, options.speed * 177);
920                                 if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
921 //                              disc_set_speed(d, 0xffff);
922
923                                 /* Windows may attach filesystem/autoplay polling to odd GC/Wii discs and
924                                  * steal the drive during the HLDS 0xE7 seed phase, especially on Type1
925                                  * GCC-4160N/GCC-4240N.  Reuse the same volume guard mechanism that the
926                                  * Xbox path already uses, but apply it before disc_init()/seed reads for
927                                  * all HLDS 0xE7 GC/Wii profiles.  Failure is warning-only because Explorer
928                                  * may already have a transient handle; the read path itself remains the
929                                  * authority. */
930                                 if (!xbox_output_requested && disc_get_hlds_e7_type (d) != 0) {
931                                         if (disc_get_hlds_e7_type (d) == 44 || disc_get_hlds_e7_type (d) == 45) {
932                                                 fprintf (stderr,
933                                                         "\nGDR-8050L modified-firmware warning:\n"
934                                                         "  This GC/Wii 0xE7 path assumes a cross-flashed or modified GDR-8050L firmware with 0xE7 memdump support added.\n"
935                                                         "  Stock GDR-8050L firmware is still supported for Xbox ripping, but it is not expected to dump GC/Wii discs through this path.\n");
936                                         }
937                                         fprintf (stderr,
938                                                 "\nWindows AutoPlay warning:\n"
939                                                 "  HLDS 0xE7 GC/Wii seed reads are sensitive to Windows polling.\n"
940                                                 "  Disable AutoPlay for this drive/media and close File Explorer or any \"insert a disc\" dialogs before dumping.\n"
941                                                 "  FriiDump will try to lock the volume, but AutoPlay can still interfere before or during seed retrieval.\n");
942                                         fprintf (stderr, "Applying Windows volume lock guard for HLDS 0xE7 seed reads... ");
943                                         disc_refresh_volume (d);
944                                         if (disc_lock_volume (d) < 0)
945                                                 fprintf (stderr, "Warning: failed; disable AutoPlay, close File Explorer/AutoPlay dialogs for this drive, then rerun.\n");
946                                         else
947                                                 fprintf (stderr, "OK\n");
948                                 }
949
950                                 if (options.hlds_e7_scan) {
951                                         out = disc_hlds_e7_scan (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
952                                         fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 scan... ");
953                                         fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
954                                         return out;
955                                 }
956
957                                 if (options.hlds_e7_subcmd_sweep) {
958                                         out = disc_hlds_e7_subcmd_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
959                                         fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 subcommand sweep... ");
960                                         fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
961                                         return out;
962                                 }
963
964                                 if (options.hlds_e7_memrange_sweep) {
965                                         out = disc_hlds_e7_memrange_sweep (d, options.hlds_e7_scan_log, options.hlds_e7_scan_dump_prefix);
966                                         fprintf (stderr, "Issuing STOP UNIT / spin-down after HLDS 0xE7 memory-range sweep... ");
967                                         fprintf (stderr, "%s\n", disc_stop_unit (d, false) ? "OK" : "Failed");
968                                         return out;
969                                 }
970
971                                 {
972                                         int media_rc, media_sense_key, media_asc, media_ascq;
973                                         fprintf (stderr, "\nChecking for ready media before disc seed retrieval... ");
974                                         media_rc = disc_media_preflight (d, 15000, &media_sense_key, &media_asc, &media_ascq);
975                                         if (media_rc <= 0) {
976                                                 if (media_rc == 0)
977                                                         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);
978                                                 else
979                                                         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);
980                                                 return false;
981                                         }
982                                         fprintf (stderr, "OK\n");
983
984                                         time_t seed_start, seed_end;
985                                         double seed_elapsed;
986                                         char seed_elapsed_buf[32];
987
988                                         if (xbox_output_requested)
989                                                 fprintf (stderr, "\nInitializing Xbox/XGD disc state... ");
990                                         else
991                                                 fprintf (stderr, "\nRetrieving disc seeds, this might take a while... ");
992
993                                         seed_start = time(NULL);
994                                         if (!disc_init (d, options.disctype, options.sectors_no)) {
995                                                 seed_end = time(NULL);
996                                                 seed_elapsed = difftime(seed_end, seed_start);
997                                                 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
998                                                 if (!xbox_output_requested)
999                                                         fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
1000                                                 if (g_validation_summary.active) {
1001                                                         g_validation_summary.have_seed_duration = true;
1002                                                         g_validation_summary.seed_duration = seed_elapsed;
1003                                                 }
1004                                                 fprintf (stderr, "Failed\n");
1005                                                 out = false;
1006                                         } else {
1007                                                 seed_end = time(NULL);
1008                                                 seed_elapsed = difftime(seed_end, seed_start);
1009                                                 friidump_format_hms(seed_elapsed, seed_elapsed_buf, sizeof(seed_elapsed_buf));
1010                                                 if (!xbox_output_requested)
1011                                                         fprintf (stderr, "[Elapsed:%s] ", seed_elapsed_buf);
1012                                                 fprintf (stderr, "OK\n");
1013                                                 if (g_validation_summary.active) {
1014                                                         g_validation_summary.seed_ok = true;
1015                                                         g_validation_summary.have_seed_duration = true;
1016                                                         g_validation_summary.seed_duration = seed_elapsed;
1017                                                 }
1018                                         disc_get_type (d, &type_id, &type);
1019                                         disc_get_gameid (d, &game_id);
1020                                         disc_get_region (d, NULL, &region);
1021                                         disc_get_maker (d, &maker_id, &maker);
1022                                         disc_get_version (d, NULL, &version);
1023                                         disc_get_title (d, &title);
1024                                         if (g_validation_summary.active) {
1025                                                 friidump_summary_copy(g_validation_summary.disc_type, sizeof(g_validation_summary.disc_type), type);
1026                                                 friidump_summary_copy(g_validation_summary.game_id, sizeof(g_validation_summary.game_id), game_id);
1027                                                 friidump_summary_copy(g_validation_summary.title, sizeof(g_validation_summary.title), title);
1028                                                 g_validation_summary.sectors = disc_get_sectors_no(d);
1029                                         }
1030                                         fprintf (stderr, 
1031                                                 "\n"
1032                                                 "Disc information:\n"
1033                                                 "----------------------------------------------------------------------\n");
1034
1035                                         if (options.disctype!=-1) fprintf (stderr, 
1036                                                 "Disc type..........: %s (forced)\n", type);
1037                                         else fprintf (stderr, 
1038                                                 "Disc type..........: %s\n", type);
1039                                         if (options.sectors_no!=-1) fprintf (stderr, 
1040                                                 "Disc size..........: %d (forced)\n", disc_get_sectors_no(d));
1041                                         else fprintf (stderr, 
1042                                                 "Disc size..........: %d\n", disc_get_sectors_no(d));
1043
1044                                         if (disc_get_layerbreak(d)>0 && type_id==DISC_TYPE_DVD) fprintf (stderr, 
1045                                                 "Layer break........: %d\n", disc_get_layerbreak(d));
1046
1047                                         if ((type_id==DISC_TYPE_GAMECUBE) || (type_id==DISC_TYPE_WII) || (type_id==DISC_TYPE_WII_DL)) fprintf (stderr, 
1048                                                 "Game ID............: %s\n"
1049                                                 "Region.............: %s\n"
1050                                                 "Maker..............: %s - %s\n"
1051                                                 "Version............: %s\n"
1052                                                 "Game title.........: %s\n", game_id, region, maker_id, maker, version, title
1053                                         );
1054
1055                                         if (type_id == DISC_TYPE_WII || type_id == DISC_TYPE_WII_DL)
1056                                                 fprintf (stderr, "Contains update....: %s\n" , disc_get_update (d) ? "Yes" : "No");
1057                                         fprintf (stderr, "\n");
1058                                         
1059                                         disc_set_unscrambling (d, !options.no_unscrambling);
1060
1061                                         if (type_id <= DISC_TYPE_DVD)
1062                                                 unscrambler_set_disctype (type_id);
1063
1064                                         if (options.autodump) {
1065                                                 snprintf (tmp, sizeof (tmp), "%s.iso", title);
1066                                                 my_strdup (options.iso_out, tmp);
1067                                                 options.iso_requested = true;
1068                                         }
1069
1070                                         /* Xbox/GDR-8050L supports default XBE/DMI-derived names.
1071                                          * Use an empty-string placeholder so the copied reference path can
1072                                          * derive Title[MediaID].iso/.xiso after the first unlock/XBE probe. */
1073                                         if (type_id == DISC_TYPE_XBOX && options.iso_requested && !options.iso_out)
1074                                                 my_strdup (options.iso_out, "");
1075                                         if (type_id == DISC_TYPE_XBOX && options.xiso_requested && !options.xiso_out)
1076                                                 my_strdup (options.xiso_out, "");
1077
1078                                         //set speed 2nd time after rotation is started and some sectors read
1079                                         if (options.speed != -1) disc_set_streaming_speed(d, options.speed * 177);
1080                                         if (options.speed != -1) disc_set_speed(d, options.speed * 177);
1081
1082                                         /* If at least an output file was specified, proceed dumping, otherwise stop here */
1083                                         if (options.xiso_requested) {
1084                                                 if (type_id != DISC_TYPE_XBOX) {
1085                                                         fprintf (stderr, "Xbox XISO output requires Xbox/XGD disc type. Use -T 4 or an Xbox-capable drive/disc.\n");
1086                                                         out = false;
1087                                                 } else {
1088                                                         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 : "");
1089
1090                                                         dmp = dumper_new (d);
1091                                                         dumper_set_hashing (dmp, !options.no_hashing);
1092                                                         dumper_set_flushing (dmp, !options.no_flushing);
1093
1094                                                         if (!dumper_set_xiso_output_file (dmp, options.xiso_out, options.resume)) {
1095                                                                 fprintf (stderr, "Cannot setup Xbox XISO output file\n");
1096                                                         } else if (!dumper_prepare_xiso (dmp)) {
1097                                                                 fprintf (stderr, "Cannot prepare Xbox XISO dumper");
1098                                                         } else {
1099                                                                 if (options.gui)
1100                                                                         dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
1101                                                                 else
1102                                                                         dumper_set_progress_callback (dmp, (progress_func) progress, stats);
1103
1104                                                                 dump_attempted = true;
1105                                                                 if (g_validation_summary.active)
1106                                                                         g_validation_summary.dump_attempted = true;
1107                                                                 if (dumper_dump_xiso (dmp, &current_sector)) {
1108                                                                         fprintf (stderr, "Xbox XISO dump completed successfully!\n");
1109                                                                         if (!options.no_hashing && !(type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d)))
1110                                                                                 fprintf (stderr,
1111                                                                                 "XISO image hashes:\n"
1112                                                                                 "CRC32...: %s\n"
1113                                                                                 "MD5.....: %s\n"
1114                                                                                 "SHA-1...: %s\n"
1115                                                                                 "SHA-256.: %s\n",
1116                                                                                 dumper_get_xiso_crc32 (dmp), dumper_get_xiso_md5 (dmp),
1117                                                                                 dumper_get_xiso_sha1 (dmp), dumper_get_xiso_sha2 (dmp)
1118                                                                         );
1119                                                                 out = true;
1120                                                                 if (g_validation_summary.active)
1121                                                                         g_validation_summary.dump_ok = true;
1122                                                                 } else {
1123                                                                         fprintf (stderr, "\nXbox XISO dump failed at output sector: %u\n", current_sector);
1124                                                                         out = false;
1125                                                                         if (g_validation_summary.active) {
1126                                                                                 g_validation_summary.dump_ok = false;
1127                                                                                 g_validation_summary.fail_sector = current_sector;
1128                                                                         }
1129                                                                 }
1130                                                         }
1131
1132                                                         dmp = dumper_destroy (dmp);
1133                                                 }
1134                                                 } else if (options.raw_out || options.iso_requested) {
1135                                                         if (type_id == DISC_TYPE_XBOX && options.raw_out)
1136                                                                 fprintf (stderr, "Xbox/XGD output does not support -r/raw together with the redump-style ISO path. Use -i or -X.\n");
1137                                                         else if (options.raw_out)
1138                                                                 fprintf (stderr, "Writing to file \"%s\" in raw format\n", options.raw_out);
1139                                                         if (options.iso_out) {
1140                                                                 if (type_id == DISC_TYPE_XBOX)
1141                                                                         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 : "");
1142                                                                 else
1143                                                                         fprintf (stderr, "Writing to file \"%s\" in ISO format\n", options.iso_out);
1144                                                         }
1145                                                         fprintf (stderr, "\n");
1146
1147                                                 dmp = dumper_new (d);
1148
1149                                                 dumper_set_hashing (dmp, !options.no_hashing);
1150                                                 dumper_set_flushing (dmp, !options.no_flushing);
1151
1152                                                 if (!dumper_set_raw_output_file (dmp, options.raw_out, options.resume)) {
1153                                                         fprintf (stderr, "Cannot setup raw output file\n");
1154                                                 } else if (!dumper_set_iso_output_file (dmp, options.iso_out, options.resume)) {
1155                                                         fprintf (stderr, "Cannot setup ISO output file\n");
1156                                                 } else if (!dumper_prepare (dmp)) {
1157                                                         fprintf (stderr, "Cannot prepare dumper");
1158                                                 } else {
1159 //                                                      fprintf (stderr, "Starting dump process from sector %u...\n", dmp -> start_sector);
1160 //                                                      opdd.start_sector = options.start_sector;
1161
1162                                                         if (options.gui)
1163                                                                 dumper_set_progress_callback (dmp, (progress_func) progress_for_guis, stats);
1164                                                         else
1165                                                                 dumper_set_progress_callback (dmp, (progress_func) progress, stats);
1166
1167                                                         dump_attempted = true;
1168                                                         if (g_validation_summary.active)
1169                                                                 g_validation_summary.dump_attempted = true;
1170                                                         if (dumper_dump (dmp, &current_sector)) {
1171                                                                 fprintf (stderr, "Dump completed successfully!\n");
1172                                                                 if (!options.no_hashing && options.raw_out)
1173                                                                         fprintf (stderr,
1174                                                                                 "Raw image hashes:\n"
1175                                                                                 "CRC32...: %s\n"
1176                                                                                 //"MD4.....: %s\n"
1177                                                                                 "MD5.....: %s\n"
1178                                                                                 "SHA-1...: %s\n"
1179                                                                                 "SHA-256.: %s\n"
1180                                                                                 /*"ED2K....: %s\n"*/,
1181                                                                                 dumper_get_raw_crc32 (dmp), /*dumper_get_raw_md4 (dmp),*/ dumper_get_raw_md5 (dmp),
1182                                                                                 dumper_get_raw_sha1 (dmp), dumper_get_raw_sha2 (dmp)/*, dumper_get_raw_ed2k (dmp)*/
1183                                                                         );
1184                                                                 if (!options.no_hashing && options.iso_out && !(type_id == DISC_TYPE_XBOX && disc_is_xbox_challenge_drive (d)))
1185                                                                         fprintf (stderr,
1186                                                                                 "ISO image hashes:\n"
1187                                                                                 "CRC32...: %s\n"
1188                                                                                 //"MD4.....: %s\n"
1189                                                                                 "MD5.....: %s\n"
1190                                                                                 "SHA-1...: %s\n"
1191                                                                                 "SHA-256.: %s\n"
1192                                                                                 /*"ED2K....: %s\n"*/,
1193                                                                                 dumper_get_iso_crc32 (dmp), /*dumper_get_iso_md4 (dmp),*/ dumper_get_iso_md5 (dmp),
1194                                                                                 dumper_get_iso_sha1 (dmp), dumper_get_iso_sha2 (dmp)/*, dumper_get_iso_ed2k (dmp)*/
1195                                                                         );
1196
1197                                                                 out = true;
1198                                                                 if (g_validation_summary.active)
1199                                                                         g_validation_summary.dump_ok = true;
1200                                                         } else {
1201                                                                 if (g_validation_summary.active) {
1202                                                                         g_validation_summary.dump_ok = false;
1203                                                                         g_validation_summary.fail_sector = current_sector;
1204                                                                 }
1205                                                                 if (current_sector == 0xFFFFFFFFU)
1206                                                                         fprintf (stderr, "\nXbox reference dumper failed; see the Xbox-specific message above.\n");
1207                                                                 else
1208                                                                         fprintf (stderr, "\nDump failed at sectors: %u..%u\n", current_sector, current_sector+15);
1209                                                                 out = false;
1210                                                                 //disc_stop_unit (d, 0);
1211                                                         }
1212                                                 }
1213
1214                                                 dmp = dumper_destroy (dmp);
1215                                         } else {
1216                                                 fprintf (stderr, "No output file for dumping specified, please take a look at the -i, -r, -X and -a options\n");
1217                                         }
1218                                 }
1219                         }
1220         if (dump_attempted) {
1221                 bool stop_ok = disc_stop_unit (d, false);
1222                 fprintf (stderr, "\nIssuing STOP UNIT / spin-down after dump attempt... %s\n", stop_ok ? "OK" : "Failed");
1223                 if (g_validation_summary.active) {
1224                         g_validation_summary.stop_attempted = true;
1225                         g_validation_summary.stop_ok = stop_ok;
1226                 }
1227         }
1228
1229         return out;
1230 }
1231
1232
1233 static int try_all_methods (progstats *stats) {
1234         u_int32_t saved_command;
1235         int saved_method;
1236         u_int32_t command;
1237         int method;
1238         int out;
1239         disc *d;
1240
1241         saved_command = options.command;
1242         saved_method = options.dump_method;
1243         out = false;
1244
1245         fprintf (stderr, "Trying all command/method combinations... This will take a LOOOONG time and generate an insanely long console output :p\n");
1246
1247         for (command = 0; command <= 4 && !out; command++) {
1248                 for (method = 0; method <= 10 && !out; method++) {
1249                         options.command = command;
1250                         options.dump_method = method;
1251                         memset (stats, 0, sizeof (*stats));
1252
1253                         fprintf (stderr, "\nTrying with command %u, method %d\n", command, method);
1254                         fprintf (stderr, "Initializing DVD drive... ");
1255
1256                         d = disc_new (options.device, options.command);
1257                         if (!d) {
1258                                 fprintf (stderr, "Failed\n");
1259 #ifndef WIN32
1260                                 fprintf (stderr,
1261                                         "Probably you do not have access to the DVD device. Ask the administrator\n"
1262                                         "to add you to the proper group, or use 'sudo'.\n"
1263                                 );
1264 #endif
1265                                 continue;
1266                         }
1267
1268                         fprintf (stderr, "OK\n");
1269                         out = dologic (d, stats);
1270                         d = disc_destroy (d);
1271
1272                         if (out)
1273                                 fprintf (stderr, "Command %u and method %d combination worked!\n", command, method);
1274                 }
1275         }
1276
1277         if (!out) {
1278                 options.command = saved_command;
1279                 options.dump_method = saved_method;
1280         }
1281
1282         return out;
1283 }
1284
1285 int main (int argc, char *argv[]) {
1286         disc *d;
1287         progstats stats;
1288         double duration;
1289         suseconds_t us;
1290         int out, ret;
1291         unscrambler *u;
1292         unscrambler_progress_func pfunc;
1293         u_int32_t current_sector;
1294
1295         /* First of all... */
1296         drop_euid ();
1297         xbox_ref_log_open_for_target(NULL, 0);
1298         
1299         welcome ();
1300
1301         memset (&stats, 0, sizeof (stats));
1302         d = NULL;
1303         out = false;
1304         ret = EXIT_FAILURE;
1305         if (optparse (argc, argv)) {
1306                 friidump_retarget_log_from_options();
1307                 if (options.device) {
1308                         if (options.allmethods) {
1309                                 out = try_all_methods (&stats);
1310                         } else {
1311                                 /* Dump DVD to file */
1312                                 fprintf (stderr, "Initializing DVD drive... ");
1313
1314                                 if (!(d = disc_new (options.device, options.command))) {
1315                                         fprintf (stderr, "Failed\n");
1316 #ifndef WIN32
1317                                         fprintf (stderr,
1318                                                 "Probably you do not have access to the DVD device. Ask the administrator\n"
1319                                                 "to add you to the proper group, or use 'sudo'.\n"
1320                                         );
1321 #endif
1322                                 } else {
1323                                         fprintf (stderr, "OK\n");
1324                                         out = dologic (d, &stats);
1325                                         d = disc_destroy (d);
1326                                 }
1327                         }
1328                 } else if (options.raw_in) {
1329                         /* Convert raw image to ISO format */
1330                         u = unscrambler_new ();
1331                         
1332                         if (options.gui)
1333                                 pfunc = (unscrambler_progress_func) progress_for_guis;
1334                         else
1335                                 pfunc = (unscrambler_progress_func) progress;
1336
1337                         if ((out = unscrambler_unscramble_file (u, options.raw_in, options.iso_out, pfunc, &stats, &current_sector)))
1338                                 fprintf (stderr, "Unscrambling completed successfully!\n");
1339                         else
1340                                 fprintf (stderr, "\nUnscrambling failed at sectors: %u..%u\n", current_sector, current_sector+15);
1341
1342                         u = unscrambler_destroy (u);
1343                 } else {
1344                         MY_ASSERT (0);
1345                 }
1346
1347                 if (out) {
1348                         duration = stats.end_time.tv_sec - stats.start_time.tv_sec;
1349                         if (stats.end_time.tv_usec >= stats.start_time.tv_usec) {
1350                                 us = stats.end_time.tv_usec - stats.start_time.tv_usec;
1351                         } else {
1352                                 if (duration > 0)
1353                                         duration--;
1354                                 us = USECS_PER_SEC + stats.end_time.tv_usec - stats.start_time.tv_usec;
1355                         }
1356                         duration += ((double) us / (double) USECS_PER_SEC);
1357                         if (duration < 0)
1358                                 duration = 0;
1359                         fprintf (stderr, "Operation took %.2f seconds\n", duration);
1360                         friidump_print_validation_summary(duration, true);
1361
1362                         ret = EXIT_SUCCESS;
1363                 } else {
1364                         friidump_print_validation_summary(0.0, false);
1365                         ret = EXIT_FAILURE;
1366                 }
1367                 
1368                 my_free (options.device);
1369                 my_free (options.iso_out);
1370                 my_free (options.xiso_out);
1371                 my_free (options.raw_out);
1372                 my_free (options.raw_in);
1373                 my_free (options.hlds_profile_report);
1374         }
1375
1376         if (xbox_ref_log_path())
1377                 fprintf (stderr, "Log file complete: %s\n", xbox_ref_log_path());
1378         xbox_ref_log_close();
1379
1380         return (ret);
1381 }