#include "xbox_ref_bridge.h"

#include <string.h>

#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <winioctl.h>
#include <ntddscsi.h>
#include <stdio.h>
#include <stddef.h>
#include <io.h>

#ifndef SCSI_IOCTL_DATA_UNSPECIFIED
#define SCSI_IOCTL_DATA_UNSPECIFIED 2
#endif

#include "xbox_ref/utils.h"
#include "xbox_ref/sha1.h"
#include "xbox_ref/unlock.h"
#include "xbox_ref/xbox_ref_log.h"

#define printf xbox_ref_printf

#define XGD1_RAW_ID_PROBE_SCHEMA_VERSION 2
#define XGD1_RAW_ID_CACHE_BASE 0x80000000U
#define XGD1_RAW_SECTOR_BYTES 2064U
#define XGD1_LOGICAL_SECTOR_BYTES 2048U
#define XGD1_CACHE_FILL_SECTORS 16U
#define XGD1_RAW_ID_MAX_SAMPLES 16U

typedef struct xgd1_sptd_with_sense_s
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    ULONG filler;
    UCHAR sense[32];
} xgd1_sptd_with_sense;

typedef struct xgd1_cmd_result_s
{
    int ok;
    DWORD win32_error;
    UCHAR scsi_status;
    UCHAR sense_key;
    UCHAR asc;
    UCHAR ascq;
} xgd1_cmd_result;

typedef struct xgd1_raw_id_sample_s
{
    uint32_t lba;
    const char *label;
    uint32_t cache_fill_lba;
    uint32_t cache_index;
    uint32_t cache_address;
    xgd1_cmd_result cache_flush;
    xgd1_cmd_result cache_fill;
    xgd1_cmd_result memdump_header;
    xgd1_cmd_result memdump_edc;
    int raw_header_valid;
    UCHAR sector_information;
    uint32_t layer_number;
    uint32_t physical_sector_number;
    uint32_t normalized_physical_sector_number;
    uint32_t output_lba;
    uint32_t expected_layer_number;
    uint32_t expected_physical_sector_number;
    uint32_t expected_normalized_physical_sector_number;
    int layer_matches_expected;
    int psn_matches_expected;
    int geometry_matches_expected;
    char logical_sha1[41];
    unsigned char logical[XGD1_LOGICAL_SECTOR_BYTES];
    unsigned char raw_header[12];
    unsigned char raw_edc[4];
} xgd1_raw_id_sample;

typedef struct xgd1_raw_id_state_s
{
    const char *name;
    int locked_view;
    uint32_t capacity;
    int capacity_valid;
    int state_verified;
    uint32_t sample_count;
    uint32_t geometry_validation_failures;
    int geometry_verified;
    xgd1_raw_id_sample samples[XGD1_RAW_ID_MAX_SAMPLES];
} xgd1_raw_id_state;

typedef struct xgd1_probe_point_s
{
    uint32_t lba;
    const char *label;
} xgd1_probe_point;

static const xgd1_probe_point xgd1_locked_points[] = {
    {0U, "locked_logical_start"},
    {31U, "locked_early_lba_31"},
    {32U, "locked_early_lba_32"},
    {XGD1_VIDEO_L0_SECTORS - 1U, "video_l0_last"},
    {XGD1_VIDEO_L0_SECTORS, "video_l1_first"},
    {XGD1_VIDEO_TOTAL_SECTORS - 1U, "locked_video_last"}
};

static const xgd1_probe_point xgd1_unlocked_points[] = {
    {0U, "unlocked_source_start"},
    {XGD1_XISO_LEADIN_SECTORS - 1U, "unlocked_leadin_last"},
    {XGD1_GAME_SOURCE_START_LBA, "xdfs_source_start"},
    {XGD1_GAME_SOURCE_START_LBA + 1U, "xdfs_source_next"},
    {(XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA) - 1U, "mapped_layer_break_source_minus_one"},
    {XGD1_REDUMP_LAYER_BREAK_LBA - XGD1_GAME_OUTPUT_START_LBA, "mapped_layer_break_source"},
    {REDUMP_SECTORS - 1U, "unlocked_source_last"}
};

static char xgd1_drive_letter_from_device(const char *device)
{
    if (!device || !device[0]) return 0;
    if (device[0] && device[1] == ':') return device[0];
    if (device[0] == '\\' && device[1] == '\\' &&
        device[2] == '.' && device[3] == '\\' &&
        device[4] && device[5] == ':')
        return device[4];
    return device[0];
}

static int xgd1_capacity_is_locked(uint32_t sectors)
{
    return sectors == XGD1_VIDEO_TOTAL_SECTORS;
}

static int xgd1_capacity_is_game(uint32_t sectors)
{
    return sectors == REDUMP_SECTORS;
}

static uint32_t xgd1_refresh_ready_capacity(HANDLE hDevice)
{
    RefreshVolume(hDevice);
    Sleep(2000);
    EnsureDriveReady(hDevice, 30000);
    return GetTotalSectors(hDevice);
}

