]> FriiDump Source - friidump.git/blob - libfriidump/dumper.c
52795358a73672c06b41832b54d2be56a0a88dee
[friidump.git] / libfriidump / dumper.c
1 /***************************************************************************
2  *   Copyright (C) 2007 by Arep                                            *
3  *   Support is provided through the forums at                             *
4  *   http://www.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 <inttypes.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <multihash.h>
28 #include "constants.h"
29 #include "disc.h"
30 #include "dumper.h"
31 #include "xbox_ref_bridge.h"
32 #include "xbox_ref/xbox_ref_log.h"
33
34 #ifndef WIN32
35 #include <unistd.h>
36 #include <sys/types.h>
37 #endif
38
39 struct dumper_s {
40         disc *dsk;
41         char *outfile_raw;
42         u_int32_t start_sector_raw;
43         FILE *fp_raw;
44         char *outfile_iso;
45         u_int32_t start_sector_iso;
46         FILE *fp_iso;
47         char *outfile_xiso;
48         u_int32_t start_sector_xiso;
49         FILE *fp_xiso;
50         u_int32_t start_sector;
51         bool hashing;
52         bool flushing;
53
54         multihash hash_raw;
55         multihash hash_iso;
56         multihash hash_xiso;
57         xbox_ref_dump_result xbox_ref_result;
58         
59         progress_func progress;
60         void *progress_data;
61 };
62
63 #define DUMPER_READ_BLOCK_RETRIES 3
64
65 static bool dumper_read_sector_with_retry (dumper *dmp, u_int32_t sector_no, u_int8_t **isobuf, u_int8_t **rawbuf) {
66         int attempt;
67         bool need_iso, need_raw;
68
69         if (!dmp || !dmp -> dsk)
70                 return false;
71
72         need_iso = dmp -> fp_iso != NULL;
73         need_raw = dmp -> fp_raw != NULL;
74
75         for (attempt = 0; attempt < DUMPER_READ_BLOCK_RETRIES; attempt++) {
76                 if (isobuf) *isobuf = NULL;
77                 if (rawbuf) *rawbuf = NULL;
78
79                 disc_read_sector (dmp -> dsk, sector_no, isobuf, rawbuf);
80                 if ((!need_iso || (isobuf && *isobuf)) && (!need_raw || (rawbuf && *rawbuf))) {
81                         if (attempt > 0)
82                                 warning ("Recovered sectors %u..%u after dump-level retry %d", sector_no, sector_no + SECTORS_PER_BLOCK - 1, attempt);
83                         return true;
84                 }
85
86                 if (attempt + 1 < DUMPER_READ_BLOCK_RETRIES)
87                         warning ("Dump-level read retry %d/%d for sectors %u..%u", attempt + 1, DUMPER_READ_BLOCK_RETRIES - 1, sector_no, sector_no + SECTORS_PER_BLOCK - 1);
88         }
89
90         return false;
91 }
92
93 #define XBOX_XISO_LEADIN_SECTORS 32
94 #define XBOX_XISO_STANDARD_GAME_LBA 32
95 #define XBOX_XISO_START_LBA_MAGIC 306112
96 #define XBOX_XISO_DUAL_LAYER_END_LBA 1913920
97
98 /* DiscImageCreator-compatible Original Xbox/XGD1 2048-byte-sector layout
99  * constants. DIC uses XBOX_SIZE=3820880, XBOX_LAYER_BREAK=1913776,
100  * DVD start PSN 0x30000, and Xbox start PSN 0x60600. The virtual redump
101  * layout below follows those constants: video L0, middle/padding, game
102  * partition, middle/padding, and video L1 tail.
103  */
104 #define XBOX_XGD1_DIC_DVD_START_PSN 0x30000
105 #define XBOX_XGD1_DIC_XBOX_START_PSN 0x60600
106 #define XBOX_XGD1_DIC_XBOX_SIZE 3820880
107 #define XBOX_XGD1_DIC_LAYER_BREAK 1913776
108 #define XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS 3431264
109 #define XBOX_XGD1_FULL_REDUMP_SECTORS XBOX_XGD1_DIC_XBOX_SIZE
110 #define XBOX_XGD1_GAME_OUTPUT_START_LBA (XBOX_XGD1_DIC_XBOX_START_PSN - XBOX_XGD1_DIC_DVD_START_PSN)
111 #define XBOX_XGD1_VIDEO_L0_SECTORS 6832
112 #define XBOX_XGD1_VIDEO_L1_SECTORS 160
113 #define XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA (XBOX_XGD1_FULL_REDUMP_SECTORS - XBOX_XGD1_VIDEO_L1_SECTORS)
114 #define XBOX_XGD1_GAME_SOURCE_SECTORS (XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS - XBOX_XISO_LEADIN_SECTORS)
115 #define XBOX_XGD1_LAYER_THRESHOLD 2000000
116 #define XBOX_METADATA_PATH_MAX 4096
117
118 static u_int32_t read_le32 (const u_int8_t *p) {
119         return ((u_int32_t) p[0]) | ((u_int32_t) p[1] << 8) | ((u_int32_t) p[2] << 16) | ((u_int32_t) p[3] << 24);
120 }
121
122 static bool xbox_probe_xdfs_volume (disc *d, u_int32_t lba, u_int32_t *root_lba, u_int32_t *volume_size_raw) {
123         u_int8_t sector[SECTOR_SIZE];
124
125         memset (sector, 0, sizeof (sector));
126         if (disc_xbox_read_10 (d, lba, 1, sector, sizeof (sector)) < 0)
127                 return false;
128
129         if (memcmp (sector, "MICROSOFT", 9) != 0)
130                 return false;
131
132         if (root_lba)
133                 *root_lba = read_le32 (sector + 0x14);
134         if (volume_size_raw)
135                 *volume_size_raw = read_le32 (sector + 0x1C);
136
137         return true;
138 }
139
140 static bool xbox_redump_layout_constants_match_dic (void) {
141         return XBOX_XGD1_FULL_REDUMP_SECTORS == XBOX_XGD1_DIC_XBOX_SIZE &&
142                XBOX_XGD1_DIC_LAYER_BREAK == 1913776 &&
143                XBOX_XGD1_GAME_OUTPUT_START_LBA == 0x30600 &&
144                XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA == 3820720 &&
145                XBOX_XGD1_VIDEO_L0_SECTORS +
146                (XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS) +
147                XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS +
148                (XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA -
149                 (XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS)) +
150                XBOX_XGD1_VIDEO_L1_SECTORS == XBOX_XGD1_FULL_REDUMP_SECTORS;
151 }
152
153
154 typedef struct {
155         bool has_pfi;
156         bool has_dmi;
157         bool pfi_written;
158         bool dmi_written;
159         u_int8_t pfi[2048];
160         u_int8_t dmi[2048];
161         u_int32_t locked_visible_sectors;
162         u_int32_t unlocked_visible_sectors;
163         u_int32_t output_sectors;
164         u_int32_t game_source_lba;
165         u_int32_t game_source_sectors;
166         u_int32_t game_leadin_read_sectors;
167         u_int32_t game_leadin_zero_sectors;
168         char pfi_path[XBOX_METADATA_PATH_MAX];
169         char dmi_path[XBOX_METADATA_PATH_MAX];
170         char json_path[XBOX_METADATA_PATH_MAX];
171 } xbox_redump_metadata;
172
173 static void xbox_make_related_path (const char *filename, const char *suffix, char *out, size_t out_size) {
174         const char *slash1, *slash2, *slash, *dot;
175         size_t suffix_len, base_len;
176
177         if (!out || out_size == 0)
178                 return;
179         out[0] = '\0';
180         if (!filename || !suffix)
181                 return;
182
183         suffix_len = strlen (suffix);
184         if (out_size <= suffix_len + 1)
185                 return;
186
187         slash1 = strrchr (filename, '/');
188         slash2 = strrchr (filename, '\\');
189         if (slash1 && slash2)
190                 slash = slash1 > slash2 ? slash1 : slash2;
191         else
192                 slash = slash1 ? slash1 : slash2;
193
194         dot = strrchr (filename, '.');
195         if (!dot || (slash && dot < slash))
196                 dot = filename + strlen (filename);
197
198         base_len = (size_t) (dot - filename);
199         if (base_len + suffix_len + 1 > out_size)
200                 base_len = out_size - suffix_len - 1;
201         memcpy (out, filename, base_len);
202         out[base_len] = '\0';
203         strncat (out, suffix, out_size - strlen (out) - 1);
204 }
205
206 static const char *xbox_path_leaf (const char *path) {
207         const char *slash1, *slash2;
208         if (!path)
209                 return "";
210         slash1 = strrchr (path, '/');
211         slash2 = strrchr (path, '\\');
212         if (slash1 && slash2)
213                 return (slash1 > slash2 ? slash1 : slash2) + 1;
214         if (slash1)
215                 return slash1 + 1;
216         if (slash2)
217                 return slash2 + 1;
218         return path;
219 }
220
221 static void xbox_json_string (FILE *f, const char *s) {
222         fputc ('"', f);
223         if (s) {
224                 while (*s) {
225                         unsigned char c = (unsigned char) *s++;
226                         if (c == '"' || c == '\\') {
227                                 fputc ('\\', f);
228                                 fputc (c, f);
229                         } else if (c == '\n') {
230                                 fputs ("\\n", f);
231                         } else if (c == '\r') {
232                                 fputs ("\\r", f);
233                         } else if (c == '\t') {
234                                 fputs ("\\t", f);
235                         } else if (c < 0x20) {
236                                 fprintf (f, "\\u%04x", c);
237                         } else {
238                                 fputc (c, f);
239                         }
240                 }
241         }
242         fputc ('"', f);
243 }
244
245 static bool xbox_write_binary_file (const char *path, const u_int8_t *data, size_t len) {
246         FILE *f;
247         if (!path || !data || len == 0)
248                 return false;
249         f = fopen (path, "wb");
250         if (!f)
251                 return false;
252         if (fwrite (data, 1, len, f) != len) {
253                 fclose (f);
254                 return false;
255         }
256         return fclose (f) == 0;
257 }
258
259 static void xbox_capture_pfi_dmi (disc *d, xbox_redump_metadata *meta) {
260         if (!d || !meta)
261                 return;
262         if (!meta -> has_pfi)
263                 meta -> has_pfi = disc_xbox_read_dvd_structure (d, 0x00, 0x00, meta -> pfi, sizeof (meta -> pfi)) >= 0;
264         if (!meta -> has_dmi)
265                 meta -> has_dmi = disc_xbox_read_dvd_structure (d, 0x04, 0x00, meta -> dmi, sizeof (meta -> dmi)) >= 0;
266 }
267
268 static void xbox_write_redump_metadata (dumper *dmp, xbox_redump_metadata *meta) {
269         FILE *json;
270         const char *pfi_leaf;
271         const char *dmi_leaf;
272
273         if (!dmp || !meta || !dmp -> outfile_iso)
274                 return;
275
276         xbox_make_related_path (dmp -> outfile_iso, ".pfi.bin", meta -> pfi_path, sizeof (meta -> pfi_path));
277         xbox_make_related_path (dmp -> outfile_iso, ".dmi.bin", meta -> dmi_path, sizeof (meta -> dmi_path));
278         xbox_make_related_path (dmp -> outfile_iso, ".redump.json", meta -> json_path, sizeof (meta -> json_path));
279
280         if (meta -> has_pfi)
281                 meta -> pfi_written = xbox_write_binary_file (meta -> pfi_path, meta -> pfi, sizeof (meta -> pfi));
282         if (meta -> has_dmi)
283                 meta -> dmi_written = xbox_write_binary_file (meta -> dmi_path, meta -> dmi, sizeof (meta -> dmi));
284
285         json = fopen (meta -> json_path, "wb");
286         if (!json) {
287                 warning ("Could not write Xbox redump metadata JSON");
288                 return;
289         }
290
291         pfi_leaf = meta -> pfi_written ? xbox_path_leaf (meta -> pfi_path) : NULL;
292         dmi_leaf = meta -> dmi_written ? xbox_path_leaf (meta -> dmi_path) : NULL;
293
294         fprintf (json, "{\n");
295         fprintf (json, "  \"format\": \"friidump-xbox-redump\",\n");
296         fprintf (json, "  \"version\": 1,\n");
297         fprintf (json, "  \"image\": {\n");
298         fprintf (json, "    \"file\": "); xbox_json_string (json, xbox_path_leaf (dmp -> outfile_iso)); fprintf (json, ",\n");
299         fprintf (json, "    \"sector_size\": %u,\n", SECTOR_SIZE);
300         fprintf (json, "    \"sector_count\": %u,\n", meta -> output_sectors);
301         fprintf (json, "    \"byte_count\": %llu,\n", (unsigned long long) meta -> output_sectors * SECTOR_SIZE);
302         fprintf (json, "    \"hashes\": {\n");
303         fprintf (json, "      \"crc32\": "); xbox_json_string (json, dumper_get_iso_crc32 (dmp)); fprintf (json, ",\n");
304         fprintf (json, "      \"md5\": "); xbox_json_string (json, dumper_get_iso_md5 (dmp)); fprintf (json, ",\n");
305         fprintf (json, "      \"sha1\": "); xbox_json_string (json, dumper_get_iso_sha1 (dmp)); fprintf (json, ",\n");
306         fprintf (json, "      \"sha256\": "); xbox_json_string (json, dumper_get_iso_sha2 (dmp)); fprintf (json, "\n");
307         fprintf (json, "    }\n");
308         fprintf (json, "  },\n");
309         fprintf (json, "  \"drive\": {\n");
310         fprintf (json, "    \"model\": "); xbox_json_string (json, disc_get_drive_model_string (dmp -> dsk)); fprintf (json, ",\n");
311         fprintf (json, "    \"command\": %u,\n", disc_get_command (dmp -> dsk));
312         fprintf (json, "    \"method\": %u\n", disc_get_method (dmp -> dsk));
313         fprintf (json, "  },\n");
314         fprintf (json, "  \"layout\": {\n");
315         fprintf (json, "    \"layout_type\": \"original_xbox_xgd1_redump_style_2048\",\n");
316         fprintf (json, "    \"locked_visible_sector_count\": %u,\n", meta -> locked_visible_sectors);
317         fprintf (json, "    \"unlocked_visible_sector_count\": %u,\n", meta -> unlocked_visible_sectors);
318         fprintf (json, "    \"video_l0_start_lba\": 0,\n");
319         fprintf (json, "    \"video_l0_sector_count\": %u,\n", XBOX_XGD1_VIDEO_L0_SECTORS);
320         fprintf (json, "    \"pregame_padding_start_lba\": %u,\n", XBOX_XGD1_VIDEO_L0_SECTORS);
321         fprintf (json, "    \"pregame_padding_sector_count\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS);
322         fprintf (json, "    \"game_output_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA);
323         fprintf (json, "    \"game_leadin_sector_count\": %u,\n", XBOX_XISO_LEADIN_SECTORS);
324         fprintf (json, "    \"game_leadin_unlocked_source_start_lba\": %u,\n",
325                 (meta -> game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ?
326                 meta -> game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0);
327         fprintf (json, "    \"game_leadin_source_policy\": \"drive_read10_with_per_sector_zero_fallback\",\n");
328         fprintf (json, "    \"game_unlocked_source_start_lba\": %u,\n", meta -> game_source_lba);
329         fprintf (json, "    \"game_unlocked_source_sector_count\": %u,\n", meta -> game_source_sectors);
330         fprintf (json, "    \"postgame_padding_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS);
331         fprintf (json, "    \"postgame_padding_sector_count\": %u,\n", XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA - (XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS));
332         fprintf (json, "    \"video_l1_start_lba\": %u,\n", XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA);
333         fprintf (json, "    \"video_l1_sector_count\": %u,\n", XBOX_XGD1_VIDEO_L1_SECTORS);
334         fprintf (json, "    \"layer_break_lba\": %u\n", XBOX_XGD1_DIC_LAYER_BREAK);
335         fprintf (json, "  },\n");
336         fprintf (json, "  \"discimagecreator_reference\": {\n");
337         fprintf (json, "    \"xbox_size_sectors\": %u,\n", XBOX_XGD1_DIC_XBOX_SIZE);
338         fprintf (json, "    \"xbox_layer_break_lba\": %u,\n", XBOX_XGD1_DIC_LAYER_BREAK);
339         fprintf (json, "    \"dvd_start_psn\": %u,\n", XBOX_XGD1_DIC_DVD_START_PSN);
340         fprintf (json, "    \"xbox_start_psn\": %u,\n", XBOX_XGD1_DIC_XBOX_START_PSN);
341         fprintf (json, "    \"game_output_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA);
342         fprintf (json, "    \"layout_constants_match\": %s\n", xbox_redump_layout_constants_match_dic () ? "true" : "false");
343         fprintf (json, "  },\n");
344         fprintf (json, "  \"lead_in_capture\": {\n");
345         fprintf (json, "    \"game_leadin_attempted\": true,\n");
346         fprintf (json, "    \"unlocked_source_start_lba\": %u,\n",
347                 (meta -> game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ?
348                 meta -> game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0);
349         fprintf (json, "    \"sector_count\": %u,\n", XBOX_XISO_LEADIN_SECTORS);
350         fprintf (json, "    \"readable_sectors\": %u,\n", meta -> game_leadin_read_sectors);
351         fprintf (json, "    \"zero_filled_sectors\": %u,\n", meta -> game_leadin_zero_sectors);
352         fprintf (json, "    \"physical_geometry_evidence\": \"friidump_0.5.3.13_cache_aligned_raw_id_probe\"\n");
353         fprintf (json, "  },\n");
354         fprintf (json, "  \"dvd_structures\": {\n");
355         fprintf (json, "    \"pfi\": "); if (pfi_leaf) xbox_json_string (json, pfi_leaf); else fprintf (json, "null"); fprintf (json, ",\n");
356         fprintf (json, "    \"dmi\": "); if (dmi_leaf) xbox_json_string (json, dmi_leaf); else fprintf (json, "null"); fprintf (json, "\n");
357         fprintf (json, "  },\n");
358         fprintf (json, "  \"notes\": [\n");
359         fprintf (json, "    \"PFI and DMI are stored as separate binary READ DVD STRUCTURE captures because they are not READ(10) user-data sectors.\",\n");
360         fprintf (json, "    \"The 32-sector game lead-in is captured from unlocked source LBA 0..31; only unreadable lead-in sectors use zero-fill fallback.\",\n");
361         fprintf (json, "    \"Pregame and postgame padding retain zero-filled placeholder content; their physical locations are resolved but their inaccessible bytes are not.\"\n");
362         fprintf (json, "  ]\n");
363         fprintf (json, "}\n");
364         fclose (json);
365
366         if (meta -> pfi_written) {
367                 debug ("Wrote Xbox PFI: %s", meta -> pfi_path);
368         } else {
369                 warning ("Xbox PFI was not captured or could not be written");
370         }
371         if (meta -> dmi_written) {
372                 debug ("Wrote Xbox DMI: %s", meta -> dmi_path);
373         } else {
374                 warning ("Xbox DMI was not captured or could not be written");
375         }
376         debug ("Wrote Xbox redump metadata: %s", meta -> json_path);
377 }
378
379 static bool xbox_write_iso_sectors (dumper *dmp, const u_int8_t *buf, u_int32_t sectors, u_int32_t output_lba, u_int32_t total_sectors) {
380         if (!dmp || !dmp -> fp_iso || !buf)
381                 return false;
382         clearerr (dmp -> fp_iso);
383         if (fwrite (buf, SECTOR_SIZE, sectors, dmp -> fp_iso) != sectors || ferror (dmp -> fp_iso)) {
384                 error ("fwrite() to Xbox redump ISO failed at output LBA %u", output_lba);
385                 return false;
386         }
387         if (dmp -> hashing)
388                 multihash_update (&(dmp -> hash_iso), (u_int8_t *) buf, (size_t) sectors * SECTOR_SIZE);
389         if (dmp -> flushing)
390                 fflush (dmp -> fp_iso);
391         if (dmp -> progress && ((output_lba % 320) == 0 || output_lba + sectors >= total_sectors))
392                 dmp -> progress (false, output_lba + sectors, total_sectors, dmp -> progress_data);
393         return true;
394 }
395
396 static bool xbox_write_zero_iso_range (dumper *dmp, u_int32_t sectors, u_int32_t output_lba, u_int32_t total_sectors, const char *label) {
397         u_int8_t zero[BLOCK_SIZE];
398         u_int32_t done = 0;
399         memset (zero, 0, sizeof (zero));
400         debug ("Writing Xbox %s zero-fill: output LBA %u, sectors %u", label ? label : "range", output_lba, sectors);
401         while (done < sectors) {
402                 u_int32_t todo = sectors - done;
403                 if (todo > SECTORS_PER_BLOCK)
404                         todo = SECTORS_PER_BLOCK;
405                 if (!xbox_write_iso_sectors (dmp, zero, todo, output_lba + done, total_sectors))
406                         return false;
407                 done += todo;
408         }
409         return true;
410 }
411
412 static bool xbox_dump_read10_iso_range (dumper *dmp, u_int32_t source_lba, u_int32_t sectors, u_int32_t output_lba, u_int32_t total_sectors, const char *label, bool auth_recovery, u_int32_t *current_sector) {
413         u_int8_t buf[BLOCK_SIZE];
414         u_int8_t one[SECTOR_SIZE];
415         u_int32_t done = 0;
416         debug ("Reading Xbox %s: source LBA %u, output LBA %u, sectors %u", label ? label : "range", source_lba, output_lba, sectors);
417         disc_xbox_recovery_kick (dmp -> dsk, auth_recovery);
418         while (done < sectors) {
419                 u_int32_t todo = sectors - done;
420                 int retry;
421                 bool ok = false;
422                 if (todo > SECTORS_PER_BLOCK)
423                         todo = SECTORS_PER_BLOCK;
424                 if (current_sector)
425                         *current_sector = output_lba + done;
426                 for (retry = 0; retry < 3 && !ok; retry++) {
427                         if (retry > 0)
428                                 disc_xbox_recovery_kick (dmp -> dsk, auth_recovery);
429                         memset (buf, 0, sizeof (buf));
430                         if (disc_xbox_read_10 (dmp -> dsk, source_lba + done, todo, buf, sizeof (buf)) >= 0)
431                                 ok = true;
432                 }
433                 if (!ok && todo > 1) {
434                         /* Some Xbox-capable drives are more reliable at visible DVD-video edges
435                          * and layer/layout boundaries with single-sector READ(10) commands.
436                          * Fall back before declaring the whole 16-sector batch bad. */
437                         u_int32_t i;
438                         bool singles_ok = true;
439                         for (i = 0; i < todo; i++) {
440                                 bool one_ok = false;
441                                 for (retry = 0; retry < 3 && !one_ok; retry++) {
442                                         if (retry > 0)
443                                                 disc_xbox_recovery_kick (dmp -> dsk, auth_recovery);
444                                         memset (one, 0, sizeof (one));
445                                         if (disc_xbox_read_10 (dmp -> dsk, source_lba + done + i, 1, one, sizeof (one)) >= 0)
446                                                 one_ok = true;
447                                 }
448                                 if (!one_ok) {
449                                         if (current_sector)
450                                                 *current_sector = output_lba + done + i;
451                                         xbox_ref_log_fprintf (stderr, "\n[XBOX] READ(10) failed for %s at source LBA %u / output LBA %u\n", label ? label : "range", source_lba + done + i, output_lba + done + i);
452                                         error ("Xbox READ(10) failed for %s at source LBA %u", label ? label : "range", source_lba + done + i);
453                                         singles_ok = false;
454                                         break;
455                                 }
456                                 memcpy (buf + ((size_t) i * SECTOR_SIZE), one, SECTOR_SIZE);
457                         }
458                         ok = singles_ok;
459                 }
460                 if (!ok) {
461                         if (current_sector)
462                                 *current_sector = output_lba + done;
463                         xbox_ref_log_fprintf (stderr, "\n[XBOX] READ(10) failed for %s at source LBA %u / output LBA %u\n", label ? label : "range", source_lba + done, output_lba + done);
464                         error ("Xbox READ(10) failed for %s at source LBA %u", label ? label : "range", source_lba + done);
465                         return false;
466                 }
467                 if (!xbox_write_iso_sectors (dmp, buf, todo, output_lba + done, total_sectors))
468                         return false;
469                 done += todo;
470         }
471         return true;
472 }
473
474 static bool xbox_read_range_to_memory (disc *d, u_int32_t source_lba, u_int32_t sectors, u_int8_t *out, size_t out_size, const char *label, bool auth_recovery) {
475         u_int8_t one[SECTOR_SIZE];
476         u_int32_t done = 0;
477         if (!d || !out || out_size < (size_t) sectors * SECTOR_SIZE)
478                 return false;
479         debug ("Capturing Xbox %s to memory: source LBA %u, sectors %u", label ? label : "range", source_lba, sectors);
480         disc_xbox_recovery_kick (d, auth_recovery);
481         while (done < sectors) {
482                 u_int32_t todo = sectors - done;
483                 int retry;
484                 bool ok = false;
485                 if (todo > SECTORS_PER_BLOCK)
486                         todo = SECTORS_PER_BLOCK;
487                 for (retry = 0; retry < 3 && !ok; retry++) {
488                         if (retry > 0)
489                                 disc_xbox_recovery_kick (d, auth_recovery);
490                         if (disc_xbox_read_10 (d, source_lba + done, todo, out + ((size_t) done * SECTOR_SIZE), out_size - ((size_t) done * SECTOR_SIZE)) >= 0)
491                                 ok = true;
492                 }
493                 if (!ok && todo > 1) {
494                         u_int32_t i;
495                         bool singles_ok = true;
496                         for (i = 0; i < todo; i++) {
497                                 bool one_ok = false;
498                                 for (retry = 0; retry < 3 && !one_ok; retry++) {
499                                         if (retry > 0)
500                                                 disc_xbox_recovery_kick (d, auth_recovery);
501                                         memset (one, 0, sizeof (one));
502                                         if (disc_xbox_read_10 (d, source_lba + done + i, 1, one, sizeof (one)) >= 0)
503                                                 one_ok = true;
504                                 }
505                                 if (!one_ok) {
506                                         xbox_ref_log_fprintf (stderr, "\n[XBOX] READ(10) failed while capturing %s at source LBA %u\n", label ? label : "range", source_lba + done + i);
507                                         error ("Xbox READ(10) failed while capturing %s at source LBA %u", label ? label : "range", source_lba + done + i);
508                                         singles_ok = false;
509                                         break;
510                                 }
511                                 memcpy (out + ((size_t) (done + i) * SECTOR_SIZE), one, SECTOR_SIZE);
512                         }
513                         ok = singles_ok;
514                 }
515                 if (!ok) {
516                         xbox_ref_log_fprintf (stderr, "\n[XBOX] READ(10) failed while capturing %s at source LBA %u\n", label ? label : "range", source_lba + done);
517                         error ("Xbox READ(10) failed while capturing %s at source LBA %u", label ? label : "range", source_lba + done);
518                         return false;
519                 }
520                 done += todo;
521         }
522         return true;
523 }
524
525 static bool xbox_write_leadin_iso_range (dumper *dmp, u_int32_t source_lba, u_int32_t output_lba, u_int32_t total_sectors, xbox_redump_metadata *meta, const char *label, bool auth_recovery, u_int32_t *current_sector) {
526         u_int8_t buf[SECTOR_SIZE];
527         u_int8_t zero[SECTOR_SIZE];
528         u_int32_t i;
529         bool warned = false;
530         memset (zero, 0, sizeof (zero));
531         debug ("Attempting Xbox %s lead-in from source LBA %u..%u", label ? label : "game", source_lba, source_lba + XBOX_XISO_LEADIN_SECTORS - 1);
532         disc_xbox_recovery_kick (dmp -> dsk, auth_recovery);
533         for (i = 0; i < XBOX_XISO_LEADIN_SECTORS; i++) {
534                 const u_int8_t *src = buf;
535                 if (current_sector)
536                         *current_sector = output_lba + i;
537                 memset (buf, 0, sizeof (buf));
538                 if (disc_xbox_read_10 (dmp -> dsk, source_lba + i, 1, buf, sizeof (buf)) < 0) {
539                         if (!warned) {
540                                 warning ("Drive could not read one or more Xbox lead-in sectors; zero-filling only unreadable lead-in sectors");
541                                 warned = true;
542                         }
543                         src = zero;
544                         if (meta) meta -> game_leadin_zero_sectors++;
545                 } else {
546                         if (meta) meta -> game_leadin_read_sectors++;
547                 }
548                 if (!xbox_write_iso_sectors (dmp, src, 1, output_lba + i, total_sectors))
549                         return false;
550         }
551         return true;
552 }
553
554 static bool xbox_get_xiso_range (disc *d, u_int32_t *source_lba, u_int32_t *source_sectors, u_int32_t *target_sectors) {
555         u_int32_t total_sectors, root_lba, volume_size_raw, volume_sectors;
556
557         if (!d || !source_lba || !source_sectors || !target_sectors)
558                 return false;
559
560         if (disc_xbox_unlock (d) < 0) {
561                 error ("Could not switch Xbox drive into the game/XDVDFS view");
562                 return false;
563         }
564
565         total_sectors = disc_get_sectors_no (d);
566         root_lba = 0;
567         volume_size_raw = 0;
568
569         /* Try the historical physical/game-partition XDVDFS location first,
570          * then the unlocked/XISO-visible LBA 0x20. GDR-8050L after the
571          * native Xbox handshake normally resolves at 0x20.
572          */
573         if (xbox_probe_xdfs_volume (d, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw)) {
574                 *source_lba = XBOX_XISO_START_LBA_MAGIC;
575         } else if (xbox_probe_xdfs_volume (d, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw)) {
576                 *source_lba = XBOX_XISO_STANDARD_GAME_LBA;
577         } else {
578                 error ("No Xbox XDVDFS volume found at LBA %u or %u", XBOX_XISO_START_LBA_MAGIC, XBOX_XISO_STANDARD_GAME_LBA);
579                 return false;
580         }
581
582         if (total_sectors > 3300000) {
583                 /* Retail dual-layer XISO copies from the detected game/XDVDFS
584                  * start LBA through logical LBA 1913920, then prepends 32
585                  * output lead-in sectors.
586                  */
587                 if (*source_lba >= XBOX_XISO_DUAL_LAYER_END_LBA) {
588                         error ("Xbox XISO source LBA %u is beyond dual-layer XISO end LBA %u", *source_lba, XBOX_XISO_DUAL_LAYER_END_LBA);
589                         return false;
590                 }
591                 *source_sectors = XBOX_XISO_DUAL_LAYER_END_LBA - *source_lba;
592         } else {
593                 /* For single-layer/homebrew-style media, XDVDFS VolumeSize is a
594                  * byte count, so convert it to 2048-byte sectors.
595                  */
596                 if (volume_size_raw == 0) {
597                         error ("Xbox XDVDFS header reported a zero volume size");
598                         return false;
599                 }
600
601                 volume_sectors = volume_size_raw / SECTOR_SIZE;
602                 if (volume_sectors == 0) {
603                         error ("Xbox XDVDFS volume size %u is smaller than one sector", volume_size_raw);
604                         return false;
605                 }
606                 if (*source_lba + volume_sectors > total_sectors) {
607                         warning ("Xbox XISO volume size extends beyond visible capacity; clipping to remaining visible sectors");
608                         if (total_sectors > *source_lba)
609                                 volume_sectors = total_sectors - *source_lba;
610                         else
611                                 return false;
612                 }
613                 *source_sectors = volume_sectors;
614         }
615
616         *target_sectors = *source_sectors + XBOX_XISO_LEADIN_SECTORS;
617         debug ("Xbox XISO range: source LBA %u, descriptor RootLBA %u, source sectors %u, output sectors %u", *source_lba, root_lba, *source_sectors, *target_sectors);
618         return true;
619 }
620
621
622 /**
623  * Tries to open the output file for writing and to find out if it contains valid data so that the dump can continue.
624  * @param dvd 
625  * @param outfile 
626  * @param[out] fp The file pointer to write to.
627  * @param[out] start_sector The sector to start reading from.
628  * @return true if the dumping can start/continue, false otherwise (for instance if outfile cannot be written to).
629  */
630 bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume) {
631         bool out;
632         my_off_t filesize;
633         FILE *fp;
634
635         if (!outfile_raw) {
636                 /* Raw output disabled */
637                 out = true;
638                 dmp -> start_sector_raw = -1;
639                 my_free (dmp -> outfile_raw);
640                 dmp -> outfile_raw = NULL;
641         } else if (dmp -> outfile_raw) {
642                 error ("Raw output file already defined");
643                 out = false;
644         } else if (!(fp = fopen (outfile_raw, "rb"))) {         /** @todo Maybe we could not open file for permission problems */
645                 /* Raw output file does not exist, start from scratch */
646                 out = true;
647                 dmp -> start_sector_raw = 0;
648                 my_strdup (dmp -> outfile_raw, outfile_raw);
649         } else if (resume) {
650                 /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */
651                 my_fseek (fp, 0, SEEK_END);
652                 filesize = my_ftell (fp);
653                 fclose (fp);
654                 out = true;
655                 dmp -> start_sector_raw = (u_int32_t) (filesize / RAW_SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
656                 debug ("Raw output can restart from sector %u", dmp -> start_sector_raw);
657                 my_strdup (dmp -> outfile_raw, outfile_raw);
658         } else {
659                 /* Raw output file exists but resume was not requested, error */
660                 fclose (fp);
661                 error ("Raw output file exists, but resume was not requested.");
662                 out = false;
663                 dmp -> start_sector_raw = -1;
664                 my_free (dmp -> outfile_raw);
665                 dmp -> outfile_raw = NULL;
666         }
667
668         return (out);
669 }
670
671
672 bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume) {
673         bool out;
674         my_off_t filesize;
675         FILE *fp;
676
677         if (!outfile_iso) {
678                 /* ISO output disabled */
679                 out = true;
680                 dmp -> start_sector_iso = -1;
681                 my_free (dmp -> outfile_iso);
682                 dmp -> outfile_iso = NULL;
683         } else if (outfile_iso[0] == '\0') {
684                 /* Xbox/GDR-8050L auto-name placeholder; the copied reference path opens the final file. */
685                 out = true;
686                 dmp -> start_sector_iso = 0;
687                 my_strdup (dmp -> outfile_iso, outfile_iso);
688         } else if (dmp -> outfile_iso) {
689                 error ("ISO output file already defined");
690                 out = false;
691         } else if (!(fp = fopen (outfile_iso, "rb"))) {         /** @todo Maybe we could not open file for permission problems */
692                 /* Raw output file does not exist, start from scratch */
693                 out = true;
694                 dmp -> start_sector_iso = 0;
695                 my_strdup (dmp -> outfile_iso, outfile_iso);
696         } else if (resume) {
697                 /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */
698                 my_fseek (fp, 0, SEEK_END);
699                 filesize = my_ftell (fp);
700                 fclose (fp);
701                 out = true;
702                 dmp -> start_sector_iso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
703                 debug ("ISO output can restart from sector %u", dmp -> start_sector_iso);
704                 my_strdup (dmp -> outfile_iso, outfile_iso);
705         } else {
706                 /* Raw output file exists but resume was not requested, error */
707                 fclose (fp);
708                 error ("ISO output file exists, but resume was not requested.");
709                 out = false;
710                 dmp -> start_sector_iso = -1;
711                 my_free (dmp -> outfile_iso);
712                 dmp -> outfile_iso = NULL;
713         }
714
715         return (out);
716 }
717
718
719 bool dumper_set_xiso_output_file (dumper *dmp, char *outfile_xiso, bool resume) {
720         bool out;
721         my_off_t filesize;
722         FILE *fp;
723
724         if (!outfile_xiso) {
725                 out = true;
726                 dmp -> start_sector_xiso = -1;
727                 my_free (dmp -> outfile_xiso);
728                 dmp -> outfile_xiso = NULL;
729         } else if (outfile_xiso[0] == '\0') {
730                 /* Xbox/GDR-8050L auto-name placeholder; the copied reference path opens the final file. */
731                 out = true;
732                 dmp -> start_sector_xiso = 0;
733                 my_strdup (dmp -> outfile_xiso, outfile_xiso);
734         } else if (dmp -> outfile_xiso) {
735                 error ("XISO output file already defined");
736                 out = false;
737         } else if (!(fp = fopen (outfile_xiso, "rb"))) {
738                 out = true;
739                 dmp -> start_sector_xiso = 0;
740                 my_strdup (dmp -> outfile_xiso, outfile_xiso);
741         } else if (resume) {
742                 my_fseek (fp, 0, SEEK_END);
743                 filesize = my_ftell (fp);
744                 fclose (fp);
745                 out = true;
746                 dmp -> start_sector_xiso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
747                 debug ("XISO output can restart from output sector %u", dmp -> start_sector_xiso);
748                 my_strdup (dmp -> outfile_xiso, outfile_xiso);
749         } else {
750                 fclose (fp);
751                 error ("XISO output file exists, but resume was not requested.");
752                 out = false;
753                 dmp -> start_sector_xiso = -1;
754                 my_free (dmp -> outfile_xiso);
755                 dmp -> outfile_xiso = NULL;
756         }
757
758         return (out);
759 }
760
761
762 bool dumper_prepare_xiso (dumper *dmp) {
763         bool out;
764         u_int8_t buf[SECTOR_SIZE];
765         size_t r;
766         u_int32_t i;
767
768         if (!dmp -> outfile_xiso) {
769                 error ("No XISO output file defined");
770                 return false;
771         }
772
773         dmp -> start_sector = dmp -> start_sector_xiso;
774
775 #ifdef WIN32
776         if (dmp -> outfile_xiso[0] == '\0' && disc_is_xbox_challenge_drive (dmp -> dsk)) {
777                 if (dmp -> hashing)
778                         multihash_init (&(dmp -> hash_xiso));
779                 dmp -> fp_xiso = NULL;
780                 return true;
781         }
782 #endif
783
784         if (dmp -> hashing)
785                 multihash_init (&(dmp -> hash_xiso));
786
787         dmp -> fp_xiso = fopen (dmp -> outfile_xiso, "a+b");
788         if (!dmp -> fp_xiso)
789                 return false;
790
791         if (dmp -> hashing) {
792                 debug ("Calculating hashes for pre-existing XISO dump data");
793                 if (dmp -> start_sector > 0) {
794                         for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_xiso)) > 0; i++)
795                                 multihash_update (&(dmp -> hash_xiso), buf, SECTOR_SIZE);
796                         MY_ASSERT (r > 0);
797                 }
798         }
799
800         if (my_fseek (dmp -> fp_xiso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 &&
801             ftruncate (fileno (dmp -> fp_xiso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) {
802                 out = true;
803                 debug ("Writing to file \"%s\" in Xbox XISO format (fseeked() to %lld)", dmp -> outfile_xiso, my_ftell (dmp -> fp_xiso));
804         } else {
805                 out = false;
806                 fclose (dmp -> fp_xiso);
807                 dmp -> fp_xiso = NULL;
808         }
809
810         return (out);
811 }
812
813
814 bool dumper_prepare (dumper *dmp) {
815         bool out;
816         u_int8_t buf[RAW_SECTOR_SIZE];
817         size_t r;
818         u_int32_t i;
819
820         /* Outputting to both files, resume must start from the file with the least sectors. Hopefully they will have the same number of sectors, anyway... */
821         if (dmp -> outfile_raw && dmp -> outfile_iso && dmp -> start_sector_raw != dmp -> start_sector_iso) {
822                 if (dmp -> start_sector_raw < dmp -> start_sector_iso)
823                         dmp -> start_sector = dmp -> start_sector_raw;
824                 else
825                         dmp -> start_sector = dmp -> start_sector_iso;
826         } else if (dmp -> outfile_raw) {
827                 dmp -> start_sector = dmp -> start_sector_raw;
828         } else if (dmp -> outfile_iso) {
829                 dmp -> start_sector = dmp -> start_sector_iso;
830         } else {
831                 MY_ASSERT (0);
832         }
833
834 #ifdef WIN32
835         if (dmp -> outfile_iso && dmp -> outfile_iso[0] == '\0' && disc_is_xbox_challenge_drive (dmp -> dsk)) {
836                 if (dmp -> hashing) {
837                         multihash_init (&(dmp -> hash_raw));
838                         multihash_init (&(dmp -> hash_iso));
839                 }
840                 dmp -> fp_raw = NULL;
841                 dmp -> fp_iso = NULL;
842                 return true;
843         }
844 #endif
845
846         /* Prepare hashes */
847         if (dmp -> hashing) {
848                 multihash_init (&(dmp -> hash_raw));
849                 multihash_init (&(dmp -> hash_iso));
850         }
851
852         /* Setup raw output file */
853         if (dmp -> outfile_raw) {
854                 dmp -> fp_raw = fopen (dmp -> outfile_raw, "a+b");
855
856                 if (dmp -> hashing) {
857                         debug ("Calculating hashes for pre-existing raw dump data");
858                         if (dmp -> start_sector > 0) {
859                                 for (i = 0; i < dmp -> start_sector && (r = fread (buf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw)) > 0; i++)
860                                         multihash_update (&(dmp -> hash_raw), buf, RAW_SECTOR_SIZE);
861                                 MY_ASSERT (r > 0);
862                         }
863                 }
864
865                 /* Now call fseek as file will only be written, from now on */
866                 if (my_fseek (dmp -> fp_raw, dmp -> start_sector * RAW_SECTOR_SIZE, SEEK_SET) == 0 &&
867                     ftruncate (fileno (dmp -> fp_raw), (int64_t) dmp -> start_sector * RAW_SECTOR_SIZE) == 0) {
868                         out = true;
869                         debug ("Writing to file \"%s\" in raw format (fseeked() to %lld)", dmp -> outfile_raw, my_ftell (dmp -> fp_raw));
870                 } else {
871                         out = false;
872                         fclose (dmp -> fp_raw);
873                         dmp -> fp_raw = NULL;
874                 }
875         } else {
876                 dmp -> fp_raw = NULL;
877         }
878
879         /* Setup ISO output file */
880         if (dmp -> outfile_iso) {
881                 dmp -> fp_iso = fopen (dmp -> outfile_iso, "a+b");
882
883                 if (dmp -> hashing) {
884                         debug ("Calculating hashes for pre-existing ISO dump data");
885                         if (dmp -> start_sector > 0) {
886                                 for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_iso)) > 0; i++)
887                                         multihash_update (&(dmp -> hash_iso), buf, SECTOR_SIZE);
888                                 MY_ASSERT (r > 0);
889                         }
890                 }
891
892                 if (my_fseek (dmp -> fp_iso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 &&
893                     ftruncate (fileno (dmp -> fp_iso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) {
894                         out = true;
895                         debug ("Writing to file \"%s\" in ISO format (fseeked() to %lld)", dmp -> outfile_iso, my_ftell (dmp -> fp_iso));
896                 } else {
897                         out = false;
898                         fclose (dmp -> fp_iso);
899                         dmp -> fp_iso = NULL;
900                 }
901         } else {
902                 dmp -> fp_iso = NULL;
903         }
904
905         return (out);
906 }
907
908
909
910 static int dumper_dump_xbox_redump_iso (dumper *dmp, u_int32_t *current_sector) {
911         xbox_redump_metadata meta;
912         u_int8_t *video_l1 = NULL;
913         u_int32_t locked_sectors = 0, unlocked_sectors = 0, sector_size = 0;
914         u_int32_t game_source_lba = 0xFFFFFFFFU;
915         u_int32_t root_lba = 0, volume_size_raw = 0;
916         u_int32_t pregame_padding = XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS;
917         u_int32_t postgame_padding = XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA - (XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS);
918         bool out = false;
919         bool locked_view_is_xdfs;
920         disc_type type_id;
921         char *type = NULL;
922
923         if (!dmp)
924                 return false;
925
926         disc_get_type (dmp -> dsk, &type_id, &type);
927         if (type_id != DISC_TYPE_XBOX)
928                 return false;
929
930         if (dmp -> fp_raw) {
931                 error ("Xbox redump-style ISO output cannot be combined with -r/raw output in one run");
932                 return false;
933         }
934
935         if (dmp -> start_sector != 0) {
936                 error ("Resume is not supported for Xbox redump-style ISO reconstruction because the drive view changes mid-dump");
937                 return false;
938         }
939
940 #ifdef WIN32
941         if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
942                 int ref_status;
943                 if (dmp -> fp_iso) {
944                         fclose (dmp -> fp_iso);
945                         dmp -> fp_iso = NULL;
946                 }
947                 if (current_sector)
948                         *current_sector = 0;
949                 xbox_ref_log_fprintf (stderr, "[XBOX] Dispatching GDR-8050L -i directly to copied original dumper code using FriiDump's existing drive HANDLE.\n");
950                 ref_status = xbox_ref_gdr8050l_dump_with_handle (disc_get_native_handle (dmp -> dsk), disc_get_device (dmp -> dsk), dmp -> outfile_iso, '1', &(dmp -> xbox_ref_result));
951                 return ref_status == 0;
952         }
953 #endif
954
955         if (!dmp -> fp_iso)
956                 return false;
957
958         memset (&meta, 0, sizeof (meta));
959
960         /* Original GDR-8050L dumper order for redump-style output:
961          *   1. unlock/authenticate into the game view;
962          *   2. read XDVDFS/game metadata while that view is available;
963          *   3. perform a real media transition to return to the visible DVD-video view;
964          *   4. dump video/front/tail, unlock again, then dump game data.
965          *
966          * The video partition is not encrypted/locked; "lock" here only means the
967          * drive-visible DVD-video state before the Xbox game-view unlock. */
968         if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
969                 xbox_ref_log_fprintf (stderr, "[XBOX] GDR-8050L flow follows original main.c/DumpXboxGameDisc option 1.\n");
970                 xbox_ref_log_fprintf (stderr, "[XBOX] Stage 1/6: primary UnlockDrive() handshake.\n");
971                 disc_xbox_wait_ready (dmp -> dsk, 30000);
972                 if (disc_xbox_unlock (dmp -> dsk) < 0)
973                         xbox_ref_log_fprintf (stderr, "[XBOX][WARN] Stage 1 UnlockDrive() returned failure; continuing like the reference dumper and letting later reads prove state.\n");
974                 xbox_ref_log_fprintf (stderr, "[XBOX] Stage 2/6: media cycle after primary handshake.\n");
975                 if (disc_xbox_media_cycle (dmp -> dsk) < 0) {
976                         xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Stage 2 failed: required media-cycle failed.\n");
977                         return false;
978                 }
979                 xbox_ref_log_fprintf (stderr, "[XBOX] Stage 3/6: re-apply UnlockDrive() after media change.\n");
980                 if (disc_xbox_unlock (dmp -> dsk) < 0)
981                         xbox_ref_log_fprintf (stderr, "[XBOX][WARN] Stage 3 UnlockDrive() returned failure; continuing like the reference dumper and letting GetXboxGameInfo/XDVDFS probe prove state.\n");
982
983                 xbox_ref_log_fprintf (stderr, "[XBOX] Stage 4/6: RefreshVolume, settle, EnsureDriveReady, SetDriveSpeedMax, and volume lock before metadata probe.\n");
984                 disc_xbox_refresh_volume (dmp -> dsk);
985                 disc_xbox_wait_ready (dmp -> dsk, 30000);
986                 disc_set_speed (dmp -> dsk, 0xFFFF);
987                 if (disc_xbox_lock_volume (dmp -> dsk) < 0)
988                         xbox_ref_log_fprintf (stderr, "[XBOX][WARN] FSCTL_LOCK_VOLUME equivalent failed; continuing like reference dumper warning path.\n");
989                 disc_xbox_read_capacity_10 (dmp -> dsk, &unlocked_sectors, &sector_size);
990                 meta.unlocked_visible_sectors = unlocked_sectors;
991                 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
992                 if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw)) {
993                         meta.game_source_lba = XBOX_XISO_STANDARD_GAME_LBA;
994                         meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
995                 } else if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw)) {
996                         meta.game_source_lba = XBOX_XISO_START_LBA_MAGIC;
997                         meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
998                 }
999
1000                 xbox_ref_log_fprintf (stderr, "[XBOX] Stage 5/6: media cycle back to visible DVD-video view before sector 0 capture.\n");
1001                 if (disc_xbox_media_cycle (dmp -> dsk) < 0) {
1002                         xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Stage 5 failed: could not restore visible DVD-video view.\n");
1003                         return false;
1004                 }
1005                 disc_xbox_wait_ready (dmp -> dsk, 45000);
1006         } else if (disc_is_xbox_vendor_unlock_drive (dmp -> dsk)) {
1007                 /* GDR-3120L/Kreon-style FF 08 01 profiles can explicitly select
1008                  * state 0 for the visible DVD-video view. */
1009                 if (disc_xbox_lock (dmp -> dsk) < 0)
1010                         warning ("Could not set Xbox vendor lock state 0; continuing with the current visible view");
1011         }
1012
1013         disc_xbox_read_capacity_10 (dmp -> dsk, &locked_sectors, &sector_size);
1014         meta.locked_visible_sectors = locked_sectors;
1015         xbox_capture_pfi_dmi (dmp -> dsk, &meta);
1016
1017         locked_view_is_xdfs = xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, NULL, NULL);
1018         if (locked_view_is_xdfs && disc_is_xbox_challenge_drive (dmp -> dsk)) {
1019                 error ("Current GDR-8050L view still exposes XDVDFS at LBA 32 after media cycle; cannot capture visible DVD-video view for redump-style ISO");
1020                 return false;
1021         }
1022
1023         if (!xbox_redump_layout_constants_match_dic ()) {
1024                 error ("Xbox redump layout constants do not match the DiscImageCreator-compatible XGD1 layout");
1025                 return false;
1026         }
1027
1028         meta.output_sectors = XBOX_XGD1_FULL_REDUMP_SECTORS;
1029         if (dmp -> progress)
1030                 dmp -> progress (true, 0, meta.output_sectors, dmp -> progress_data);
1031
1032         debug ("Xbox redump-style layout: video L0=%u, pregame padding=%u, game view=%u, postgame padding=%u, video L1=%u",
1033                 XBOX_XGD1_VIDEO_L0_SECTORS, pregame_padding, XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS, postgame_padding, XBOX_XGD1_VIDEO_L1_SECTORS);
1034
1035         video_l1 = (u_int8_t *) malloc ((size_t) XBOX_XGD1_VIDEO_L1_SECTORS * SECTOR_SIZE);
1036         if (!video_l1) {
1037                 error ("Could not allocate Xbox VIDEO-L1 capture buffer");
1038                 return false;
1039         }
1040
1041         /* Capture the tail of the visible DVD-video view before switching to the game view. */
1042         if (!xbox_read_range_to_memory (dmp -> dsk, XBOX_XGD1_VIDEO_L0_SECTORS, XBOX_XGD1_VIDEO_L1_SECTORS,
1043                         video_l1, (size_t) XBOX_XGD1_VIDEO_L1_SECTORS * SECTOR_SIZE, "VIDEO-L1", false))
1044                 goto cleanup;
1045
1046         if (!xbox_dump_read10_iso_range (dmp, 0, XBOX_XGD1_VIDEO_L0_SECTORS, 0, meta.output_sectors, "VIDEO-L0", false, current_sector))
1047                 goto cleanup;
1048
1049         if (current_sector) *current_sector = XBOX_XGD1_VIDEO_L0_SECTORS;
1050         if (!xbox_write_zero_iso_range (dmp, pregame_padding, XBOX_XGD1_VIDEO_L0_SECTORS, meta.output_sectors, "PREGAME-PADDING"))
1051                 goto cleanup;
1052
1053         xbox_ref_log_fprintf (stderr, "[XBOX] Stage 6/6: re-apply UnlockDrive() for unlocked game/XDVDFS data.\n");
1054         if (disc_xbox_unlock (dmp -> dsk) < 0)
1055                 xbox_ref_log_fprintf (stderr, "[XBOX][WARN] Stage 6 UnlockDrive() returned failure; continuing until the XDVDFS probe/read proves failure.\n");
1056         disc_xbox_refresh_volume (dmp -> dsk);
1057         disc_xbox_wait_ready (dmp -> dsk, 30000);
1058         disc_set_speed (dmp -> dsk, 0xFFFF);
1059         disc_xbox_read_capacity_10 (dmp -> dsk, &unlocked_sectors, &sector_size);
1060         meta.unlocked_visible_sectors = unlocked_sectors;
1061         xbox_capture_pfi_dmi (dmp -> dsk, &meta);
1062
1063         if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw))
1064                 game_source_lba = XBOX_XISO_STANDARD_GAME_LBA;
1065         else if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw))
1066                 game_source_lba = XBOX_XISO_START_LBA_MAGIC;
1067         else {
1068                 error ("Could not locate Xbox XDVDFS header after unlock at LBA %u or %u", XBOX_XISO_STANDARD_GAME_LBA, XBOX_XISO_START_LBA_MAGIC);
1069                 goto cleanup;
1070         }
1071
1072         meta.game_source_lba = game_source_lba;
1073         meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
1074         if (game_source_lba != XBOX_XISO_STANDARD_GAME_LBA && disc_is_xbox_challenge_drive (dmp -> dsk))
1075                 warning ("Xbox XDVDFS was detected at LBA %u, not the expected GDR-8050L game-view LBA 32", game_source_lba);
1076
1077         /* FriiDump 0.5.3.13 cache-aligned raw-ID validation proved that the
1078          * unlocked GDR-8050L view exposes source LBA 0..31 as the physical
1079          * game-region lead-in immediately preceding the XDVDFS header at LBA 32.
1080          * Read those sectors from the drive; retain the existing per-sector
1081          * zero-fill fallback only for genuinely unreadable sectors. */
1082         xbox_ref_log_fprintf (stderr,
1083                 "[XBOX] Capturing 32-sector game lead-in from unlocked source LBA %u..%u.\n",
1084                 (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0,
1085                 (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - 1 : XBOX_XISO_LEADIN_SECTORS - 1);
1086         if (!xbox_write_leadin_iso_range (dmp,
1087                         (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0,
1088                         XBOX_XGD1_GAME_OUTPUT_START_LBA, meta.output_sectors, &meta, "redump game", true, current_sector))
1089                 goto cleanup;
1090
1091         if (!xbox_dump_read10_iso_range (dmp, game_source_lba, XBOX_XGD1_GAME_SOURCE_SECTORS,
1092                         XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XISO_LEADIN_SECTORS, meta.output_sectors, "GAME-XDVDFS", true, current_sector))
1093                 goto cleanup;
1094
1095         if (current_sector) *current_sector = XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS;
1096         if (!xbox_write_zero_iso_range (dmp, postgame_padding, XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS,
1097                         meta.output_sectors, "POSTGAME-PADDING"))
1098                 goto cleanup;
1099
1100         if (!xbox_write_iso_sectors (dmp, video_l1, XBOX_XGD1_VIDEO_L1_SECTORS, XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA, meta.output_sectors))
1101                 goto cleanup;
1102
1103         if (dmp -> hashing)
1104                 multihash_finish (&(dmp -> hash_iso));
1105         if (dmp -> fp_iso) {
1106                 fclose (dmp -> fp_iso);
1107                 dmp -> fp_iso = NULL;
1108         }
1109
1110         xbox_write_redump_metadata (dmp, &meta);
1111         out = true;
1112
1113 cleanup:
1114         if (video_l1)
1115                 free (video_l1);
1116         /* Keep current_sector at the last phase-specific LBA on failure so the
1117          * CLI can report a useful range instead of collapsing every setup failure
1118          * back to 0..15. */
1119         return out;
1120 }
1121
1122 int dumper_dump (dumper *dmp, u_int32_t *current_sector) {
1123         bool out;
1124         u_int8_t *rawbuf, *isobuf;
1125         u_int32_t i, sectors_no, last_sector;
1126         disc_type type_id;
1127         char *type_string = NULL;
1128 #ifdef DEBUGaa
1129         bool no_unscrambling;
1130 #endif
1131
1132 #ifdef DEBUGaa
1133         no_unscrambling = dd -> no_unscrambling;
1134         if (fp_iso && no_unscrambling) {
1135                 warning ("Output to ISO format requested, ignoring no_unscrambling!");
1136                 no_unscrambling = false;
1137         }
1138 #endif
1139
1140         disc_get_type (dmp -> dsk, &type_id, &type_string);
1141         if (type_id == DISC_TYPE_XBOX && dmp -> fp_iso)
1142                 return dumper_dump_xbox_redump_iso (dmp, current_sector);
1143
1144         sectors_no = disc_get_sectors_no (dmp -> dsk);
1145 #if 0
1146         if (dd -> start_sector != -1) {
1147                 if (dd -> start_sector >= sectors_no) {
1148                         error ("Cannot start dumping from sector %u as the inserted disc only has %u sectors\n", dd -> start_sector, sectors_no);
1149                         out = false;
1150                 } else {
1151                         warning ("Start sector forced to %u\n", dd -> start_sector);
1152                         ss = dd -> start_sector;
1153
1154                         if (fp_iso)
1155                                 fseek (fp_iso, ss * 2048, SEEK_SET);
1156                         if (fp_raw)
1157                                 fseek (fp_raw, ss * 2064, SEEK_SET);
1158                 }
1159         }
1160 #endif
1161         
1162         if (true) {
1163                 debug ("Starting dump process from sector %u...\n", dmp -> start_sector);
1164
1165                 /* First call to progress function */
1166                 if (dmp -> progress)
1167                         dmp -> progress (true, dmp -> start_sector, sectors_no, dmp -> progress_data);
1168
1169                 last_sector=sectors_no-1;
1170                 
1171                 for (i = dmp -> start_sector, out = true; i < sectors_no && out; i++) {
1172                         if (!dumper_read_sector_with_retry (dmp, i, &isobuf, &rawbuf)) {
1173                                 error ("Read failed after dump-level retries at sectors %u..%u", i, i + SECTORS_PER_BLOCK - 1);
1174                                 out = false;
1175                                 *(current_sector) = i;
1176                         }
1177
1178                         if (out && dmp -> fp_raw) {
1179                                 clearerr (dmp -> fp_raw);
1180
1181                                 if (!rawbuf) {
1182                                         error ("NULL buffer");
1183                                         out = false;
1184                                         *(current_sector) = i;
1185                                 }
1186                                 else fwrite (rawbuf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw);
1187                                 if (ferror (dmp -> fp_raw)) {
1188                                         error ("fwrite() to raw output file failed");
1189                                         out = false;
1190                                         *(current_sector) = i;
1191                                 }
1192
1193                                 if (dmp -> flushing)
1194                                         fflush (dmp -> fp_raw);
1195
1196                                 if (dmp -> hashing && out)
1197                                         multihash_update (&(dmp -> hash_raw), rawbuf, RAW_SECTOR_SIZE);
1198                         }
1199
1200                         if (out && dmp -> fp_iso) {
1201                                 clearerr (dmp -> fp_iso);
1202
1203                                 if (!isobuf) {
1204                                         error ("NULL buffer");
1205                                         out = false;
1206                                         *(current_sector) = i;
1207                                 }
1208                                 else fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_iso);
1209
1210                                 if (ferror (dmp -> fp_iso)) {
1211                                         error ("fwrite() to ISO output file failed");
1212                                         out = false;
1213                                         *(current_sector) = i;
1214                                 }
1215
1216                                 if (dmp -> flushing)
1217                                         fflush (dmp -> fp_iso);
1218
1219                                 if (dmp -> hashing && out)
1220                                         multihash_update (&(dmp -> hash_iso), isobuf, SECTOR_SIZE);
1221                         }
1222
1223                         if ((i % 320 == 0) || (i == last_sector)) { //speedhack
1224                                 if (dmp -> progress)
1225                                         dmp -> progress (false, i + 1, sectors_no, dmp -> progress_data);               /* i + 1 'cause sectors range from 0 to N */
1226                         }
1227                 }
1228
1229                 if (dmp -> hashing) {
1230                         multihash_finish (&(dmp -> hash_raw));
1231                         multihash_finish (&(dmp -> hash_iso));
1232                 }
1233
1234                 if (dmp -> fp_raw)
1235                         fclose (dmp -> fp_raw);
1236                 if (dmp -> fp_iso)
1237                         fclose (dmp -> fp_iso);
1238                 if (out) {
1239
1240
1241                 }
1242         }
1243
1244         return (out);
1245 }
1246
1247
1248 int dumper_dump_xiso (dumper *dmp, u_int32_t *current_sector) {
1249         bool out;
1250         u_int8_t *rawbuf, *isobuf;
1251         u_int8_t sector_buf[SECTOR_SIZE];
1252         u_int32_t source_lba, source_sectors, target_sectors, i, source_sector, last_sector, leadin_source_lba;
1253         bool leadin_fallback_warned;
1254
1255         if (!dmp) {
1256                 error ("XISO dumper is not prepared");
1257                 return false;
1258         }
1259
1260 #ifdef WIN32
1261         if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1262                 int ref_status;
1263                 if (dmp -> fp_xiso) {
1264                         fclose (dmp -> fp_xiso);
1265                         dmp -> fp_xiso = NULL;
1266                 }
1267                 if (current_sector)
1268                         *current_sector = 0;
1269                 xbox_ref_log_fprintf (stderr, "[XBOX] Dispatching GDR-8050L -X directly to copied original dumper code using FriiDump's existing drive HANDLE.\n");
1270                 ref_status = xbox_ref_gdr8050l_dump_with_handle (disc_get_native_handle (dmp -> dsk), disc_get_device (dmp -> dsk), dmp -> outfile_xiso, '2', &(dmp -> xbox_ref_result));
1271                 return ref_status == 0;
1272         }
1273 #endif
1274
1275         if (!dmp -> fp_xiso) {
1276                 error ("XISO dumper is not prepared");
1277                 return false;
1278         }
1279
1280         if (!xbox_get_xiso_range (dmp -> dsk, &source_lba, &source_sectors, &target_sectors)) {
1281                 if (current_sector)
1282                         *current_sector = dmp -> start_sector;
1283                 return false;
1284         }
1285
1286         if (dmp -> start_sector >= target_sectors) {
1287                 error ("Cannot resume Xbox XISO at output sector %u; target has only %u sectors", dmp -> start_sector, target_sectors);
1288                 if (current_sector)
1289                         *current_sector = dmp -> start_sector;
1290                 return false;
1291         }
1292
1293         memset (sector_buf, 0, sizeof (sector_buf));
1294         leadin_source_lba = (source_lba >= XBOX_XISO_LEADIN_SECTORS) ? (source_lba - XBOX_XISO_LEADIN_SECTORS) : 0;
1295         leadin_fallback_warned = false;
1296         out = true;
1297         last_sector = target_sectors - 1;
1298
1299         debug ("Starting Xbox XISO dump from output sector %u...", dmp -> start_sector);
1300         debug ("Xbox XISO lead-in source LBA %u..%u will be attempted before zero-fill fallback", leadin_source_lba, leadin_source_lba + XBOX_XISO_LEADIN_SECTORS - 1);
1301         if (dmp -> progress)
1302                 dmp -> progress (true, dmp -> start_sector, target_sectors, dmp -> progress_data);
1303
1304         for (i = dmp -> start_sector; i < target_sectors && out; i++) {
1305                 if (i < XBOX_XISO_LEADIN_SECTORS) {
1306                         isobuf = NULL;
1307                         rawbuf = NULL;
1308                         if (disc_xbox_read_10 (dmp -> dsk, leadin_source_lba + i, 1, sector_buf, sizeof (sector_buf)) >= 0) {
1309                                 isobuf = sector_buf;
1310                         } else {
1311                                 if (!leadin_fallback_warned) {
1312                                         warning ("Drive could not read one or more Xbox XISO lead-in sectors; using zero-fill fallback for unreadable lead-in sectors");
1313                                         leadin_fallback_warned = true;
1314                                 }
1315                                 memset (sector_buf, 0, sizeof (sector_buf));
1316                                 isobuf = sector_buf;
1317                         }
1318
1319                         clearerr (dmp -> fp_xiso);
1320                         fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_xiso);
1321                         if (ferror (dmp -> fp_xiso)) {
1322                                 error ("fwrite() to XISO output file failed");
1323                                 out = false;
1324                                 if (current_sector)
1325                                         *current_sector = i;
1326                         }
1327
1328                         if (dmp -> hashing && out)
1329                                 multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1330                 } else {
1331                         source_sector = source_lba + (i - XBOX_XISO_LEADIN_SECTORS);
1332                         isobuf = sector_buf;
1333                         rawbuf = NULL;
1334                         if (disc_xbox_read_10 (dmp -> dsk, source_sector, 1, sector_buf, sizeof (sector_buf)) < 0) {
1335                                 error ("Xbox XISO READ(10) failed at source LBA %u", source_sector);
1336                                 out = false;
1337                                 if (current_sector)
1338                                         *current_sector = i;
1339                         } else {
1340                                 clearerr (dmp -> fp_xiso);
1341                                 fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_xiso);
1342                                 if (ferror (dmp -> fp_xiso)) {
1343                                         error ("fwrite() to XISO output file failed");
1344                                         out = false;
1345                                         if (current_sector)
1346                                                 *current_sector = i;
1347                                 }
1348
1349                                 if (dmp -> hashing && out)
1350                                         multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1351                         }
1352                 }
1353
1354                 if (dmp -> flushing)
1355                         fflush (dmp -> fp_xiso);
1356
1357                 if ((i % 320 == 0) || (i == last_sector)) {
1358                         if (dmp -> progress)
1359                                 dmp -> progress (false, i + 1, target_sectors, dmp -> progress_data);
1360                 }
1361         }
1362
1363         if (dmp -> hashing)
1364                 multihash_finish (&(dmp -> hash_xiso));
1365
1366         if (dmp -> fp_xiso) {
1367                 fclose (dmp -> fp_xiso);
1368                 dmp -> fp_xiso = NULL;
1369         }
1370
1371         return (out);
1372 }
1373
1374
1375 dumper *dumper_new (disc *d) {
1376         dumper *dmp;
1377
1378         dmp = (dumper *) malloc (sizeof (dumper));
1379         memset (dmp, 0, sizeof (dumper));
1380         dmp -> dsk = d;
1381         xbox_ref_dump_result_init (&(dmp -> xbox_ref_result));
1382         dumper_set_hashing (dmp, true);
1383         dumper_set_flushing (dmp, true);
1384
1385         return (dmp);
1386 }
1387
1388
1389 void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data) {
1390         dmp -> progress = progress;
1391         dmp -> progress_data = progress_data;
1392
1393         return;
1394 }
1395
1396
1397 void dumper_set_hashing (dumper *dmp, bool h) {
1398         dmp -> hashing = h;
1399         debug ("Hashing %s", h ? "enabled" : "disabled");
1400
1401         return;
1402 }
1403
1404
1405 void dumper_set_flushing (dumper *dmp, bool f) {
1406         dmp -> flushing = f;
1407         debug ("Flushing %s", f ? "enabled" : "disabled");
1408
1409         return;
1410 }
1411
1412 void *dumper_destroy (dumper *dmp) {
1413         my_free (dmp -> outfile_raw);
1414         my_free (dmp -> outfile_iso);
1415         my_free (dmp -> outfile_xiso);
1416         my_free (dmp);
1417
1418         return (NULL);
1419 }
1420
1421
1422 char *dumper_get_iso_crc32 (dumper *dmp) {
1423         return ((dmp -> hash_iso).crc32_s);
1424 }
1425
1426
1427 char *dumper_get_raw_crc32 (dumper *dmp) {
1428         return ((dmp -> hash_raw).crc32_s);
1429 }
1430
1431 char *dumper_get_xiso_crc32 (dumper *dmp) {
1432         return ((dmp -> hash_xiso).crc32_s);
1433 }
1434
1435
1436 char *dumper_get_iso_md4 (dumper *dmp) {
1437         return ((dmp -> hash_iso).md4_s);
1438 }
1439
1440
1441 char *dumper_get_raw_md4 (dumper *dmp) {
1442         return ((dmp -> hash_raw).md4_s);
1443 }
1444
1445 char *dumper_get_xiso_md4 (dumper *dmp) {
1446         return ((dmp -> hash_xiso).md4_s);
1447 }
1448
1449
1450 char *dumper_get_iso_md5 (dumper *dmp) {
1451         return ((dmp -> hash_iso).md5_s);
1452 }
1453
1454
1455 char *dumper_get_raw_md5 (dumper *dmp) {
1456         return ((dmp -> hash_raw).md5_s);
1457 }
1458
1459 char *dumper_get_xiso_md5 (dumper *dmp) {
1460         return ((dmp -> hash_xiso).md5_s);
1461 }
1462
1463
1464 char *dumper_get_iso_ed2k (dumper *dmp) {
1465         return ((dmp -> hash_iso).ed2k_s);
1466 }
1467
1468
1469 char *dumper_get_raw_ed2k (dumper *dmp) {
1470         return ((dmp -> hash_raw).ed2k_s);
1471 }
1472
1473
1474 char *dumper_get_iso_sha1 (dumper *dmp) {
1475         return ((dmp -> hash_iso).sha1_s);
1476 }
1477
1478
1479 char *dumper_get_raw_sha1 (dumper *dmp) {
1480         return ((dmp -> hash_raw).sha1_s);
1481 }
1482
1483 char *dumper_get_xiso_sha1 (dumper *dmp) {
1484         return ((dmp -> hash_xiso).sha1_s);
1485 }
1486
1487
1488 char *dumper_get_iso_sha2 (dumper *dmp) {
1489         return ((dmp -> hash_iso).sha2_s);
1490 }
1491
1492
1493 char *dumper_get_raw_sha2 (dumper *dmp) {
1494         return ((dmp -> hash_raw).sha2_s);
1495 }
1496
1497 char *dumper_get_xiso_sha2 (dumper *dmp) {
1498         return ((dmp -> hash_xiso).sha2_s);
1499 }
1500
1501
1502 bool dumper_get_xbox_reference_result (dumper *dmp, xbox_ref_dump_result *result) {
1503         if (!dmp || !result || !(dmp -> xbox_ref_result).attempted)
1504                 return false;
1505         *result = dmp -> xbox_ref_result;
1506         return true;
1507 }