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