static void xgd1_parse_sense(const UCHAR *sense,
                             UCHAR *sense_key,
                             UCHAR *asc,
                             UCHAR *ascq)
{
    UCHAR response;

    if (sense_key) *sense_key = 0;
    if (asc) *asc = 0;
    if (ascq) *ascq = 0;
    if (!sense)
        return;

    response = (UCHAR)(sense[0] & 0x7f);
    if (response == 0x70 || response == 0x71)
    {
        if (sense_key) *sense_key = (UCHAR)(sense[2] & 0x0f);
        if (asc) *asc = sense[12];
        if (ascq) *ascq = sense[13];
    }
    else if (response == 0x72 || response == 0x73)
    {
        if (sense_key) *sense_key = (UCHAR)(sense[1] & 0x0f);
        if (asc) *asc = sense[2];
        if (ascq) *ascq = sense[3];
    }
}

static void xgd1_sha1(const unsigned char *data,
                       uint32_t data_size,
                       char out_sha1[41])
{
    SHA1_CTX ctx;
    unsigned char digest[20];
    uint32_t i;

    if (!out_sha1)
        return;
    out_sha1[0] = '\0';
    if (!data || data_size == 0)
        return;

    SHA1_Init(&ctx);
    SHA1_Update(&ctx, data, data_size);
    SHA1_Final(digest, &ctx);

    for (i = 0; i < 20U; ++i)
        sprintf(out_sha1 + (i * 2U), "%02x", digest[i]);
    out_sha1[40] = '\0';
}

static void xgd1_cmd_result_init(xgd1_cmd_result *result)
{
    if (result)
        memset(result, 0, sizeof(*result));
}

static void xgd1_execute_data_in(HANDLE hDevice,
                                 const UCHAR *cdb,
                                 UCHAR cdb_length,
                                 unsigned char *buffer,
                                 DWORD buffer_length,
                                 DWORD timeout_seconds,
                                 xgd1_cmd_result *result)
{
    xgd1_sptd_with_sense packet;
    DWORD bytes_returned;
    BOOL ioctl_ok;

    xgd1_cmd_result_init(result);
    if (!result || !cdb || !buffer || buffer_length == 0U)
        return;

    memset(buffer, 0, buffer_length);
    memset(&packet, 0, sizeof(packet));

    packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    packet.sptd.CdbLength = cdb_length;
    packet.sptd.SenseInfoLength = sizeof(packet.sense);
    packet.sptd.DataIn = SCSI_IOCTL_DATA_IN;
    packet.sptd.DataTransferLength = buffer_length;
    packet.sptd.TimeOutValue = timeout_seconds;
    packet.sptd.DataBuffer = buffer;
    packet.sptd.SenseInfoOffset = offsetof(xgd1_sptd_with_sense, sense);
    memcpy(packet.sptd.Cdb, cdb, cdb_length);

    bytes_returned = 0;
    ioctl_ok = DeviceIoControl(hDevice,
                               IOCTL_SCSI_PASS_THROUGH_DIRECT,
                               &packet,
                               sizeof(packet),
                               &packet,
                               sizeof(packet),
                               &bytes_returned,
                               NULL);

    result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
    result->scsi_status = packet.sptd.ScsiStatus;
    xgd1_parse_sense(packet.sense,
                     &result->sense_key,
                     &result->asc,
                     &result->ascq);
    result->ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
}

static void xgd1_execute_no_data(HANDLE hDevice,
                                 const UCHAR *cdb,
                                 UCHAR cdb_length,
                                 DWORD timeout_seconds,
                                 xgd1_cmd_result *result)
{
    xgd1_sptd_with_sense packet;
    DWORD bytes_returned;
    BOOL ioctl_ok;

    xgd1_cmd_result_init(result);
    if (!result || !cdb)
        return;

    memset(&packet, 0, sizeof(packet));
    packet.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    packet.sptd.CdbLength = cdb_length;
    packet.sptd.SenseInfoLength = sizeof(packet.sense);
    packet.sptd.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
    packet.sptd.DataTransferLength = 0;
    packet.sptd.TimeOutValue = timeout_seconds;
    packet.sptd.DataBuffer = NULL;
    packet.sptd.SenseInfoOffset = offsetof(xgd1_sptd_with_sense, sense);
    memcpy(packet.sptd.Cdb, cdb, cdb_length);

    bytes_returned = 0;
    ioctl_ok = DeviceIoControl(hDevice,
                               IOCTL_SCSI_PASS_THROUGH_DIRECT,
                               &packet,
                               sizeof(packet),
                               &packet,
                               sizeof(packet),
                               &bytes_returned,
                               NULL);

    result->win32_error = ioctl_ok ? ERROR_SUCCESS : GetLastError();
    result->scsi_status = packet.sptd.ScsiStatus;
    xgd1_parse_sense(packet.sense,
                     &result->sense_key,
                     &result->asc,
                     &result->ascq);
    result->ok = (ioctl_ok && packet.sptd.ScsiStatus == 0) ? 1 : 0;
}

static void xgd1_set_lba(UCHAR *cdb, uint32_t lba)
{
    cdb[2] = (UCHAR)((lba >> 24) & 0xff);
    cdb[3] = (UCHAR)((lba >> 16) & 0xff);
    cdb[4] = (UCHAR)((lba >> 8) & 0xff);
    cdb[5] = (UCHAR)(lba & 0xff);
}

