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_portable.h"
33 #include "xbox_ref/xbox_ref_log.h"
37 #include <sys/types.h>
44 u_int32_t start_sector_raw;
47 u_int32_t start_sector_iso;
50 u_int32_t start_sector_xiso;
52 u_int32_t start_sector;
59 xbox_ref_dump_result xbox_ref_result;
61 progress_func progress;
63 dumper_cancel_func cancel;
68 #define DUMPER_READ_BLOCK_RETRIES 3
70 static bool dumper_cancel_requested (dumper *dmp) {
71 if (!dmp || !dmp -> cancel)
73 if (!dmp -> cancel (dmp -> cancel_data))
75 dmp -> cancelled = true;
79 static int dumper_xbox_ref_cancel_requested (void *cancel_data) {
80 return dumper_cancel_requested ((dumper *) cancel_data) ? 1 : 0;
83 static double xbox_portable_now_seconds (void) {
85 gettimeofday (&now, NULL);
86 return (double) now.tv_sec + ((double) now.tv_usec / 1000000.0);
89 static u_int64_t xbox_portable_file_size (const char *path) {
93 if (!path || path[0] == '\0')
95 file = fopen (path, "rb");
98 if (my_fseek (file, 0, SEEK_END) != 0) {
102 position = my_ftell (file);
106 return (u_int64_t) position;
109 static void xbox_portable_copy_string (char *destination, size_t destination_size, const char *source) {
110 if (!destination || destination_size == 0)
114 snprintf (destination, destination_size, "%s", source);
117 static void xbox_portable_result_begin (dumper *dmp, const char *path, char mode) {
120 xbox_ref_dump_result_init (&(dmp -> xbox_ref_result));
121 dmp -> xbox_ref_result.attempted = 1;
122 dmp -> xbox_ref_result.mode = mode;
123 xbox_portable_copy_string (dmp -> xbox_ref_result.output_path,
124 sizeof (dmp -> xbox_ref_result.output_path), path);
127 static void xbox_portable_sanitize_filename_component (char *value) {
136 previous_space = true;
137 for (read_index = 0; value[read_index] != '\0'; ++read_index) {
138 unsigned char c = (unsigned char) value[read_index];
139 bool forbidden = c < 32 || c == '"' || c == '*' || c == '/' ||
140 c == ':' || c == '<' || c == '>' || c == '?' || c == '\\' || c == '|';
147 value[write_index++] = ' ';
148 previous_space = true;
150 value[write_index++] = (char) c;
151 previous_space = false;
155 while (write_index > 0 && (value[write_index - 1] == ' ' ||
156 value[write_index - 1] == '.'))
158 value[write_index] = '\0';
161 static bool xbox_portable_resolve_output_path (dumper *dmp, bool xiso_mode) {
162 xbox_portable_metadata metadata;
163 char title[XBOX_PORTABLE_TITLE_MAX];
164 char path[XBOX_REF_RESULT_PATH_MAX];
166 const char *extension;
171 target = xiso_mode ? &(dmp -> outfile_xiso) : &(dmp -> outfile_iso);
172 if (!*target || (*target)[0] != '\0')
175 xbox_ref_log_fprintf (stderr,
176 "[XBOX-PORTABLE] Resolving the default output filename from the unlocked XBE/DMI view.\n");
177 disc_xbox_wait_ready (dmp -> dsk, 30000);
178 if (disc_xbox_unlock (dmp -> dsk) < 0)
179 xbox_ref_log_fprintf (stderr,
180 "[XBOX-PORTABLE][WARN] Filename preflight unlock returned failure; attempting metadata reads anyway.\n");
181 disc_xbox_refresh_volume (dmp -> dsk);
182 disc_xbox_wait_ready (dmp -> dsk, 30000);
184 xbox_portable_metadata_init (&metadata);
185 if (!xbox_portable_read_metadata_from_disc (dmp -> dsk, &metadata)) {
186 error ("Could not derive an Xbox output filename because XBE/DMI metadata was unavailable; provide an explicit output path");
190 xbox_portable_copy_string (title, sizeof (title),
191 metadata.title[0] ? metadata.title : "Xbox DVD");
192 xbox_portable_sanitize_filename_component (title);
194 xbox_portable_copy_string (title, sizeof (title), "Xbox DVD");
195 extension = xiso_mode ? ".xiso" : ".iso";
197 if (metadata.media_id[0])
198 snprintf (path, sizeof (path), "%s[%s]%s", title, metadata.media_id, extension);
200 snprintf (path, sizeof (path), "%s%s", title, extension);
203 my_strdup (*target, path);
204 xbox_ref_log_retarget (*target);
205 xbox_ref_log_fprintf (stderr,
206 "[XBOX-PORTABLE] Derived output filename: %s\n", *target);
210 static void xbox_portable_result_capture_metadata (dumper *dmp) {
211 xbox_portable_metadata metadata;
213 if (!dmp || (dmp -> xbox_ref_result.title[0] &&
214 dmp -> xbox_ref_result.have_game_region))
217 xbox_portable_metadata_init (&metadata);
218 if (!xbox_portable_read_metadata_from_disc (dmp -> dsk, &metadata)) {
219 xbox_ref_log_fprintf (stderr,
220 "[XBOX-PORTABLE][WARN] XBE title/region metadata was not available from the unlocked view.\n");
224 xbox_portable_copy_string (dmp -> xbox_ref_result.title,
225 sizeof (dmp -> xbox_ref_result.title), metadata.title);
226 xbox_portable_copy_string (dmp -> xbox_ref_result.media_id,
227 sizeof (dmp -> xbox_ref_result.media_id), metadata.media_id);
228 xbox_portable_copy_string (dmp -> xbox_ref_result.region,
229 sizeof (dmp -> xbox_ref_result.region), metadata.region);
230 dmp -> xbox_ref_result.have_game_region = metadata.have_game_region;
231 dmp -> xbox_ref_result.game_region = metadata.game_region;
233 xbox_ref_log_fprintf (stderr,
234 "[XBOX-PORTABLE] XBE metadata: title='%s', media_id='%s', region='%s', GameRegion=0x%08x.\n",
235 dmp -> xbox_ref_result.title[0] ? dmp -> xbox_ref_result.title : "(unknown)",
236 dmp -> xbox_ref_result.media_id[0] ? dmp -> xbox_ref_result.media_id : "(unknown)",
237 dmp -> xbox_ref_result.region[0] ? dmp -> xbox_ref_result.region : "(unknown)",
238 dmp -> xbox_ref_result.game_region);
241 static void xbox_portable_result_finish (dumper *dmp,
243 u_int32_t expected_output_sectors,
244 double started_seconds,
246 xbox_ref_dump_result *result;
252 if (!dmp || !(dmp -> xbox_ref_result).attempted)
255 result = &(dmp -> xbox_ref_result);
256 result -> dump_success = success ? 1 : 0;
257 result -> cancelled = dmp -> cancelled ? 1 : 0;
258 result -> output_sectors = expected_output_sectors;
259 result -> output_size = xbox_portable_file_size (result -> output_path);
260 result -> completed_sectors = (u_int32_t) (result -> output_size / SECTOR_SIZE);
261 result -> elapsed_seconds = xbox_portable_now_seconds () - started_seconds;
263 if (!dmp -> hashing || result -> output_size == 0)
267 crc32 = dumper_get_xiso_crc32 (dmp);
268 md5 = dumper_get_xiso_md5 (dmp);
269 sha1 = dumper_get_xiso_sha1 (dmp);
270 sha256 = dumper_get_xiso_sha2 (dmp);
272 crc32 = dumper_get_iso_crc32 (dmp);
273 md5 = dumper_get_iso_md5 (dmp);
274 sha1 = dumper_get_iso_sha1 (dmp);
275 sha256 = dumper_get_iso_sha2 (dmp);
278 if (!crc32 || !md5 || !sha1 || !sha256 ||
279 !crc32[0] || !md5[0] || !sha1[0] || !sha256[0])
282 xbox_portable_copy_string (result -> crc32, sizeof (result -> crc32), crc32);
283 xbox_portable_copy_string (result -> md5, sizeof (result -> md5), md5);
284 xbox_portable_copy_string (result -> sha1, sizeof (result -> sha1), sha1);
285 xbox_portable_copy_string (result -> sha256, sizeof (result -> sha256), sha256);
286 result -> hashes_complete = 1;
289 static bool dumper_read_sector_with_retry (dumper *dmp, u_int32_t sector_no, u_int8_t **isobuf, u_int8_t **rawbuf) {
291 bool need_iso, need_raw;
293 if (!dmp || !dmp -> dsk)
296 need_iso = dmp -> fp_iso != NULL;
297 need_raw = dmp -> fp_raw != NULL;
299 for (attempt = 0; attempt < DUMPER_READ_BLOCK_RETRIES; attempt++) {
300 if (isobuf) *isobuf = NULL;
301 if (rawbuf) *rawbuf = NULL;
303 disc_read_sector (dmp -> dsk, sector_no, isobuf, rawbuf);
304 if ((!need_iso || (isobuf && *isobuf)) && (!need_raw || (rawbuf && *rawbuf))) {
306 warning ("Recovered sectors %u..%u after dump-level retry %d", sector_no, sector_no + SECTORS_PER_BLOCK - 1, attempt);
310 if (attempt + 1 < DUMPER_READ_BLOCK_RETRIES)
311 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);
317 #define XBOX_XISO_LEADIN_SECTORS 32
318 #define XBOX_XISO_STANDARD_GAME_LBA 32
319 #define XBOX_XISO_START_LBA_MAGIC 306112
320 #define XBOX_XISO_DUAL_LAYER_END_LBA 1913920
322 /* DiscImageCreator-compatible Original Xbox/XGD1 2048-byte-sector layout
323 * constants. DIC uses XBOX_SIZE=3820880, XBOX_LAYER_BREAK=1913776,
324 * DVD start PSN 0x30000, and Xbox start PSN 0x60600. The virtual redump
325 * layout below follows those constants: video L0, middle/padding, game
326 * partition, middle/padding, and video L1 tail.
328 #define XBOX_XGD1_DIC_DVD_START_PSN 0x30000
329 #define XBOX_XGD1_DIC_XBOX_START_PSN 0x60600
330 #define XBOX_XGD1_DIC_XBOX_SIZE 3820880
331 #define XBOX_XGD1_DIC_LAYER_BREAK 1913776
332 #define XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS 3431264
333 #define XBOX_XGD1_FULL_REDUMP_SECTORS XBOX_XGD1_DIC_XBOX_SIZE
334 #define XBOX_XGD1_GAME_OUTPUT_START_LBA (XBOX_XGD1_DIC_XBOX_START_PSN - XBOX_XGD1_DIC_DVD_START_PSN)
335 #define XBOX_XGD1_VIDEO_L0_SECTORS 6832
336 #define XBOX_XGD1_VIDEO_L1_SECTORS 160
337 #define XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA (XBOX_XGD1_FULL_REDUMP_SECTORS - XBOX_XGD1_VIDEO_L1_SECTORS)
338 #define XBOX_XGD1_GAME_SOURCE_SECTORS (XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS - XBOX_XISO_LEADIN_SECTORS)
339 #define XBOX_XGD1_LAYER_THRESHOLD 2000000
340 #define XBOX_METADATA_PATH_MAX 4096
342 static u_int32_t read_le32 (const u_int8_t *p) {
343 return ((u_int32_t) p[0]) | ((u_int32_t) p[1] << 8) | ((u_int32_t) p[2] << 16) | ((u_int32_t) p[3] << 24);
346 static bool xbox_probe_xdfs_volume (disc *d, u_int32_t lba, u_int32_t *root_lba, u_int32_t *volume_size_raw) {
347 u_int8_t sector[SECTOR_SIZE];
349 memset (sector, 0, sizeof (sector));
350 if (disc_xbox_read_10 (d, lba, 1, sector, sizeof (sector)) < 0)
353 if (memcmp (sector, "MICROSOFT", 9) != 0)
357 *root_lba = read_le32 (sector + 0x14);
359 *volume_size_raw = read_le32 (sector + 0x1C);
364 static bool xbox_redump_layout_constants_match_dic (void) {
365 return XBOX_XGD1_FULL_REDUMP_SECTORS == XBOX_XGD1_DIC_XBOX_SIZE &&
366 XBOX_XGD1_DIC_LAYER_BREAK == 1913776 &&
367 XBOX_XGD1_GAME_OUTPUT_START_LBA == 0x30600 &&
368 XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA == 3820720 &&
369 XBOX_XGD1_VIDEO_L0_SECTORS +
370 (XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS) +
371 XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS +
372 (XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA -
373 (XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS)) +
374 XBOX_XGD1_VIDEO_L1_SECTORS == XBOX_XGD1_FULL_REDUMP_SECTORS;
385 u_int32_t locked_visible_sectors;
386 u_int32_t unlocked_visible_sectors;
387 u_int32_t output_sectors;
388 u_int32_t game_source_lba;
389 u_int32_t game_source_sectors;
390 u_int32_t game_leadin_read_sectors;
391 u_int32_t game_leadin_zero_sectors;
392 char pfi_path[XBOX_METADATA_PATH_MAX];
393 char dmi_path[XBOX_METADATA_PATH_MAX];
394 char json_path[XBOX_METADATA_PATH_MAX];
395 } xbox_redump_metadata;
397 static void xbox_make_related_path (const char *filename, const char *suffix, char *out, size_t out_size) {
398 const char *slash1, *slash2, *slash, *dot;
399 size_t suffix_len, base_len;
401 if (!out || out_size == 0)
404 if (!filename || !suffix)
407 suffix_len = strlen (suffix);
408 if (out_size <= suffix_len + 1)
411 slash1 = strrchr (filename, '/');
412 slash2 = strrchr (filename, '\\');
413 if (slash1 && slash2)
414 slash = slash1 > slash2 ? slash1 : slash2;
416 slash = slash1 ? slash1 : slash2;
418 dot = strrchr (filename, '.');
419 if (!dot || (slash && dot < slash))
420 dot = filename + strlen (filename);
422 base_len = (size_t) (dot - filename);
423 if (base_len + suffix_len + 1 > out_size)
424 base_len = out_size - suffix_len - 1;
425 memcpy (out, filename, base_len);
426 out[base_len] = '\0';
427 strncat (out, suffix, out_size - strlen (out) - 1);
430 static const char *xbox_path_leaf (const char *path) {
431 const char *slash1, *slash2;
434 slash1 = strrchr (path, '/');
435 slash2 = strrchr (path, '\\');
436 if (slash1 && slash2)
437 return (slash1 > slash2 ? slash1 : slash2) + 1;
445 static void xbox_json_string (FILE *f, const char *s) {
449 unsigned char c = (unsigned char) *s++;
450 if (c == '"' || c == '\\') {
453 } else if (c == '\n') {
455 } else if (c == '\r') {
457 } else if (c == '\t') {
459 } else if (c < 0x20) {
460 fprintf (f, "\\u%04x", c);
469 static bool xbox_write_binary_file (const char *path, const u_int8_t *data, size_t len) {
471 if (!path || !data || len == 0)
473 f = fopen (path, "wb");
476 if (fwrite (data, 1, len, f) != len) {
480 return fclose (f) == 0;
483 static void xbox_capture_pfi_dmi (disc *d, xbox_redump_metadata *meta) {
487 if (!meta -> has_pfi) {
488 rc = disc_xbox_read_dvd_structure (d, 0x00, 0x00, meta -> pfi, sizeof (meta -> pfi));
489 meta -> has_pfi = rc >= 0;
490 xbox_ref_log_fprintf (
492 "[XBOX-DVD-STRUCTURE] format=0x00 name=PFI cdb_format_byte=7 result=%s\n",
493 meta -> has_pfi ? "PASS" : "FAIL");
495 if (!meta -> has_dmi) {
496 rc = disc_xbox_read_dvd_structure (d, 0x04, 0x00, meta -> dmi, sizeof (meta -> dmi));
497 meta -> has_dmi = rc >= 0;
498 xbox_ref_log_fprintf (
500 "[XBOX-DVD-STRUCTURE] format=0x04 name=DMI cdb_format_byte=7 result=%s\n",
501 meta -> has_dmi ? "PASS" : "FAIL");
505 static void xbox_write_redump_metadata (dumper *dmp, xbox_redump_metadata *meta) {
507 const char *pfi_leaf;
508 const char *dmi_leaf;
510 if (!dmp || !meta || !dmp -> outfile_iso)
513 xbox_make_related_path (dmp -> outfile_iso, ".pfi.bin", meta -> pfi_path, sizeof (meta -> pfi_path));
514 xbox_make_related_path (dmp -> outfile_iso, ".dmi.bin", meta -> dmi_path, sizeof (meta -> dmi_path));
515 xbox_make_related_path (dmp -> outfile_iso, ".redump.json", meta -> json_path, sizeof (meta -> json_path));
518 meta -> pfi_written = xbox_write_binary_file (meta -> pfi_path, meta -> pfi, sizeof (meta -> pfi));
520 meta -> dmi_written = xbox_write_binary_file (meta -> dmi_path, meta -> dmi, sizeof (meta -> dmi));
522 json = fopen (meta -> json_path, "wb");
524 warning ("Could not write Xbox redump metadata JSON");
528 pfi_leaf = meta -> pfi_written ? xbox_path_leaf (meta -> pfi_path) : NULL;
529 dmi_leaf = meta -> dmi_written ? xbox_path_leaf (meta -> dmi_path) : NULL;
531 fprintf (json, "{\n");
532 fprintf (json, " \"format\": \"friidump-xbox-redump\",\n");
533 fprintf (json, " \"version\": 1,\n");
534 fprintf (json, " \"image\": {\n");
535 fprintf (json, " \"file\": "); xbox_json_string (json, xbox_path_leaf (dmp -> outfile_iso)); fprintf (json, ",\n");
536 fprintf (json, " \"sector_size\": %u,\n", SECTOR_SIZE);
537 fprintf (json, " \"sector_count\": %u,\n", meta -> output_sectors);
538 fprintf (json, " \"byte_count\": %llu,\n", (unsigned long long) meta -> output_sectors * SECTOR_SIZE);
539 fprintf (json, " \"hashes\": {\n");
540 fprintf (json, " \"crc32\": "); xbox_json_string (json, dumper_get_iso_crc32 (dmp)); fprintf (json, ",\n");
541 fprintf (json, " \"md5\": "); xbox_json_string (json, dumper_get_iso_md5 (dmp)); fprintf (json, ",\n");
542 fprintf (json, " \"sha1\": "); xbox_json_string (json, dumper_get_iso_sha1 (dmp)); fprintf (json, ",\n");
543 fprintf (json, " \"sha256\": "); xbox_json_string (json, dumper_get_iso_sha2 (dmp)); fprintf (json, "\n");
544 fprintf (json, " }\n");
545 fprintf (json, " },\n");
546 fprintf (json, " \"drive\": {\n");
547 fprintf (json, " \"model\": "); xbox_json_string (json, disc_get_drive_model_string (dmp -> dsk)); fprintf (json, ",\n");
548 fprintf (json, " \"command\": %u,\n", disc_get_command (dmp -> dsk));
549 fprintf (json, " \"method\": %u\n", disc_get_method (dmp -> dsk));
550 fprintf (json, " },\n");
551 fprintf (json, " \"layout\": {\n");
552 fprintf (json, " \"layout_type\": \"original_xbox_xgd1_redump_style_2048\",\n");
553 fprintf (json, " \"locked_visible_sector_count\": %u,\n", meta -> locked_visible_sectors);
554 fprintf (json, " \"unlocked_visible_sector_count\": %u,\n", meta -> unlocked_visible_sectors);
555 fprintf (json, " \"video_l0_start_lba\": 0,\n");
556 fprintf (json, " \"video_l0_sector_count\": %u,\n", XBOX_XGD1_VIDEO_L0_SECTORS);
557 fprintf (json, " \"pregame_padding_start_lba\": %u,\n", XBOX_XGD1_VIDEO_L0_SECTORS);
558 fprintf (json, " \"pregame_padding_sector_count\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS);
559 fprintf (json, " \"game_output_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA);
560 fprintf (json, " \"game_leadin_sector_count\": %u,\n", XBOX_XISO_LEADIN_SECTORS);
561 fprintf (json, " \"game_leadin_unlocked_source_start_lba\": %u,\n",
562 (meta -> game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ?
563 meta -> game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0);
564 fprintf (json, " \"game_leadin_source_policy\": \"drive_read10_with_per_sector_zero_fallback\",\n");
565 fprintf (json, " \"game_unlocked_source_start_lba\": %u,\n", meta -> game_source_lba);
566 fprintf (json, " \"game_unlocked_source_sector_count\": %u,\n", meta -> game_source_sectors);
567 fprintf (json, " \"postgame_padding_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS);
568 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));
569 fprintf (json, " \"video_l1_start_lba\": %u,\n", XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA);
570 fprintf (json, " \"video_l1_sector_count\": %u,\n", XBOX_XGD1_VIDEO_L1_SECTORS);
571 fprintf (json, " \"layer_break_lba\": %u\n", XBOX_XGD1_DIC_LAYER_BREAK);
572 fprintf (json, " },\n");
573 fprintf (json, " \"discimagecreator_reference\": {\n");
574 fprintf (json, " \"xbox_size_sectors\": %u,\n", XBOX_XGD1_DIC_XBOX_SIZE);
575 fprintf (json, " \"xbox_layer_break_lba\": %u,\n", XBOX_XGD1_DIC_LAYER_BREAK);
576 fprintf (json, " \"dvd_start_psn\": %u,\n", XBOX_XGD1_DIC_DVD_START_PSN);
577 fprintf (json, " \"xbox_start_psn\": %u,\n", XBOX_XGD1_DIC_XBOX_START_PSN);
578 fprintf (json, " \"game_output_start_lba\": %u,\n", XBOX_XGD1_GAME_OUTPUT_START_LBA);
579 fprintf (json, " \"layout_constants_match\": %s\n", xbox_redump_layout_constants_match_dic () ? "true" : "false");
580 fprintf (json, " },\n");
581 fprintf (json, " \"lead_in_capture\": {\n");
582 fprintf (json, " \"game_leadin_attempted\": true,\n");
583 fprintf (json, " \"unlocked_source_start_lba\": %u,\n",
584 (meta -> game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ?
585 meta -> game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0);
586 fprintf (json, " \"sector_count\": %u,\n", XBOX_XISO_LEADIN_SECTORS);
587 fprintf (json, " \"readable_sectors\": %u,\n", meta -> game_leadin_read_sectors);
588 fprintf (json, " \"zero_filled_sectors\": %u,\n", meta -> game_leadin_zero_sectors);
589 fprintf (json, " \"physical_geometry_evidence\": \"friidump_0.5.3.13_cache_aligned_raw_id_probe\"\n");
590 fprintf (json, " },\n");
591 fprintf (json, " \"dvd_structures\": {\n");
592 fprintf (json, " \"pfi\": "); if (pfi_leaf) xbox_json_string (json, pfi_leaf); else fprintf (json, "null"); fprintf (json, ",\n");
593 fprintf (json, " \"dmi\": "); if (dmi_leaf) xbox_json_string (json, dmi_leaf); else fprintf (json, "null"); fprintf (json, "\n");
594 fprintf (json, " },\n");
595 fprintf (json, " \"notes\": [\n");
596 fprintf (json, " \"PFI and DMI are stored as separate binary READ DVD STRUCTURE captures because they are not READ(10) user-data sectors.\",\n");
597 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");
598 fprintf (json, " \"Pregame and postgame padding retain zero-filled placeholder content; their physical locations are resolved but their inaccessible bytes are not.\"\n");
599 fprintf (json, " ]\n");
600 fprintf (json, "}\n");
603 if (meta -> pfi_written) {
604 debug ("Wrote Xbox PFI: %s", meta -> pfi_path);
606 warning ("Xbox PFI was not captured or could not be written");
608 if (meta -> dmi_written) {
609 debug ("Wrote Xbox DMI: %s", meta -> dmi_path);
611 warning ("Xbox DMI was not captured or could not be written");
613 debug ("Wrote Xbox redump metadata: %s", meta -> json_path);
616 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) {
617 if (dumper_cancel_requested (dmp))
619 if (!dmp || !dmp -> fp_iso || !buf)
621 clearerr (dmp -> fp_iso);
622 if (fwrite (buf, SECTOR_SIZE, sectors, dmp -> fp_iso) != sectors || ferror (dmp -> fp_iso)) {
623 error ("fwrite() to Xbox redump ISO failed at output LBA %u", output_lba);
627 multihash_update (&(dmp -> hash_iso), (u_int8_t *) buf, (size_t) sectors * SECTOR_SIZE);
629 fflush (dmp -> fp_iso);
630 if (dmp -> progress && ((output_lba % 320) == 0 || output_lba + sectors >= total_sectors))
631 dmp -> progress (false, output_lba + sectors, total_sectors, dmp -> progress_data);
635 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) {
636 u_int8_t zero[BLOCK_SIZE];
638 memset (zero, 0, sizeof (zero));
639 debug ("Writing Xbox %s zero-fill: output LBA %u, sectors %u", label ? label : "range", output_lba, sectors);
640 while (done < sectors) {
642 if (dumper_cancel_requested (dmp))
644 todo = sectors - done;
645 if (todo > SECTORS_PER_BLOCK)
646 todo = SECTORS_PER_BLOCK;
647 if (!xbox_write_iso_sectors (dmp, zero, todo, output_lba + done, total_sectors))
654 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, u_int32_t *current_sector) {
655 u_int8_t buf[BLOCK_SIZE];
656 u_int8_t one[SECTOR_SIZE];
658 debug ("Reading Xbox %s: source LBA %u, output LBA %u, sectors %u", label ? label : "range", source_lba, output_lba, sectors);
659 while (done < sectors) {
663 if (dumper_cancel_requested (dmp))
665 todo = sectors - done;
666 if (todo > SECTORS_PER_BLOCK)
667 todo = SECTORS_PER_BLOCK;
669 *current_sector = output_lba + done;
670 for (retry = 0; retry < 3 && !ok; retry++) {
671 memset (buf, 0, sizeof (buf));
672 if (disc_xbox_read_10 (dmp -> dsk, source_lba + done, todo, buf, sizeof (buf)) >= 0)
675 if (!ok && todo > 1) {
676 /* Some Xbox-capable drives are more reliable at visible DVD-video edges
677 * and layer/layout boundaries with single-sector READ(10) commands.
678 * Fall back before declaring the whole 16-sector batch bad. */
680 bool singles_ok = true;
681 for (i = 0; i < todo; i++) {
683 if (dumper_cancel_requested (dmp))
685 for (retry = 0; retry < 3 && !one_ok; retry++) {
686 memset (one, 0, sizeof (one));
687 if (disc_xbox_read_10 (dmp -> dsk, source_lba + done + i, 1, one, sizeof (one)) >= 0)
692 *current_sector = output_lba + done + i;
693 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);
694 error ("Xbox READ(10) failed for %s at source LBA %u", label ? label : "range", source_lba + done + i);
698 memcpy (buf + ((size_t) i * SECTOR_SIZE), one, SECTOR_SIZE);
704 *current_sector = output_lba + done;
705 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);
706 error ("Xbox READ(10) failed for %s at source LBA %u", label ? label : "range", source_lba + done);
709 if (!xbox_write_iso_sectors (dmp, buf, todo, output_lba + done, total_sectors))
716 static bool xbox_read_range_to_memory (dumper *dmp, u_int32_t source_lba, u_int32_t sectors, u_int8_t *out, size_t out_size, const char *label) {
717 u_int8_t one[SECTOR_SIZE];
719 if (!dmp || !dmp -> dsk || !out || out_size < (size_t) sectors * SECTOR_SIZE)
721 debug ("Capturing Xbox %s to memory: source LBA %u, sectors %u", label ? label : "range", source_lba, sectors);
722 while (done < sectors) {
726 if (dumper_cancel_requested (dmp))
728 todo = sectors - done;
729 if (todo > SECTORS_PER_BLOCK)
730 todo = SECTORS_PER_BLOCK;
731 for (retry = 0; retry < 3 && !ok; retry++) {
732 if (disc_xbox_read_10 (dmp -> dsk, source_lba + done, todo, out + ((size_t) done * SECTOR_SIZE), out_size - ((size_t) done * SECTOR_SIZE)) >= 0)
735 if (!ok && todo > 1) {
737 bool singles_ok = true;
738 for (i = 0; i < todo; i++) {
740 if (dumper_cancel_requested (dmp))
742 for (retry = 0; retry < 3 && !one_ok; retry++) {
743 memset (one, 0, sizeof (one));
744 if (disc_xbox_read_10 (dmp -> dsk, source_lba + done + i, 1, one, sizeof (one)) >= 0)
748 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);
749 error ("Xbox READ(10) failed while capturing %s at source LBA %u", label ? label : "range", source_lba + done + i);
753 memcpy (out + ((size_t) (done + i) * SECTOR_SIZE), one, SECTOR_SIZE);
758 xbox_ref_log_fprintf (stderr, "\n[XBOX] READ(10) failed while capturing %s at source LBA %u\n", label ? label : "range", source_lba + done);
759 error ("Xbox READ(10) failed while capturing %s at source LBA %u", label ? label : "range", source_lba + done);
767 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, u_int32_t *current_sector) {
768 u_int8_t buf[SECTOR_SIZE];
769 u_int8_t zero[SECTOR_SIZE];
772 memset (zero, 0, sizeof (zero));
773 debug ("Attempting Xbox %s lead-in from source LBA %u..%u", label ? label : "game", source_lba, source_lba + XBOX_XISO_LEADIN_SECTORS - 1);
774 for (i = 0; i < XBOX_XISO_LEADIN_SECTORS; i++) {
775 const u_int8_t *src = buf;
776 if (dumper_cancel_requested (dmp))
779 *current_sector = output_lba + i;
780 memset (buf, 0, sizeof (buf));
781 if (disc_xbox_read_10 (dmp -> dsk, source_lba + i, 1, buf, sizeof (buf)) < 0) {
783 warning ("Drive could not read one or more Xbox lead-in sectors; zero-filling only unreadable lead-in sectors");
787 if (meta) meta -> game_leadin_zero_sectors++;
789 if (meta) meta -> game_leadin_read_sectors++;
791 if (!xbox_write_iso_sectors (dmp, src, 1, output_lba + i, total_sectors))
797 static bool xbox_get_xiso_range (disc *d, u_int32_t *source_lba, u_int32_t *source_sectors, u_int32_t *target_sectors) {
798 u_int32_t total_sectors, root_lba, volume_size_raw, volume_sectors;
800 if (!d || !source_lba || !source_sectors || !target_sectors)
803 if (disc_xbox_unlock (d) < 0) {
804 error ("Could not switch Xbox drive into the game/XDVDFS view");
808 total_sectors = disc_get_sectors_no (d);
812 /* Try the historical physical/game-partition XDVDFS location first,
813 * then the unlocked/XISO-visible LBA 0x20. GDR-8050L after the
814 * native Xbox handshake normally resolves at 0x20.
816 if (xbox_probe_xdfs_volume (d, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw)) {
817 *source_lba = XBOX_XISO_START_LBA_MAGIC;
818 } else if (xbox_probe_xdfs_volume (d, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw)) {
819 *source_lba = XBOX_XISO_STANDARD_GAME_LBA;
821 error ("No Xbox XDVDFS volume found at LBA %u or %u", XBOX_XISO_START_LBA_MAGIC, XBOX_XISO_STANDARD_GAME_LBA);
825 if (total_sectors > 3300000) {
826 /* Retail dual-layer XISO copies from the detected game/XDVDFS
827 * start LBA through logical LBA 1913920, then prepends 32
828 * output lead-in sectors.
830 if (*source_lba >= XBOX_XISO_DUAL_LAYER_END_LBA) {
831 error ("Xbox XISO source LBA %u is beyond dual-layer XISO end LBA %u", *source_lba, XBOX_XISO_DUAL_LAYER_END_LBA);
834 *source_sectors = XBOX_XISO_DUAL_LAYER_END_LBA - *source_lba;
836 /* For single-layer/homebrew-style media, XDVDFS VolumeSize is a
837 * byte count, so convert it to 2048-byte sectors.
839 if (volume_size_raw == 0) {
840 error ("Xbox XDVDFS header reported a zero volume size");
844 volume_sectors = volume_size_raw / SECTOR_SIZE;
845 if (volume_sectors == 0) {
846 error ("Xbox XDVDFS volume size %u is smaller than one sector", volume_size_raw);
849 if (*source_lba + volume_sectors > total_sectors) {
850 warning ("Xbox XISO volume size extends beyond visible capacity; clipping to remaining visible sectors");
851 if (total_sectors > *source_lba)
852 volume_sectors = total_sectors - *source_lba;
856 *source_sectors = volume_sectors;
859 *target_sectors = *source_sectors + XBOX_XISO_LEADIN_SECTORS;
860 debug ("Xbox XISO range: source LBA %u, descriptor RootLBA %u, source sectors %u, output sectors %u", *source_lba, root_lba, *source_sectors, *target_sectors);
866 * Tries to open the output file for writing and to find out if it contains valid data so that the dump can continue.
869 * @param[out] fp The file pointer to write to.
870 * @param[out] start_sector The sector to start reading from.
871 * @return true if the dumping can start/continue, false otherwise (for instance if outfile cannot be written to).
873 bool dumper_set_raw_output_file (dumper *dmp, char *outfile_raw, bool resume) {
879 /* Raw output disabled */
881 dmp -> start_sector_raw = -1;
882 my_free (dmp -> outfile_raw);
883 dmp -> outfile_raw = NULL;
884 } else if (dmp -> outfile_raw) {
885 error ("Raw output file already defined");
887 } else if (!(fp = fopen (outfile_raw, "rb"))) { /** @todo Maybe we could not open file for permission problems */
888 /* Raw output file does not exist, start from scratch */
890 dmp -> start_sector_raw = 0;
891 my_strdup (dmp -> outfile_raw, outfile_raw);
893 /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */
894 my_fseek (fp, 0, SEEK_END);
895 filesize = my_ftell (fp);
898 dmp -> start_sector_raw = (u_int32_t) (filesize / RAW_SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
899 debug ("Raw output can restart from sector %u", dmp -> start_sector_raw);
900 my_strdup (dmp -> outfile_raw, outfile_raw);
902 /* Raw output file exists but resume was not requested, error */
904 error ("Raw output file exists, but resume was not requested.");
906 dmp -> start_sector_raw = -1;
907 my_free (dmp -> outfile_raw);
908 dmp -> outfile_raw = NULL;
915 bool dumper_set_iso_output_file (dumper *dmp, char *outfile_iso, bool resume) {
921 /* ISO output disabled */
923 dmp -> start_sector_iso = -1;
924 my_free (dmp -> outfile_iso);
925 dmp -> outfile_iso = NULL;
926 } else if (outfile_iso[0] == '\0') {
927 /* Xbox/GDR-8050L auto-name placeholder; the copied reference path opens the final file. */
929 dmp -> start_sector_iso = 0;
930 my_strdup (dmp -> outfile_iso, outfile_iso);
931 } else if (dmp -> outfile_iso) {
932 error ("ISO output file already defined");
934 } else if (!(fp = fopen (outfile_iso, "rb"))) { /** @todo Maybe we could not open file for permission problems */
935 /* Raw output file does not exist, start from scratch */
937 dmp -> start_sector_iso = 0;
938 my_strdup (dmp -> outfile_iso, outfile_iso);
940 /* Raw output file exists and resume was requested, so see how many dumped sectors it contains */
941 my_fseek (fp, 0, SEEK_END);
942 filesize = my_ftell (fp);
945 dmp -> start_sector_iso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
946 debug ("ISO output can restart from sector %u", dmp -> start_sector_iso);
947 my_strdup (dmp -> outfile_iso, outfile_iso);
949 /* Raw output file exists but resume was not requested, error */
951 error ("ISO output file exists, but resume was not requested.");
953 dmp -> start_sector_iso = -1;
954 my_free (dmp -> outfile_iso);
955 dmp -> outfile_iso = NULL;
962 bool dumper_set_xiso_output_file (dumper *dmp, char *outfile_xiso, bool resume) {
969 dmp -> start_sector_xiso = -1;
970 my_free (dmp -> outfile_xiso);
971 dmp -> outfile_xiso = NULL;
972 } else if (outfile_xiso[0] == '\0') {
973 /* Xbox/GDR-8050L auto-name placeholder; the copied reference path opens the final file. */
975 dmp -> start_sector_xiso = 0;
976 my_strdup (dmp -> outfile_xiso, outfile_xiso);
977 } else if (dmp -> outfile_xiso) {
978 error ("XISO output file already defined");
980 } else if (!(fp = fopen (outfile_xiso, "rb"))) {
982 dmp -> start_sector_xiso = 0;
983 my_strdup (dmp -> outfile_xiso, outfile_xiso);
985 my_fseek (fp, 0, SEEK_END);
986 filesize = my_ftell (fp);
989 dmp -> start_sector_xiso = (u_int32_t) (filesize / SECTOR_SIZE / SECTORS_PER_BLOCK) * SECTORS_PER_BLOCK;
990 debug ("XISO output can restart from output sector %u", dmp -> start_sector_xiso);
991 my_strdup (dmp -> outfile_xiso, outfile_xiso);
994 error ("XISO output file exists, but resume was not requested.");
996 dmp -> start_sector_xiso = -1;
997 my_free (dmp -> outfile_xiso);
998 dmp -> outfile_xiso = NULL;
1005 bool dumper_prepare_xiso (dumper *dmp) {
1007 u_int8_t buf[SECTOR_SIZE];
1011 if (!dmp -> outfile_xiso) {
1012 error ("No XISO output file defined");
1016 dmp -> start_sector = dmp -> start_sector_xiso;
1019 if (!xbox_portable_resolve_output_path (dmp, true))
1024 if (dmp -> outfile_xiso[0] == '\0' && disc_is_xbox_challenge_drive (dmp -> dsk)) {
1026 multihash_init (&(dmp -> hash_xiso));
1027 dmp -> fp_xiso = NULL;
1033 multihash_init (&(dmp -> hash_xiso));
1035 dmp -> fp_xiso = fopen (dmp -> outfile_xiso, "a+b");
1036 if (!dmp -> fp_xiso)
1039 if (dmp -> hashing) {
1040 debug ("Calculating hashes for pre-existing XISO dump data");
1041 if (dmp -> start_sector > 0) {
1042 for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_xiso)) > 0; i++)
1043 multihash_update (&(dmp -> hash_xiso), buf, SECTOR_SIZE);
1048 if (my_fseek (dmp -> fp_xiso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 &&
1049 ftruncate (fileno (dmp -> fp_xiso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) {
1051 debug ("Writing to file \"%s\" in Xbox XISO format (fseeked() to %lld)", dmp -> outfile_xiso, my_ftell (dmp -> fp_xiso));
1054 fclose (dmp -> fp_xiso);
1055 dmp -> fp_xiso = NULL;
1062 bool dumper_prepare (dumper *dmp) {
1064 u_int8_t buf[RAW_SECTOR_SIZE];
1068 /* Outputting to both files, resume must start from the file with the least sectors. Hopefully they will have the same number of sectors, anyway... */
1069 if (dmp -> outfile_raw && dmp -> outfile_iso && dmp -> start_sector_raw != dmp -> start_sector_iso) {
1070 if (dmp -> start_sector_raw < dmp -> start_sector_iso)
1071 dmp -> start_sector = dmp -> start_sector_raw;
1073 dmp -> start_sector = dmp -> start_sector_iso;
1074 } else if (dmp -> outfile_raw) {
1075 dmp -> start_sector = dmp -> start_sector_raw;
1076 } else if (dmp -> outfile_iso) {
1077 dmp -> start_sector = dmp -> start_sector_iso;
1083 if (dmp -> outfile_iso && dmp -> outfile_iso[0] == '\0' &&
1084 disc_is_xbox_challenge_drive (dmp -> dsk) &&
1085 !xbox_portable_resolve_output_path (dmp, false))
1090 if (dmp -> outfile_iso && dmp -> outfile_iso[0] == '\0' && disc_is_xbox_challenge_drive (dmp -> dsk)) {
1091 if (dmp -> hashing) {
1092 multihash_init (&(dmp -> hash_raw));
1093 multihash_init (&(dmp -> hash_iso));
1095 dmp -> fp_raw = NULL;
1096 dmp -> fp_iso = NULL;
1101 /* Prepare hashes */
1102 if (dmp -> hashing) {
1103 multihash_init (&(dmp -> hash_raw));
1104 multihash_init (&(dmp -> hash_iso));
1107 /* Setup raw output file */
1108 if (dmp -> outfile_raw) {
1109 dmp -> fp_raw = fopen (dmp -> outfile_raw, "a+b");
1111 if (dmp -> hashing) {
1112 debug ("Calculating hashes for pre-existing raw dump data");
1113 if (dmp -> start_sector > 0) {
1114 for (i = 0; i < dmp -> start_sector && (r = fread (buf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw)) > 0; i++)
1115 multihash_update (&(dmp -> hash_raw), buf, RAW_SECTOR_SIZE);
1120 /* Now call fseek as file will only be written, from now on */
1121 if (my_fseek (dmp -> fp_raw, dmp -> start_sector * RAW_SECTOR_SIZE, SEEK_SET) == 0 &&
1122 ftruncate (fileno (dmp -> fp_raw), (int64_t) dmp -> start_sector * RAW_SECTOR_SIZE) == 0) {
1124 debug ("Writing to file \"%s\" in raw format (fseeked() to %lld)", dmp -> outfile_raw, my_ftell (dmp -> fp_raw));
1127 fclose (dmp -> fp_raw);
1128 dmp -> fp_raw = NULL;
1131 dmp -> fp_raw = NULL;
1134 /* Setup ISO output file */
1135 if (dmp -> outfile_iso) {
1136 dmp -> fp_iso = fopen (dmp -> outfile_iso, "a+b");
1138 if (dmp -> hashing) {
1139 debug ("Calculating hashes for pre-existing ISO dump data");
1140 if (dmp -> start_sector > 0) {
1141 for (i = 0; i < dmp -> start_sector && (r = fread (buf, SECTOR_SIZE, 1, dmp -> fp_iso)) > 0; i++)
1142 multihash_update (&(dmp -> hash_iso), buf, SECTOR_SIZE);
1147 if (my_fseek (dmp -> fp_iso, dmp -> start_sector * SECTOR_SIZE, SEEK_SET) == 0 &&
1148 ftruncate (fileno (dmp -> fp_iso), (int64_t) dmp -> start_sector * SECTOR_SIZE) == 0) {
1150 debug ("Writing to file \"%s\" in ISO format (fseeked() to %lld)", dmp -> outfile_iso, my_ftell (dmp -> fp_iso));
1153 fclose (dmp -> fp_iso);
1154 dmp -> fp_iso = NULL;
1157 dmp -> fp_iso = NULL;
1165 static int dumper_dump_xbox_redump_iso (dumper *dmp, u_int32_t *current_sector) {
1166 xbox_redump_metadata meta;
1167 u_int8_t *video_l1 = NULL;
1168 u_int32_t locked_sectors = 0, unlocked_sectors = 0, sector_size = 0;
1169 u_int32_t game_source_lba = 0xFFFFFFFFU;
1170 u_int32_t root_lba = 0, volume_size_raw = 0;
1171 u_int32_t pregame_padding = XBOX_XGD1_GAME_OUTPUT_START_LBA - XBOX_XGD1_VIDEO_L0_SECTORS;
1172 u_int32_t postgame_padding = XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA - (XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS);
1174 bool locked_view_is_xdfs;
1177 double portable_start = 0.0;
1178 int volume_lock_result;
1183 disc_get_type (dmp -> dsk, &type_id, &type);
1184 if (type_id != DISC_TYPE_XBOX)
1187 if (dmp -> fp_raw) {
1188 error ("Xbox redump-style ISO output cannot be combined with -r/raw output in one run");
1192 if (dmp -> start_sector != 0) {
1193 error ("Resume is not supported for Xbox redump-style ISO reconstruction because the drive view changes mid-dump");
1198 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1200 if (dmp -> fp_iso) {
1201 fclose (dmp -> fp_iso);
1202 dmp -> fp_iso = NULL;
1205 *current_sector = 0;
1206 xbox_ref_log_fprintf (stderr, "[XBOX] Dispatching GDR-8050L -i directly to copied original dumper code using FriiDump's existing drive HANDLE.\n");
1207 ref_status = xbox_ref_gdr8050l_dump_with_handle (disc_get_native_handle (dmp -> dsk), disc_get_device (dmp -> dsk), dmp -> outfile_iso, '1', dumper_xbox_ref_cancel_requested, dmp, &(dmp -> xbox_ref_result));
1209 *current_sector = dmp -> xbox_ref_result.completed_sectors;
1210 return ref_status == 0;
1217 memset (&meta, 0, sizeof (meta));
1218 portable_start = xbox_portable_now_seconds ();
1219 xbox_portable_result_begin (dmp, dmp -> outfile_iso, '1');
1221 /* Original GDR-8050L dumper order for redump-style output:
1222 * 1. unlock/authenticate into the game view;
1223 * 2. read XDVDFS/game metadata while that view is available;
1224 * 3. perform a real media transition to return to the visible DVD-video view;
1225 * 4. dump video/front/tail, unlock again, then dump game data.
1227 * The video partition is not encrypted/locked; "lock" here only means the
1228 * drive-visible DVD-video state before the Xbox game-view unlock. */
1229 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1230 xbox_ref_log_fprintf (stderr, "[XBOX] GDR-8050L Linux flow mirrors the proven Windows state-aware sequence.\n");
1231 xbox_ref_log_fprintf (stderr, "[XBOX] Windows sequence 1/4: establish and verify the game view.\n");
1232 if (disc_xbox_prepare_game_view (dmp -> dsk, &unlocked_sectors, §or_size) < 0) {
1233 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Windows-sequence game-view preparation failed.\n");
1237 xbox_ref_log_fprintf (stderr, "[XBOX] Windows sequence 2/4: acquire the volume lock and probe metadata.\n");
1238 volume_lock_result = disc_xbox_lock_volume (dmp -> dsk);
1239 if (volume_lock_result < 0)
1240 xbox_ref_log_fprintf (stderr, "[XBOX][WARN] Exclusive volume lock failed; continuing like reference dumper warning path.\n");
1241 else if (volume_lock_result > 0)
1242 xbox_ref_log_fprintf (stderr, "[XBOX][INFO] Linux exclusive optical-device lock is unavailable; continuing without one.\n");
1243 disc_xbox_read_capacity_10 (dmp -> dsk, &unlocked_sectors, §or_size);
1244 meta.unlocked_visible_sectors = unlocked_sectors;
1245 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
1246 if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw)) {
1247 meta.game_source_lba = XBOX_XISO_STANDARD_GAME_LBA;
1248 meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
1249 } else if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw)) {
1250 meta.game_source_lba = XBOX_XISO_START_LBA_MAGIC;
1251 meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
1253 xbox_portable_result_capture_metadata (dmp);
1255 xbox_ref_log_fprintf (stderr, "[XBOX] Windows sequence 3/4: media cycle back to visible DVD-video view before sector 0 capture.\n");
1256 if (disc_xbox_media_cycle (dmp -> dsk) < 0) {
1257 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Stage 5 failed: could not restore visible DVD-video view.\n");
1260 /* Windows raw path: AutomateTrayCycle(); RefreshVolume(); SetDriveSpeedMax(). */
1261 disc_xbox_refresh_volume (dmp -> dsk);
1262 disc_set_speed (dmp -> dsk, 0xFFFF);
1263 } else if (disc_is_xbox_vendor_unlock_drive (dmp -> dsk)) {
1264 /* GDR-3120L/Kreon-style FF 08 01 profiles can explicitly select
1265 * state 0 for the visible DVD-video view. */
1266 if (disc_xbox_lock (dmp -> dsk) < 0)
1267 warning ("Could not set Xbox vendor lock state 0; continuing with the current visible view");
1270 disc_xbox_read_capacity_10 (dmp -> dsk, &locked_sectors, §or_size);
1271 meta.locked_visible_sectors = locked_sectors;
1272 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
1274 locked_view_is_xdfs = xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, NULL, NULL);
1275 if (locked_view_is_xdfs && disc_is_xbox_challenge_drive (dmp -> dsk)) {
1276 error ("Current GDR-8050L view still exposes XDVDFS at LBA 32 after media cycle; cannot capture visible DVD-video view for redump-style ISO");
1280 if (!xbox_redump_layout_constants_match_dic ()) {
1281 error ("Xbox redump layout constants do not match the DiscImageCreator-compatible XGD1 layout");
1285 meta.output_sectors = XBOX_XGD1_FULL_REDUMP_SECTORS;
1286 if (dmp -> progress)
1287 dmp -> progress (true, 0, meta.output_sectors, dmp -> progress_data);
1289 debug ("Xbox redump-style layout: video L0=%u, pregame padding=%u, game view=%u, postgame padding=%u, video L1=%u",
1290 XBOX_XGD1_VIDEO_L0_SECTORS, pregame_padding, XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS, postgame_padding, XBOX_XGD1_VIDEO_L1_SECTORS);
1292 video_l1 = (u_int8_t *) malloc ((size_t) XBOX_XGD1_VIDEO_L1_SECTORS * SECTOR_SIZE);
1294 error ("Could not allocate Xbox VIDEO-L1 capture buffer");
1298 /* Capture the tail of the visible DVD-video view before switching to the game view. */
1299 if (!xbox_read_range_to_memory (dmp, XBOX_XGD1_VIDEO_L0_SECTORS, XBOX_XGD1_VIDEO_L1_SECTORS,
1300 video_l1, (size_t) XBOX_XGD1_VIDEO_L1_SECTORS * SECTOR_SIZE, "VIDEO-L1"))
1303 if (!xbox_dump_read10_iso_range (dmp, 0, XBOX_XGD1_VIDEO_L0_SECTORS, 0, meta.output_sectors, "VIDEO-L0", current_sector))
1306 if (current_sector) *current_sector = XBOX_XGD1_VIDEO_L0_SECTORS;
1307 if (!xbox_write_zero_iso_range (dmp, pregame_padding, XBOX_XGD1_VIDEO_L0_SECTORS, meta.output_sectors, "PREGAME-PADDING"))
1310 xbox_ref_log_fprintf (stderr, "[XBOX] Windows sequence 4/4: re-establish and verify the game view for XDVDFS data.\n");
1311 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1312 if (disc_xbox_prepare_game_view (dmp -> dsk, &unlocked_sectors, §or_size) < 0) {
1313 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Final Windows-sequence game-view preparation failed.\n");
1317 if (disc_xbox_unlock (dmp -> dsk) < 0)
1318 xbox_ref_log_fprintf (stderr, "[XBOX][WARN] Xbox vendor unlock returned failure; XDVDFS probe remains authoritative.\n");
1319 disc_xbox_read_capacity_10 (dmp -> dsk, &unlocked_sectors, §or_size);
1321 meta.unlocked_visible_sectors = unlocked_sectors;
1322 xbox_capture_pfi_dmi (dmp -> dsk, &meta);
1324 if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_STANDARD_GAME_LBA, &root_lba, &volume_size_raw))
1325 game_source_lba = XBOX_XISO_STANDARD_GAME_LBA;
1326 else if (xbox_probe_xdfs_volume (dmp -> dsk, XBOX_XISO_START_LBA_MAGIC, &root_lba, &volume_size_raw))
1327 game_source_lba = XBOX_XISO_START_LBA_MAGIC;
1329 error ("Could not locate Xbox XDVDFS header after unlock at LBA %u or %u", XBOX_XISO_STANDARD_GAME_LBA, XBOX_XISO_START_LBA_MAGIC);
1333 meta.game_source_lba = game_source_lba;
1334 meta.game_source_sectors = XBOX_XGD1_GAME_SOURCE_SECTORS;
1335 xbox_portable_result_capture_metadata (dmp);
1336 if (game_source_lba != XBOX_XISO_STANDARD_GAME_LBA && disc_is_xbox_challenge_drive (dmp -> dsk))
1337 warning ("Xbox XDVDFS was detected at LBA %u, not the expected GDR-8050L game-view LBA 32", game_source_lba);
1339 /* FriiDump 0.5.3.13 cache-aligned raw-ID validation proved that the
1340 * unlocked GDR-8050L view exposes source LBA 0..31 as the physical
1341 * game-region lead-in immediately preceding the XDVDFS header at LBA 32.
1342 * Read those sectors from the drive; retain the existing per-sector
1343 * zero-fill fallback only for genuinely unreadable sectors. */
1344 xbox_ref_log_fprintf (stderr,
1345 "[XBOX] Capturing 32-sector game lead-in from unlocked source LBA %u..%u.\n",
1346 (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0,
1347 (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - 1 : XBOX_XISO_LEADIN_SECTORS - 1);
1348 if (!xbox_write_leadin_iso_range (dmp,
1349 (game_source_lba >= XBOX_XISO_LEADIN_SECTORS) ? game_source_lba - XBOX_XISO_LEADIN_SECTORS : 0,
1350 XBOX_XGD1_GAME_OUTPUT_START_LBA, meta.output_sectors, &meta, "redump game", current_sector))
1353 if (!xbox_dump_read10_iso_range (dmp, game_source_lba, XBOX_XGD1_GAME_SOURCE_SECTORS,
1354 XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XISO_LEADIN_SECTORS, meta.output_sectors, "GAME-XDVDFS", current_sector))
1357 if (current_sector) *current_sector = XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS;
1358 if (!xbox_write_zero_iso_range (dmp, postgame_padding, XBOX_XGD1_GAME_OUTPUT_START_LBA + XBOX_XGD1_UNLOCKED_GAME_VIEW_SECTORS,
1359 meta.output_sectors, "POSTGAME-PADDING"))
1362 if (!xbox_write_iso_sectors (dmp, video_l1, XBOX_XGD1_VIDEO_L1_SECTORS, XBOX_XGD1_VIDEO_L1_OUTPUT_START_LBA, meta.output_sectors))
1366 multihash_finish (&(dmp -> hash_iso));
1367 if (dmp -> fp_iso) {
1368 fclose (dmp -> fp_iso);
1369 dmp -> fp_iso = NULL;
1372 xbox_write_redump_metadata (dmp, &meta);
1377 /* A failed or cancelled Xbox reconstruction still owns an open output
1378 * stream. Finalize the partial streaming hash and close the file so the
1379 * native report can observe the exact completed byte count and the caller
1380 * never leaks the handle. */
1382 multihash_finish (&(dmp -> hash_iso));
1383 if (dmp -> fp_iso) {
1384 fflush (dmp -> fp_iso);
1385 fclose (dmp -> fp_iso);
1386 dmp -> fp_iso = NULL;
1391 xbox_portable_result_finish (dmp, out, meta.output_sectors, portable_start, false);
1392 /* Keep current_sector at the last phase-specific LBA on failure so the
1393 * CLI can report a useful range instead of collapsing every setup failure
1398 int dumper_dump (dumper *dmp, u_int32_t *current_sector) {
1400 u_int8_t *rawbuf, *isobuf;
1401 u_int32_t i, sectors_no, last_sector;
1403 char *type_string = NULL;
1405 bool no_unscrambling;
1409 no_unscrambling = dd -> no_unscrambling;
1410 if (fp_iso && no_unscrambling) {
1411 warning ("Output to ISO format requested, ignoring no_unscrambling!");
1412 no_unscrambling = false;
1416 disc_get_type (dmp -> dsk, &type_id, &type_string);
1417 if (type_id == DISC_TYPE_XBOX && dmp -> fp_iso)
1418 return dumper_dump_xbox_redump_iso (dmp, current_sector);
1420 sectors_no = disc_get_sectors_no (dmp -> dsk);
1422 if (dd -> start_sector != -1) {
1423 if (dd -> start_sector >= sectors_no) {
1424 error ("Cannot start dumping from sector %u as the inserted disc only has %u sectors\n", dd -> start_sector, sectors_no);
1427 warning ("Start sector forced to %u\n", dd -> start_sector);
1428 ss = dd -> start_sector;
1431 fseek (fp_iso, ss * 2048, SEEK_SET);
1433 fseek (fp_raw, ss * 2064, SEEK_SET);
1439 debug ("Starting dump process from sector %u...\n", dmp -> start_sector);
1441 /* First call to progress function */
1442 if (dmp -> progress)
1443 dmp -> progress (true, dmp -> start_sector, sectors_no, dmp -> progress_data);
1445 last_sector=sectors_no-1;
1447 for (i = dmp -> start_sector, out = true; i < sectors_no && out; i++) {
1448 if (dumper_cancel_requested (dmp)) {
1451 *current_sector = i;
1454 if (!dumper_read_sector_with_retry (dmp, i, &isobuf, &rawbuf)) {
1455 error ("Read failed after dump-level retries at sectors %u..%u", i, i + SECTORS_PER_BLOCK - 1);
1457 *(current_sector) = i;
1460 if (out && dmp -> fp_raw) {
1461 clearerr (dmp -> fp_raw);
1464 error ("NULL buffer");
1466 *(current_sector) = i;
1468 else fwrite (rawbuf, RAW_SECTOR_SIZE, 1, dmp -> fp_raw);
1469 if (ferror (dmp -> fp_raw)) {
1470 error ("fwrite() to raw output file failed");
1472 *(current_sector) = i;
1475 if (dmp -> flushing)
1476 fflush (dmp -> fp_raw);
1478 if (dmp -> hashing && out)
1479 multihash_update (&(dmp -> hash_raw), rawbuf, RAW_SECTOR_SIZE);
1482 if (out && dmp -> fp_iso) {
1483 clearerr (dmp -> fp_iso);
1486 error ("NULL buffer");
1488 *(current_sector) = i;
1490 else fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_iso);
1492 if (ferror (dmp -> fp_iso)) {
1493 error ("fwrite() to ISO output file failed");
1495 *(current_sector) = i;
1498 if (dmp -> flushing)
1499 fflush (dmp -> fp_iso);
1501 if (dmp -> hashing && out)
1502 multihash_update (&(dmp -> hash_iso), isobuf, SECTOR_SIZE);
1505 if ((i % 320 == 0) || (i == last_sector)) { //speedhack
1506 if (dmp -> progress)
1507 dmp -> progress (false, i + 1, sectors_no, dmp -> progress_data); /* i + 1 'cause sectors range from 0 to N */
1511 if (dmp -> hashing) {
1512 multihash_finish (&(dmp -> hash_raw));
1513 multihash_finish (&(dmp -> hash_iso));
1517 fclose (dmp -> fp_raw);
1519 fclose (dmp -> fp_iso);
1530 int dumper_dump_xiso (dumper *dmp, u_int32_t *current_sector) {
1532 u_int8_t *rawbuf, *isobuf;
1533 u_int8_t sector_buf[SECTOR_SIZE];
1534 u_int32_t source_lba, source_sectors, target_sectors, i, source_sector, last_sector, leadin_source_lba;
1535 bool leadin_fallback_warned;
1536 double portable_start = 0.0;
1539 error ("XISO dumper is not prepared");
1544 if (disc_is_xbox_challenge_drive (dmp -> dsk)) {
1546 if (dmp -> fp_xiso) {
1547 fclose (dmp -> fp_xiso);
1548 dmp -> fp_xiso = NULL;
1551 *current_sector = 0;
1552 xbox_ref_log_fprintf (stderr, "[XBOX] Dispatching GDR-8050L -X directly to copied original dumper code using FriiDump's existing drive HANDLE.\n");
1553 ref_status = xbox_ref_gdr8050l_dump_with_handle (disc_get_native_handle (dmp -> dsk), disc_get_device (dmp -> dsk), dmp -> outfile_xiso, '2', dumper_xbox_ref_cancel_requested, dmp, &(dmp -> xbox_ref_result));
1555 *current_sector = dmp -> xbox_ref_result.completed_sectors;
1556 return ref_status == 0;
1560 if (!dmp -> fp_xiso) {
1561 error ("XISO dumper is not prepared");
1565 portable_start = xbox_portable_now_seconds ();
1566 xbox_portable_result_begin (dmp, dmp -> outfile_xiso, '2');
1568 if (!xbox_get_xiso_range (dmp -> dsk, &source_lba, &source_sectors, &target_sectors)) {
1570 *current_sector = dmp -> start_sector;
1571 xbox_portable_result_finish (dmp, false, 0, portable_start, true);
1574 xbox_portable_result_capture_metadata (dmp);
1576 if (dmp -> start_sector >= target_sectors) {
1577 error ("Cannot resume Xbox XISO at output sector %u; target has only %u sectors", dmp -> start_sector, target_sectors);
1579 *current_sector = dmp -> start_sector;
1580 xbox_portable_result_finish (dmp, false, target_sectors, portable_start, true);
1584 memset (sector_buf, 0, sizeof (sector_buf));
1585 leadin_source_lba = (source_lba >= XBOX_XISO_LEADIN_SECTORS) ? (source_lba - XBOX_XISO_LEADIN_SECTORS) : 0;
1586 leadin_fallback_warned = false;
1588 last_sector = target_sectors - 1;
1590 debug ("Starting Xbox XISO dump from output sector %u...", dmp -> start_sector);
1591 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);
1592 if (dmp -> progress)
1593 dmp -> progress (true, dmp -> start_sector, target_sectors, dmp -> progress_data);
1595 for (i = dmp -> start_sector; i < target_sectors && out; i++) {
1596 if (dumper_cancel_requested (dmp)) {
1599 *current_sector = i;
1602 if (i < XBOX_XISO_LEADIN_SECTORS) {
1605 if (disc_xbox_read_10 (dmp -> dsk, leadin_source_lba + i, 1, sector_buf, sizeof (sector_buf)) >= 0) {
1606 isobuf = sector_buf;
1608 if (!leadin_fallback_warned) {
1609 warning ("Drive could not read one or more Xbox XISO lead-in sectors; using zero-fill fallback for unreadable lead-in sectors");
1610 leadin_fallback_warned = true;
1612 memset (sector_buf, 0, sizeof (sector_buf));
1613 isobuf = sector_buf;
1616 clearerr (dmp -> fp_xiso);
1617 fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_xiso);
1618 if (ferror (dmp -> fp_xiso)) {
1619 error ("fwrite() to XISO output file failed");
1622 *current_sector = i;
1625 if (dmp -> hashing && out)
1626 multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1628 source_sector = source_lba + (i - XBOX_XISO_LEADIN_SECTORS);
1629 isobuf = sector_buf;
1631 if (disc_xbox_read_10 (dmp -> dsk, source_sector, 1, sector_buf, sizeof (sector_buf)) < 0) {
1632 error ("Xbox XISO READ(10) failed at source LBA %u", source_sector);
1635 *current_sector = i;
1637 clearerr (dmp -> fp_xiso);
1638 fwrite (isobuf, SECTOR_SIZE, 1, dmp -> fp_xiso);
1639 if (ferror (dmp -> fp_xiso)) {
1640 error ("fwrite() to XISO output file failed");
1643 *current_sector = i;
1646 if (dmp -> hashing && out)
1647 multihash_update (&(dmp -> hash_xiso), isobuf, SECTOR_SIZE);
1651 if (dmp -> flushing)
1652 fflush (dmp -> fp_xiso);
1654 if ((i % 320 == 0) || (i == last_sector)) {
1655 if (dmp -> progress)
1656 dmp -> progress (false, i + 1, target_sectors, dmp -> progress_data);
1661 multihash_finish (&(dmp -> hash_xiso));
1663 if (dmp -> fp_xiso) {
1664 fclose (dmp -> fp_xiso);
1665 dmp -> fp_xiso = NULL;
1668 xbox_portable_result_finish (dmp, out, target_sectors, portable_start, true);
1674 dumper *dumper_new (disc *d) {
1677 dmp = (dumper *) malloc (sizeof (dumper));
1678 memset (dmp, 0, sizeof (dumper));
1680 xbox_ref_dump_result_init (&(dmp -> xbox_ref_result));
1681 dumper_set_hashing (dmp, true);
1682 dumper_set_flushing (dmp, true);
1688 void dumper_set_progress_callback (dumper *dmp, progress_func progress, void *progress_data) {
1689 dmp -> progress = progress;
1690 dmp -> progress_data = progress_data;
1696 void dumper_set_cancel_callback (dumper *dmp, dumper_cancel_func cancel, void *cancel_data) {
1699 dmp -> cancel = cancel;
1700 dmp -> cancel_data = cancel_data;
1704 bool dumper_was_cancelled (dumper *dmp) {
1705 return dmp && dmp -> cancelled;
1709 void dumper_set_hashing (dumper *dmp, bool h) {
1711 debug ("Hashing %s", h ? "enabled" : "disabled");
1717 void dumper_set_flushing (dumper *dmp, bool f) {
1718 dmp -> flushing = f;
1719 debug ("Flushing %s", f ? "enabled" : "disabled");
1724 void *dumper_destroy (dumper *dmp) {
1725 my_free (dmp -> outfile_raw);
1726 my_free (dmp -> outfile_iso);
1727 my_free (dmp -> outfile_xiso);
1734 char *dumper_get_iso_crc32 (dumper *dmp) {
1735 return ((dmp -> hash_iso).crc32_s);
1739 char *dumper_get_raw_crc32 (dumper *dmp) {
1740 return ((dmp -> hash_raw).crc32_s);
1743 char *dumper_get_xiso_crc32 (dumper *dmp) {
1744 return ((dmp -> hash_xiso).crc32_s);
1748 char *dumper_get_iso_md4 (dumper *dmp) {
1749 return ((dmp -> hash_iso).md4_s);
1753 char *dumper_get_raw_md4 (dumper *dmp) {
1754 return ((dmp -> hash_raw).md4_s);
1757 char *dumper_get_xiso_md4 (dumper *dmp) {
1758 return ((dmp -> hash_xiso).md4_s);
1762 char *dumper_get_iso_md5 (dumper *dmp) {
1763 return ((dmp -> hash_iso).md5_s);
1767 char *dumper_get_raw_md5 (dumper *dmp) {
1768 return ((dmp -> hash_raw).md5_s);
1771 char *dumper_get_xiso_md5 (dumper *dmp) {
1772 return ((dmp -> hash_xiso).md5_s);
1776 char *dumper_get_iso_ed2k (dumper *dmp) {
1777 return ((dmp -> hash_iso).ed2k_s);
1781 char *dumper_get_raw_ed2k (dumper *dmp) {
1782 return ((dmp -> hash_raw).ed2k_s);
1786 char *dumper_get_iso_sha1 (dumper *dmp) {
1787 return ((dmp -> hash_iso).sha1_s);
1791 char *dumper_get_raw_sha1 (dumper *dmp) {
1792 return ((dmp -> hash_raw).sha1_s);
1795 char *dumper_get_xiso_sha1 (dumper *dmp) {
1796 return ((dmp -> hash_xiso).sha1_s);
1800 char *dumper_get_iso_sha2 (dumper *dmp) {
1801 return ((dmp -> hash_iso).sha2_s);
1805 char *dumper_get_raw_sha2 (dumper *dmp) {
1806 return ((dmp -> hash_raw).sha2_s);
1809 char *dumper_get_xiso_sha2 (dumper *dmp) {
1810 return ((dmp -> hash_xiso).sha2_s);
1814 bool dumper_get_xbox_reference_result (dumper *dmp, xbox_ref_dump_result *result) {
1815 if (!dmp || !result || !(dmp -> xbox_ref_result).attempted)
1817 *result = dmp -> xbox_ref_result;
1821 u_int32_t dumper_get_start_sector (dumper *dmp) {
1822 return dmp ? dmp -> start_sector : 0;