1 #include "xbox_ref_bridge.h"
5 void xbox_ref_dump_result_init(xbox_ref_dump_result *result)
8 memset(result, 0, sizeof(*result));
12 #define WIN32_LEAN_AND_MEAN
13 #define _WIN32_WINNT 0x0500
20 #include "xbox_ref/utils.h"
21 #include "xbox_ref/sha1.h"
22 #include "xbox_ref/unlock.h"
23 #include "xbox_ref/xbox_ref_log.h"
25 #define printf xbox_ref_printf
27 #define XBOX_REF_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
29 static BOOL xbox_ref_capacity_is_game_view(uint32_t sectors)
31 return sectors > XBOX_REF_LOCKED_VIDEO_VIEW_MAX_SECTORS;
34 static BOOL xbox_ref_capacity_is_xgd1_locked_view(uint32_t sectors)
36 return sectors == XGD1_VIDEO_TOTAL_SECTORS;
39 static BOOL xbox_ref_capacity_is_xgd1_game_view(uint32_t sectors)
41 return sectors == REDUMP_SECTORS;
44 static uint32_t xbox_ref_refresh_ready_capacity(HANDLE hDevice)
46 RefreshVolume(hDevice);
48 EnsureDriveReady(hDevice, 30000);
49 return GetTotalSectors(hDevice);
52 static char xbox_ref_drive_letter_from_device(const char *device)
54 if (!device || !device[0]) return 0;
55 if (device[0] && device[1] == ':') return device[0];
56 if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
60 #define XGD1_LAYOUT_PROBE_SCHEMA_VERSION 1
61 #define XGD1_LAYOUT_PROBE_SECTOR_BYTES 2048U
62 #define XGD1_LAYOUT_PROBE_MAX_SAMPLES 20U
64 typedef struct xbox_ref_probe_read_s
72 uint32_t nonzero_bytes;
75 unsigned char data[XGD1_LAYOUT_PROBE_SECTOR_BYTES];
76 } xbox_ref_probe_read;
78 typedef struct xbox_ref_probe_sample_s
82 xbox_ref_probe_read read10;
83 xbox_ref_probe_read read12;
84 } xbox_ref_probe_sample;
86 typedef struct xbox_ref_probe_state_s
92 uint32_t sample_count;
93 xbox_ref_probe_sample samples[XGD1_LAYOUT_PROBE_MAX_SAMPLES];
94 } xbox_ref_probe_state;
96 typedef struct xbox_ref_probe_point_s
100 } xbox_ref_probe_point;
102 typedef struct xbox_ref_sptd_with_sense_s
104 SCSI_PASS_THROUGH_DIRECT sptd;
107 } xbox_ref_sptd_with_sense;
109 static const xbox_ref_probe_point xbox_ref_locked_probe_points[] = {
110 {0U, "locked_logical_start"},
111 {31U, "locked_early_lba_31"},
112 {32U, "locked_early_lba_32"},
113 {XGD1_VIDEO_L0_SECTORS - 1U, "video_l0_last"},
114 {XGD1_VIDEO_L0_SECTORS, "locked_video_l1_first"},
115 {XGD1_VIDEO_TOTAL_SECTORS - 1U, "locked_video_last"},
116 {XGD1_VIDEO_TOTAL_SECTORS, "locked_capacity_plus_one"},
117 {XGD1_GAME_OUTPUT_START_LBA - 1U, "pregame_output_last"},
118 {XGD1_GAME_OUTPUT_START_LBA, "game_output_start"},
119 {XGD1_REDUMP_LAYER_BREAK_LBA - 1U, "output_layer_break_minus_one"},
120 {XGD1_REDUMP_LAYER_BREAK_LBA, "output_layer_break"},
121 {XGD1_VIDEO_L1_OUTPUT_START_LBA - 1U, "postgame_output_last"},
122 {XGD1_VIDEO_L1_OUTPUT_START_LBA, "video_l1_output_start"},
123 {XGD1_FULL_REDUMP_SECTORS - 1U, "full_output_last"}
126 static const xbox_ref_probe_point xbox_ref_unlocked_probe_points[] = {
127 {0U, "unlocked_source_start"},
128 {XGD1_XISO_LEADIN_SECTORS - 1U, "unlocked_source_leadin_last"},
129 {XGD1_GAME_SOURCE_START_LBA, "xdfs_source_start"},
130 {XGD1_GAME_SOURCE_START_LBA + 1U, "xdfs_source_next"},
131 {(XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) - 1U, "synthetic_gap_length_minus_one"},
132 {XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS, "synthetic_gap_length"},
133 {XGD1_GAME_OUTPUT_START_LBA - 1U, "pregame_output_last_as_unlocked_source"},
134 {XGD1_GAME_OUTPUT_START_LBA, "game_output_start_as_unlocked_source"},
135 {(XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA) - 1U, "mapped_layer_break_source_minus_one"},
136 {XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA, "mapped_layer_break_source"},
137 {XGD1_GAME_SOURCE_START_LBA + XGD1_GAME_SOURCE_SECTORS - 1U, "unlocked_source_last"},
138 {REDUMP_SECTORS, "unlocked_capacity_plus_one"},
139 {XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS, "postgame_output_start_as_unlocked_source"},
140 {XGD1_VIDEO_L1_OUTPUT_START_LBA, "video_l1_output_start_as_unlocked_source"}
143 static void xbox_ref_probe_parse_sense(const UCHAR *sense,
150 if (sense_key) *sense_key = 0;
156 response = (UCHAR)(sense[0] & 0x7f);
157 if (response == 0x70 || response == 0x71)
159 if (sense_key) *sense_key = (UCHAR)(sense[2] & 0x0f);
160 if (asc) *asc = sense[12];
161 if (ascq) *ascq = sense[13];
163 else if (response == 0x72 || response == 0x73)
165 if (sense_key) *sense_key = (UCHAR)(sense[1] & 0x0f);
166 if (asc) *asc = sense[2];
167 if (ascq) *ascq = sense[3];
171 static void xbox_ref_probe_sha1(const unsigned char *data,
176 unsigned char digest[20];
182 if (!data || data_size == 0)
186 SHA1_Update(&ctx, data, data_size);
187 SHA1_Final(digest, &ctx);
189 for (i = 0; i < 20U; ++i)
190 sprintf(out_sha1 + (i * 2U), "%02x", digest[i]);
194 static void xbox_ref_probe_read_one(HANDLE hDevice,
197 xbox_ref_probe_read *result)
199 xbox_ref_sptd_with_sense packet;
200 unsigned char *buffer;
201 DWORD bytes_returned;
207 memset(result, 0, sizeof(*result));
209 buffer = (unsigned char *)VirtualAlloc(NULL,
210 XGD1_LAYOUT_PROBE_SECTOR_BYTES,
211 MEM_COMMIT | MEM_RESERVE,
215 result->win32_error = ERROR_NOT_ENOUGH_MEMORY;
219 memset(buffer, 0, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
220 memset(&packet, 0, sizeof(packet));
222 packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
223 packet.sptd.CdbLength = (opcode == 0xA8) ? 12 : 10;
224 packet.sptd.SenseInfoLength = sizeof(packet.sense);
225 packet.sptd.DataIn = SCSI_IOCTL_DATA_IN;
226 packet.sptd.DataTransferLength = XGD1_LAYOUT_PROBE_SECTOR_BYTES;
227 packet.sptd.TimeOutValue = 10;
228 packet.sptd.DataBuffer = buffer;
229 packet.sptd.SenseInfoOffset = offsetof(xbox_ref_sptd_with_sense, sense);
230 packet.sptd.Cdb[0] = opcode;
231 packet.sptd.Cdb[2] = (UCHAR)((lba >> 24) & 0xff);
232 packet.sptd.Cdb[3] = (UCHAR)((lba >> 16) & 0xff);
233 packet.sptd.Cdb[4] = (UCHAR)((lba >> 8) & 0xff);
234 packet.sptd.Cdb[5] = (UCHAR)(lba & 0xff);
238 packet.sptd.Cdb[9] = 1;
242 packet.sptd.Cdb[8] = 1;
246 ioctl_ok = DeviceIoControl(hDevice,
247 IOCTL_SCSI_PASS_THROUGH_DIRECT,
255 result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
256 result->scsi_status = packet.sptd.ScsiStatus;
257 xbox_ref_probe_parse_sense(packet.sense,
261 result->read_ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
265 memcpy(result->data, buffer, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
266 result->nonzero_bytes = 0;
267 for (i = 0; i < XGD1_LAYOUT_PROBE_SECTOR_BYTES; ++i)
270 result->nonzero_bytes++;
272 result->all_zero = (result->nonzero_bytes == 0U) ? 1 : 0;
273 xbox_ref_probe_sha1(buffer,
274 XGD1_LAYOUT_PROBE_SECTOR_BYTES,
278 VirtualFree(buffer, 0, MEM_RELEASE);
281 static void xbox_ref_probe_capture_state(HANDLE hDevice,
282 xbox_ref_probe_state *state,
283 const xbox_ref_probe_point *points,
284 uint32_t point_count)
288 if (!state || !points)
291 if (point_count > XGD1_LAYOUT_PROBE_MAX_SAMPLES)
292 point_count = XGD1_LAYOUT_PROBE_MAX_SAMPLES;
294 state->sample_count = point_count;
295 for (i = 0; i < point_count; ++i)
297 state->samples[i].lba = points[i].lba;
298 state->samples[i].label = points[i].label;
300 printf("[XGD1-PROBE] %s: probing LBA %u (%s) with READ(10) and READ(12).\n",
301 state->name ? state->name : "state",
303 points[i].label ? points[i].label : "unlabeled");
305 xbox_ref_probe_read_one(hDevice,
308 &state->samples[i].read10);
309 xbox_ref_probe_read_one(hDevice,
312 &state->samples[i].read12);
316 static void xbox_ref_probe_json_string(FILE *json, const char *value)
318 const unsigned char *p;
326 p = (const unsigned char *)value;
331 case '\\': fputs("\\\\", json); break;
332 case '"': fputs("\\\"", json); break;
333 case '\b': fputs("\\b", json); break;
334 case '\f': fputs("\\f", json); break;
335 case '\n': fputs("\\n", json); break;
336 case '\r': fputs("\\r", json); break;
337 case '\t': fputs("\\t", json); break;
340 fprintf(json, "\\u%04x", (unsigned int)*p);
350 static void xbox_ref_probe_json_hex(FILE *json,
351 const unsigned char *data,
361 for (i = 0; i < data_size; ++i)
362 fprintf(json, "%02x", data[i]);
367 static void xbox_ref_probe_write_cdb(FILE *json,
377 "\"a800%08x000000010000\"",
383 "\"2800%08x00000100\"",
388 static void xbox_ref_probe_write_read(FILE *json,
391 const xbox_ref_probe_read *read)
396 fprintf(json, "%s", indent);
397 xbox_ref_probe_json_string(json, name);
398 fprintf(json, ": {\n");
399 fprintf(json, "%s \"read_ok\": %s,\n", indent, read->read_ok ? "true" : "false");
400 fprintf(json, "%s \"win32_error\": %lu,\n", indent, (unsigned long)read->win32_error);
401 fprintf(json, "%s \"scsi_status\": %u,\n", indent, (unsigned int)read->scsi_status);
402 fprintf(json, "%s \"sense_key\": %u,\n", indent, (unsigned int)read->sense_key);
403 fprintf(json, "%s \"asc\": %u,\n", indent, (unsigned int)read->asc);
404 fprintf(json, "%s \"ascq\": %u,\n", indent, (unsigned int)read->ascq);
407 fprintf(json, "%s \"nonzero_bytes\": %u,\n", indent, read->nonzero_bytes);
408 fprintf(json, "%s \"all_zero\": %s,\n", indent, read->all_zero ? "true" : "false");
412 fprintf(json, "%s \"nonzero_bytes\": null,\n", indent);
413 fprintf(json, "%s \"all_zero\": null,\n", indent);
415 fprintf(json, "%s \"sha1\": ", indent);
417 xbox_ref_probe_json_string(json, read->sha1);
419 fprintf(json, "null");
420 fprintf(json, ",\n");
421 fprintf(json, "%s \"data_hex\": ", indent);
423 xbox_ref_probe_json_hex(json, read->data, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
425 xbox_ref_probe_json_string(json, "");
426 fprintf(json, "\n%s}", indent);
429 static void xbox_ref_probe_write_state(FILE *json,
430 const xbox_ref_probe_state *state,
435 fprintf(json, " {\n");
436 fprintf(json, " \"name\": ");
437 xbox_ref_probe_json_string(json, state->name);
438 fprintf(json, ",\n");
439 fprintf(json, " \"capacity_valid\": %s,\n", state->capacity_valid ? "true" : "false");
440 fprintf(json, " \"capacity_sectors\": %u,\n", state->capacity);
441 fprintf(json, " \"state_verified\": %s,\n", state->state_verified ? "true" : "false");
442 fprintf(json, " \"samples\": [\n");
444 for (i = 0; i < state->sample_count; ++i)
446 const xbox_ref_probe_sample *sample = &state->samples[i];
449 if (sample->read10.read_ok && sample->read12.read_ok)
451 identical = (memcmp(sample->read10.data,
453 XGD1_LAYOUT_PROBE_SECTOR_BYTES) == 0) ? 1 : 0;
456 fprintf(json, " {\n");
457 fprintf(json, " \"lba\": %u,\n", sample->lba);
458 fprintf(json, " \"label\": ");
459 xbox_ref_probe_json_string(json, sample->label);
460 fprintf(json, ",\n");
461 fprintf(json, " \"read10_cdb_hex\": ");
462 xbox_ref_probe_write_cdb(json, 0x28, sample->lba);
463 fprintf(json, ",\n");
464 fprintf(json, " \"read12_cdb_hex\": ");
465 xbox_ref_probe_write_cdb(json, 0xA8, sample->lba);
466 fprintf(json, ",\n");
467 xbox_ref_probe_write_read(json, " ", "read10", &sample->read10);
468 fprintf(json, ",\n");
469 xbox_ref_probe_write_read(json, " ", "read12", &sample->read12);
470 fprintf(json, ",\n");
472 " \"read10_read12_comparable\": %s,\n",
473 (sample->read10.read_ok && sample->read12.read_ok) ? "true" : "false");
474 fprintf(json, " \"read10_read12_identical\": ");
475 if (sample->read10.read_ok && sample->read12.read_ok)
476 fprintf(json, "%s\n", identical ? "true" : "false");
478 fprintf(json, "null\n");
479 fprintf(json, " }%s\n", (i + 1U < state->sample_count) ? "," : "");
482 fprintf(json, " ]\n");
483 fprintf(json, " }%s\n", trailing_comma ? "," : "");
486 static BOOL xbox_ref_probe_write_report(const char *report_path,
488 uint32_t entry_capacity,
490 int volume_lock_acquired,
491 const xbox_ref_probe_state *locked_state,
492 const xbox_ref_probe_state *unlocked_state,
493 int restore_attempted,
494 uint32_t restored_capacity,
495 int restore_verified)
497 char tmp_path[XBOX_REF_RESULT_PATH_MAX];
498 const char *final_path;
503 final_path = (report_path && report_path[0]) ? report_path : "xgd1_layout_probe.json";
505 int path_length = snprintf(tmp_path,
509 (unsigned long)GetCurrentProcessId());
510 if (path_length < 0 || (size_t)path_length >= sizeof(tmp_path))
512 printf("[XGD1-PROBE] Fatal: report path is too long.\n");
517 json = fopen(tmp_path, "wb");
520 printf("[XGD1-PROBE] Fatal: could not create temporary report %s.\n", tmp_path);
524 fprintf(json, "{\n");
525 fprintf(json, " \"schema_version\": %d,\n", XGD1_LAYOUT_PROBE_SCHEMA_VERSION);
526 fprintf(json, " \"producer\": \"friidump-0.5.3.11\",\n");
527 fprintf(json, " \"probe\": \"original_xbox_xgd1_logical_layout_boundary\",\n");
528 fprintf(json, " \"device\": ");
529 xbox_ref_probe_json_string(json, device ? device : "");
530 fprintf(json, ",\n");
531 fprintf(json, " \"safety\": {\n");
532 fprintf(json, " \"read_only_sector_commands\": true,\n");
533 fprintf(json, " \"authentication_handshake_used\": true,\n");
534 fprintf(json, " \"tray_cycle_used\": true,\n");
535 fprintf(json, " \"firmware_write_used\": false,\n");
536 fprintf(json, " \"flash_command_used\": false\n");
537 fprintf(json, " },\n");
538 fprintf(json, " \"constants\": {\n");
539 fprintf(json, " \"full_output_sectors\": %u,\n", XGD1_FULL_REDUMP_SECTORS);
540 fprintf(json, " \"game_output_start_lba\": %u,\n", XGD1_GAME_OUTPUT_START_LBA);
541 fprintf(json, " \"game_view_sectors\": %u,\n", REDUMP_SECTORS);
542 fprintf(json, " \"video_total_sectors\": %u,\n", XGD1_VIDEO_TOTAL_SECTORS);
543 fprintf(json, " \"video_l0_sectors\": %u,\n", XGD1_VIDEO_L0_SECTORS);
544 fprintf(json, " \"video_l1_sectors\": %u,\n", XGD1_VIDEO_L1_SECTORS);
545 fprintf(json, " \"video_l1_output_start_lba\": %u,\n", XGD1_VIDEO_L1_OUTPUT_START_LBA);
546 fprintf(json, " \"output_layer_break_lba\": %u\n", XGD1_REDUMP_LAYER_BREAK_LBA);
547 fprintf(json, " },\n");
548 fprintf(json, " \"entry\": {\n");
549 fprintf(json, " \"capacity_sectors\": %u,\n", entry_capacity);
550 fprintf(json, " \"game_view\": %s,\n", entry_game_view ? "true" : "false");
551 fprintf(json, " \"classification\": ");
552 if (xbox_ref_capacity_is_xgd1_game_view(entry_capacity))
553 xbox_ref_probe_json_string(json, "unlocked_game");
554 else if (xbox_ref_capacity_is_xgd1_locked_view(entry_capacity))
555 xbox_ref_probe_json_string(json, "locked_video");
557 xbox_ref_probe_json_string(json, "other_or_unknown");
559 fprintf(json, " },\n");
560 fprintf(json, " \"volume_lock_acquired_at_least_once\": %s,\n", volume_lock_acquired ? "true" : "false");
562 " \"required_states_verified\": %s,\n",
563 (locked_state->state_verified && unlocked_state->state_verified) ? "true" : "false");
564 fprintf(json, " \"states\": [\n");
565 xbox_ref_probe_write_state(json, locked_state, 1);
566 xbox_ref_probe_write_state(json, unlocked_state, 0);
567 fprintf(json, " ],\n");
568 fprintf(json, " \"restoration\": {\n");
569 fprintf(json, " \"attempted\": %s,\n", restore_attempted ? "true" : "false");
570 fprintf(json, " \"capacity_sectors\": %u,\n", restored_capacity);
571 fprintf(json, " \"verified\": %s\n", restore_verified ? "true" : "false");
572 fprintf(json, " },\n");
573 fprintf(json, " \"interpretation_boundary\": {\n");
574 fprintf(json, " \"pregame_padding_resolved\": false,\n");
575 fprintf(json, " \"postgame_padding_resolved\": false,\n");
576 fprintf(json, " \"game_leadin_resolved\": false,\n");
577 fprintf(json, " \"note\": \"This report records ordinary logical READ(10)/READ(12) behavior in locked/video and unlocked/game states. It does not claim that inaccessible physical filler sectors are zero or that the current reconstructed image is Redump-exact.\"\n");
578 fprintf(json, " }\n");
579 fprintf(json, "}\n");
581 if (fflush(json) != 0)
584 DeleteFileA(tmp_path);
589 if (fd < 0 || _commit(fd) != 0)
592 DeleteFileA(tmp_path);
596 if (fclose(json) != 0)
598 DeleteFileA(tmp_path);
602 moved = MoveFileExA(tmp_path,
604 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
607 printf("[XGD1-PROBE] Fatal: could not atomically publish report %s (error %lu).\n",
609 (unsigned long)GetLastError());
610 DeleteFileA(tmp_path);
614 printf("[XGD1-PROBE] Atomic report written: %s\n", final_path);
618 static int xbox_ref_xgd1_layout_probe_core(HANDLE hDevice,
620 const char *report_path,
623 xbox_ref_probe_state locked_state;
624 xbox_ref_probe_state unlocked_state;
625 uint32_t entry_capacity;
626 uint32_t restored_capacity;
628 int volume_lock_acquired;
629 int volume_lock_ever;
630 int restore_attempted;
631 int restore_verified;
633 DWORD bytes_returned;
636 memset(&locked_state, 0, sizeof(locked_state));
637 memset(&unlocked_state, 0, sizeof(unlocked_state));
638 locked_state.name = "locked_video";
639 unlocked_state.name = "unlocked_game";
641 restored_capacity = 0;
643 volume_lock_acquired = 0;
644 volume_lock_ever = 0;
645 restore_attempted = 0;
646 restore_verified = 0;
650 xbox_ref_log_open_for_target(report_path, xbox_ref_drive_letter_from_device(device));
651 printf("[XGD1-PROBE] Starting read-only logical layout boundary probe.\n");
652 printf("[XGD1-PROBE] The existing XGD1 output layout will not be modified.\n");
654 if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
656 printf("[XGD1-PROBE] Fatal: invalid drive handle.\n");
658 xbox_ref_log_close();
662 if (!EnsureDriveReady(hDevice, 30000))
664 printf("[XGD1-PROBE] Fatal: media did not become ready.\n");
666 xbox_ref_log_close();
670 entry_capacity = GetTotalSectors(hDevice);
671 entry_game_view = xbox_ref_capacity_is_xgd1_game_view(entry_capacity) ? 1 : 0;
672 printf("[XGD1-PROBE] Entry READ CAPACITY: %u sectors (%s).\n",
674 entry_game_view ? "game view" : "locked/video or unknown view");
676 DeviceIoControl(hDevice,
685 printf("[XGD1-PROBE] Cycling tray to establish locked/video view.\n");
686 AutomateTrayCycle(hDevice);
687 locked_state.capacity = xbox_ref_refresh_ready_capacity(hDevice);
688 locked_state.capacity_valid = locked_state.capacity != 0U;
689 locked_state.state_verified = (locked_state.capacity_valid &&
690 xbox_ref_capacity_is_xgd1_locked_view(locked_state.capacity)) ? 1 : 0;
691 printf("[XGD1-PROBE] Locked/video READ CAPACITY: %u sectors; verified=%s.\n",
692 locked_state.capacity,
693 locked_state.state_verified ? "yes" : "no");
695 if (DeviceIoControl(hDevice,
704 volume_lock_acquired = 1;
705 volume_lock_ever = 1;
706 printf("[XGD1-PROBE] Windows volume lock acquired.\n");
710 printf("[XGD1-PROBE][WARN] Windows volume lock was not acquired; close AutoPlay and Explorer dialogs if results are unstable.\n");
713 SetDriveSpeedMax(hDevice);
714 xbox_ref_probe_capture_state(hDevice,
716 xbox_ref_locked_probe_points,
717 (uint32_t)(sizeof(xbox_ref_locked_probe_points) /
718 sizeof(xbox_ref_locked_probe_points[0])));
720 printf("[XGD1-PROBE] Applying the full Xbox handshake to establish game view.\n");
721 UnlockDrive(hDevice);
722 unlocked_state.capacity = xbox_ref_refresh_ready_capacity(hDevice);
723 unlocked_state.capacity_valid = unlocked_state.capacity != 0U;
724 unlocked_state.state_verified = (unlocked_state.capacity_valid &&
725 xbox_ref_capacity_is_xgd1_game_view(unlocked_state.capacity)) ? 1 : 0;
726 printf("[XGD1-PROBE] Unlocked/game READ CAPACITY: %u sectors; verified=%s.\n",
727 unlocked_state.capacity,
728 unlocked_state.state_verified ? "yes" : "no");
730 xbox_ref_probe_capture_state(hDevice,
732 xbox_ref_unlocked_probe_points,
733 (uint32_t)(sizeof(xbox_ref_unlocked_probe_points) /
734 sizeof(xbox_ref_unlocked_probe_points[0])));
736 if (volume_lock_acquired)
738 DeviceIoControl(hDevice,
746 volume_lock_acquired = 0;
749 if (!entry_game_view && entry_capacity != 0U)
751 restore_attempted = 1;
752 printf("[XGD1-PROBE] Restoring the entry locked/video state with a final tray cycle.\n");
753 AutomateTrayCycle(hDevice);
754 restored_capacity = xbox_ref_refresh_ready_capacity(hDevice);
755 restore_verified = (restored_capacity != 0U &&
756 xbox_ref_capacity_is_xgd1_locked_view(restored_capacity)) ? 1 : 0;
760 restored_capacity = GetTotalSectors(hDevice);
761 restore_verified = (entry_game_view &&
762 xbox_ref_capacity_is_xgd1_game_view(restored_capacity)) ? 1 : 0;
765 states_verified = (locked_state.state_verified &&
766 unlocked_state.state_verified) ? 1 : 0;
768 report_ok = xbox_ref_probe_write_report(report_path,
781 printf("[XGD1-PROBE] Issuing STOP UNIT / spin-down after probe... ");
782 printf("%s\n", StopDriveUnit(hDevice) ? "OK" : "FAILED");
784 xbox_ref_log_close();
789 if (!states_verified)
791 printf("[XGD1-PROBE] Probe report is partial because one or more drive states were not verified.\n");
795 printf("[XGD1-PROBE] Probe complete. Synthetic ranges remain unresolved until the captured evidence supports a source mapping.\n");
800 static int xbox_ref_gdr8050l_dump_core(HANDLE hDevice, const char *device, const char *filename, char mode, BOOL ownsHandle, xbox_ref_dump_result *result)
804 BOOL isDualLayer = FALSE;
805 bool isDualLayerBool = false;
806 char mediaId[33] = {0};
807 char finalFilename[MAX_PATH] = {0};
808 BOOL filenameOverride;
809 uint32_t totalSectors = 0;
810 DWORD dwBytesReturned = 0;
811 BOOL volumeLocked = FALSE;
814 xbox_ref_dump_result_init(result);
816 result->attempted = 1;
820 filenameOverride = (filename && filename[0]);
822 if (mode != '1' && mode != '2') {
823 xbox_ref_log_open_for_target(filename, 0);
824 printf("[XBOX-REF] Fatal: unsupported Xbox reference dump mode '%c'.\n", mode);
826 xbox_ref_log_close();
830 driveLetter = xbox_ref_drive_letter_from_device(device);
832 xbox_ref_log_open_for_target(filename, 0);
833 printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
835 xbox_ref_log_close();
839 xbox_ref_log_open_for_target(filename, driveLetter);
840 printf("[XBOX-REF] Using copied original GDR-8050L dumper flow for drive %c:.\n", driveLetter);
841 printf("[XBOX-REF] Handle mode: %s.\n", ownsHandle ? "opened by xbox_ref wrapper" : "using FriiDump's existing drive HANDLE");
842 printf("[XBOX-REF] Preserving reference timing sleeps: tray 3000ms, ready-settle 1500ms/10000ms fallback, RefreshVolume 1000ms + post-refresh 2000ms.\n");
844 if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
845 printf("[XBOX-REF] Fatal: invalid drive handle for %c:.\n", driveLetter);
850 uint32_t entrySectors;
851 uint32_t verifiedSectors;
852 BOOL preparedAfterHandshake = FALSE;
854 printf("Opening drive %c: for state-aware Xbox handshake...\n", driveLetter);
855 if (!EnsureDriveReady(hDevice, 30000)) {
856 printf("[XBOX-REF] Fatal: drive did not become ready before Xbox state preparation.\n");
860 entrySectors = GetTotalSectors(hDevice);
861 printf("[XBOX-REF] Entry READ CAPACITY: %u sectors.\n", entrySectors);
863 if (xbox_ref_capacity_is_game_view(entrySectors)) {
864 printf("[XBOX-REF] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
866 printf("[XBOX-REF] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
867 UnlockDrive(hDevice);
868 verifiedSectors = xbox_ref_refresh_ready_capacity(hDevice);
869 preparedAfterHandshake = TRUE;
870 printf("[XBOX-REF] Direct-handshake READ CAPACITY: %u sectors.\n", verifiedSectors);
872 if (!xbox_ref_capacity_is_game_view(verifiedSectors)) {
873 printf("[XBOX-REF][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
874 AutomateTrayCycle(hDevice);
875 printf("[XBOX-REF] Re-applying the full handshake after recovery media change.\n");
876 UnlockDrive(hDevice);
877 verifiedSectors = xbox_ref_refresh_ready_capacity(hDevice);
878 printf("[XBOX-REF] Recovery-handshake READ CAPACITY: %u sectors.\n", verifiedSectors);
880 if (!xbox_ref_capacity_is_game_view(verifiedSectors)) {
881 printf("[XBOX-REF] Fatal: Xbox game view was not established after direct and recovery handshakes.\n");
887 if (!preparedAfterHandshake)
888 xbox_ref_refresh_ready_capacity(hDevice);
891 printf("Setting drive to maximum performance mode...\n");
892 SetDriveSpeedMax(hDevice);
894 if (!DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL)) {
895 printf("[!] Warning: Could not lock volume. Close any open folders pointing to the drive.\n");
896 printf("[XBOX-REF] The reference dumper treats this as fatal before XBE/title probing.\n");
901 printf("Searching for Xbox Game Signature (Raw SCSI Scan)...\n");
902 game = GetXboxGameInfo(hDevice);
903 printf("Game Title Raw: %s\n", game.TitleName);
904 ReadXboxGameDir(hDevice);
906 totalSectors = GetXboxPhysicalSectors(hDevice);
907 isDualLayerBool = false;
908 GetDiscMetadata(hDevice, &totalSectors, &isDualLayerBool, &game);
909 isDualLayer = isDualLayerBool ? TRUE : FALSE;
910 GetMediaID(hDevice, mediaId);
913 if (game.Success && game.TitleName[0]) {
914 strncpy(result->title, game.TitleName, sizeof(result->title) - 1);
915 result->title[sizeof(result->title) - 1] = '\0';
918 strncpy(result->media_id, mediaId, sizeof(result->media_id) - 1);
919 result->media_id[sizeof(result->media_id) - 1] = '\0';
924 char titleCopy[sizeof(game.TitleName)];
925 memset(titleCopy, 0, sizeof(titleCopy));
926 strncpy(titleCopy, game.TitleName, sizeof(titleCopy) - 1);
927 SanitizeFilename(titleCopy);
928 if (filenameOverride) {
929 strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
930 finalFilename[sizeof(finalFilename) - 1] = '\0';
931 printf("[XBOX-REF] Output filename override: %s\n", finalFilename);
933 snprintf(finalFilename, sizeof(finalFilename), "%s[%s].%s",
934 titleCopy[0] ? titleCopy : "XboxDisc",
935 mediaId[0] ? mediaId : "UNKNOWN_ID",
936 (mode == '1') ? "iso" : "xiso");
937 printf("[XBOX-REF] XBE/DMI-derived output filename: %s\n", finalFilename);
938 xbox_ref_log_retarget(finalFilename);
940 DisplayXboxGameInfo(game);
941 printf("\n--- DISC INFORMATION ---\n");
942 printf("Physical Sectors: %u\n", totalSectors);
943 printf("Layers: %d\n", isDualLayer ? 2 : 1);
944 printf("Media ID: %s\n", mediaId);
945 printf("Target File: %s\n", finalFilename);
946 printf("-------------------------------\n\n");
948 if (filenameOverride) {
949 strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
950 finalFilename[sizeof(finalFilename) - 1] = '\0';
951 printf("Error: Could not retrieve Xbox game information; using override filename %s.\n", finalFilename);
953 printf("Error: Could not retrieve Xbox game information and no output filename override was supplied.\n");
954 printf(" Rerun with -i <file> or -X <file> if metadata probing cannot complete.\n");
959 if (result && finalFilename[0]) {
960 strncpy(result->output_path, finalFilename, sizeof(result->output_path) - 1);
961 result->output_path[sizeof(result->output_path) - 1] = '\0';
964 if (totalSectors != (uint32_t)(REDUMP_SECTORS)) {
965 printf("[WARNING] Unlocked drive-reported sectors: %u does not match expected Xbox game-view sectors: %u\n", totalSectors, (uint32_t)(REDUMP_SECTORS));
966 printf("-Homebrew games may have different sizes.\n\n");
969 if (totalSectors == 0) {
970 printf("Error: Could not determine total sectors for dumping.\n");
974 printf("Starting dump with copied DumpXboxGameDisc() mode %c...\n", mode);
975 ok = DumpXboxGameDisc(hDevice, finalFilename, mode, totalSectors, isDualLayerBool, FALSE, result);
979 DeviceIoControl(hDevice, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
980 if (ownsHandle && hDevice != INVALID_HANDLE_VALUE && hDevice != NULL) {
981 printf("Issuing STOP UNIT / spin-down after dump attempt... ");
982 if (StopDriveUnit(hDevice)) printf("OK\n"); else printf("FAILED\n");
985 printf("[XBOX-REF] Returning to FriiDump; final STOP UNIT cleanup will use the same handle.\n");
988 printf("[XBOX-REF] Log file complete: %s\n", xbox_ref_log_path() ? xbox_ref_log_path() : "(unavailable)");
989 xbox_ref_log_close();
991 printf("[XBOX-REF] Returning finalized output evidence to FriiDump's shared Redump verifier.\n");
993 printf("[XBOX-REF] No completed output evidence is available for shared Redump verification.\n");
998 int xbox_ref_xgd1_layout_probe_with_handle(void *native_handle,
1000 const char *report_path)
1002 return xbox_ref_xgd1_layout_probe_core((HANDLE)native_handle,
1008 int xbox_ref_xgd1_layout_probe(const char *device,
1009 const char *report_path)
1014 drive_letter = xbox_ref_drive_letter_from_device(device);
1017 xbox_ref_log_open_for_target(report_path, 0);
1018 printf("[XGD1-PROBE] Fatal: could not parse drive letter from device '%s'.\n",
1019 device ? device : "(null)");
1020 xbox_ref_log_close();
1024 hDevice = OpenDrive(drive_letter);
1025 if (hDevice == INVALID_HANDLE_VALUE)
1027 xbox_ref_log_open_for_target(report_path, drive_letter);
1028 printf("[XGD1-PROBE] Fatal: cannot open drive %c:. Run as Administrator.\n",
1030 xbox_ref_log_close();
1034 return xbox_ref_xgd1_layout_probe_core(hDevice,
1040 int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1042 return xbox_ref_gdr8050l_dump_core((HANDLE)native_handle, device, filename, mode, FALSE, result);
1045 int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1050 driveLetter = xbox_ref_drive_letter_from_device(device);
1052 xbox_ref_log_open_for_target(filename, 0);
1053 printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
1054 xbox_ref_log_close();
1058 hDevice = OpenDrive(driveLetter);
1059 if (hDevice == INVALID_HANDLE_VALUE) {
1060 xbox_ref_log_open_for_target(filename, driveLetter);
1061 printf("[XBOX-REF] Fatal: Cannot open drive %c:. Run as Administrator.\n", driveLetter);
1062 xbox_ref_log_close();
1066 return xbox_ref_gdr8050l_dump_core(hDevice, device, filename, mode, TRUE, result);
1069 int xbox_ref_xgd1_layout_probe_with_handle(void *native_handle,
1071 const char *report_path)
1073 (void)native_handle;
1079 int xbox_ref_xgd1_layout_probe(const char *device,
1080 const char *report_path)
1087 int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1089 (void)native_handle; (void)device; (void)filename; (void)mode; xbox_ref_dump_result_init(result); return 1;
1092 int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1094 (void)device; (void)filename; (void)mode; xbox_ref_dump_result_init(result); return 1;