static void xgd1_build_memdump_cdb(UCHAR cdb[12],
                                   uint32_t address,
                                   uint32_t length)
{
    memset(cdb, 0, 12);
    cdb[0] = 0xE7;
    cdb[1] = 0x48;
    cdb[2] = 0x49;
    cdb[3] = 0x54;
    cdb[4] = 0x01;
    cdb[6] = (UCHAR)((address >> 24) & 0xff);
    cdb[7] = (UCHAR)((address >> 16) & 0xff);
    cdb[8] = (UCHAR)((address >> 8) & 0xff);
    cdb[9] = (UCHAR)(address & 0xff);
    cdb[10] = (UCHAR)((length >> 8) & 0xff);
    cdb[11] = (UCHAR)(length & 0xff);
}

static void xgd1_set_expected_geometry(int locked_view,
                                       xgd1_raw_id_sample *sample)
{
    uint32_t output_lba;
    uint32_t layer_zero_last_psn;
    uint32_t normalized_psn;
    uint32_t expected_normalized_psn;
    uint32_t expected_layer;
    uint32_t expected_raw_psn;

    if (!sample)
        return;

    if (locked_view)
    {
        if (sample->lba < XGD1_VIDEO_L0_SECTORS)
            output_lba = sample->lba;
        else
            output_lba = XGD1_VIDEO_L1_OUTPUT_START_LBA +
                         (sample->lba - XGD1_VIDEO_L0_SECTORS);
    }
    else
    {
        output_lba = XGD1_GAME_OUTPUT_START_LBA + sample->lba;
    }

    layer_zero_last_psn = 0x30000U + XGD1_REDUMP_LAYER_BREAK_LBA - 1U;
    if (output_lba < XGD1_REDUMP_LAYER_BREAK_LBA)
    {
        expected_layer = 0U;
        expected_normalized_psn = 0x30000U + output_lba;
        expected_raw_psn = expected_normalized_psn;
    }
    else
    {
        expected_layer = 1U;
        expected_normalized_psn = layer_zero_last_psn -
            (output_lba - XGD1_REDUMP_LAYER_BREAK_LBA);
        expected_raw_psn = (~expected_normalized_psn) & 0x00ffffffU;
    }

    normalized_psn = (sample->layer_number != 0U)
        ? ((~sample->physical_sector_number) & 0x00ffffffU)
        : sample->physical_sector_number;

    sample->output_lba = output_lba;
    sample->normalized_physical_sector_number = normalized_psn;
    sample->expected_layer_number = expected_layer;
    sample->expected_physical_sector_number = expected_raw_psn;
    sample->expected_normalized_physical_sector_number = expected_normalized_psn;
    sample->layer_matches_expected =
        (sample->layer_number == expected_layer) ? 1 : 0;
    sample->psn_matches_expected =
        (sample->physical_sector_number == expected_raw_psn) ? 1 : 0;
    sample->geometry_matches_expected =
        (sample->layer_matches_expected && sample->psn_matches_expected) ? 1 : 0;
}


static void xgd1_capture_sample(HANDLE hDevice,
                                uint32_t capacity,
                                int locked_view,
                                const xgd1_probe_point *point,
                                xgd1_raw_id_sample *sample)
{
    unsigned char *read_buffer;
    UCHAR flush_cdb[12];
    UCHAR read_cdb[12];
    UCHAR e7_cdb[12];
    uint32_t fill_lba;
    uint32_t index;
    uint32_t address;

    if (!sample || !point)
        return;

    memset(sample, 0, sizeof(*sample));
    sample->lba = point->lba;
    sample->label = point->label;

    if (capacity < XGD1_CACHE_FILL_SECTORS || point->lba >= capacity)
    {
        sample->cache_flush.win32_error = ERROR_INVALID_PARAMETER;
        return;
    }

    fill_lba = point->lba & ~(XGD1_CACHE_FILL_SECTORS - 1U);
    if (fill_lba > capacity - XGD1_CACHE_FILL_SECTORS)
        fill_lba = capacity - XGD1_CACHE_FILL_SECTORS;
    index = point->lba - fill_lba;
    address = XGD1_RAW_ID_CACHE_BASE + index * XGD1_RAW_SECTOR_BYTES;

    sample->cache_fill_lba = fill_lba;
    sample->cache_index = index;
    sample->cache_address = address;

    memset(flush_cdb, 0, sizeof(flush_cdb));
    flush_cdb[0] = 0xA8;
    flush_cdb[1] = 0x08;
    xgd1_set_lba(flush_cdb, fill_lba);
    xgd1_execute_no_data(hDevice,
                         flush_cdb,
                         12,
                         15,
                         &sample->cache_flush);
    if (!sample->cache_flush.ok)
        return;

    read_buffer = (unsigned char *)VirtualAlloc(
        NULL,
        XGD1_CACHE_FILL_SECTORS * XGD1_LOGICAL_SECTOR_BYTES,
        MEM_COMMIT | MEM_RESERVE,
        PAGE_READWRITE);
    if (!read_buffer)
    {
        sample->cache_fill.win32_error = ERROR_NOT_ENOUGH_MEMORY;
        return;
    }

    memset(read_cdb, 0, sizeof(read_cdb));
    read_cdb[0] = 0xA8;
    xgd1_set_lba(read_cdb, fill_lba);
    read_cdb[9] = (UCHAR)XGD1_CACHE_FILL_SECTORS;
    read_cdb[10] = 0x80;

    xgd1_execute_data_in(hDevice,
                         read_cdb,
                         12,
                         read_buffer,
                         XGD1_CACHE_FILL_SECTORS * XGD1_LOGICAL_SECTOR_BYTES,
                         15,
                         &sample->cache_fill);

    if (sample->cache_fill.ok)
    {
        memcpy(sample->logical,
               read_buffer + index * XGD1_LOGICAL_SECTOR_BYTES,
               XGD1_LOGICAL_SECTOR_BYTES);
        xgd1_sha1(sample->logical,
                  XGD1_LOGICAL_SECTOR_BYTES,
                  sample->logical_sha1);

        xgd1_build_memdump_cdb(e7_cdb, address, 12U);
        xgd1_execute_data_in(hDevice,
                             e7_cdb,
                             12,
                             sample->raw_header,
                             12U,
                             15,
                             &sample->memdump_header);

        xgd1_build_memdump_cdb(e7_cdb, address + 2060U, 4U);
        xgd1_execute_data_in(hDevice,
                             e7_cdb,
                             12,
                             sample->raw_edc,
                             4U,
                             15,
                             &sample->memdump_edc);

        if (sample->memdump_header.ok && sample->memdump_edc.ok)
        {
            sample->raw_header_valid = 1;
            sample->sector_information = sample->raw_header[0];
            sample->layer_number = (uint32_t)(sample->sector_information & 1U);
            sample->physical_sector_number =
                ((uint32_t)sample->raw_header[1] << 16) |
                ((uint32_t)sample->raw_header[2] << 8) |
                (uint32_t)sample->raw_header[3];
            xgd1_set_expected_geometry(locked_view, sample);
        }
    }

    VirtualFree(read_buffer, 0, MEM_RELEASE);
}

