1 #include "xbox_ref_bridge.h"
2 #include "xbox_region.h"
6 void xbox_ref_dump_result_init(xbox_ref_dump_result *result)
9 memset(result, 0, sizeof(*result));
13 #define WIN32_LEAN_AND_MEAN
14 #define _WIN32_WINNT 0x0500
21 #include "xbox_ref/utils.h"
22 #include "xbox_ref/sha1.h"
23 #include "xbox_ref/unlock.h"
24 #include "xbox_ref/xbox_ref_log.h"
26 #define printf xbox_ref_printf
28 #define XBOX_REF_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
30 static BOOL xbox_ref_capacity_is_game_view(uint32_t sectors)
32 return sectors > XBOX_REF_LOCKED_VIDEO_VIEW_MAX_SECTORS;
35 static BOOL xbox_ref_capacity_is_xgd1_locked_view(uint32_t sectors)
37 return sectors == XGD1_VIDEO_TOTAL_SECTORS;
40 static BOOL xbox_ref_capacity_is_xgd1_game_view(uint32_t sectors)
42 return sectors == REDUMP_SECTORS;
45 static uint32_t xbox_ref_refresh_ready_capacity(HANDLE hDevice)
47 RefreshVolume(hDevice);
49 EnsureDriveReady(hDevice, 30000);
50 return GetTotalSectors(hDevice);
53 static char xbox_ref_drive_letter_from_device(const char *device)
55 if (!device || !device[0]) return 0;
56 if (device[0] && device[1] == ':') return device[0];
57 if (device[0] == '\\' && device[1] == '\\' && device[2] == '.' && device[3] == '\\' && device[4] && device[5] == ':') return device[4];
61 #define XGD1_LAYOUT_PROBE_SCHEMA_VERSION 1
62 #define XGD1_LAYOUT_PROBE_SECTOR_BYTES 2048U
63 #define XGD1_LAYOUT_PROBE_MAX_SAMPLES 20U
65 typedef struct xbox_ref_probe_read_s
73 uint32_t nonzero_bytes;
76 unsigned char data[XGD1_LAYOUT_PROBE_SECTOR_BYTES];
77 } xbox_ref_probe_read;
79 typedef struct xbox_ref_probe_sample_s
83 xbox_ref_probe_read read10;
84 xbox_ref_probe_read read12;
85 } xbox_ref_probe_sample;
87 typedef struct xbox_ref_probe_state_s
93 uint32_t sample_count;
94 xbox_ref_probe_sample samples[XGD1_LAYOUT_PROBE_MAX_SAMPLES];
95 } xbox_ref_probe_state;
97 typedef struct xbox_ref_probe_point_s
101 } xbox_ref_probe_point;
103 typedef struct xbox_ref_sptd_with_sense_s
105 SCSI_PASS_THROUGH_DIRECT sptd;
108 } xbox_ref_sptd_with_sense;
110 static const xbox_ref_probe_point xbox_ref_locked_probe_points[] = {
111 {0U, "locked_logical_start"},
112 {31U, "locked_early_lba_31"},
113 {32U, "locked_early_lba_32"},
114 {XGD1_VIDEO_L0_SECTORS - 1U, "video_l0_last"},
115 {XGD1_VIDEO_L0_SECTORS, "locked_video_l1_first"},
116 {XGD1_VIDEO_TOTAL_SECTORS - 1U, "locked_video_last"},
117 {XGD1_VIDEO_TOTAL_SECTORS, "locked_capacity_plus_one"},
118 {XGD1_GAME_OUTPUT_START_LBA - 1U, "pregame_output_last"},
119 {XGD1_GAME_OUTPUT_START_LBA, "game_output_start"},
120 {XGD1_REDUMP_LAYER_BREAK_LBA - 1U, "output_layer_break_minus_one"},
121 {XGD1_REDUMP_LAYER_BREAK_LBA, "output_layer_break"},
122 {XGD1_VIDEO_L1_OUTPUT_START_LBA - 1U, "postgame_output_last"},
123 {XGD1_VIDEO_L1_OUTPUT_START_LBA, "video_l1_output_start"},
124 {XGD1_FULL_REDUMP_SECTORS - 1U, "full_output_last"}
127 static const xbox_ref_probe_point xbox_ref_unlocked_probe_points[] = {
128 {0U, "unlocked_source_start"},
129 {XGD1_XISO_LEADIN_SECTORS - 1U, "unlocked_source_leadin_last"},
130 {XGD1_GAME_SOURCE_START_LBA, "xdfs_source_start"},
131 {XGD1_GAME_SOURCE_START_LBA + 1U, "xdfs_source_next"},
132 {(XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) - 1U, "synthetic_gap_length_minus_one"},
133 {XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS, "synthetic_gap_length"},
134 {XGD1_GAME_OUTPUT_START_LBA - 1U, "pregame_output_last_as_unlocked_source"},
135 {XGD1_GAME_OUTPUT_START_LBA, "game_output_start_as_unlocked_source"},
136 {(XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA) - 1U, "mapped_layer_break_source_minus_one"},
137 {XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA, "mapped_layer_break_source"},
138 {XGD1_GAME_SOURCE_START_LBA + XGD1_GAME_SOURCE_SECTORS - 1U, "unlocked_source_last"},
139 {REDUMP_SECTORS, "unlocked_capacity_plus_one"},
140 {XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS, "postgame_output_start_as_unlocked_source"},
141 {XGD1_VIDEO_L1_OUTPUT_START_LBA, "video_l1_output_start_as_unlocked_source"}
144 static void xbox_ref_probe_parse_sense(const UCHAR *sense,
151 if (sense_key) *sense_key = 0;
157 response = (UCHAR)(sense[0] & 0x7f);
158 if (response == 0x70 || response == 0x71)
160 if (sense_key) *sense_key = (UCHAR)(sense[2] & 0x0f);
161 if (asc) *asc = sense[12];
162 if (ascq) *ascq = sense[13];
164 else if (response == 0x72 || response == 0x73)
166 if (sense_key) *sense_key = (UCHAR)(sense[1] & 0x0f);
167 if (asc) *asc = sense[2];
168 if (ascq) *ascq = sense[3];
172 static void xbox_ref_probe_sha1(const unsigned char *data,
177 unsigned char digest[20];
183 if (!data || data_size == 0)
187 SHA1_Update(&ctx, data, data_size);
188 SHA1_Final(digest, &ctx);
190 for (i = 0; i < 20U; ++i)
191 sprintf(out_sha1 + (i * 2U), "%02x", digest[i]);
195 static void xbox_ref_probe_read_one(HANDLE hDevice,
198 xbox_ref_probe_read *result)
200 xbox_ref_sptd_with_sense packet;
201 unsigned char *buffer;
202 DWORD bytes_returned;
208 memset(result, 0, sizeof(*result));
210 buffer = (unsigned char *)VirtualAlloc(NULL,
211 XGD1_LAYOUT_PROBE_SECTOR_BYTES,
212 MEM_COMMIT | MEM_RESERVE,
216 result->win32_error = ERROR_NOT_ENOUGH_MEMORY;
220 memset(buffer, 0, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
221 memset(&packet, 0, sizeof(packet));
223 packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
224 packet.sptd.CdbLength = (opcode == 0xA8) ? 12 : 10;
225 packet.sptd.SenseInfoLength = sizeof(packet.sense);
226 packet.sptd.DataIn = SCSI_IOCTL_DATA_IN;
227 packet.sptd.DataTransferLength = XGD1_LAYOUT_PROBE_SECTOR_BYTES;
228 packet.sptd.TimeOutValue = 10;
229 packet.sptd.DataBuffer = buffer;
230 packet.sptd.SenseInfoOffset = offsetof(xbox_ref_sptd_with_sense, sense);
231 packet.sptd.Cdb[0] = opcode;
232 packet.sptd.Cdb[2] = (UCHAR)((lba >> 24) & 0xff);
233 packet.sptd.Cdb[3] = (UCHAR)((lba >> 16) & 0xff);
234 packet.sptd.Cdb[4] = (UCHAR)((lba >> 8) & 0xff);
235 packet.sptd.Cdb[5] = (UCHAR)(lba & 0xff);
239 packet.sptd.Cdb[9] = 1;
243 packet.sptd.Cdb[8] = 1;
247 ioctl_ok = DeviceIoControl(hDevice,
248 IOCTL_SCSI_PASS_THROUGH_DIRECT,
256 result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
257 result->scsi_status = packet.sptd.ScsiStatus;
258 xbox_ref_probe_parse_sense(packet.sense,
262 result->read_ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
266 memcpy(result->data, buffer, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
267 result->nonzero_bytes = 0;
268 for (i = 0; i < XGD1_LAYOUT_PROBE_SECTOR_BYTES; ++i)
271 result->nonzero_bytes++;
273 result->all_zero = (result->nonzero_bytes == 0U) ? 1 : 0;
274 xbox_ref_probe_sha1(buffer,
275 XGD1_LAYOUT_PROBE_SECTOR_BYTES,
279 VirtualFree(buffer, 0, MEM_RELEASE);
282 static void xbox_ref_probe_capture_state(HANDLE hDevice,
283 xbox_ref_probe_state *state,
284 const xbox_ref_probe_point *points,
285 uint32_t point_count)
289 if (!state || !points)
292 if (point_count > XGD1_LAYOUT_PROBE_MAX_SAMPLES)
293 point_count = XGD1_LAYOUT_PROBE_MAX_SAMPLES;
295 state->sample_count = point_count;
296 for (i = 0; i < point_count; ++i)
298 state->samples[i].lba = points[i].lba;
299 state->samples[i].label = points[i].label;
301 printf("[XGD1-PROBE] %s: probing LBA %u (%s) with READ(10) and READ(12).\n",
302 state->name ? state->name : "state",
304 points[i].label ? points[i].label : "unlabeled");
306 xbox_ref_probe_read_one(hDevice,
309 &state->samples[i].read10);
310 xbox_ref_probe_read_one(hDevice,
313 &state->samples[i].read12);
317 static void xbox_ref_probe_json_string(FILE *json, const char *value)
319 const unsigned char *p;
327 p = (const unsigned char *)value;
332 case '\\': fputs("\\\\", json); break;
333 case '"': fputs("\\\"", json); break;
334 case '\b': fputs("\\b", json); break;
335 case '\f': fputs("\\f", json); break;
336 case '\n': fputs("\\n", json); break;
337 case '\r': fputs("\\r", json); break;
338 case '\t': fputs("\\t", json); break;
341 fprintf(json, "\\u%04x", (unsigned int)*p);
351 static void xbox_ref_probe_json_hex(FILE *json,
352 const unsigned char *data,
362 for (i = 0; i < data_size; ++i)
363 fprintf(json, "%02x", data[i]);
368 static void xbox_ref_probe_write_cdb(FILE *json,
378 "\"a800%08x000000010000\"",
384 "\"2800%08x00000100\"",
389 static void xbox_ref_probe_write_read(FILE *json,
392 const xbox_ref_probe_read *read)
397 fprintf(json, "%s", indent);
398 xbox_ref_probe_json_string(json, name);
399 fprintf(json, ": {\n");
400 fprintf(json, "%s \"read_ok\": %s,\n", indent, read->read_ok ? "true" : "false");
401 fprintf(json, "%s \"win32_error\": %lu,\n", indent, (unsigned long)read->win32_error);
402 fprintf(json, "%s \"scsi_status\": %u,\n", indent, (unsigned int)read->scsi_status);
403 fprintf(json, "%s \"sense_key\": %u,\n", indent, (unsigned int)read->sense_key);
404 fprintf(json, "%s \"asc\": %u,\n", indent, (unsigned int)read->asc);
405 fprintf(json, "%s \"ascq\": %u,\n", indent, (unsigned int)read->ascq);
408 fprintf(json, "%s \"nonzero_bytes\": %u,\n", indent, read->nonzero_bytes);
409 fprintf(json, "%s \"all_zero\": %s,\n", indent, read->all_zero ? "true" : "false");
413 fprintf(json, "%s \"nonzero_bytes\": null,\n", indent);
414 fprintf(json, "%s \"all_zero\": null,\n", indent);
416 fprintf(json, "%s \"sha1\": ", indent);
418 xbox_ref_probe_json_string(json, read->sha1);
420 fprintf(json, "null");
421 fprintf(json, ",\n");
422 fprintf(json, "%s \"data_hex\": ", indent);
424 xbox_ref_probe_json_hex(json, read->data, XGD1_LAYOUT_PROBE_SECTOR_BYTES);
426 xbox_ref_probe_json_string(json, "");
427 fprintf(json, "\n%s}", indent);
430 static void xbox_ref_probe_write_state(FILE *json,
431 const xbox_ref_probe_state *state,
436 fprintf(json, " {\n");
437 fprintf(json, " \"name\": ");
438 xbox_ref_probe_json_string(json, state->name);
439 fprintf(json, ",\n");
440 fprintf(json, " \"capacity_valid\": %s,\n", state->capacity_valid ? "true" : "false");
441 fprintf(json, " \"capacity_sectors\": %u,\n", state->capacity);
442 fprintf(json, " \"state_verified\": %s,\n", state->state_verified ? "true" : "false");
443 fprintf(json, " \"samples\": [\n");
445 for (i = 0; i < state->sample_count; ++i)
447 const xbox_ref_probe_sample *sample = &state->samples[i];
450 if (sample->read10.read_ok && sample->read12.read_ok)
452 identical = (memcmp(sample->read10.data,
454 XGD1_LAYOUT_PROBE_SECTOR_BYTES) == 0) ? 1 : 0;
457 fprintf(json, " {\n");
458 fprintf(json, " \"lba\": %u,\n", sample->lba);
459 fprintf(json, " \"label\": ");
460 xbox_ref_probe_json_string(json, sample->label);
461 fprintf(json, ",\n");
462 fprintf(json, " \"read10_cdb_hex\": ");
463 xbox_ref_probe_write_cdb(json, 0x28, sample->lba);
464 fprintf(json, ",\n");
465 fprintf(json, " \"read12_cdb_hex\": ");
466 xbox_ref_probe_write_cdb(json, 0xA8, sample->lba);
467 fprintf(json, ",\n");
468 xbox_ref_probe_write_read(json, " ", "read10", &sample->read10);
469 fprintf(json, ",\n");
470 xbox_ref_probe_write_read(json, " ", "read12", &sample->read12);
471 fprintf(json, ",\n");
473 " \"read10_read12_comparable\": %s,\n",
474 (sample->read10.read_ok && sample->read12.read_ok) ? "true" : "false");
475 fprintf(json, " \"read10_read12_identical\": ");
476 if (sample->read10.read_ok && sample->read12.read_ok)
477 fprintf(json, "%s\n", identical ? "true" : "false");
479 fprintf(json, "null\n");
480 fprintf(json, " }%s\n", (i + 1U < state->sample_count) ? "," : "");
483 fprintf(json, " ]\n");
484 fprintf(json, " }%s\n", trailing_comma ? "," : "");
487 static BOOL xbox_ref_probe_write_report(const char *report_path,
489 uint32_t entry_capacity,
491 int volume_lock_acquired,
492 const xbox_ref_probe_state *locked_state,
493 const xbox_ref_probe_state *unlocked_state,
494 int restore_attempted,
495 uint32_t restored_capacity,
496 int restore_verified)
498 char tmp_path[XBOX_REF_RESULT_PATH_MAX];
499 const char *final_path;
504 final_path = (report_path && report_path[0]) ? report_path : "xgd1_layout_probe.json";
506 int path_length = snprintf(tmp_path,
510 (unsigned long)GetCurrentProcessId());
511 if (path_length < 0 || (size_t)path_length >= sizeof(tmp_path))
513 printf("[XGD1-PROBE] Fatal: report path is too long.\n");
518 json = fopen(tmp_path, "wb");
521 printf("[XGD1-PROBE] Fatal: could not create temporary report %s.\n", tmp_path);
525 fprintf(json, "{\n");
526 fprintf(json, " \"schema_version\": %d,\n", XGD1_LAYOUT_PROBE_SCHEMA_VERSION);
527 fprintf(json, " \"producer\": \"friidump-0.5.3.11\",\n");
528 fprintf(json, " \"probe\": \"original_xbox_xgd1_logical_layout_boundary\",\n");
529 fprintf(json, " \"device\": ");
530 xbox_ref_probe_json_string(json, device ? device : "");
531 fprintf(json, ",\n");
532 fprintf(json, " \"safety\": {\n");
533 fprintf(json, " \"read_only_sector_commands\": true,\n");
534 fprintf(json, " \"authentication_handshake_used\": true,\n");
535 fprintf(json, " \"tray_cycle_used\": true,\n");
536 fprintf(json, " \"firmware_write_used\": false,\n");
537 fprintf(json, " \"flash_command_used\": false\n");
538 fprintf(json, " },\n");
539 fprintf(json, " \"constants\": {\n");
540 fprintf(json, " \"full_output_sectors\": %u,\n", XGD1_FULL_REDUMP_SECTORS);
541 fprintf(json, " \"game_output_start_lba\": %u,\n", XGD1_GAME_OUTPUT_START_LBA);
542 fprintf(json, " \"game_view_sectors\": %u,\n", REDUMP_SECTORS);
543 fprintf(json, " \"video_total_sectors\": %u,\n", XGD1_VIDEO_TOTAL_SECTORS);
544 fprintf(json, " \"video_l0_sectors\": %u,\n", XGD1_VIDEO_L0_SECTORS);
545 fprintf(json, " \"video_l1_sectors\": %u,\n", XGD1_VIDEO_L1_SECTORS);
546 fprintf(json, " \"video_l1_output_start_lba\": %u,\n", XGD1_VIDEO_L1_OUTPUT_START_LBA);
547 fprintf(json, " \"output_layer_break_lba\": %u\n", XGD1_REDUMP_LAYER_BREAK_LBA);
548 fprintf(json, " },\n");
549 fprintf(json, " \"entry\": {\n");
550 fprintf(json, " \"capacity_sectors\": %u,\n", entry_capacity);
551 fprintf(json, " \"game_view\": %s,\n", entry_game_view ? "true" : "false");
552 fprintf(json, " \"classification\": ");
553 if (xbox_ref_capacity_is_xgd1_game_view(entry_capacity))
554 xbox_ref_probe_json_string(json, "unlocked_game");
555 else if (xbox_ref_capacity_is_xgd1_locked_view(entry_capacity))
556 xbox_ref_probe_json_string(json, "locked_video");
558 xbox_ref_probe_json_string(json, "other_or_unknown");
560 fprintf(json, " },\n");
561 fprintf(json, " \"volume_lock_acquired_at_least_once\": %s,\n", volume_lock_acquired ? "true" : "false");
563 " \"required_states_verified\": %s,\n",
564 (locked_state->state_verified && unlocked_state->state_verified) ? "true" : "false");
565 fprintf(json, " \"states\": [\n");
566 xbox_ref_probe_write_state(json, locked_state, 1);
567 xbox_ref_probe_write_state(json, unlocked_state, 0);
568 fprintf(json, " ],\n");
569 fprintf(json, " \"restoration\": {\n");
570 fprintf(json, " \"attempted\": %s,\n", restore_attempted ? "true" : "false");
571 fprintf(json, " \"capacity_sectors\": %u,\n", restored_capacity);
572 fprintf(json, " \"verified\": %s\n", restore_verified ? "true" : "false");
573 fprintf(json, " },\n");
574 fprintf(json, " \"interpretation_boundary\": {\n");
575 fprintf(json, " \"pregame_padding_resolved\": false,\n");
576 fprintf(json, " \"postgame_padding_resolved\": false,\n");
577 fprintf(json, " \"game_leadin_resolved\": false,\n");
578 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");
579 fprintf(json, " }\n");
580 fprintf(json, "}\n");
582 if (fflush(json) != 0)
585 DeleteFileA(tmp_path);
590 if (fd < 0 || _commit(fd) != 0)
593 DeleteFileA(tmp_path);
597 if (fclose(json) != 0)
599 DeleteFileA(tmp_path);
603 moved = MoveFileExA(tmp_path,
605 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
608 printf("[XGD1-PROBE] Fatal: could not atomically publish report %s (error %lu).\n",
610 (unsigned long)GetLastError());
611 DeleteFileA(tmp_path);
615 printf("[XGD1-PROBE] Atomic report written: %s\n", final_path);
619 static int xbox_ref_xgd1_layout_probe_core(HANDLE hDevice,
621 const char *report_path,
624 xbox_ref_probe_state locked_state;
625 xbox_ref_probe_state unlocked_state;
626 uint32_t entry_capacity;
627 uint32_t restored_capacity;
629 int volume_lock_acquired;
630 int volume_lock_ever;
631 int restore_attempted;
632 int restore_verified;
634 DWORD bytes_returned;
637 memset(&locked_state, 0, sizeof(locked_state));
638 memset(&unlocked_state, 0, sizeof(unlocked_state));
639 locked_state.name = "locked_video";
640 unlocked_state.name = "unlocked_game";
642 restored_capacity = 0;
644 volume_lock_acquired = 0;
645 volume_lock_ever = 0;
646 restore_attempted = 0;
647 restore_verified = 0;
651 xbox_ref_log_open_for_target(report_path, xbox_ref_drive_letter_from_device(device));
652 printf("[XGD1-PROBE] Starting read-only logical layout boundary probe.\n");
653 printf("[XGD1-PROBE] The existing XGD1 output layout will not be modified.\n");
655 if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
657 printf("[XGD1-PROBE] Fatal: invalid drive handle.\n");
659 xbox_ref_log_close();
663 if (!EnsureDriveReady(hDevice, 30000))
665 printf("[XGD1-PROBE] Fatal: media did not become ready.\n");
667 xbox_ref_log_close();
671 entry_capacity = GetTotalSectors(hDevice);
672 entry_game_view = xbox_ref_capacity_is_xgd1_game_view(entry_capacity) ? 1 : 0;
673 printf("[XGD1-PROBE] Entry READ CAPACITY: %u sectors (%s).\n",
675 entry_game_view ? "game view" : "locked/video or unknown view");
677 DeviceIoControl(hDevice,
686 printf("[XGD1-PROBE] Cycling tray to establish locked/video view.\n");
687 AutomateTrayCycle(hDevice);
688 locked_state.capacity = xbox_ref_refresh_ready_capacity(hDevice);
689 locked_state.capacity_valid = locked_state.capacity != 0U;
690 locked_state.state_verified = (locked_state.capacity_valid &&
691 xbox_ref_capacity_is_xgd1_locked_view(locked_state.capacity)) ? 1 : 0;
692 printf("[XGD1-PROBE] Locked/video READ CAPACITY: %u sectors; verified=%s.\n",
693 locked_state.capacity,
694 locked_state.state_verified ? "yes" : "no");
696 if (DeviceIoControl(hDevice,
705 volume_lock_acquired = 1;
706 volume_lock_ever = 1;
707 printf("[XGD1-PROBE] Windows volume lock acquired.\n");
711 printf("[XGD1-PROBE][WARN] Windows volume lock was not acquired; close AutoPlay and Explorer dialogs if results are unstable.\n");
714 SetDriveSpeedMax(hDevice);
715 xbox_ref_probe_capture_state(hDevice,
717 xbox_ref_locked_probe_points,
718 (uint32_t)(sizeof(xbox_ref_locked_probe_points) /
719 sizeof(xbox_ref_locked_probe_points[0])));
721 printf("[XGD1-PROBE] Applying the full Xbox handshake to establish game view.\n");
722 UnlockDrive(hDevice);
723 unlocked_state.capacity = xbox_ref_refresh_ready_capacity(hDevice);
724 unlocked_state.capacity_valid = unlocked_state.capacity != 0U;
725 unlocked_state.state_verified = (unlocked_state.capacity_valid &&
726 xbox_ref_capacity_is_xgd1_game_view(unlocked_state.capacity)) ? 1 : 0;
727 printf("[XGD1-PROBE] Unlocked/game READ CAPACITY: %u sectors; verified=%s.\n",
728 unlocked_state.capacity,
729 unlocked_state.state_verified ? "yes" : "no");
731 xbox_ref_probe_capture_state(hDevice,
733 xbox_ref_unlocked_probe_points,
734 (uint32_t)(sizeof(xbox_ref_unlocked_probe_points) /
735 sizeof(xbox_ref_unlocked_probe_points[0])));
737 if (volume_lock_acquired)
739 DeviceIoControl(hDevice,
747 volume_lock_acquired = 0;
750 if (!entry_game_view && entry_capacity != 0U)
752 restore_attempted = 1;
753 printf("[XGD1-PROBE] Restoring the entry locked/video state with a final tray cycle.\n");
754 AutomateTrayCycle(hDevice);
755 restored_capacity = xbox_ref_refresh_ready_capacity(hDevice);
756 restore_verified = (restored_capacity != 0U &&
757 xbox_ref_capacity_is_xgd1_locked_view(restored_capacity)) ? 1 : 0;
761 restored_capacity = GetTotalSectors(hDevice);
762 restore_verified = (entry_game_view &&
763 xbox_ref_capacity_is_xgd1_game_view(restored_capacity)) ? 1 : 0;
766 states_verified = (locked_state.state_verified &&
767 unlocked_state.state_verified) ? 1 : 0;
769 report_ok = xbox_ref_probe_write_report(report_path,
782 printf("[XGD1-PROBE] Issuing STOP UNIT / spin-down after probe... ");
783 printf("%s\n", StopDriveUnit(hDevice) ? "OK" : "FAILED");
785 xbox_ref_log_close();
790 if (!states_verified)
792 printf("[XGD1-PROBE] Probe report is partial because one or more drive states were not verified.\n");
796 printf("[XGD1-PROBE] Probe complete. Synthetic ranges remain unresolved until the captured evidence supports a source mapping.\n");
801 static int xbox_ref_gdr8050l_dump_core(HANDLE hDevice, const char *device,
802 const char *filename, char mode,
804 xbox_ref_cancel_func cancel,
806 xbox_ref_dump_result *result)
810 BOOL isDualLayer = FALSE;
811 bool isDualLayerBool = false;
812 char mediaId[33] = {0};
813 char finalFilename[MAX_PATH] = {0};
814 BOOL filenameOverride;
815 uint32_t totalSectors = 0;
816 DWORD dwBytesReturned = 0;
817 BOOL volumeLocked = FALSE;
820 xbox_ref_dump_result_init(result);
822 result->attempted = 1;
826 filenameOverride = (filename && filename[0]);
828 if (mode != '1' && mode != '2') {
829 xbox_ref_log_open_for_target(filename, 0);
830 printf("[XBOX-REF] Fatal: unsupported Xbox reference dump mode '%c'.\n", mode);
832 xbox_ref_log_close();
836 driveLetter = xbox_ref_drive_letter_from_device(device);
838 xbox_ref_log_open_for_target(filename, 0);
839 printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
841 xbox_ref_log_close();
845 xbox_ref_log_open_for_target(filename, driveLetter);
846 printf("[XBOX-REF] Using copied original GDR-8050L dumper flow for drive %c:.\n", driveLetter);
847 printf("[XBOX-REF] Handle mode: %s.\n", ownsHandle ? "opened by xbox_ref wrapper" : "using FriiDump's existing drive HANDLE");
848 printf("[XBOX-REF] Preserving reference timing sleeps: tray 3000ms, ready-settle 1500ms/10000ms fallback, RefreshVolume 1000ms + post-refresh 2000ms.\n");
850 if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL) {
851 printf("[XBOX-REF] Fatal: invalid drive handle for %c:.\n", driveLetter);
856 uint32_t entrySectors;
857 uint32_t verifiedSectors;
858 BOOL preparedAfterHandshake = FALSE;
860 printf("Opening drive %c: for state-aware Xbox handshake...\n", driveLetter);
861 if (!EnsureDriveReady(hDevice, 30000)) {
862 printf("[XBOX-REF] Fatal: drive did not become ready before Xbox state preparation.\n");
866 entrySectors = GetTotalSectors(hDevice);
867 printf("[XBOX-REF] Entry READ CAPACITY: %u sectors.\n", entrySectors);
869 if (xbox_ref_capacity_is_game_view(entrySectors)) {
870 printf("[XBOX-REF] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
872 printf("[XBOX-REF] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
873 UnlockDrive(hDevice);
874 verifiedSectors = xbox_ref_refresh_ready_capacity(hDevice);
875 preparedAfterHandshake = TRUE;
876 printf("[XBOX-REF] Direct-handshake READ CAPACITY: %u sectors.\n", verifiedSectors);
878 if (!xbox_ref_capacity_is_game_view(verifiedSectors)) {
879 printf("[XBOX-REF][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
880 AutomateTrayCycle(hDevice);
881 printf("[XBOX-REF] Re-applying the full handshake after recovery media change.\n");
882 UnlockDrive(hDevice);
883 verifiedSectors = xbox_ref_refresh_ready_capacity(hDevice);
884 printf("[XBOX-REF] Recovery-handshake READ CAPACITY: %u sectors.\n", verifiedSectors);
886 if (!xbox_ref_capacity_is_game_view(verifiedSectors)) {
887 printf("[XBOX-REF] Fatal: Xbox game view was not established after direct and recovery handshakes.\n");
893 if (!preparedAfterHandshake)
894 xbox_ref_refresh_ready_capacity(hDevice);
897 printf("Setting drive to maximum performance mode...\n");
898 SetDriveSpeedMax(hDevice);
900 if (!DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL)) {
901 printf("[!] Warning: Could not lock volume. Close any open folders pointing to the drive.\n");
902 printf("[XBOX-REF] The reference dumper treats this as fatal before XBE/title probing.\n");
907 printf("Searching for Xbox Game Signature (Raw SCSI Scan)...\n");
908 game = GetXboxGameInfo(hDevice);
909 printf("Game Title Raw: %s\n", game.TitleName);
910 ReadXboxGameDir(hDevice);
912 totalSectors = GetXboxPhysicalSectors(hDevice);
913 isDualLayerBool = false;
914 GetDiscMetadata(hDevice, &totalSectors, &isDualLayerBool, &game);
915 isDualLayer = isDualLayerBool ? TRUE : FALSE;
916 GetMediaID(hDevice, mediaId);
919 if (game.Success && game.TitleName[0]) {
920 strncpy(result->title, game.TitleName, sizeof(result->title) - 1);
921 result->title[sizeof(result->title) - 1] = '\0';
924 strncpy(result->media_id, mediaId, sizeof(result->media_id) - 1);
925 result->media_id[sizeof(result->media_id) - 1] = '\0';
928 result->have_game_region = 1;
929 result->game_region = game.GameRegion;
930 xbox_region_format(game.GameRegion,
932 sizeof(result->region));
937 char titleCopy[sizeof(game.TitleName)];
938 memset(titleCopy, 0, sizeof(titleCopy));
939 strncpy(titleCopy, game.TitleName, sizeof(titleCopy) - 1);
940 SanitizeFilename(titleCopy);
941 if (filenameOverride) {
942 strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
943 finalFilename[sizeof(finalFilename) - 1] = '\0';
944 printf("[XBOX-REF] Output filename override: %s\n", finalFilename);
946 snprintf(finalFilename, sizeof(finalFilename), "%s[%s].%s",
947 titleCopy[0] ? titleCopy : "XboxDisc",
948 mediaId[0] ? mediaId : "UNKNOWN_ID",
949 (mode == '1') ? "iso" : "xiso");
950 printf("[XBOX-REF] XBE/DMI-derived output filename: %s\n", finalFilename);
951 xbox_ref_log_retarget(finalFilename);
953 DisplayXboxGameInfo(game);
954 printf("\n--- DISC INFORMATION ---\n");
955 printf("Physical Sectors: %u\n", totalSectors);
956 printf("Layers: %d\n", isDualLayer ? 2 : 1);
957 printf("Media ID: %s\n", mediaId);
958 printf("Target File: %s\n", finalFilename);
959 printf("-------------------------------\n\n");
961 if (filenameOverride) {
962 strncpy(finalFilename, filename, sizeof(finalFilename) - 1);
963 finalFilename[sizeof(finalFilename) - 1] = '\0';
964 printf("Error: Could not retrieve Xbox game information; using override filename %s.\n", finalFilename);
966 printf("Error: Could not retrieve Xbox game information and no output filename override was supplied.\n");
967 printf(" Rerun with -i <file> or -X <file> if metadata probing cannot complete.\n");
972 if (result && finalFilename[0]) {
973 strncpy(result->output_path, finalFilename, sizeof(result->output_path) - 1);
974 result->output_path[sizeof(result->output_path) - 1] = '\0';
977 if (totalSectors != (uint32_t)(REDUMP_SECTORS)) {
978 printf("[WARNING] Unlocked drive-reported sectors: %u does not match expected Xbox game-view sectors: %u\n", totalSectors, (uint32_t)(REDUMP_SECTORS));
979 printf("-Homebrew games may have different sizes.\n\n");
982 if (totalSectors == 0) {
983 printf("Error: Could not determine total sectors for dumping.\n");
987 printf("Starting dump with copied DumpXboxGameDisc() mode %c...\n", mode);
988 ok = DumpXboxGameDisc(hDevice, finalFilename, mode, totalSectors, isDualLayerBool, FALSE, cancel, cancel_data, result);
992 DeviceIoControl(hDevice, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwBytesReturned, NULL);
993 if (ownsHandle && hDevice != INVALID_HANDLE_VALUE && hDevice != NULL) {
994 printf("Issuing STOP UNIT / spin-down after dump attempt... ");
995 if (StopDriveUnit(hDevice)) printf("OK\n"); else printf("FAILED\n");
998 printf("[XBOX-REF] Returning to FriiDump; final STOP UNIT cleanup will use the same handle.\n");
1001 printf("[XBOX-REF] Log file complete: %s\n", xbox_ref_log_path() ? xbox_ref_log_path() : "(unavailable)");
1002 xbox_ref_log_close();
1004 printf("[XBOX-REF] Returning finalized output evidence to FriiDump's shared Redump verifier.\n");
1006 printf("[XBOX-REF] No completed full-output evidence is available for shared Redump verification.\n");
1011 int xbox_ref_xgd1_layout_probe_with_handle(void *native_handle,
1013 const char *report_path)
1015 return xbox_ref_xgd1_layout_probe_core((HANDLE)native_handle,
1021 int xbox_ref_xgd1_layout_probe(const char *device,
1022 const char *report_path)
1027 drive_letter = xbox_ref_drive_letter_from_device(device);
1030 xbox_ref_log_open_for_target(report_path, 0);
1031 printf("[XGD1-PROBE] Fatal: could not parse drive letter from device '%s'.\n",
1032 device ? device : "(null)");
1033 xbox_ref_log_close();
1037 hDevice = OpenDrive(drive_letter);
1038 if (hDevice == INVALID_HANDLE_VALUE)
1040 xbox_ref_log_open_for_target(report_path, drive_letter);
1041 printf("[XGD1-PROBE] Fatal: cannot open drive %c:. Run as Administrator.\n",
1043 xbox_ref_log_close();
1047 return xbox_ref_xgd1_layout_probe_core(hDevice,
1053 int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device,
1054 const char *filename, char mode,
1055 xbox_ref_cancel_func cancel,
1057 xbox_ref_dump_result *result)
1059 return xbox_ref_gdr8050l_dump_core((HANDLE)native_handle, device, filename,
1060 mode, FALSE, cancel, cancel_data, result);
1063 int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1068 driveLetter = xbox_ref_drive_letter_from_device(device);
1070 xbox_ref_log_open_for_target(filename, 0);
1071 printf("[XBOX-REF] Fatal: could not parse drive letter from device '%s'.\n", device ? device : "(null)");
1072 xbox_ref_log_close();
1076 hDevice = OpenDrive(driveLetter);
1077 if (hDevice == INVALID_HANDLE_VALUE) {
1078 xbox_ref_log_open_for_target(filename, driveLetter);
1079 printf("[XBOX-REF] Fatal: Cannot open drive %c:. Run as Administrator.\n", driveLetter);
1080 xbox_ref_log_close();
1084 return xbox_ref_gdr8050l_dump_core(hDevice, device, filename, mode, TRUE, NULL, NULL, result);
1087 int xbox_ref_xgd1_layout_probe_with_handle(void *native_handle,
1089 const char *report_path)
1091 (void)native_handle;
1097 int xbox_ref_xgd1_layout_probe(const char *device,
1098 const char *report_path)
1105 int xbox_ref_gdr8050l_dump_with_handle(void *native_handle, const char *device,
1106 const char *filename, char mode,
1107 xbox_ref_cancel_func cancel,
1109 xbox_ref_dump_result *result)
1111 (void)native_handle; (void)device; (void)filename; (void)mode;
1112 (void)cancel; (void)cancel_data;
1113 xbox_ref_dump_result_init(result);
1117 int xbox_ref_gdr8050l_dump(const char *device, const char *filename, char mode, xbox_ref_dump_result *result)
1119 (void)device; (void)filename; (void)mode; xbox_ref_dump_result_init(result); return 1;