1 /***************************************************************************
2 * Copyright (C) 2007 by Arep *
3 * Support is provided through the forums at *
4 * http://www.console-tribe.com *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20 ***************************************************************************/
27 #include <multihash.h>
28 #include "constants.h"
31 #include "xbox_ref_bridge.h"
32 #include "xbox_ref/xbox_ref_log.h"
36 #include <sys/types.h>
42 u_int32_t start_sector_raw;
45 u_int32_t start_sector_iso;
48 u_int32_t start_sector_xiso;
50 u_int32_t start_sector;
57 xbox_ref_dump_result xbox_ref_result;
59 progress_func progress;
63 #define DUMPER_READ_BLOCK_RETRIES 3
65 static bool dumper_read_sector_with_retry (dumper *dmp, u_int32_t sector_no, u_int8_t **isobuf, u_int8_t **rawbuf) {
67 bool need_iso, need_raw;
69 if (!dmp || !dmp -> dsk)
72 need_iso = dmp -> fp_iso != NULL;
73 need_raw = dmp -> fp_raw != NULL;
75 for (attempt = 0; attempt < DUMPER_READ_BLOCK_RETRIES; attempt++) {
76 if (isobuf) *isobuf = NULL;
77 if (rawbuf) *rawbuf = NULL;
79 disc_read_sector (dmp -> dsk, sector_no, isobuf, rawbuf);
80 if ((!need_iso || (isobuf && *isobuf)) && (!need_raw || (rawbuf && *rawbuf))) {
82 warning ("Recovered sectors %u..%u after dump-level retry %d", sector_no, sector_no + SECTORS_PER_BLOCK - 1, attempt);
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);
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
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.
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
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);
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];
125 memset (sector, 0, sizeof (sector));
126 if (disc_xbox_read_10 (d, lba, 1, sector, sizeof (sector)) < 0)
129 if (memcmp (sector, "MICROSOFT", 9) != 0)
133 *root_lba = read_le32 (sector + 0x14);
135 *volume_size_raw = read_le32 (sector + 0x1C);
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;
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;
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;
177 if (!out || out_size == 0)
180 if (!filename || !suffix)
183 suffix_len = strlen (suffix);
184 if (out_size <= suffix_len + 1)
187 slash1 = strrchr (filename, '/');
188 slash2 = strrchr (filename, '\\');
189 if (slash1 && slash2)
190 slash = slash1 > slash2 ? slash1 : slash2;
192 slash = slash1 ? slash1 : slash2;
194 dot = strrchr (filename, '.');
195 if (!dot || (slash && dot < slash))
196 dot = filename + strlen (filename);
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);
206 static const char *xbox_path_leaf (const char *path) {
207 const char *slash1, *slash2;
210 slash1 = strrchr (path, '/');
211 slash2 = strrchr (path, '\\');
212 if (slash1 && slash2)
213 return (slash1 > slash2 ? slash1 : slash2) + 1;
221 static void xbox_json_string (FILE *f, const char *s) {
225 unsigned char c = (unsigned char) *s++;
226 if (c == '"' || c == '\\') {
229 } else if (c == '\n') {
231 } else if (c == '\r') {
233 } else if (c == '\t') {
235 } else if (c < 0x20) {
236 fprintf (f, "\\u%04x", c);
245 static bool xbox_write_binary_file (const char *path, const u_int8_t *data, size_t len) {
247 if (!path || !data || len == 0)
249 f = fopen (path, "wb");
252 if (fwrite (data, 1, len, f) != len) {
256 return fclose (f) == 0;
259 static void xbox_capture_pfi_dmi (disc *d, xbox_redump_metadata *meta) {
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;
268 static void xbox_write_redump_metadata (dumper *dmp, xbox_redump_metadata *meta) {
270 const char *pfi_leaf;
271 const char *dmi_leaf;
273 if (!dmp || !meta || !dmp -> outfile_iso)
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));
281 meta -> pfi_written = xbox_write_binary_file (meta -> pfi_path, meta -> pfi, sizeof (meta -> pfi));
283 meta -> dmi_written = xbox_write_binary_file (meta -> dmi_path, meta -> dmi, sizeof (meta -> dmi));
285 json = fopen (meta -> json_path, "wb");
287 warning ("Could not write Xbox redump metadata JSON");
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;
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");
366 if (meta -> pfi_written) {
367 debug ("Wrote Xbox PFI: %s", meta -> pfi_path);
369 warning ("Xbox PFI was not captured or could not be written");
371 if (meta -> dmi_written) {
372 debug ("Wrote Xbox DMI: %s", meta -> dmi_path);
374 warning ("Xbox DMI was not captured or could not be written");
376 debug ("Wrote Xbox redump metadata: %s", meta -> json_path);
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)
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);
388 multihash_update (&(dmp -> hash_iso), (u_int8_t *) buf, (size_t) sectors * SECTOR_SIZE);
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);
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];
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))
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];
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;
422 if (todo > SECTORS_PER_BLOCK)
423 todo = SECTORS_PER_BLOCK;
425 *current_sector = output_lba + done;
426 for (retry = 0; retry < 3 && !ok; retry++) {
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)
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. */
438 bool singles_ok = true;
439 for (i = 0; i < todo; i++) {
441 for (retry = 0; retry < 3 && !one_ok; retry++) {
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)
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);
456 memcpy (buf + ((size_t) i * SECTOR_SIZE), one, SECTOR_SIZE);
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);
467 if (!xbox_write_iso_sectors (dmp, buf, todo, output_lba + done, total_sectors))
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];
477 if (!d || !out || out_size < (size_t) sectors * SECTOR_SIZE)
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;
485 if (todo > SECTORS_PER_BLOCK)
486 todo = SECTORS_PER_BLOCK;
487 for (retry = 0; retry < 3 && !ok; retry++) {
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)
493 if (!ok && todo > 1) {
495 bool singles_ok = true;
496 for (i = 0; i < todo; i++) {
498 for (retry = 0; retry < 3 && !one_ok; retry++) {
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)
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);
511 memcpy (out + ((size_t) (done + i) * SECTOR_SIZE), one, SECTOR_SIZE);
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);
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];
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;
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) {
540 warning ("Drive could not read one or more Xbox lead-in sectors; zero-filling only unreadable lead-in sectors");
544 if (meta) meta -> game_leadin_zero_sectors++;
546 if (meta) meta -> game_leadin_read_sectors++;
548 if (!xbox_write_iso_sectors (dmp, src, 1, output_lba + i, total_sectors))
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;
557 if (!d || !source_lba || !source_sectors || !target_sectors)
560 if (disc_xbox_unlock (d) < 0) {
561 error ("Could not switch Xbox drive into the game/XDVDFS view");
565 total_sectors = disc_get_sectors_no (d);
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.
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;
578 error ("No Xbox XDVDFS volume found at LBA %u or %u", XBOX_XISO_START_LBA_MAGIC, XBOX_XISO_STANDARD_GAME_LBA);
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.
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);
591 *source_sectors = XBOX_XISO_DUAL_LAYER_END_LBA - *source_lba;
593 /* For single-layer/homebrew-style media, XDVDFS VolumeSize is a
594 * byte count, so convert it to 2048-byte sectors.
596 if (volume_size_raw == 0) {
597 error ("Xbox XDVDFS header reported a zero volume size");
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);
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;
613 *source_sectors = volume_sectors;
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);
623 * Tries to open the output file for writing and to find out if it contains valid data so that the dump can continue.
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).
630 bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume) {
636 /* Raw output disabled */
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");
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 */
647 dmp -> start_sector_raw = 0;
648 my_strdup (dmp -> outfile_raw, outfile_raw);
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);
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);
659 /* Raw output file exists but resume was not requested, error */
661 error ("Raw output file exists, but resume was not requested.");
663 dmp -> start_sector_raw = -1;
664 my_free (dmp -> outfile_raw);
665 dmp -> outfile_raw = NULL;
672 bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume) {
678 /* ISO output disabled */
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. */
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");
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 */
694 dmp -> start_sector_iso = 0;
695 my_strdup (dmp -> outfile_iso, outfile_iso);
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);
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);
706 /* Raw output file exists but resume was not requested, error */
708 error ("ISO output file exists, but resume was not requested.");
710 dmp -> start_sector_iso = -1;
711 my_free (dmp -> outfile_iso);
712 dmp -> outfile_iso = NULL;
719 bool dumper_set_xiso_output_file (dumper *dmp, char *outfile_xiso, bool resume) {
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. */
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");
737 } else if (!(fp = fopen (outfile_xiso, "rb"))) {
739 dmp -> start_sector_xiso = 0;
740 my_strdup (dmp -> outfile_xiso, outfile_xiso);
742 my_fseek (fp, 0, SEEK_END);
743 filesize = my_ftell (fp);
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);
751 error ("XISO output file exists, but resume was not requested.");
753 dmp -> start_sector_xiso = -1;
754 my_free (dmp -> outfile_xiso);
755 dmp -> outfile_xiso = NULL;
762 bool dumper_prepare_xiso (dumper *dmp) {
764 u_int8_t buf[SECTOR_SIZE];
768 if (!dmp -> outfile_xiso) {
769 error ("No XISO output file defined");
773 dmp -> start_sector = dmp -> start_sector_xiso;
776 if (dmp -> outfile_xiso[0] == '\0' && disc_is_xbox_challenge_drive (dmp -> dsk)) {
778 multihash_init (&(dmp -> hash_xiso));
779 dmp -> fp_xiso = NULL;
785 multihash_init (&(dmp -> hash_xiso));
787 dmp -> fp_xiso = fopen (dmp -> outfile_xiso, "a+b");
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);
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) {
803 debug ("Writing to file \"%s\" in Xbox XISO format (fseeked() to %lld)", dmp -> outfile_xiso, my_ftell (dmp -> fp_xiso));
806 fclose (dmp -> fp_xiso);
807 dmp -> fp_xiso = NULL;
814 bool dumper_prepare (dumper *dmp) {
816 u_int8_t buf[RAW_SECTOR_SIZE];
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;
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;
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));
840 dmp -> fp_raw = NULL;
841 dmp -> fp_iso = NULL;
847 if (dmp -> hashing) {
848 multihash_init (&(dmp -> hash_raw));
849 multihash_init (&(dmp -> hash_iso));
852 /* Setup raw output file */
853 if (dmp -> outfile_raw) {
854 dmp -> fp_raw = fopen (dmp -> outfile_raw, "a+b");
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);
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) {
869 debug ("Writing to file \"%s\" in raw format (fseeked() to %lld)", dmp -> outfile_raw, my_ftell (dmp -> fp_raw));
872 fclose (dmp -> fp_raw);
873 dmp -> fp_raw = NULL;
876 dmp -> fp_raw = NULL;
879 /* Setup ISO output file */
880 if (dmp -> outfile_iso) {
881 dmp -> fp_iso = fopen (dmp -> outfile_iso, "a+b");
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);
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) {
895 debug ("Writing to file \"%s\" in ISO format (fseeked() to %lld)", dmp -> outfile_iso, my_ftell (dmp -> fp_iso));
898 fclose (dmp -> fp_iso);
899 dmp -> fp_iso = NULL;
902 dmp -> fp_iso = NULL;
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);
919 bool locked_view_is_xdfs;
926 disc_get_type (dmp -> dsk, &type_id, &type);
927 if (type_id != DISC_TYPE_XBOX)
931 error ("Xbox redump-style ISO output cannot be combined with -r/raw output in one run");
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");
941 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
944 fclose (dmp -> fp_iso);
945 dmp -> fp_iso = NULL;
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;
958 memset (&meta, 0, sizeof (meta));
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.
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");
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");
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, §or_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;
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");
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");
1013 disc_xbox_read_capacity_10 (dmp -> dsk, &locked_sectors, §or_size);
1014 meta.locked_visible_sectors = locked_sectors;
1015 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
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");
1023 if (!xbox_redump_layout_constants_match_dic ()) {
1024 error ("Xbox redump layout constants do not match the DiscImageCreator-compatible XGD1 layout");
1028 meta.output_sectors = XBOX_XGD1_FULL_REDUMP_SECTORS;
1029 if (dmp -> progress)
1030 dmp -> progress (true, 0, meta.output_sectors, dmp -> progress_data);
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);
1035 video_l1 = (u_int8_t *) malloc ((size_t) XBOX_XGD1_VIDEO_L1_SECTORS * SECTOR_SIZE);
1037 error ("Could not allocate Xbox VIDEO-L1 capture buffer");
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))
1046 if (!xbox_dump_read10_iso_range (dmp, 0, XBOX_XGD1_VIDEO_L0_SECTORS, 0, meta.output_sectors, "VIDEO-L0", false, current_sector))
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"))
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, §or_size);
1060 meta.unlocked_visible_sectors = unlocked_sectors;
1061 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
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;
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);
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);
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))
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))
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"))
1100 if (!xbox_write_iso_sectors (dmp, video_l1, XBOX_XGD1_VIDEO_L1_SECTORS, XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA, meta.output_sectors))
1104 multihash_finish (&(dmp -> hash_iso));
1105 if (dmp -> fp_iso) {
1106 fclose (dmp -> fp_iso);
1107 dmp -> fp_iso = NULL;
1110 xbox_write_redump_metadata (dmp, &meta);
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
1122 int dumper_dump (dumper *dmp, u_int32_t *current_sector) {
1124 u_int8_t *rawbuf, *isobuf;
1125 u_int32_t i, sectors_no, last_sector;
1127 char *type_string = NULL;
1129 bool no_unscrambling;
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;
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);
1144 sectors_no = disc_get_sectors_no (dmp -> dsk);
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);
1151 warning ("Start sector forced to %u\n", dd -> start_sector);
1152 ss = dd -> start_sector;
1155 fseek (fp_iso, ss * 2048, SEEK_SET);
1157 fseek (fp_raw, ss * 2064, SEEK_SET);
1163 debug ("Starting dump process from sector %u...\n", dmp -> start_sector);
1165 /* First call to progress function */
1166 if (dmp -> progress)
1167 dmp -> progress (true, dmp -> start_sector, sectors_no, dmp -> progress_data);
1169 last_sector=sectors_no-1;
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);
1175 *(current_sector) = i;
1178 if (out && dmp -> fp_raw) {
1179 clearerr (dmp -> fp_raw);
1182 error ("NULL buffer");
1184 *(current_sector) = i;
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");
1190 *(current_sector) = i;
1193 if (dmp -> flushing)
1194 fflush (dmp -> fp_raw);
1196 if (dmp -> hashing && out)
1197 multihash_update (&(dmp -> hash_raw), rawbuf, RAW_SECTOR_SIZE);
1200 if (out && dmp -> fp_iso) {
1201 clearerr (dmp -> fp_iso);
1204 error ("NULL buffer");
1206 *(current_sector) = i;
1208 else fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_iso);
1210 if (ferror (dmp -> fp_iso)) {
1211 error ("fwrite() to ISO output file failed");
1213 *(current_sector) = i;
1216 if (dmp -> flushing)
1217 fflush (dmp -> fp_iso);
1219 if (dmp -> hashing && out)
1220 multihash_update (&(dmp -> hash_iso), isobuf, SECTOR_SIZE);
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 */
1229 if (dmp -> hashing) {
1230 multihash_finish (&(dmp -> hash_raw));
1231 multihash_finish (&(dmp -> hash_iso));
1235 fclose (dmp -> fp_raw);
1237 fclose (dmp -> fp_iso);
1248 int dumper_dump_xiso (dumper *dmp, u_int32_t *current_sector) {
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;
1256 error ("XISO dumper is not prepared");
1261 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1263 if (dmp -> fp_xiso) {
1264 fclose (dmp -> fp_xiso);
1265 dmp -> fp_xiso = NULL;
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;
1275 if (!dmp -> fp_xiso) {
1276 error ("XISO dumper is not prepared");
1280 if (!xbox_get_xiso_range (dmp -> dsk, &source_lba, &source_sectors, &target_sectors)) {
1282 *current_sector = dmp -> start_sector;
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);
1289 *current_sector = dmp -> start_sector;
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;
1297 last_sector = target_sectors - 1;
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);
1304 for (i = dmp -> start_sector; i < target_sectors && out; i++) {
1305 if (i < XBOX_XISO_LEADIN_SECTORS) {
1308 if (disc_xbox_read_10 (dmp -> dsk, leadin_source_lba + i, 1, sector_buf, sizeof (sector_buf)) >= 0) {
1309 isobuf = sector_buf;
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;
1315 memset (sector_buf, 0, sizeof (sector_buf));
1316 isobuf = sector_buf;
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");
1325 *current_sector = i;
1328 if (dmp -> hashing && out)
1329 multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1331 source_sector = source_lba + (i - XBOX_XISO_LEADIN_SECTORS);
1332 isobuf = sector_buf;
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);
1338 *current_sector = i;
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");
1346 *current_sector = i;
1349 if (dmp -> hashing && out)
1350 multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1354 if (dmp -> flushing)
1355 fflush (dmp -> fp_xiso);
1357 if ((i % 320 == 0) || (i == last_sector)) {
1358 if (dmp -> progress)
1359 dmp -> progress (false, i + 1, target_sectors, dmp -> progress_data);
1364 multihash_finish (&(dmp -> hash_xiso));
1366 if (dmp -> fp_xiso) {
1367 fclose (dmp -> fp_xiso);
1368 dmp -> fp_xiso = NULL;
1375 dumper *dumper_new (disc *d) {
1378 dmp = (dumper *) malloc (sizeof (dumper));
1379 memset (dmp, 0, sizeof (dumper));
1381 xbox_ref_dump_result_init (&(dmp -> xbox_ref_result));
1382 dumper_set_hashing (dmp, true);
1383 dumper_set_flushing (dmp, true);
1389 void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data) {
1390 dmp -> progress = progress;
1391 dmp -> progress_data = progress_data;
1397 void dumper_set_hashing (dumper *dmp, bool h) {
1399 debug ("Hashing %s", h ? "enabled" : "disabled");
1405 void dumper_set_flushing (dumper *dmp, bool f) {
1406 dmp -> flushing = f;
1407 debug ("Flushing %s", f ? "enabled" : "disabled");
1412 void *dumper_destroy (dumper *dmp) {
1413 my_free (dmp -> outfile_raw);
1414 my_free (dmp -> outfile_iso);
1415 my_free (dmp -> outfile_xiso);
1422 char *dumper_get_iso_crc32 (dumper *dmp) {
1423 return ((dmp -> hash_iso).crc32_s);
1427 char *dumper_get_raw_crc32 (dumper *dmp) {
1428 return ((dmp -> hash_raw).crc32_s);
1431 char *dumper_get_xiso_crc32 (dumper *dmp) {
1432 return ((dmp -> hash_xiso).crc32_s);
1436 char *dumper_get_iso_md4 (dumper *dmp) {
1437 return ((dmp -> hash_iso).md4_s);
1441 char *dumper_get_raw_md4 (dumper *dmp) {
1442 return ((dmp -> hash_raw).md4_s);
1445 char *dumper_get_xiso_md4 (dumper *dmp) {
1446 return ((dmp -> hash_xiso).md4_s);
1450 char *dumper_get_iso_md5 (dumper *dmp) {
1451 return ((dmp -> hash_iso).md5_s);
1455 char *dumper_get_raw_md5 (dumper *dmp) {
1456 return ((dmp -> hash_raw).md5_s);
1459 char *dumper_get_xiso_md5 (dumper *dmp) {
1460 return ((dmp -> hash_xiso).md5_s);
1464 char *dumper_get_iso_ed2k (dumper *dmp) {
1465 return ((dmp -> hash_iso).ed2k_s);
1469 char *dumper_get_raw_ed2k (dumper *dmp) {
1470 return ((dmp -> hash_raw).ed2k_s);
1474 char *dumper_get_iso_sha1 (dumper *dmp) {
1475 return ((dmp -> hash_iso).sha1_s);
1479 char *dumper_get_raw_sha1 (dumper *dmp) {
1480 return ((dmp -> hash_raw).sha1_s);
1483 char *dumper_get_xiso_sha1 (dumper *dmp) {
1484 return ((dmp -> hash_xiso).sha1_s);
1488 char *dumper_get_iso_sha2 (dumper *dmp) {
1489 return ((dmp -> hash_iso).sha2_s);
1493 char *dumper_get_raw_sha2 (dumper *dmp) {
1494 return ((dmp -> hash_raw).sha2_s);
1497 char *dumper_get_xiso_sha2 (dumper *dmp) {
1498 return ((dmp -> hash_xiso).sha2_s);
1502 bool dumper_get_xbox_reference_result (dumper *dmp, xbox_ref_dump_result *result) {
1503 if (!dmp || !result || !(dmp -> xbox_ref_result).attempted)
1505 *result = dmp -> xbox_ref_result;