static void xgd1_capture_state(HANDLE hDevice,
                               xgd1_raw_id_state *state,
                               const xgd1_probe_point *points,
                               uint32_t point_count)
{
    uint32_t i;

    if (!state || !points || !state->state_verified)
        return;
    if (point_count > XGD1_RAW_ID_MAX_SAMPLES)
        point_count = XGD1_RAW_ID_MAX_SAMPLES;

    state->sample_count = point_count;
    state->geometry_validation_failures = 0U;
    for (i = 0; i < point_count; ++i)
    {
        printf("[XGD1-RAW-ID] %s: cache-flush, aligned READ(12), and HIT 0xE7 header/EDC capture for LBA %u (%s).\n",
               state->name ? state->name : "state",
               points[i].lba,
               points[i].label ? points[i].label : "unlabeled");
        xgd1_capture_sample(hDevice,
                            state->capacity,
                            state->locked_view,
                            &points[i],
                            &state->samples[i]);
        if (!state->samples[i].cache_flush.ok ||
            !state->samples[i].cache_fill.ok ||
            !state->samples[i].raw_header_valid ||
            !state->samples[i].geometry_matches_expected)
            state->geometry_validation_failures++;
    }

    state->geometry_verified =
        (state->state_verified &&
         state->sample_count > 0U &&
         state->geometry_validation_failures == 0U) ? 1 : 0;
}

static void xgd1_json_string(FILE *f, const char *s)
{
    const unsigned char *p;

    fputc('"', f);
    if (s)
    {
        for (p = (const unsigned char *)s; *p; ++p)
        {
            switch (*p)
            {
                case '\\': fputs("\\\\", f); break;
                case '"': fputs("\\\"", f); break;
                case '\b': fputs("\\b", f); break;
                case '\f': fputs("\\f", f); break;
                case '\n': fputs("\\n", f); break;
                case '\r': fputs("\\r", f); break;
                case '\t': fputs("\\t", f); break;
                default:
                    if (*p < 0x20)
                        fprintf(f, "\\u%04x", (unsigned int)*p);
                    else
                        fputc(*p, f);
                    break;
            }
        }
    }
    fputc('"', f);
}

static void xgd1_json_hex(FILE *f,
                          const unsigned char *data,
                          uint32_t size)
{
    uint32_t i;

    fputc('"', f);
    for (i = 0; i < size; ++i)
        fprintf(f, "%02x", data[i]);
    fputc('"', f);
}

static void xgd1_write_cmd_result(FILE *f,
                                  const xgd1_cmd_result *r)
{
    fprintf(f, "{\"ok\":%s,\"win32_error\":%lu,\"scsi_status\":%u,"
               "\"sense_key\":%u,\"asc\":%u,\"ascq\":%u}",
            (r && r->ok) ? "true" : "false",
            (unsigned long)(r ? r->win32_error : 0),
            (unsigned int)(r ? r->scsi_status : 0),
            (unsigned int)(r ? r->sense_key : 0),
            (unsigned int)(r ? r->asc : 0),
            (unsigned int)(r ? r->ascq : 0));
}

