1 #include "xbox_ref_bridge.h"
6 #define WIN32_LEAN_AND_MEAN
7 #define _WIN32_WINNT 0x0500
15 #ifndef SCSI_IOCTL_DATA_UNSPECIFIED
16 #define SCSI_IOCTL_DATA_UNSPECIFIED 2
19 #include "xbox_ref/utils.h"
20 #include "xbox_ref/sha1.h"
21 #include "xbox_ref/unlock.h"
22 #include "xbox_ref/xbox_ref_log.h"
24 #define printf xbox_ref_printf
26 #define XGD1_RAW_ID_PROBE_SCHEMA_VERSION 2
27 #define XGD1_RAW_ID_CACHE_BASE 0x80000000U
28 #define XGD1_RAW_SECTOR_BYTES 2064U
29 #define XGD1_LOGICAL_SECTOR_BYTES 2048U
30 #define XGD1_CACHE_FILL_SECTORS 16U
31 #define XGD1_RAW_ID_MAX_SAMPLES 16U
33 typedef struct xgd1_sptd_with_sense_s
35 SCSI_PASS_THROUGH_DIRECT sptd;
38 } xgd1_sptd_with_sense;
40 typedef struct xgd1_cmd_result_s
50 typedef struct xgd1_raw_id_sample_s
54 uint32_t cache_fill_lba;
56 uint32_t cache_address;
57 xgd1_cmd_result cache_flush;
58 xgd1_cmd_result cache_fill;
59 xgd1_cmd_result memdump_header;
60 xgd1_cmd_result memdump_edc;
62 UCHAR sector_information;
63 uint32_t layer_number;
64 uint32_t physical_sector_number;
65 uint32_t normalized_physical_sector_number;
67 uint32_t expected_layer_number;
68 uint32_t expected_physical_sector_number;
69 uint32_t expected_normalized_physical_sector_number;
70 int layer_matches_expected;
71 int psn_matches_expected;
72 int geometry_matches_expected;
73 char logical_sha1[41];
74 unsigned char logical[XGD1_LOGICAL_SECTOR_BYTES];
75 unsigned char raw_header[12];
76 unsigned char raw_edc[4];
79 typedef struct xgd1_raw_id_state_s
86 uint32_t sample_count;
87 uint32_t geometry_validation_failures;
88 int geometry_verified;
89 xgd1_raw_id_sample samples[XGD1_RAW_ID_MAX_SAMPLES];
92 typedef struct xgd1_probe_point_s
98 static const xgd1_probe_point xgd1_locked_points[] = {
99 {0U, "locked_logical_start"},
100 {31U, "locked_early_lba_31"},
101 {32U, "locked_early_lba_32"},
102 {XGD1_VIDEO_L0_SECTORS - 1U, "video_l0_last"},
103 {XGD1_VIDEO_L0_SECTORS, "video_l1_first"},
104 {XGD1_VIDEO_TOTAL_SECTORS - 1U, "locked_video_last"}
107 static const xgd1_probe_point xgd1_unlocked_points[] = {
108 {0U, "unlocked_source_start"},
109 {XGD1_XISO_LEADIN_SECTORS - 1U, "unlocked_leadin_last"},
110 {XGD1_GAME_SOURCE_START_LBA, "xdfs_source_start"},
111 {XGD1_GAME_SOURCE_START_LBA + 1U, "xdfs_source_next"},
112 {(XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA) - 1U, "mapped_layer_break_source_minus_one"},
113 {XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA, "mapped_layer_break_source"},
114 {REDUMP_SECTORS - 1U, "unlocked_source_last"}
117 static char xgd1_drive_letter_from_device(const char *device)
119 if (!device || !device[0]) return 0;
120 if (device[0] && device[1] == ':') return device[0];
121 if (device[0] == '\\' && device[1] == '\\' &&
122 device[2] == '.' && device[3] == '\\' &&
123 device[4] && device[5] == ':')
128 static int xgd1_capacity_is_locked(uint32_t sectors)
130 return sectors == XGD1_VIDEO_TOTAL_SECTORS;
133 static int xgd1_capacity_is_game(uint32_t sectors)
135 return sectors == REDUMP_SECTORS;
138 static uint32_t xgd1_refresh_ready_capacity(HANDLE hDevice)
140 RefreshVolume(hDevice);
142 EnsureDriveReady(hDevice, 30000);
143 return GetTotalSectors(hDevice);
146 static void xgd1_parse_sense(const UCHAR *sense,
153 if (sense_key) *sense_key = 0;
159 response = (UCHAR)(sense[0] & 0x7f);
160 if (response == 0x70 || response == 0x71)
162 if (sense_key) *sense_key = (UCHAR)(sense[2] & 0x0f);
163 if (asc) *asc = sense[12];
164 if (ascq) *ascq = sense[13];
166 else if (response == 0x72 || response == 0x73)
168 if (sense_key) *sense_key = (UCHAR)(sense[1] & 0x0f);
169 if (asc) *asc = sense[2];
170 if (ascq) *ascq = sense[3];
174 static void xgd1_sha1(const unsigned char *data,
179 unsigned char digest[20];
185 if (!data || data_size == 0)
189 SHA1_Update(&ctx, data, data_size);
190 SHA1_Final(digest, &ctx);
192 for (i = 0; i < 20U; ++i)
193 sprintf(out_sha1 + (i * 2U), "%02x", digest[i]);
197 static void xgd1_cmd_result_init(xgd1_cmd_result *result)
200 memset(result, 0, sizeof(*result));
203 static void xgd1_execute_data_in(HANDLE hDevice,
206 unsigned char *buffer,
208 DWORD timeout_seconds,
209 xgd1_cmd_result *result)
211 xgd1_sptd_with_sense packet;
212 DWORD bytes_returned;
215 xgd1_cmd_result_init(result);
216 if (!result || !cdb || !buffer || buffer_length == 0U)
219 memset(buffer, 0, buffer_length);
220 memset(&packet, 0, sizeof(packet));
222 packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
223 packet.sptd.CdbLength = cdb_length;
224 packet.sptd.SenseInfoLength = sizeof(packet.sense);
225 packet.sptd.DataIn = SCSI_IOCTL_DATA_IN;
226 packet.sptd.DataTransferLength = buffer_length;
227 packet.sptd.TimeOutValue = timeout_seconds;
228 packet.sptd.DataBuffer = buffer;
229 packet.sptd.SenseInfoOffset = offsetof(xgd1_sptd_with_sense, sense);
230 memcpy(packet.sptd.Cdb, cdb, cdb_length);
233 ioctl_ok = DeviceIoControl(hDevice,
234 IOCTL_SCSI_PASS_THROUGH_DIRECT,
242 result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
243 result->scsi_status = packet.sptd.ScsiStatus;
244 xgd1_parse_sense(packet.sense,
248 result->ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
251 static void xgd1_execute_no_data(HANDLE hDevice,
254 DWORD timeout_seconds,
255 xgd1_cmd_result *result)
257 xgd1_sptd_with_sense packet;
258 DWORD bytes_returned;
261 xgd1_cmd_result_init(result);
265 memset(&packet, 0, sizeof(packet));
266 packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
267 packet.sptd.CdbLength = cdb_length;
268 packet.sptd.SenseInfoLength = sizeof(packet.sense);
269 packet.sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
270 packet.sptd.DataTransferLength = 0;
271 packet.sptd.TimeOutValue = timeout_seconds;
272 packet.sptd.DataBuffer = NULL;
273 packet.sptd.SenseInfoOffset = offsetof(xgd1_sptd_with_sense, sense);
274 memcpy(packet.sptd.Cdb, cdb, cdb_length);
277 ioctl_ok = DeviceIoControl(hDevice,
278 IOCTL_SCSI_PASS_THROUGH_DIRECT,
286 result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
287 result->scsi_status = packet.sptd.ScsiStatus;
288 xgd1_parse_sense(packet.sense,
292 result->ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
295 static void xgd1_set_lba(UCHAR *cdb, uint32_t lba)
297 cdb[2] = (UCHAR)((lba >> 24) & 0xff);
298 cdb[3] = (UCHAR)((lba >> 16) & 0xff);
299 cdb[4] = (UCHAR)((lba >> 8) & 0xff);
300 cdb[5] = (UCHAR)(lba & 0xff);
303 static void xgd1_build_memdump_cdb(UCHAR cdb[12],
313 cdb[6] = (UCHAR)((address >> 24) & 0xff);
314 cdb[7] = (UCHAR)((address >> 16) & 0xff);
315 cdb[8] = (UCHAR)((address >> 8) & 0xff);
316 cdb[9] = (UCHAR)(address & 0xff);
317 cdb[10] = (UCHAR)((length >> 8) & 0xff);
318 cdb[11] = (UCHAR)(length & 0xff);
321 static void xgd1_set_expected_geometry(int locked_view,
322 xgd1_raw_id_sample *sample)
325 uint32_t layer_zero_last_psn;
326 uint32_t normalized_psn;
327 uint32_t expected_normalized_psn;
328 uint32_t expected_layer;
329 uint32_t expected_raw_psn;
336 if (sample->lba < XGD1_VIDEO_L0_SECTORS)
337 output_lba = sample->lba;
339 output_lba = XGD1_VIDEO_L1_OUTPUT_START_LBA +
340 (sample->lba - XGD1_VIDEO_L0_SECTORS);
344 output_lba = XGD1_GAME_OUTPUT_START_LBA + sample->lba;
347 layer_zero_last_psn = 0x30000U + XGD1_REDUMP_LAYER_BREAK_LBA - 1U;
348 if (output_lba < XGD1_REDUMP_LAYER_BREAK_LBA)
351 expected_normalized_psn = 0x30000U + output_lba;
352 expected_raw_psn = expected_normalized_psn;
357 expected_normalized_psn = layer_zero_last_psn -
358 (output_lba - XGD1_REDUMP_LAYER_BREAK_LBA);
359 expected_raw_psn = (~expected_normalized_psn) & 0x00ffffffU;
362 normalized_psn = (sample->layer_number != 0U)
363 ? ((~sample->physical_sector_number) & 0x00ffffffU)
364 : sample->physical_sector_number;
366 sample->output_lba = output_lba;
367 sample->normalized_physical_sector_number = normalized_psn;
368 sample->expected_layer_number = expected_layer;
369 sample->expected_physical_sector_number = expected_raw_psn;
370 sample->expected_normalized_physical_sector_number = expected_normalized_psn;
371 sample->layer_matches_expected =
372 (sample->layer_number == expected_layer) ? 1 : 0;
373 sample->psn_matches_expected =
374 (sample->physical_sector_number == expected_raw_psn) ? 1 : 0;
375 sample->geometry_matches_expected =
376 (sample->layer_matches_expected && sample->psn_matches_expected) ? 1 : 0;
380 static void xgd1_capture_sample(HANDLE hDevice,
383 const xgd1_probe_point *point,
384 xgd1_raw_id_sample *sample)
386 unsigned char *read_buffer;
394 if (!sample || !point)
397 memset(sample, 0, sizeof(*sample));
398 sample->lba = point->lba;
399 sample->label = point->label;
401 if (capacity < XGD1_CACHE_FILL_SECTORS || point->lba >= capacity)
403 sample->cache_flush.win32_error = ERROR_INVALID_PARAMETER;
407 fill_lba = point->lba & ~(XGD1_CACHE_FILL_SECTORS - 1U);
408 if (fill_lba > capacity - XGD1_CACHE_FILL_SECTORS)
409 fill_lba = capacity - XGD1_CACHE_FILL_SECTORS;
410 index = point->lba - fill_lba;
411 address = XGD1_RAW_ID_CACHE_BASE + index * XGD1_RAW_SECTOR_BYTES;
413 sample->cache_fill_lba = fill_lba;
414 sample->cache_index = index;
415 sample->cache_address = address;
417 memset(flush_cdb, 0, sizeof(flush_cdb));
420 xgd1_set_lba(flush_cdb, fill_lba);
421 xgd1_execute_no_data(hDevice,
425 &sample->cache_flush);
426 if (!sample->cache_flush.ok)
429 read_buffer = (unsigned char *)VirtualAlloc(
431 XGD1_CACHE_FILL_SECTORS * XGD1_LOGICAL_SECTOR_BYTES,
432 MEM_COMMIT | MEM_RESERVE,
436 sample->cache_fill.win32_error = ERROR_NOT_ENOUGH_MEMORY;
440 memset(read_cdb, 0, sizeof(read_cdb));
442 xgd1_set_lba(read_cdb, fill_lba);
443 read_cdb[9] = (UCHAR)XGD1_CACHE_FILL_SECTORS;
446 xgd1_execute_data_in(hDevice,
450 XGD1_CACHE_FILL_SECTORS * XGD1_LOGICAL_SECTOR_BYTES,
452 &sample->cache_fill);
454 if (sample->cache_fill.ok)
456 memcpy(sample->logical,
457 read_buffer + index * XGD1_LOGICAL_SECTOR_BYTES,
458 XGD1_LOGICAL_SECTOR_BYTES);
459 xgd1_sha1(sample->logical,
460 XGD1_LOGICAL_SECTOR_BYTES,
461 sample->logical_sha1);
463 xgd1_build_memdump_cdb(e7_cdb, address, 12U);
464 xgd1_execute_data_in(hDevice,
470 &sample->memdump_header);
472 xgd1_build_memdump_cdb(e7_cdb, address + 2060U, 4U);
473 xgd1_execute_data_in(hDevice,
479 &sample->memdump_edc);
481 if (sample->memdump_header.ok && sample->memdump_edc.ok)
483 sample->raw_header_valid = 1;
484 sample->sector_information = sample->raw_header[0];
485 sample->layer_number = (uint32_t)(sample->sector_information & 1U);
486 sample->physical_sector_number =
487 ((uint32_t)sample->raw_header[1] << 16) |
488 ((uint32_t)sample->raw_header[2] << 8) |
489 (uint32_t)sample->raw_header[3];
490 xgd1_set_expected_geometry(locked_view, sample);
494 VirtualFree(read_buffer, 0, MEM_RELEASE);
497 static void xgd1_capture_state(HANDLE hDevice,
498 xgd1_raw_id_state *state,
499 const xgd1_probe_point *points,
500 uint32_t point_count)
504 if (!state || !points || !state->state_verified)
506 if (point_count > XGD1_RAW_ID_MAX_SAMPLES)
507 point_count = XGD1_RAW_ID_MAX_SAMPLES;
509 state->sample_count = point_count;
510 state->geometry_validation_failures = 0U;
511 for (i = 0; i < point_count; ++i)
513 printf("[XGD1-RAW-ID] %s: cache-flush, aligned READ(12), and HIT 0xE7 header/EDC capture for LBA %u (%s).\n",
514 state->name ? state->name : "state",
516 points[i].label ? points[i].label : "unlabeled");
517 xgd1_capture_sample(hDevice,
522 if (!state->samples[i].cache_flush.ok ||
523 !state->samples[i].cache_fill.ok ||
524 !state->samples[i].raw_header_valid ||
525 !state->samples[i].geometry_matches_expected)
526 state->geometry_validation_failures++;
529 state->geometry_verified =
530 (state->state_verified &&
531 state->sample_count > 0U &&
532 state->geometry_validation_failures == 0U) ? 1 : 0;
535 static void xgd1_json_string(FILE *f, const char *s)
537 const unsigned char *p;
542 for (p = (const unsigned char *)s; *p; ++p)
546 case '\\': fputs("\\\\", f); break;
547 case '"': fputs("\\\"", f); break;
548 case '\b': fputs("\\b", f); break;
549 case '\f': fputs("\\f", f); break;
550 case '\n': fputs("\\n", f); break;
551 case '\r': fputs("\\r", f); break;
552 case '\t': fputs("\\t", f); break;
555 fprintf(f, "\\u%04x", (unsigned int)*p);
565 static void xgd1_json_hex(FILE *f,
566 const unsigned char *data,
572 for (i = 0; i < size; ++i)
573 fprintf(f, "%02x", data[i]);
577 static void xgd1_write_cmd_result(FILE *f,
578 const xgd1_cmd_result *r)
580 fprintf(f, "{\"ok\":%s,\"win32_error\":%lu,\"scsi_status\":%u,"
581 "\"sense_key\":%u,\"asc\":%u,\"ascq\":%u}",
582 (r && r->ok) ? "true" : "false",
583 (unsigned long)(r ? r->win32_error : 0),
584 (unsigned int)(r ? r->scsi_status : 0),
585 (unsigned int)(r ? r->sense_key : 0),
586 (unsigned int)(r ? r->asc : 0),
587 (unsigned int)(r ? r->ascq : 0));
590 static void xgd1_write_sample(FILE *f,
591 const xgd1_raw_id_sample *s,
595 fprintf(f, " \"lba\": %u,\n", s->lba);
596 fprintf(f, " \"label\": ");
597 xgd1_json_string(f, s->label ? s->label : "");
599 fprintf(f, " \"cache_fill_lba\": %u,\n", s->cache_fill_lba);
600 fprintf(f, " \"cache_index\": %u,\n", s->cache_index);
601 fprintf(f, " \"cache_address\": \"0x%08x\",\n", s->cache_address);
602 fprintf(f, " \"cache_flush\": ");
603 xgd1_write_cmd_result(f, &s->cache_flush);
605 fprintf(f, " \"cache_fill\": ");
606 xgd1_write_cmd_result(f, &s->cache_fill);
608 fprintf(f, " \"memdump_header\": ");
609 xgd1_write_cmd_result(f, &s->memdump_header);
611 fprintf(f, " \"memdump_edc\": ");
612 xgd1_write_cmd_result(f, &s->memdump_edc);
614 fprintf(f, " \"raw_header_valid\": %s,\n",
615 s->raw_header_valid ? "true" : "false");
616 if (s->raw_header_valid)
618 fprintf(f, " \"sector_information\": %u,\n",
619 (unsigned int)s->sector_information);
620 fprintf(f, " \"layer_number\": %u,\n", s->layer_number);
621 fprintf(f, " \"physical_sector_number\": %u,\n",
622 s->physical_sector_number);
623 fprintf(f, " \"normalized_physical_sector_number\": %u,\n",
624 s->normalized_physical_sector_number);
625 fprintf(f, " \"output_lba\": %u,\n", s->output_lba);
626 fprintf(f, " \"expected_layer_number\": %u,\n",
627 s->expected_layer_number);
628 fprintf(f, " \"expected_physical_sector_number\": %u,\n",
629 s->expected_physical_sector_number);
630 fprintf(f, " \"expected_normalized_physical_sector_number\": %u,\n",
631 s->expected_normalized_physical_sector_number);
632 fprintf(f, " \"layer_matches_expected\": %s,\n",
633 s->layer_matches_expected ? "true" : "false");
634 fprintf(f, " \"psn_matches_expected\": %s,\n",
635 s->psn_matches_expected ? "true" : "false");
636 fprintf(f, " \"geometry_matches_expected\": %s,\n",
637 s->geometry_matches_expected ? "true" : "false");
638 fprintf(f, " \"logical_sha1\": \"%s\",\n", s->logical_sha1);
639 fprintf(f, " \"raw_header_hex\": ");
640 xgd1_json_hex(f, s->raw_header, 12U);
642 fprintf(f, " \"raw_edc_hex\": ");
643 xgd1_json_hex(f, s->raw_edc, 4U);
645 fprintf(f, " \"logical_data_hex\": ");
646 xgd1_json_hex(f, s->logical, XGD1_LOGICAL_SECTOR_BYTES);
651 fprintf(f, " \"sector_information\": null,\n");
652 fprintf(f, " \"layer_number\": null,\n");
653 fprintf(f, " \"physical_sector_number\": null,\n");
654 fprintf(f, " \"normalized_physical_sector_number\": null,\n");
655 fprintf(f, " \"output_lba\": null,\n");
656 fprintf(f, " \"expected_layer_number\": null,\n");
657 fprintf(f, " \"expected_physical_sector_number\": null,\n");
658 fprintf(f, " \"expected_normalized_physical_sector_number\": null,\n");
659 fprintf(f, " \"layer_matches_expected\": false,\n");
660 fprintf(f, " \"psn_matches_expected\": false,\n");
661 fprintf(f, " \"geometry_matches_expected\": false,\n");
662 fprintf(f, " \"logical_sha1\": null,\n");
663 fprintf(f, " \"raw_header_hex\": \"\",\n");
664 fprintf(f, " \"raw_edc_hex\": \"\",\n");
665 fprintf(f, " \"logical_data_hex\": \"\"\n");
667 fprintf(f, " }%s\n", comma ? "," : "");
670 static void xgd1_write_state(FILE *f,
671 const xgd1_raw_id_state *state,
677 fprintf(f, " \"name\": ");
678 xgd1_json_string(f, state->name ? state->name : "");
680 fprintf(f, " \"capacity_valid\": %s,\n",
681 state->capacity_valid ? "true" : "false");
682 fprintf(f, " \"capacity_sectors\": %u,\n", state->capacity);
683 fprintf(f, " \"state_verified\": %s,\n",
684 state->state_verified ? "true" : "false");
685 fprintf(f, " \"geometry_validation_failures\": %u,\n",
686 state->geometry_validation_failures);
687 fprintf(f, " \"geometry_verified\": %s,\n",
688 state->geometry_verified ? "true" : "false");
689 fprintf(f, " \"samples\": [\n");
690 for (i = 0; i < state->sample_count; ++i)
693 (i + 1U < state->sample_count));
695 fprintf(f, " }%s\n", comma ? "," : "");
698 static int xgd1_write_report(const char *report_path,
700 uint32_t entry_capacity,
702 int volume_lock_ever,
703 const xgd1_raw_id_state *locked_state,
704 const xgd1_raw_id_state *unlocked_state,
705 int restore_attempted,
706 uint32_t restored_capacity,
707 int restore_verified)
709 const char *final_path;
710 char tmp_path[MAX_PATH * 4];
714 uint32_t validation_failures;
715 int geometry_resolved;
717 final_path = (report_path && report_path[0])
719 : "xgd1_raw_id_probe.json";
721 validation_failures =
722 (locked_state ? locked_state->geometry_validation_failures : 0U) +
723 (unlocked_state ? unlocked_state->geometry_validation_failures : 0U);
725 (locked_state && locked_state->geometry_verified &&
726 unlocked_state && unlocked_state->geometry_verified) ? 1 : 0;
728 if (strlen(final_path) + 32U >= sizeof(tmp_path))
730 printf("[XGD1-RAW-ID] Fatal: report path is too long.\n");
737 (unsigned long)GetCurrentProcessId(),
738 (unsigned long)GetTickCount());
740 f = fopen(tmp_path, "wb");
743 printf("[XGD1-RAW-ID] Fatal: could not create temporary report %s.\n",
749 fprintf(f, " \"schema_version\": %u,\n",
750 XGD1_RAW_ID_PROBE_SCHEMA_VERSION);
751 fprintf(f, " \"producer\": \"friidump-0.5.3.13\",\n");
752 fprintf(f, " \"probe\": \"original_xbox_xgd1_cache_aligned_raw_id_geometry\",\n");
753 fprintf(f, " \"device\": ");
754 xgd1_json_string(f, device ? device : "");
756 fprintf(f, " \"cache\": {\n");
757 fprintf(f, " \"memdump_command\": \"E7 48 49 54 01\",\n");
758 fprintf(f, " \"base_address\": \"0x%08x\",\n",
759 XGD1_RAW_ID_CACHE_BASE);
760 fprintf(f, " \"raw_sector_stride\": %u,\n",
761 XGD1_RAW_SECTOR_BYTES);
762 fprintf(f, " \"cache_flush_command\": \"READ(12), byte1=0x08, zero transfer length\",\n");
763 fprintf(f, " \"cache_fill_command\": \"READ(12) streaming, 16 sectors, block-aligned LBA\",\n");
764 fprintf(f, " \"memdump_regions\": \"12-byte ID/IED/CPR_MAI header plus 4-byte EDC only\",\n");
765 fprintf(f, " \"raw_user_data_compared_to_logical\": false,\n");
766 fprintf(f, " \"raw_user_data_note\": \"The proven Method 8 path replaces the drive-cache user field with READ(12) data before unscrambling; equality is not expected and is not a geometry test.\"\n");
768 fprintf(f, " \"geometry_model\": {\n");
769 fprintf(f, " \"dvd_start_psn\": 196608,\n");
770 fprintf(f, " \"game_output_start_lba\": %u,\n", XGD1_GAME_OUTPUT_START_LBA);
771 fprintf(f, " \"layer_break_lba\": %u,\n", XGD1_REDUMP_LAYER_BREAK_LBA);
772 fprintf(f, " \"layer_zero_last_normalized_psn\": %u,\n",
773 0x30000U + XGD1_REDUMP_LAYER_BREAK_LBA - 1U);
774 fprintf(f, " \"layer_one_raw_psn_is_24bit_complement\": true\n");
776 fprintf(f, " \"safety\": {\n");
777 fprintf(f, " \"read_only_sector_commands\": true,\n");
778 fprintf(f, " \"authentication_handshake_used\": true,\n");
779 fprintf(f, " \"tray_cycle_used\": true,\n");
780 fprintf(f, " \"firmware_write_used\": false,\n");
781 fprintf(f, " \"flash_command_used\": false\n");
783 fprintf(f, " \"entry\": {\n");
784 fprintf(f, " \"capacity_sectors\": %u,\n", entry_capacity);
785 fprintf(f, " \"game_view\": %s,\n",
786 entry_game_view ? "true" : "false");
787 fprintf(f, " \"classification\": \"%s\"\n",
788 entry_game_view ? "unlocked_game" : "locked_video_or_unknown");
790 fprintf(f, " \"volume_lock_acquired_at_least_once\": %s,\n",
791 volume_lock_ever ? "true" : "false");
792 fprintf(f, " \"required_states_verified\": %s,\n",
793 (locked_state && locked_state->state_verified &&
794 unlocked_state && unlocked_state->state_verified)
796 fprintf(f, " \"geometry_validation_failures\": %u,\n",
797 validation_failures);
798 fprintf(f, " \"physical_geometry_resolved\": %s,\n",
799 geometry_resolved ? "true" : "false");
800 fprintf(f, " \"states\": [\n");
801 xgd1_write_state(f, locked_state, 1);
802 xgd1_write_state(f, unlocked_state, 0);
804 fprintf(f, " \"restoration\": {\n");
805 fprintf(f, " \"attempted\": %s,\n",
806 restore_attempted ? "true" : "false");
807 fprintf(f, " \"capacity_sectors\": %u,\n", restored_capacity);
808 fprintf(f, " \"verified\": %s\n",
809 restore_verified ? "true" : "false");
811 fprintf(f, " \"interpretation_boundary\": {\n");
812 fprintf(f, " \"physical_geometry_resolved\": %s,\n",
813 geometry_resolved ? "true" : "false");
814 fprintf(f, " \"pregame_filler_content_resolved\": false,\n");
815 fprintf(f, " \"postgame_filler_content_resolved\": false,\n");
816 fprintf(f, " \"note\": \"A complete cache-aligned raw-ID match can validate the logical-to-physical placement used by the current reconstruction. It does not make inaccessible filler sectors readable and does not establish exact filler bytes.\"\n");
823 DeleteFileA(tmp_path);
828 if (fd < 0 || _commit(fd) != 0)
831 DeleteFileA(tmp_path);
837 DeleteFileA(tmp_path);
841 moved = MoveFileExA(tmp_path,
843 MOVEFILE_REPLACE_EXISTING |
844 MOVEFILE_WRITE_THROUGH);
847 printf("[XGD1-RAW-ID] Fatal: could not atomically publish report %s (error %lu).\n",
849 (unsigned long)GetLastError());
850 DeleteFileA(tmp_path);
854 printf("[XGD1-RAW-ID] Atomic report written: %s\n", final_path);
858 static int xgd1_raw_id_probe_core(HANDLE hDevice,
860 const char *report_path,
863 xgd1_raw_id_state locked_state;
864 xgd1_raw_id_state unlocked_state;
865 uint32_t entry_capacity;
866 uint32_t restored_capacity;
868 int volume_lock_acquired;
869 int volume_lock_ever;
870 int restore_attempted;
871 int restore_verified;
873 DWORD bytes_returned;
876 memset(&locked_state, 0, sizeof(locked_state));
877 memset(&unlocked_state, 0, sizeof(unlocked_state));
878 locked_state.name = "locked_video";
879 locked_state.locked_view = 1;
880 unlocked_state.name = "unlocked_game";
881 unlocked_state.locked_view = 0;
883 restored_capacity = 0;
885 volume_lock_acquired = 0;
886 volume_lock_ever = 0;
887 restore_attempted = 0;
888 restore_verified = 0;
892 xbox_ref_log_open_for_target(report_path,
893 xgd1_drive_letter_from_device(device));
894 printf("[XGD1-RAW-ID] Starting cache-aligned raw-sector ID geometry probe.\n");
895 printf("[XGD1-RAW-ID] Requires modified GDR-8050L firmware with HIT 0xE7 memdump support.\n");
896 printf("[XGD1-RAW-ID] No ISO or firmware-write command will be issued.\n");
898 if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
900 printf("[XGD1-RAW-ID] Fatal: invalid drive handle.\n");
902 xbox_ref_log_close();
906 if (!EnsureDriveReady(hDevice, 30000))
908 printf("[XGD1-RAW-ID] Fatal: media did not become ready.\n");
910 xbox_ref_log_close();
914 entry_capacity = GetTotalSectors(hDevice);
915 entry_game_view = xgd1_capacity_is_game(entry_capacity) ? 1 : 0;
916 printf("[XGD1-RAW-ID] Entry READ CAPACITY: %u sectors (%s).\n",
918 entry_game_view ? "game view" : "locked/video or unknown view");
920 DeviceIoControl(hDevice,
929 printf("[XGD1-RAW-ID] Cycling tray to establish locked/video view.\n");
930 AutomateTrayCycle(hDevice);
931 locked_state.capacity = xgd1_refresh_ready_capacity(hDevice);
932 locked_state.capacity_valid = locked_state.capacity != 0U;
933 locked_state.state_verified =
934 (locked_state.capacity_valid &&
935 xgd1_capacity_is_locked(locked_state.capacity)) ? 1 : 0;
936 printf("[XGD1-RAW-ID] Locked/video READ CAPACITY: %u sectors; verified=%s.\n",
937 locked_state.capacity,
938 locked_state.state_verified ? "yes" : "no");
940 if (DeviceIoControl(hDevice,
949 volume_lock_acquired = 1;
950 volume_lock_ever = 1;
951 printf("[XGD1-RAW-ID] Windows volume lock acquired.\n");
955 printf("[XGD1-RAW-ID][WARN] Windows volume lock was not acquired.\n");
958 SetDriveSpeedMax(hDevice);
959 xgd1_capture_state(hDevice,
962 (uint32_t)(sizeof(xgd1_locked_points) /
963 sizeof(xgd1_locked_points[0])));
965 printf("[XGD1-RAW-ID] Applying the full Xbox handshake to establish game view.\n");
966 UnlockDrive(hDevice);
967 unlocked_state.capacity = xgd1_refresh_ready_capacity(hDevice);
968 unlocked_state.capacity_valid = unlocked_state.capacity != 0U;
969 unlocked_state.state_verified =
970 (unlocked_state.capacity_valid &&
971 xgd1_capacity_is_game(unlocked_state.capacity)) ? 1 : 0;
972 printf("[XGD1-RAW-ID] Unlocked/game READ CAPACITY: %u sectors; verified=%s.\n",
973 unlocked_state.capacity,
974 unlocked_state.state_verified ? "yes" : "no");
976 xgd1_capture_state(hDevice,
978 xgd1_unlocked_points,
979 (uint32_t)(sizeof(xgd1_unlocked_points) /
980 sizeof(xgd1_unlocked_points[0])));
982 if (volume_lock_acquired)
984 DeviceIoControl(hDevice,
992 volume_lock_acquired = 0;
995 if (!entry_game_view && entry_capacity != 0U)
997 restore_attempted = 1;
998 printf("[XGD1-RAW-ID] Restoring entry locked/video state with a final tray cycle.\n");
999 AutomateTrayCycle(hDevice);
1000 restored_capacity = xgd1_refresh_ready_capacity(hDevice);
1002 (restored_capacity != 0U &&
1003 xgd1_capacity_is_locked(restored_capacity)) ? 1 : 0;
1007 restored_capacity = GetTotalSectors(hDevice);
1010 xgd1_capacity_is_game(restored_capacity)) ? 1 : 0;
1014 (locked_state.state_verified &&
1015 unlocked_state.state_verified) ? 1 : 0;
1017 report_ok = xgd1_write_report(report_path,
1030 printf("[XGD1-RAW-ID] Issuing STOP UNIT / spin-down after probe... ");
1031 printf("%s\n", StopDriveUnit(hDevice) ? "OK" : "FAILED");
1032 CloseDrive(hDevice);
1033 xbox_ref_log_close();
1038 if (!states_verified)
1040 printf("[XGD1-RAW-ID] Probe report is partial because one or more states were not verified.\n");
1043 if (!locked_state.geometry_verified || !unlocked_state.geometry_verified)
1045 printf("[XGD1-RAW-ID] Geometry validation failed: locked failures=%u, unlocked failures=%u.\n",
1046 locked_state.geometry_validation_failures,
1047 unlocked_state.geometry_validation_failures);
1051 printf("[XGD1-RAW-ID] Probe complete. All selected raw IDs match the current XGD1 output geometry.\n");
1055 int xbox_ref_xgd1_raw_id_probe_with_handle(void *native_handle,
1057 const char *report_path)
1059 return xgd1_raw_id_probe_core((HANDLE)native_handle,
1065 int xbox_ref_xgd1_raw_id_probe(const char *device,
1066 const char *report_path)
1071 drive_letter = xgd1_drive_letter_from_device(device);
1074 printf("[XGD1-RAW-ID] Fatal: could not parse drive letter from device.\n");
1078 hDevice = OpenDrive(drive_letter);
1079 if (hDevice == INVALID_HANDLE_VALUE)
1081 printf("[XGD1-RAW-ID] Fatal: cannot open drive %c:. Run as Administrator.\n",
1086 return xgd1_raw_id_probe_core(hDevice,
1094 int xbox_ref_xgd1_raw_id_probe_with_handle(void *native_handle,
1096 const char *report_path)
1098 (void)native_handle;
1104 int xbox_ref_xgd1_raw_id_probe(const char *device,
1105 const char *report_path)