static void xgd1_write_sample(FILE *f,
                              const xgd1_raw_id_sample *s,
                              int comma)
{
    fprintf(f, "      {\n");
    fprintf(f, "        \"lba\": %u,\n", s->lba);
    fprintf(f, "        \"label\": ");
    xgd1_json_string(f, s->label ? s->label : "");
    fprintf(f, ",\n");
    fprintf(f, "        \"cache_fill_lba\": %u,\n", s->cache_fill_lba);
    fprintf(f, "        \"cache_index\": %u,\n", s->cache_index);
    fprintf(f, "        \"cache_address\": \"0x%08x\",\n", s->cache_address);
    fprintf(f, "        \"cache_flush\": ");
    xgd1_write_cmd_result(f, &s->cache_flush);
    fprintf(f, ",\n");
    fprintf(f, "        \"cache_fill\": ");
    xgd1_write_cmd_result(f, &s->cache_fill);
    fprintf(f, ",\n");
    fprintf(f, "        \"memdump_header\": ");
    xgd1_write_cmd_result(f, &s->memdump_header);
    fprintf(f, ",\n");
    fprintf(f, "        \"memdump_edc\": ");
    xgd1_write_cmd_result(f, &s->memdump_edc);
    fprintf(f, ",\n");
    fprintf(f, "        \"raw_header_valid\": %s,\n",
            s->raw_header_valid ? "true" : "false");
    if (s->raw_header_valid)
    {
        fprintf(f, "        \"sector_information\": %u,\n",
                (unsigned int)s->sector_information);
        fprintf(f, "        \"layer_number\": %u,\n", s->layer_number);
        fprintf(f, "        \"physical_sector_number\": %u,\n",
                s->physical_sector_number);
        fprintf(f, "        \"normalized_physical_sector_number\": %u,\n",
                s->normalized_physical_sector_number);
        fprintf(f, "        \"output_lba\": %u,\n", s->output_lba);
        fprintf(f, "        \"expected_layer_number\": %u,\n",
                s->expected_layer_number);
        fprintf(f, "        \"expected_physical_sector_number\": %u,\n",
                s->expected_physical_sector_number);
        fprintf(f, "        \"expected_normalized_physical_sector_number\": %u,\n",
                s->expected_normalized_physical_sector_number);
        fprintf(f, "        \"layer_matches_expected\": %s,\n",
                s->layer_matches_expected ? "true" : "false");
        fprintf(f, "        \"psn_matches_expected\": %s,\n",
                s->psn_matches_expected ? "true" : "false");
        fprintf(f, "        \"geometry_matches_expected\": %s,\n",
                s->geometry_matches_expected ? "true" : "false");
        fprintf(f, "        \"logical_sha1\": \"%s\",\n", s->logical_sha1);
        fprintf(f, "        \"raw_header_hex\": ");
        xgd1_json_hex(f, s->raw_header, 12U);
        fprintf(f, ",\n");
        fprintf(f, "        \"raw_edc_hex\": ");
        xgd1_json_hex(f, s->raw_edc, 4U);
        fprintf(f, ",\n");
        fprintf(f, "        \"logical_data_hex\": ");
        xgd1_json_hex(f, s->logical, XGD1_LOGICAL_SECTOR_BYTES);
        fprintf(f, "\n");
    }
    else
    {
        fprintf(f, "        \"sector_information\": null,\n");
        fprintf(f, "        \"layer_number\": null,\n");
        fprintf(f, "        \"physical_sector_number\": null,\n");
        fprintf(f, "        \"normalized_physical_sector_number\": null,\n");
        fprintf(f, "        \"output_lba\": null,\n");
        fprintf(f, "        \"expected_layer_number\": null,\n");
        fprintf(f, "        \"expected_physical_sector_number\": null,\n");
        fprintf(f, "        \"expected_normalized_physical_sector_number\": null,\n");
        fprintf(f, "        \"layer_matches_expected\": false,\n");
        fprintf(f, "        \"psn_matches_expected\": false,\n");
        fprintf(f, "        \"geometry_matches_expected\": false,\n");
        fprintf(f, "        \"logical_sha1\": null,\n");
        fprintf(f, "        \"raw_header_hex\": \"\",\n");
        fprintf(f, "        \"raw_edc_hex\": \"\",\n");
        fprintf(f, "        \"logical_data_hex\": \"\"\n");
    }
    fprintf(f, "      }%s\n", comma ? "," : "");
}

static void xgd1_write_state(FILE *f,
                             const xgd1_raw_id_state *state,
                             int comma)
{
    uint32_t i;

    fprintf(f, "    {\n");
    fprintf(f, "      \"name\": ");
    xgd1_json_string(f, state->name ? state->name : "");
    fprintf(f, ",\n");
    fprintf(f, "      \"capacity_valid\": %s,\n",
            state->capacity_valid ? "true" : "false");
    fprintf(f, "      \"capacity_sectors\": %u,\n", state->capacity);
    fprintf(f, "      \"state_verified\": %s,\n",
            state->state_verified ? "true" : "false");
    fprintf(f, "      \"geometry_validation_failures\": %u,\n",
            state->geometry_validation_failures);
    fprintf(f, "      \"geometry_verified\": %s,\n",
            state->geometry_verified ? "true" : "false");
    fprintf(f, "      \"samples\": [\n");
    for (i = 0; i < state->sample_count; ++i)
        xgd1_write_sample(f,
                          &state->samples[i],
                          (i + 1U < state->sample_count));
    fprintf(f, "      ]\n");
    fprintf(f, "    }%s\n", comma ? "," : "");
}

static int xgd1_write_report(const char *report_path,
                             const char *device,
                             uint32_t entry_capacity,
                             int entry_game_view,
                             int volume_lock_ever,
                             const xgd1_raw_id_state *locked_state,
                             const xgd1_raw_id_state *unlocked_state,
                             int restore_attempted,
                             uint32_t restored_capacity,
                             int restore_verified)
{
    const char *final_path;
    char tmp_path[MAX_PATH * 4];
    FILE *f;
    int fd;
    BOOL moved;
    uint32_t validation_failures;
    int geometry_resolved;

    final_path = (report_path && report_path[0])
        ? report_path
        : "xgd1_raw_id_probe.json";

    validation_failures =
        (locked_state ? locked_state->geometry_validation_failures : 0U) +
        (unlocked_state ? unlocked_state->geometry_validation_failures : 0U);
    geometry_resolved =
        (locked_state && locked_state->geometry_verified &&
         unlocked_state && unlocked_state->geometry_verified) ? 1 : 0;

    if (strlen(final_path) + 32U >= sizeof(tmp_path))
    {
        printf("[XGD1-RAW-ID] Fatal: report path is too long.\n");
        return 0;
    }

    sprintf(tmp_path,
            "%s.tmp.%lu.%lu",
            final_path,
            (unsigned long)GetCurrentProcessId(),
            (unsigned long)GetTickCount());

    f = fopen(tmp_path, "wb");
    if (!f)
    {
        printf("[XGD1-RAW-ID] Fatal: could not create temporary report %s.\n",
               tmp_path);
        return 0;
    }

    fprintf(f, "{\n");
    fprintf(f, "  \"schema_version\": %u,\n",
            XGD1_RAW_ID_PROBE_SCHEMA_VERSION);
    fprintf(f, "  \"producer\": \"friidump-0.5.3.13\",\n");
    fprintf(f, "  \"probe\": \"original_xbox_xgd1_cache_aligned_raw_id_geometry\",\n");
    fprintf(f, "  \"device\": ");
    xgd1_json_string(f, device ? device : "");
    fprintf(f, ",\n");
    fprintf(f, "  \"cache\": {\n");
    fprintf(f, "    \"memdump_command\": \"E7 48 49 54 01\",\n");
    fprintf(f, "    \"base_address\": \"0x%08x\",\n",
            XGD1_RAW_ID_CACHE_BASE);
    fprintf(f, "    \"raw_sector_stride\": %u,\n",
            XGD1_RAW_SECTOR_BYTES);
    fprintf(f, "    \"cache_flush_command\": \"READ(12), byte1=0x08, zero transfer length\",\n");
    fprintf(f, "    \"cache_fill_command\": \"READ(12) streaming, 16 sectors, block-aligned LBA\",\n");
    fprintf(f, "    \"memdump_regions\": \"12-byte ID/IED/CPR_MAI header plus 4-byte EDC only\",\n");
    fprintf(f, "    \"raw_user_data_compared_to_logical\": false,\n");
    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");
    fprintf(f, "  },\n");
    fprintf(f, "  \"geometry_model\": {\n");
    fprintf(f, "    \"dvd_start_psn\": 196608,\n");
    fprintf(f, "    \"game_output_start_lba\": %u,\n", XGD1_GAME_OUTPUT_START_LBA);
    fprintf(f, "    \"layer_break_lba\": %u,\n", XGD1_REDUMP_LAYER_BREAK_LBA);
    fprintf(f, "    \"layer_zero_last_normalized_psn\": %u,\n",
            0x30000U + XGD1_REDUMP_LAYER_BREAK_LBA - 1U);
    fprintf(f, "    \"layer_one_raw_psn_is_24bit_complement\": true\n");
    fprintf(f, "  },\n");
    fprintf(f, "  \"safety\": {\n");
    fprintf(f, "    \"read_only_sector_commands\": true,\n");
    fprintf(f, "    \"authentication_handshake_used\": true,\n");
    fprintf(f, "    \"tray_cycle_used\": true,\n");
    fprintf(f, "    \"firmware_write_used\": false,\n");
    fprintf(f, "    \"flash_command_used\": false\n");
    fprintf(f, "  },\n");
    fprintf(f, "  \"entry\": {\n");
    fprintf(f, "    \"capacity_sectors\": %u,\n", entry_capacity);
    fprintf(f, "    \"game_view\": %s,\n",
            entry_game_view ? "true" : "false");
    fprintf(f, "    \"classification\": \"%s\"\n",
            entry_game_view ? "unlocked_game" : "locked_video_or_unknown");
    fprintf(f, "  },\n");
    fprintf(f, "  \"volume_lock_acquired_at_least_once\": %s,\n",
            volume_lock_ever ? "true" : "false");
    fprintf(f, "  \"required_states_verified\": %s,\n",
            (locked_state && locked_state->state_verified &&
             unlocked_state && unlocked_state->state_verified)
                ? "true" : "false");
    fprintf(f, "  \"geometry_validation_failures\": %u,\n",
            validation_failures);
    fprintf(f, "  \"physical_geometry_resolved\": %s,\n",
            geometry_resolved ? "true" : "false");
    fprintf(f, "  \"states\": [\n");
    xgd1_write_state(f, locked_state, 1);
    xgd1_write_state(f, unlocked_state, 0);
    fprintf(f, "  ],\n");
    fprintf(f, "  \"restoration\": {\n");
    fprintf(f, "    \"attempted\": %s,\n",
            restore_attempted ? "true" : "false");
    fprintf(f, "    \"capacity_sectors\": %u,\n", restored_capacity);
    fprintf(f, "    \"verified\": %s\n",
            restore_verified ? "true" : "false");
    fprintf(f, "  },\n");
    fprintf(f, "  \"interpretation_boundary\": {\n");
    fprintf(f, "    \"physical_geometry_resolved\": %s,\n",
            geometry_resolved ? "true" : "false");
    fprintf(f, "    \"pregame_filler_content_resolved\": false,\n");
    fprintf(f, "    \"postgame_filler_content_resolved\": false,\n");
    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");
    fprintf(f, "  }\n");
    fprintf(f, "}\n");

    if (fflush(f) != 0)
    {
        fclose(f);
        DeleteFileA(tmp_path);
        return 0;
    }

    fd = _fileno(f);
    if (fd < 0 || _commit(fd) != 0)
    {
        fclose(f);
        DeleteFileA(tmp_path);
        return 0;
    }

    if (fclose(f) != 0)
    {
        DeleteFileA(tmp_path);
        return 0;
    }

    moved = MoveFileExA(tmp_path,
                        final_path,
                        MOVEFILE_REPLACE_EXISTING |
                        MOVEFILE_WRITE_THROUGH);
    if (!moved)
    {
        printf("[XGD1-RAW-ID] Fatal: could not atomically publish report %s (error %lu).\n",
               final_path,
               (unsigned long)GetLastError());
        DeleteFileA(tmp_path);
        return 0;
    }

    printf("[XGD1-RAW-ID] Atomic report written: %s\n", final_path);
    return 1;
}

static int xgd1_raw_id_probe_core(HANDLE hDevice,
                                  const char *device,
                                  const char *report_path,
                                  BOOL owns_handle)
{
    xgd1_raw_id_state locked_state;
    xgd1_raw_id_state unlocked_state;
    uint32_t entry_capacity;
    uint32_t restored_capacity;
    int entry_game_view;
    int volume_lock_acquired;
    int volume_lock_ever;
    int restore_attempted;
    int restore_verified;
    int states_verified;
    DWORD bytes_returned;
    int report_ok;

    memset(&locked_state, 0, sizeof(locked_state));
    memset(&unlocked_state, 0, sizeof(unlocked_state));
    locked_state.name = "locked_video";
    locked_state.locked_view = 1;
    unlocked_state.name = "unlocked_game";
    unlocked_state.locked_view = 0;
    entry_capacity = 0;
    restored_capacity = 0;
    entry_game_view = 0;
    volume_lock_acquired = 0;
    volume_lock_ever = 0;
    restore_attempted = 0;
    restore_verified = 0;
    states_verified = 0;
    bytes_returned = 0;

    xbox_ref_log_open_for_target(report_path,
                                 xgd1_drive_letter_from_device(device));
    printf("[XGD1-RAW-ID] Starting cache-aligned raw-sector ID geometry probe.\n");
    printf("[XGD1-RAW-ID] Requires modified GDR-8050L firmware with HIT 0xE7 memdump support.\n");
    printf("[XGD1-RAW-ID] No ISO or firmware-write command will be issued.\n");

    if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
    {
        printf("[XGD1-RAW-ID] Fatal: invalid drive handle.\n");
        if (owns_handle)
            xbox_ref_log_close();
        return 1;
    }

    if (!EnsureDriveReady(hDevice, 30000))
    {
        printf("[XGD1-RAW-ID] Fatal: media did not become ready.\n");
        if (owns_handle)
            xbox_ref_log_close();
        return 1;
    }

    entry_capacity = GetTotalSectors(hDevice);
    entry_game_view = xgd1_capacity_is_game(entry_capacity) ? 1 : 0;
    printf("[XGD1-RAW-ID] Entry READ CAPACITY: %u sectors (%s).\n",
           entry_capacity,
           entry_game_view ? "game view" : "locked/video or unknown view");

    DeviceIoControl(hDevice,
                    FSCTL_UNLOCK_VOLUME,
                    NULL,
                    0,
                    NULL,
                    0,
                    &bytes_returned,
                    NULL);

    printf("[XGD1-RAW-ID] Cycling tray to establish locked/video view.\n");
    AutomateTrayCycle(hDevice);
    locked_state.capacity = xgd1_refresh_ready_capacity(hDevice);
    locked_state.capacity_valid = locked_state.capacity != 0U;
    locked_state.state_verified =
        (locked_state.capacity_valid &&
         xgd1_capacity_is_locked(locked_state.capacity)) ? 1 : 0;
    printf("[XGD1-RAW-ID] Locked/video READ CAPACITY: %u sectors; verified=%s.\n",
           locked_state.capacity,
           locked_state.state_verified ? "yes" : "no");

    if (DeviceIoControl(hDevice,
                        FSCTL_LOCK_VOLUME,
                        NULL,
                        0,
                        NULL,
                        0,
                        &bytes_returned,
                        NULL))
    {
        volume_lock_acquired = 1;
        volume_lock_ever = 1;
        printf("[XGD1-RAW-ID] Windows volume lock acquired.\n");
    }
    else
    {
        printf("[XGD1-RAW-ID][WARN] Windows volume lock was not acquired.\n");
    }

    SetDriveSpeedMax(hDevice);
    xgd1_capture_state(hDevice,
                       &locked_state,
                       xgd1_locked_points,
                       (uint32_t)(sizeof(xgd1_locked_points) /
                                  sizeof(xgd1_locked_points[0])));

    printf("[XGD1-RAW-ID] Applying the full Xbox handshake to establish game view.\n");
    UnlockDrive(hDevice);
    unlocked_state.capacity = xgd1_refresh_ready_capacity(hDevice);
    unlocked_state.capacity_valid = unlocked_state.capacity != 0U;
    unlocked_state.state_verified =
        (unlocked_state.capacity_valid &&
         xgd1_capacity_is_game(unlocked_state.capacity)) ? 1 : 0;
    printf("[XGD1-RAW-ID] Unlocked/game READ CAPACITY: %u sectors; verified=%s.\n",
           unlocked_state.capacity,
           unlocked_state.state_verified ? "yes" : "no");

    xgd1_capture_state(hDevice,
                       &unlocked_state,
                       xgd1_unlocked_points,
                       (uint32_t)(sizeof(xgd1_unlocked_points) /
                                  sizeof(xgd1_unlocked_points[0])));

    if (volume_lock_acquired)
    {
        DeviceIoControl(hDevice,
                        FSCTL_UNLOCK_VOLUME,
                        NULL,
                        0,
                        NULL,
                        0,
                        &bytes_returned,
                        NULL);
        volume_lock_acquired = 0;
    }

    if (!entry_game_view && entry_capacity != 0U)
    {
        restore_attempted = 1;
        printf("[XGD1-RAW-ID] Restoring entry locked/video state with a final tray cycle.\n");
        AutomateTrayCycle(hDevice);
        restored_capacity = xgd1_refresh_ready_capacity(hDevice);
        restore_verified =
            (restored_capacity != 0U &&
             xgd1_capacity_is_locked(restored_capacity)) ? 1 : 0;
    }
    else
    {
        restored_capacity = GetTotalSectors(hDevice);
        restore_verified =
            (entry_game_view &&
             xgd1_capacity_is_game(restored_capacity)) ? 1 : 0;
    }

    states_verified =
        (locked_state.state_verified &&
         unlocked_state.state_verified) ? 1 : 0;

    report_ok = xgd1_write_report(report_path,
                                  device,
                                  entry_capacity,
                                  entry_game_view,
                                  volume_lock_ever,
                                  &locked_state,
                                  &unlocked_state,
                                  restore_attempted,
                                  restored_capacity,
                                  restore_verified);

    if (owns_handle)
    {
        printf("[XGD1-RAW-ID] Issuing STOP UNIT / spin-down after probe... ");
        printf("%s\n", StopDriveUnit(hDevice) ? "OK" : "FAILED");
        CloseDrive(hDevice);
        xbox_ref_log_close();
    }

    if (!report_ok)
        return 1;
    if (!states_verified)
    {
        printf("[XGD1-RAW-ID] Probe report is partial because one or more states were not verified.\n");
        return 1;
    }
    if (!locked_state.geometry_verified || !unlocked_state.geometry_verified)
    {
        printf("[XGD1-RAW-ID] Geometry validation failed: locked failures=%u, unlocked failures=%u.\n",
               locked_state.geometry_validation_failures,
               unlocked_state.geometry_validation_failures);
        return 1;
    }

    printf("[XGD1-RAW-ID] Probe complete. All selected raw IDs match the current XGD1 output geometry.\n");
    return 0;
}

int xbox_ref_xgd1_raw_id_probe_with_handle(void *native_handle,
                                           const char *device,
                                           const char *report_path)
{
    return xgd1_raw_id_probe_core((HANDLE)native_handle,
                                  device,
                                  report_path,
                                  FALSE);
}

int xbox_ref_xgd1_raw_id_probe(const char *device,
                               const char *report_path)
{
    char drive_letter;
    HANDLE hDevice;

    drive_letter = xgd1_drive_letter_from_device(device);
    if (!drive_letter)
    {
        printf("[XGD1-RAW-ID] Fatal: could not parse drive letter from device.\n");
        return 1;
    }

    hDevice = OpenDrive(drive_letter);
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        printf("[XGD1-RAW-ID] Fatal: cannot open drive %c:. Run as Administrator.\n",
               drive_letter);
        return 1;
    }

    return xgd1_raw_id_probe_core(hDevice,
                                  device,
                                  report_path,
                                  TRUE);
}

#else

int xbox_ref_xgd1_raw_id_probe_with_handle(void *native_handle,
                                           const char *device,
                                           const char *report_path)
{
    (void)native_handle;
    (void)device;
    (void)report_path;
    return 1;
}

int xbox_ref_xgd1_raw_id_probe(const char *device,
                               const char *report_path)
{
    (void)device;
    (void)report_path;
    return 1;
}

#endif
