#include "utils.h"
#include "scsi_structs.h"
#include "xbe_cert.h"
#include "unlock.h"
#include "xbox_ref_log.h"
#include "../xbox_ref_bridge.h"

#include <time.h>
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <io.h>
#include <ntddstor.h>

#include <wincrypt.h>
#pragma comment(lib, "advapi32.lib")

#ifndef PROV_RSA_AES
#define PROV_RSA_AES 24
#endif
#ifndef ALG_SID_SHA_256
#define ALG_SID_SHA_256 12
#endif
#ifndef CALG_SHA_256
#define CALG_SHA_256 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
#endif

#define printf xbox_ref_printf

#define GDR_8163B OL23

HANDLE OpenDrive(char driveLetter)
{
    char devicePath[16];
    snprintf(devicePath, sizeof(devicePath), "\\\\.\\%c:", driveLetter);

    HANDLE hDevice = CreateFileA(devicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    return hDevice;
}

void CloseDrive(HANDLE hDevice)
{
    if (hDevice && hDevice != INVALID_HANDLE_VALUE)
        CloseHandle(hDevice);
}

int IsDiscPresent(HANDLE hDevice)
{
    DWORD bytesReturned;
    return DeviceIoControl(hDevice, IOCTL_STORAGE_CHECK_VERIFY, NULL, 0, NULL, 0, &bytesReturned, NULL);
}

void ControlTray(HANDLE hDevice, BOOL eject)
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    DWORD returned;
    memset(&sptd, 0, sizeof(sptd));

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 6;
    sptd.TimeOutValue = 10;
    sptd.Cdb[0] = 0x1B; // START STOP UNIT

    if (eject)
    {
        printf("Software Ejecting tray...\n");
        sptd.Cdb[4] = 0x02; // Power Action: Eject
    }
    else
    {
        printf("Software Closing tray...\n");
        sptd.Cdb[4] = 0x03; // Power Action: Load
    }
    if (DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &returned, NULL))
    {
        printf("Tray %s successful.\n", eject ? "eject" : "close");
    }
    else
    {
        DWORD err = GetLastError();
        printf("Failed to %s tray. Error: %lu\n", eject ? "eject" : "close", err);

        if (err == ERROR_ACCESS_DENIED)
        {
            printf("Hint: Ensure no other program is locking the drive.\n");
        }
    }
}

BOOL TestUnitReady(HANDLE hDevice)
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    DWORD returned;
    memset(&sptd, 0, sizeof(sptd));

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 6;
    sptd.TimeOutValue = 10;
    sptd.DataTransferLength = 0;
    sptd.DataBuffer = NULL;

    // TEST UNIT READY.  This is our practical poll for "ready/spun up".
    // Many drives do not expose a literal spindle-state bit to normal host software;
    // after STOP UNIT, TEST UNIT READY should fail until the unit is ready again.
    sptd.Cdb[0] = 0x00;

    if (!DeviceIoControl(hDevice,
                         IOCTL_SCSI_PASS_THROUGH_DIRECT,
                         &sptd,
                         sizeof(sptd),
                         &sptd,
                         sizeof(sptd),
                         &returned,
                         NULL))
    {
        return FALSE;
    }

    return (sptd.ScsiStatus == 0);
}

BOOL StartDriveUnit(HANDLE hDevice)
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    DWORD returned;
    memset(&sptd, 0, sizeof(sptd));

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 6;
    sptd.TimeOutValue = 30;
    sptd.DataTransferLength = 0;
    sptd.DataBuffer = NULL;

    // START STOP UNIT, START=1, LOEJ=0.
    // This requests spin-up/start without ejecting/loading the tray.
    sptd.Cdb[0] = 0x1B;
    sptd.Cdb[4] = 0x01;

    printf("Sending SCSI START UNIT / spin-up command...\n");

    if (DeviceIoControl(hDevice,
                        IOCTL_SCSI_PASS_THROUGH_DIRECT,
                        &sptd,
                        sizeof(sptd),
                        &sptd,
                        sizeof(sptd),
                        &returned,
                        NULL))
    {
        printf("SCSI START UNIT / spin-up command accepted.\n");
        return TRUE;
    }

    {
        DWORD err = GetLastError();
        printf("[WARN] SCSI START UNIT / spin-up failed. Error: %lu\n", err);
        return FALSE;
    }
}

BOOL EnsureDriveReady(HANDLE hDevice, DWORD timeoutMs)
{
    DWORD startTick = GetTickCount();
    BOOL startIssued = FALSE;

    printf("Polling drive readiness with TEST UNIT READY...\n");

    for (;;)
    {
        if (TestUnitReady(hDevice))
        {
            printf("Drive reports ready.\n");
            return TRUE;
        }

        if (!startIssued)
        {
            printf("Drive is not ready/spun up yet; requesting START UNIT.\n");
            StartDriveUnit(hDevice);
            startIssued = TRUE;
        }

        if ((GetTickCount() - startTick) >= timeoutMs)
        {
            printf("[WARN] Drive did not report ready within %lu ms.\n", (unsigned long)timeoutMs);
            printf("       Continuing may fail if the unit is still spun down or still reading lead-in.\n");
            return FALSE;
        }

        Sleep(1000);
    }
}

BOOL StopDriveUnit(HANDLE hDevice)
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    DWORD returned;
    memset(&sptd, 0, sizeof(sptd));

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 6;
    sptd.TimeOutValue = 30;
    sptd.DataTransferLength = 0;
    sptd.DataBuffer = NULL;

    // START STOP UNIT, START=0, LOEJ=0.
    // This requests a normal stop/spin-down without ejecting or loading the tray.
    sptd.Cdb[0] = 0x1B;
    sptd.Cdb[4] = 0x00;

    printf("Sending SCSI STOP UNIT / spin-down command...\n");

    if (DeviceIoControl(hDevice,
                        IOCTL_SCSI_PASS_THROUGH_DIRECT,
                        &sptd,
                        sizeof(sptd),
                        &sptd,
                        sizeof(sptd),
                        &returned,
                        NULL))
    {
        printf("SCSI STOP UNIT / spin-down successful.\n");
        return TRUE;
    }

    {
        DWORD err = GetLastError();
        printf("[WARN] SCSI STOP UNIT / spin-down failed. Error: %lu\n", err);
        printf("       Dump output has already been finalized; this only affects drive spin state.\n");
        return FALSE;
    }
}

void AutomateTrayCycle(HANDLE hDevice)
{
    ControlTray(hDevice, TRUE);
    Sleep(3000); // Give the tray time to fully extend

    // --- CLOSE ---
    ControlTray(hDevice, FALSE);
    printf("Waiting for disc spin-up/readiness after tray close...\n");
    if (EnsureDriveReady(hDevice, 45000))
    {
        // Small settle period after readiness so the drive can finish lead-in/media-change bookkeeping.
        Sleep(1500);
    }
    else
    {
        // Preserve the old conservative behavior if TEST UNIT READY polling never succeeds.
        printf("[WARN] Falling back to fixed 10s post-close settle delay.\n");
        Sleep(10000);
    }
}

BOOL SetDriveSpeedMax(HANDLE hDevice)
{
    SCSI_PASS_THROUGH_DIRECT sptd = {0};
    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.PathId = 0;
    sptd.TargetId = 1;
    sptd.Lun = 0;
    sptd.CdbLength = 12; // 12-byte CDB for 0xBB
    sptd.DataIn = SCSI_IOCTL_DATA_OUT;
    sptd.TimeOutValue = 10;
    sptd.DataBuffer = NULL;
    sptd.DataTransferLength = 0;

    // CDB 0xBB: [0] Opcode, [2-3] Read Speed, [4-5] Write Speed
    sptd.Cdb[0] = 0xBB;
    sptd.Cdb[2] = 0xFF; // MSB
    sptd.Cdb[3] = 0xFF; // LSB
    sptd.Cdb[4] = 0xFF; // MSB
    sptd.Cdb[5] = 0xFF; // LSB

    DWORD returned;
    return DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
                           &sptd, sizeof(sptd), &sptd, sizeof(sptd),
                           &returned, NULL);
}

void ForceMediaRefresh(HANDLE hDevice)
{
    DWORD bytesReturned;

    // Lock the volume so Windows stops background polling
    DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL);

    // Force the storage stack to re-read the Partition Table/Capacity
    // without sending an Eject command to the hardware.
    if (DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL))
    {
        printf("Windows Partition Stack refreshed silently.\n");
    }

    // Explicitly dismount to kill the "Video DVD" file system driver (UDFS/ISO9660)
    DeviceIoControl(hDevice, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL);
    Sleep(1000);
}

void HexDump(unsigned char *buffer, uint32_t size)
{
    for (uint32_t i = 0; i < size; i++)
    {
        if (i % 16 == 0)
            printf("\n%04X: ", i);
        printf("%02X ", buffer[i]);
    }
    printf("\n");
}

void outputdata(const uint8_t *buf, uint32_t lines)
{
    for (uint32_t j = 0; j < lines; j++)
    {
        for (uint32_t k = 0; k < 16; k++)
        {
            uint32_t idx = j * 16 + k;
            if (k == 8)
                printf("- ");
            printf("%02X ", buf[idx]);
        }
        printf("\n");
    }
}

uint8_t chksum8(const unsigned char *buff, size_t len) {
    unsigned int sum = 0;
    for (sum = 0; len != 0; len--)
        sum += *(buff++);
    return (uint8_t)sum;
}

void FormatElapsedTime(DWORD dwMilliseconds, char *outStr)
{
    uint32_t totalSeconds = dwMilliseconds / 1000;
    uint32_t hours = totalSeconds / 3600;
    uint32_t minutes = (totalSeconds % 3600) / 60;
    uint32_t seconds = totalSeconds % 60;

    sprintf(outStr, "%02u:%02u:%02u", hours, minutes, seconds);
}

void PrintFormattedCapacity(unsigned char *scsibuffer)
{
    // The first 4 bytes are the Last Logical Block Address (Big Endian)
    uint32_t maxLBA = (scsibuffer[0] << 24) | (scsibuffer[1] << 16) |
                      (scsibuffer[2] << 8) | scsibuffer[3];

    // The next 4 bytes are the Block Length (Big Endian)
    uint32_t blockLen = (scsibuffer[4] << 24) | (scsibuffer[5] << 16) |
                        (scsibuffer[6] << 8) | scsibuffer[7];

    // Total bytes = (MaxLBA + 1) * BlockLen
    // Use double for the math to avoid 32-bit integer overflow
    double totalBytes = (double)(maxLBA + 1) * blockLen;
    double totalGB = totalBytes / (1024.0 * 1024.0 * 1024.0);

    printf("--------------------------------------------\n");
    printf("Drive Capacity Details:\n");
    printf("  Total Sectors: %u\n", maxLBA + 1);
    printf("  Sector Size:   %u bytes\n", blockLen);
    printf("  Total Size:    %.2f GB\n", totalGB);
    printf("--------------------------------------------\n");
}

void ListOpticalDrives()
{
    DWORD drives = GetLogicalDrives();
    char rootPath[] = "A:\\";
    char devicePath[] = "\\\\.\\A:";
    BYTE buffer[1024];

    printf("%-5s %-12s %-18s %-15s %s\n", "ID", "Vendor", "Model", "Volume Label", "Status");
    printf("-------------------------------------------------------------------------------\n");

    uint8_t driveCount = 0;
    for (int i = 0; i < 26; i++)
    {
        if (drives & (1 << i))
        {
            rootPath[0] = 'A' + i;

            if (GetDriveTypeA(rootPath) == DRIVE_CDROM)
            {
                devicePath[4] = 'A' + i;

                // 1. Get Hardware Info (Vendor/Model)
                char vendorStr[16] = "Generic";
                char productStr[21] = "Unknown";

                HANDLE h = CreateFileA(devicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE,
                                       NULL, OPEN_EXISTING, 0, NULL);

                if (h != INVALID_HANDLE_VALUE)
                {
                    STORAGE_PROPERTY_QUERY query = {0};
                    query.PropertyId = StorageDeviceProperty;
                    query.QueryType = PropertyStandardQuery;
                    DWORD bytes;

                    if (DeviceIoControl(h, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query),
                                        buffer, sizeof(buffer), &bytes, NULL))
                    {
                        PSTORAGE_DEVICE_DESCRIPTOR desc = (PSTORAGE_DEVICE_DESCRIPTOR)buffer;
                        if (desc->VendorIdOffset)
                            strcpy(vendorStr, (char *)(buffer + desc->VendorIdOffset));
                        if (desc->ProductIdOffset)
                            strcpy(productStr, (char *)(buffer + desc->ProductIdOffset));
                    }
                    CloseHandle(h);
                }

                // 2. Get Volume Info (Disc Label)
                char volumeName[MAX_PATH + 1] = {0};
                char statusStr[20] = "No Disc";

                if (GetVolumeInformationA(rootPath, volumeName, sizeof(volumeName),
                                          NULL, NULL, NULL, NULL, 0))
                {
                    if (strlen(volumeName) == 0)
                        strcpy(volumeName, "[No Label]");
                    strcpy(statusStr, "Ready");
                }

                printf("  %c:   %-12.12s %-18.18s %-15.15s %s\n",
                       rootPath[0], vendorStr, productStr, volumeName, statusStr);
                driveCount++;
            }
        }
    }
    if (driveCount == 0)
        printf("No optical drives found.\n");
    printf("-------------------------------------------------------------------------------\n");
    printf("Total Optical Drives Found: %u\n", driveCount);
}

uint32_t GetTotalSectors(HANDLE hDevice)
{
    typedef struct _SCSI_PASS_THROUGH_WITH_BUFFERS
    {
        SCSI_PASS_THROUGH spt;
        unsigned char ucDataBuf[8]; // Buffer for the 8-byte READ CAPACITY result
    } SCSI_PASS_THROUGH_WITH_BUFFERS;

    SCSI_PASS_THROUGH_WITH_BUFFERS sptwb = {0};

    sptwb.spt.Length = sizeof(SCSI_PASS_THROUGH);
    sptwb.spt.CdbLength = 10; // READ CAPACITY (10) is a 10-byte command
    sptwb.spt.DataIn = SCSI_IOCTL_DATA_IN;
    sptwb.spt.DataTransferLength = 8;
    sptwb.spt.TimeOutValue = 2; // 2 second timeout
    sptwb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);

    // CDB 0x25 = READ CAPACITY (10)
    sptwb.spt.Cdb[0] = 0x25;

    DWORD bytesReturned;
    if (DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH,
                        &sptwb, sizeof(sptwb),
                        &sptwb, sizeof(sptwb),
                        &bytesReturned, NULL))
    {

        // Extract Max LBA (Big Endian) from the first 4 bytes
        uint32_t maxLBA = (sptwb.ucDataBuf[0] << 24) |
                          (sptwb.ucDataBuf[1] << 16) |
                          (sptwb.ucDataBuf[2] << 8) |
                          sptwb.ucDataBuf[3];

        return (maxLBA + 1);
    }

    return 0; // Return 0 on failure
}

uint32_t GetXboxPhysicalSectors(HANDLE hDevice)
{
    SCSI_PASS_THROUGH_DIRECT sptd = {0};
    unsigned char buffer[2048] = {0};
    DWORD bytesReturned;

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 12;
    sptd.DataIn = SCSI_IOCTL_DATA_IN;
    sptd.DataTransferLength = 2048;
    sptd.TimeOutValue = 10;
    sptd.DataBuffer = buffer;

    // READ DVD STRUCTURE (0xAD)
    sptd.Cdb[0] = 0xAD;
    sptd.Cdb[7] = 0x00; // Format: Physical Format Information
    sptd.Cdb[8] = 0x08; // Allocation Length (MSB)
    sptd.Cdb[9] = 0x00; // Allocation Length (LSB)

    if (DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &bytesReturned, NULL))
    {
        // Bytes 13-15 of the PFI contain the End LBA of the data area
        uint32_t endLba = (buffer[13] << 16) | (buffer[14] << 8) | buffer[15];

        // For Xbox discs, we add 1 to the End LBA to get the total count
        // and add the 32 sectors of lead-in padding we manually create.
        return endLba + 1;
    }

    // Fallback for Dual Layer if command fails
    return 3431264;
}

// Forces Windows to re-evaluate the drive without ejecting the tray
void RefreshVolume(HANDLE hDevice)
{
    DWORD bytesReturned;
    printf("Refreshing Volume Stack (Quiet Mode)...\n");
    // Only update properties; do NOT dismount as it resets the GDR-8163B state.
    DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL);
    Sleep(1000); // Essential for the firmware to re-index after the OS check
}

void ListDirectoryRecursive(HANDLE hDevice, uint32_t lba, uint32_t size, int level)
{
    if (size == 0 || level > 10)
        return; // Prevent infinite recursion

    uint32_t sectorsToRead = (size + 2047) / 2048;
    unsigned char *dirBuffer = (unsigned char *)VirtualAlloc(NULL, sectorsToRead * 2048, MEM_COMMIT, PAGE_READWRITE);
    if (!dirBuffer)
        return;

    if (ScsiReadSectors(hDevice, lba, (uint16_t)sectorsToRead, dirBuffer))
    {
        uint32_t offset = 0;
        while (offset < size)
        {
            XDFS_DIR_ENTRY *entry = (XDFS_DIR_ENTRY *)&dirBuffer[offset];

            // --- SANITY CHECK 1: End of Table ---
            // If FileNameLength is 0 or 0xFF, we've hit the padding/end of the list.
            if (entry->FileNameLength == 0 || entry->FileNameLength == 0xFF)
                break;

            // --- SANITY CHECK 2: Buffer Overflow ---
            // Ensure the entry doesn't claim to exist past our allocated buffer.
            if (offset + 14 + entry->FileNameLength > size)
                break;

            // --- SANITY CHECK 3: Character Validation ---
            // If the first character isn't a printable ASCII, it's a glitch entry.
            if (entry->FileName[0] < 32 || entry->FileName[0] > 126)
                break;

            // Indentation
            for (int i = 0; i < level; i++)
                printf("  ");

            // Branch Visual
            if (entry->Attributes & 0x10)
            {
                printf("[DIR] ");
            }
            else
            {
                printf(" |-- ");
            }

            // Print Filename safely
            for (int i = 0; i < entry->FileNameLength; i++)
            {
                char c = entry->FileName[i];
                if (c >= 32 && c <= 126)
                    printf("%c", c);
                else
                    printf("?"); // Replace glitches with a placeholder
            }

            if (!(entry->Attributes & 0x10))
            {
                printf(" (%u bytes)", entry->FileSize);
            }
            printf("\n");

            // RECURSION: Only dive if it's a valid directory LBA
            if ((entry->Attributes & 0x10) && entry->StartLBA > 0x100)
            {
                ListDirectoryRecursive(hDevice, entry->StartLBA, entry->FileSize, level + 1);
            }

            // Move to next entry (4-byte alignment)
            uint32_t nextOffset = (14 + entry->FileNameLength + 3) & ~3;

            // If the calculation gives us 0, we're stuck in an infinite loop; break.
            if (nextOffset == 0)
                break;
            offset += nextOffset;
        }
    }

    VirtualFree(dirBuffer, 0, MEM_RELEASE);
}

void ReadXboxGameDir(HANDLE hDevice)
{
    // Single buffer for the Volume Descriptor read
    unsigned char *sectorBuffer = (unsigned char *)VirtualAlloc(NULL, 2048, MEM_COMMIT, PAGE_READWRITE);
    if (!sectorBuffer)
        return;

    // Read XDFS Volume Descriptor at Sector 0x20
    if (!ScsiReadSectors(hDevice, 0x20, 1, sectorBuffer))
    {
        printf("Error: Could not read XDFS Volume Descriptor.\n");
        VirtualFree(sectorBuffer, 0, MEM_RELEASE);
        return;
    }

    // Map the descriptor and extract root location/size
    XDFS_VOLUME_DESCRIPTOR *vol = (XDFS_VOLUME_DESCRIPTOR *)sectorBuffer;
    uint32_t rootLba = vol->RootLBA;
    uint32_t rootSize = vol->RootSize;

    // We no longer need this buffer once we have the Root LBA/Size
    VirtualFree(sectorBuffer, 0, MEM_RELEASE);

    // Draw the recursive tree
    printf("\n--- XDFS FILE SYSTEM TREE ---\n");

    if (rootLba > 0)
    {
        ListDirectoryRecursive(hDevice, rootLba, rootSize, 0);
    }
    else
    {
        printf("Error: Invalid Root LBA.\n");
    }

    printf("------------------------------\n");
}

void SanitizeFilename(char *filename)
{
    if (!filename || filename[0] == '\0')
        return;

    int readIndex = 0;
    int writeIndex = 0;
    int lastWasSpace = 1; // Using 1 for true to trim leading spaces

    while (filename[readIndex] != '\0')
    {
        unsigned char c = (unsigned char)filename[readIndex];

        // Whitelist: Only allow Letters (isalnum) and Spaces
        // This strips ! ' ? : " / \ | * < > and non-printable characters
        if (isalnum(c) || c == ' ')
        {

            // Collapse Multiple Spaces
            if (c == ' ')
            {
                if (!lastWasSpace)
                {
                    filename[writeIndex++] = ' ';
                    lastWasSpace = 1;
                }
            }
            else
            {
                // It's a letter or number, write it normally
                filename[writeIndex++] = c;
                lastWasSpace = 0;
            }
        }
        readIndex++;
    }

    // Null-terminate the new shorter string
    filename[writeIndex] = '\0';

    // Remove trailing space if one exists
    if (writeIndex > 0 && filename[writeIndex - 1] == ' ')
    {
        filename[writeIndex - 1] = '\0';
    }
}

BOOL ScsiReadSectors(HANDLE hDevice, uint32_t lba, uint16_t count, unsigned char *buffer)
{
    SCSI_PASS_THROUGH_DIRECT sptd = {0};
    DWORD bytesReturned;

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 10;
    sptd.DataIn = 1;
    sptd.DataTransferLength = count * 2048;
    sptd.TimeOutValue = 30;
    sptd.DataBuffer = buffer;

    sptd.Cdb[0] = 0x28; // READ(10)
    sptd.Cdb[2] = (lba >> 24) & 0xFF;
    sptd.Cdb[3] = (lba >> 16) & 0xFF;
    sptd.Cdb[4] = (lba >> 8) & 0xFF;
    sptd.Cdb[5] = lba & 0xFF;
    sptd.Cdb[7] = (unsigned char)((count >> 8) & 0xFF);
    sptd.Cdb[8] = (unsigned char)(count & 0xFF);

    return DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &bytesReturned, NULL);
}

XboxGameInfo GetXboxGameInfo(HANDLE hDevice)
{
    XboxGameInfo info;
    memset(&info, 0, sizeof(XboxGameInfo));
    unsigned char sectorBuffer[2048];

    // Get Volume Descriptor (LBA 0x20)
    if (!ScsiReadSectors(hDevice, 0x20, 1, sectorBuffer))
        return info;

    // Verify XDFS Magic "XGD2" or "MICROSOFT*XBOX*MEDIA"
    if (memcmp(sectorBuffer, "MICROSOFT", 9) != 0)
    {
        return (XboxGameInfo){.TitleName = "Not_XDFS"};
    }

    uint32_t rootLba = *(uint32_t *)&sectorBuffer[0x14];
    uint32_t rootSize = *(uint32_t *)&sectorBuffer[0x18];

    uint32_t rawVolumeSize = *(uint32_t *)&sectorBuffer[0x1C];
    // Assign to the 64-bit member (cast to ensure no weird sign extension)
    info.TotalSizeBytes = (uint64_t)rawVolumeSize;

    // Read Root Directory (Scanning multiple sectors for default.xbe)
    uint32_t sectorsToRead = (rootSize + 2047) / 2048;
    for (uint32_t s = 0; s < sectorsToRead; s++)
    {
        unsigned char dirBuffer[2048];
        if (!ScsiReadSectors(hDevice, rootLba + s, 1, dirBuffer))
            break;

        uint32_t offset = 0;
        while (offset < 2030)
        {
            uint16_t leftNode = *(uint16_t *)&dirBuffer[offset];
            if (leftNode == 0xFFFF)
                break; // End of directory

            uint32_t startLba = *(uint32_t *)&dirBuffer[offset + 4];
            uint8_t nameLen = dirBuffer[offset + 13];
            char *name = (char *)&dirBuffer[offset + 14];

            if (nameLen == 0)
                break;

            // Match "default.xbe"
            if (nameLen == 11 && _strnicmp(name, "default.xbe", 11) == 0)
            {
                unsigned char xbeHeader[2048];
                if (ScsiReadSectors(hDevice, startLba, 1, xbeHeader))
                {

                    if (*(uint32_t *)xbeHeader != 0x48454258)
                        break; // "XBEH"

                    // 4. Locate Certificate
                    uint32_t baseVA = *(uint32_t *)&xbeHeader[0x104];
                    uint32_t certVA = *(uint32_t *)&xbeHeader[0x118];
                    uint32_t fileOffset = certVA - baseVA;

                    // Certificate might be in a later sector of the XBE file
                    uint32_t certSector = startLba + (fileOffset / 2048);
                    uint32_t innerOff = (fileOffset % 2048);

                    unsigned char certBuffer[2048];
                    if (ScsiReadSectors(hDevice, certSector, 1, certBuffer))
                    {

                        // Populate the Struct from the Certificate
                        info.TitleId = *(uint32_t *)&certBuffer[innerOff + 0x008];
                        info.AllowedMedia = *(uint32_t *)&certBuffer[innerOff + 0x09C];
                        info.GameRegion = *(uint32_t *)&certBuffer[innerOff + 0x0A0];
                        info.GameRatings = *(uint32_t *)&certBuffer[innerOff + 0x0A4];
                        info.DiscNumber = *(uint32_t *)&certBuffer[innerOff + 0x0A8];
                        info.Version = *(uint32_t *)&certBuffer[innerOff + 0x0AC];

                        // Convert UTF-16 Title Name (at 0x00C) to ASCII
                        for (int i = 0; i < 40; i++)
                        {
                            char c = certBuffer[innerOff + 0x00C + (i * 2)];
                            if (c == 0)
                                break;
                            info.TitleName[i] = c;
                        }

                        info.Success = 1;
                        return info;
                    }
                }
            }
            offset += (14 + nameLen + 3) & ~3; // XDFS Alignment
        }
    }

    return info; // Success will be 0 if we never found default.xbe or failed to read the cert
}

void DisplayXboxGameInfo(XboxGameInfo info)
{
    if (!info.Success)
    {
        printf("Error: Could not retrieve Xbox game information.\n");
        return;
    }

    printf("\n--- Xbox Game Information ---\n");
    printf("Title Name:    %s\n", info.TitleName);
    printf("Title ID:      0x%08X\n", info.TitleId);
    printf("Version:       %u\n", info.Version);
    printf("Disc Number:   %u\n", info.DiscNumber);

    // Decode Regions
    printf("Regions:       ");
    if (info.GameRegion & XB_REGION_MANUFACTURING)
        printf("[Manufacturing] ");
    if (info.GameRegion & XB_REGION_US_CANADA)
        printf("North America ");
    if (info.GameRegion & XB_REGION_JAPAN)
        printf("Japan ");
    if (info.GameRegion & XB_REGION_EUROPE_AU_NZ)
        printf("Europe/AU ");
    if (info.GameRegion & XB_REGION_REST_OF_WORLD)
        printf("Rest of World ");

    // If everything is set (0x7FFFFFFF or 0xFFFFFFFF), it's Region Free
    if ((info.GameRegion & 0x7FFFFFFF) == 0x7FFFFFFF)
    {
        printf("(Region Free)");
    }
    else if (info.GameRegion == 0)
    {
        printf("None (Locked)");
    }
    printf("\n");

    // Decode Media Types
    printf("Allowed Media: ");
    if (info.AllowedMedia & XB_MEDIA_HARD_DRIVE)
        printf("HDD ");
    if (info.AllowedMedia & XB_MEDIA_DVD_X2)
        printf("Xbox_DVD ");
    if (info.AllowedMedia & XB_MEDIA_DVD_5_RO)
        printf("DVD-5 ");
    if (info.AllowedMedia & XB_MEDIA_DVD_9_RO)
        printf("DVD-9 ");
    if (info.AllowedMedia & XB_MEDIA_CD)
        printf("CD ");
    if (info.AllowedMedia & XB_MEDIA_DONGLE)
        printf("Memory_Unit ");
    printf("\n");

    DisplayXboxRating(info.GameRatings);

    printf("-----------------------------\n");
}

void DisplayXboxRating(uint32_t ratings)
{
    // ESRB (North America) - Byte 0 (Bits 0-7)
    uint8_t esrb = (uint8_t)(ratings & 0xFF);
    if (esrb != 0 && esrb != 0xFF)
    {
        printf("ESRB Rating:   ");
        switch (esrb)
        {
        case 0x01:
            printf("EC (Early Childhood)\n");
            break;
        case 0x02:
            printf("E (Everyone)\n");
            break;
        case 0x03:
            printf("K-A (Kids to Adults)\n");
            break;
        case 0x04:
            printf("T (Teen)\n");
            break;
        case 0x05:
            printf("M (Mature)\n");
            break;
        case 0x06:
            printf("AO (Adults Only)\n");
            break;
        default:
            printf("RP (Rating Pending/Unrated)\n");
            break;
        }
    }

    // PEGI (Europe) - Byte 1 (Bits 8-15)
    uint8_t pegi = (uint8_t)((ratings >> 8) & 0xFF);
    if (pegi != 0 && pegi != 0xFF)
    {
        printf("PEGI Rating:   ");
        switch (pegi)
        {
        case 0x00:
            printf("3+\n");
            break;
        case 0x01:
            printf("7+\n");
            break;
        case 0x02:
            printf("12+\n");
            break;
        case 0x03:
            printf("16+\n");
            break;
        case 0x04:
            printf("18+\n");
            break;
        default:
            printf("Other (0x%02X)\n", pegi);
            break;
        }
    }

    // CERO (Japan) - Byte 2 (Bits 16-23)
    uint8_t cero = (uint8_t)((ratings >> 16) & 0xFF);
    if (cero != 0 && cero != 0xFF)
    {
        printf("CERO Rating:   ");
        switch (cero)
        {
        case 0x00:
            printf("A (All Ages)\n");
            break;
        case 0x01:
            printf("B (12+)\n");
            break;
        case 0x02:
            printf("C (15+)\n");
            break;
        case 0x03:
            printf("D (17+)\n");
            break;
        case 0x04:
            printf("Z (18+ Only)\n");
            break;
        default:
            printf("Other (0x%02X)\n", cero);
            break;
        }
    }

    if ((ratings & 0x00FFFFFF) == 0)
    {
        printf("Rating:        None/Unrated\n");
    }
}

// --- POST-DUMP VERIFICATION ---
void PrintGamePartitionHash(const char *filename)
{
    FILE *f = fopen(filename, "rb");
    __int64 fileBytes;
    uint32_t startLba = START_LBA_MAGIC;
    unsigned long long bytesRemaining = 0ULL;
    unsigned long long totalBytesToHash = 0ULL;
    unsigned long long bytesDone = 0ULL;
    unsigned char *vBuf;
    size_t read;
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    BYTE rgbHash[20];
    DWORD cbHash = 20;
    char finalHash[41] = {0};
    DWORD startTick = 0;
    DWORD lastPrintTick = 0;
    DWORD nowTick = 0;
    DWORD elapsedMs = 0;
    DWORD etaMs = 0;
    char timeStr[12] = {0};
    char etaStr[12] = {0};

    if (!f)
        return;

    if (_fseeki64(f, 0, SEEK_END) != 0)
    {
        fclose(f);
        return;
    }
    fileBytes = _ftelli64(f);
    if (fileBytes < 0)
    {
        fclose(f);
        return;
    }

    if ((unsigned long long)fileBytes == (unsigned long long)XGD1_FULL_REDUMP_SECTORS * 2048ULL)
    {
        startLba = XGD1_GAME_OUTPUT_START_LBA;
        bytesRemaining = (unsigned long long)REDUMP_SECTORS * 2048ULL;
        printf("[HASH] Calculating Game/XISO-region SHA-1 (Redump-style output LBA %u, %u sectors)...\n",
               startLba, REDUMP_SECTORS);
    }
    else
    {
        startLba = START_LBA_MAGIC;
        bytesRemaining = ((unsigned long long)fileBytes > (unsigned long long)startLba * 2048ULL)
                         ? ((unsigned long long)fileBytes - (unsigned long long)startLba * 2048ULL)
                         : 0ULL;
        printf("[HASH] Calculating Game-Partition-Only SHA-1 (legacy contiguous output LBA %u)...\n", startLba);
    }

    totalBytesToHash = bytesRemaining;
    if (bytesRemaining == 0)
    {
        fclose(f);
        return;
    }

    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
    {
        fclose(f);
        return;
    }
    if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
    {
        CryptReleaseContext(hProv, 0);
        fclose(f);
        return;
    }

    _fseeki64(f, (__int64)startLba * 2048, SEEK_SET);

    vBuf = (unsigned char *)malloc(1024 * 1024); // 1MB buffer
    if (!vBuf)
    {
        CryptDestroyHash(hHash);
        CryptReleaseContext(hProv, 0);
        fclose(f);
        return;
    }

    startTick = GetTickCount();
    lastPrintTick = startTick;

    while (bytesRemaining > 0 && (read = fread(vBuf, 1, (bytesRemaining > 1024ULL * 1024ULL) ? 1024 * 1024 : (size_t)bytesRemaining, f)) > 0)
    {
        CryptHashData(hHash, vBuf, (DWORD)read, 0);
        bytesRemaining -= read;
        bytesDone += (unsigned long long)read;

        nowTick = GetTickCount();
        if (bytesDone >= totalBytesToHash || (nowTick - lastPrintTick) >= 1000)
        {
            double percent = ((double)bytesDone / (double)totalBytesToHash) * 100.0;
            double mbDone = (double)bytesDone / (1024.0 * 1024.0);
            double speed = 0.0;
            elapsedMs = nowTick - startTick;
            if (elapsedMs > 0)
                speed = mbDone / ((double)elapsedMs / 1000.0);
            etaMs = (bytesDone > 0 && elapsedMs > 0)
                    ? (DWORD)(((double)elapsedMs / (double)bytesDone) * (double)(totalBytesToHash - bytesDone))
                    : 0;
            FormatElapsedTime(elapsedMs, timeStr);
            FormatElapsedTime(etaMs, etaStr);
            xbox_ref_console_printf("\r[HASH] Game/XISO-region: %5.1f%% | %.1f MB | Speed: %.2f MB/s | Time: %s | ETA: %s    ",
                   percent,
                   mbDone,
                   speed,
                   timeStr,
                   etaStr);
            fflush(stdout);
            lastPrintTick = nowTick;
        }
    }

    CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0);
    for (int i = 0; i < 20; i++)
        sprintf(&finalHash[i * 2], "%02x", rgbHash[i]);

    elapsedMs = GetTickCount() - startTick;
    FormatElapsedTime(elapsedMs, timeStr);
    if (totalBytesToHash > 0)
        xbox_ref_console_printf("\r[HASH] Game/XISO-region: 100.0%% | %.1f MB | Time: %s                         \n",
               (double)totalBytesToHash / (1024.0 * 1024.0),
               timeStr);
    printf("Game/XISO-region SHA-1: %s\n", finalHash);
    printf("[OK] Game/XISO-region SHA-1 complete in %s.\n", timeStr);

    free(vBuf);
    CryptDestroyHash(hHash);
    CryptReleaseContext(hProv, 0);
    fclose(f);
}

void GetMediaID(HANDLE hDevice, char *outMediaId)
{
    SCSI_PASS_THROUGH_DIRECT sptd = {0};
    unsigned char buffer[2048] = {0};
    DWORD bytesReturned;

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 12;
    sptd.DataIn = SCSI_IOCTL_DATA_IN;
    sptd.DataTransferLength = 2048;
    sptd.TimeOutValue = 5;
    sptd.DataBuffer = buffer;

    sptd.Cdb[0] = 0xAD; // READ DVD STRUCTURE
    sptd.Cdb[7] = 0x04; // Format: Disc Manufacturing Information (DMI)
    sptd.Cdb[8] = 0x08;
    sptd.Cdb[9] = 0x00;

    if (DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &bytesReturned, NULL))
    {
        // The Media ID is typically 32 bytes starting at offset 4 in the DMI
        // Offset 8 is where "MS11..." usually starts on Xbox discs
        // We'll grab 16 characters to be safe
        int writePos = 0;
        for (int i = 8; i < 24; i++)
        {
            // Only add alphanumeric characters to keep the filename clean
            if (isalnum(buffer[i]))
            {
                outMediaId[writePos++] = buffer[i];
            }
        }
        outMediaId[writePos] = '\0'; // Null terminate the string
    }
    else
    {
        strcpy(outMediaId, "UNKNOWN_ID");
    }
}

void GetDiscMetadata(HANDLE hDevice, uint32_t *totalSectors, bool *isDualLayer, XboxGameInfo *gameInfo)
{
    SCSI_PASS_THROUGH_DIRECT sptd = {0};
    unsigned char buffer[2048] = {0};
    DWORD bytesReturned;

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = 12;
    sptd.DataIn = SCSI_IOCTL_DATA_IN;
    sptd.DataTransferLength = 2048;
    sptd.TimeOutValue = 10;
    sptd.DataBuffer = buffer;

    sptd.Cdb[0] = 0xAD; // READ DVD STRUCTURE
    sptd.Cdb[7] = 0x00; // Physical Format Information
    sptd.Cdb[8] = 0x08; // 2048 bytes
    sptd.Cdb[9] = 0x00;

    if (DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT, &sptd, sizeof(sptd), &sptd, sizeof(sptd), &bytesReturned, NULL))
    {

        // Byte 12: bits 5-6 (Number of Layers)
        // 0x20 = 00100000 (Two layers), 0x00 = 00000000 (One layer)
        unsigned char layerInfo = (buffer[12] >> 5) & 0x03;
        *isDualLayer = (layerInfo > 0);

        // Bytes 13-15: End LBA
        uint32_t endLba = (buffer[13] << 16) | (buffer[14] << 8) | buffer[15];

        // XBOX SANITY CHECK
        // If the drive reports a value much larger than a standard Xbox Dual Layer (3.4M sectors)
        // it means the drive is reporting the raw DVD-9 limit. We must cap it.
        if (endLba > ((uint32_t)REDUMP_SECTORS - 1))
        {
            printf("[!] Drive reported Raw DVD-9 geometry. Normalizing to Xbox Dual Layer...\n");
            if (gameInfo->TotalSizeBytes < LAYER_BREAK)
            {
                printf("[!] Info: Game partition size is smaller than expected for a Dual Layer disc.\n");
            }
            *totalSectors = REDUMP_SECTORS;
            *isDualLayer = true;
        }
        else
        {
            printf("[!] Drive reported Raw DVD-5 geometry. Normalizing to Xbox Single Layer...\n");
            *totalSectors = endLba + 1;
            *isDualLayer = (endLba > (uint32_t)LAYER_THRESHOLD); // Standard threshold for SL vs DL
        }
    }
    else
    {
        // Fallback safety
        *isDualLayer = true;
        *totalSectors = REDUMP_SECTORS;
        printf("Media Info: Could not read PFI. Defaulting to Dual Layer.\n");
    }
}

uint32_t GetGamePartitionSize(HANDLE hDevice, uint32_t totalDiscSectors, XDFS_VOLUME_DESCRIPTOR *vol)
{
    uint32_t sectorsToRead = 0;
    if (totalDiscSectors > 3300000) 
    {
        // DUAL LAYER (XGD2) Calculation:
        // LBA 1,913,920 is the physical end of the usable XDFS area on retail DVD-9s.
        uint32_t xgd2EndLba = 1913920;
        sectorsToRead = xgd2EndLba - vol->RootLBA;
    }
    else
    {
        // SINGLE LAYER (XGD1 / Homebrew) Calculation:
        // On single layer discs, the header's VolumeSize is trustworthy.
        sectorsToRead = vol->VolumeSize / 2048;
    }
    return sectorsToRead;
}

static BOOL ProbeXboxVolumeAt(HANDLE hDevice, uint32_t lba)
{
    unsigned char sector[2048] = {0};
    return ScsiReadSectors(hDevice, lba, 1, sector) && memcmp(sector, "MICROSOFT", 9) == 0;
}

static uint32_t DetectXboxVolumeStart(HANDLE hDevice)
{
    if (ProbeXboxVolumeAt(hDevice, START_LBA_MAGIC))
        return START_LBA_MAGIC;

    if (ProbeXboxVolumeAt(hDevice, 0x20))
        return 0x20;

    return 0xFFFFFFFFu;
}

static void RecoveryKick(HANDLE hDevice, BOOL authRecovery)
{
    unsigned char dummy[2048] = {0};

    if (authRecovery)
        KickXboxMediaAuth(hDevice);

    SetDriveSpeedMax(hDevice);

    for (int i = 0; i < 10; i++)
    {
        ScsiReadSectors(hDevice, 0, 1, dummy);
        Sleep(50);
    }
}


static void GetDirectoryForPath(const char *filename, char *outDir, DWORD outDirSize)
{
    DWORD len;
    char fullPath[MAX_PATH];
    char *filePart = NULL;

    if (!outDir || outDirSize == 0)
        return;

    outDir[0] = '\0';

    if (!filename || filename[0] == '\0')
    {
        GetCurrentDirectoryA(outDirSize, outDir);
        return;
    }

    len = GetFullPathNameA(filename, (DWORD)sizeof(fullPath), fullPath, &filePart);
    if (len == 0 || len >= sizeof(fullPath))
    {
        GetCurrentDirectoryA(outDirSize, outDir);
        return;
    }

    if (filePart && filePart > fullPath)
    {
        size_t dirLen = (size_t)(filePart - fullPath);
        if (dirLen >= outDirSize)
            dirLen = outDirSize - 1;
        memcpy(outDir, fullPath, dirLen);
        outDir[dirLen] = '\0';
    }
    else
    {
        GetCurrentDirectoryA(outDirSize, outDir);
    }
}

static BOOL FileExistsAndSize(const char *filename, unsigned long long *sizeOut)
{
    WIN32_FILE_ATTRIBUTE_DATA fad;

    if (sizeOut)
        *sizeOut = 0ULL;

    if (!filename || !GetFileAttributesExA(filename, GetFileExInfoStandard, &fad))
        return FALSE;

    if (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        return FALSE;

    if (sizeOut)
    {
        ULARGE_INTEGER size;
        size.HighPart = fad.nFileSizeHigh;
        size.LowPart = fad.nFileSizeLow;
        *sizeOut = size.QuadPart;
    }

    return TRUE;
}

static BOOL CheckOutputFreeSpace(const char *filename, unsigned long long expectedBytes, const char *label)
{
    char dir[MAX_PATH];
    ULARGE_INTEGER freeToCaller;
    ULARGE_INTEGER totalBytes;
    ULARGE_INTEGER totalFree;
    unsigned long long existingBytes = 0ULL;
    unsigned long long effectiveFree;
    unsigned long long margin;

    if (expectedBytes == 0)
        return TRUE;

    GetDirectoryForPath(filename, dir, (DWORD)sizeof(dir));

    if (!GetDiskFreeSpaceExA(dir[0] ? dir : NULL, &freeToCaller, &totalBytes, &totalFree))
    {
        DWORD err = GetLastError();
        printf("\n[WARN] Could not check free space for output path '%s' (GetDiskFreeSpaceEx error %lu).\n", filename, err);
        printf("       Continuing, but write errors will still be caught during the dump.\n");
        return TRUE;
    }

    FileExistsAndSize(filename, &existingBytes);

    // If overwriting an existing output on the same volume, its current bytes can be
    // reclaimed by fopen(..., "wb"). This avoids rejecting a valid replacement run.
    effectiveFree = freeToCaller.QuadPart + existingBytes;

    // Add a small safety margin for sidecar/profile files and filesystem metadata.
    // Keep this modest so overwriting an existing full raw ISO still passes.
    margin = 64ULL * 1024ULL * 1024ULL;

    printf("[%s] Free-space preflight for '%s':\n", label ? label : "OUTPUT", filename);
    printf("       Required output bytes: %llu\n", expectedBytes);
    printf("       Safety margin:         %llu\n", margin);
    printf("       Free to caller:        %llu\n", (unsigned long long)freeToCaller.QuadPart);
    if (existingBytes)
        printf("       Existing output bytes: %llu (counted as reclaimable overwrite space)\n", existingBytes);
    printf("       Effective available:   %llu\n", effectiveFree);

    if (effectiveFree < expectedBytes + margin)
    {
        printf("\n[FATAL] Not enough free disk space for %s.\n", label ? label : "output");
        printf("        Required + margin: %llu bytes\n", expectedBytes + margin);
        printf("        Effective free:    %llu bytes\n", effectiveFree);
        printf("        Free space can change while dumping; free extra space and rerun.\n");
        return FALSE;
    }

    return TRUE;
}

static BOOL WriteOutputBytes(FILE *outFile,
                             const void *data,
                             size_t bytesToWrite,
                             const char *phaseName,
                             uint32_t sourceLba,
                             uint32_t outputLba)
{
    size_t written;

    if (!outFile || !data || bytesToWrite == 0)
        return bytesToWrite == 0;

    written = fwrite(data, 1, bytesToWrite, outFile);
    if (written != bytesToWrite)
    {
        printf("\n[FATAL] Output write failed during %s range.\n", phaseName ? phaseName : "dump");
        printf("        Source LBA: %u | Output LBA: %u\n", sourceLba, outputLba);
        printf("        Requested:  %llu bytes\n", (unsigned long long)bytesToWrite);
        printf("        Written:    %llu bytes\n", (unsigned long long)written);
        if (errno)
            printf("        errno:      %d (%s)\n", errno, strerror(errno));
        printf("        This commonly means another program consumed free space after preflight,\n");
        printf("        the destination volume filled up, or the destination became unavailable.\n");
        return FALSE;
    }

    if (ferror(outFile))
    {
        printf("\n[FATAL] Output stream error during %s range at output LBA %u.\n",
               phaseName ? phaseName : "dump", outputLba);
        if (errno)
            printf("        errno: %d (%s)\n", errno, strerror(errno));
        return FALSE;
    }

    return TRUE;
}

static BOOL FlushAndCommitOutput(FILE *outFile, const char *label)
{
    int fd;

    if (!outFile)
        return FALSE;

    if (fflush(outFile) != 0)
    {
        printf("\n[FATAL] fflush failed for %s output.\n", label ? label : "dump");
        if (errno)
            printf("        errno: %d (%s)\n", errno, strerror(errno));
        return FALSE;
    }

    fd = _fileno(outFile);
    if (fd >= 0 && _commit(fd) != 0)
    {
        printf("\n[FATAL] _commit failed for %s output. The OS may not have accepted all buffered data.\n",
               label ? label : "dump");
        if (errno)
            printf("        errno: %d (%s)\n", errno, strerror(errno));
        return FALSE;
    }

    return TRUE;
}

static BOOL DumpSectorRangeWithRetry(HANDLE hDevice,
                                     FILE *outFile,
                                     HCRYPTHASH hHash,
                                     uint32_t sourceStartLba,
                                     uint32_t sectorsToRead,
                                     uint32_t outputBaseLba,
                                     const char *phaseName,
                                     BOOL authRecovery)
{
    const uint32_t batchSize = 32;
    unsigned char *buffer = NULL;
    uint32_t sectorsDone = 0;
    DWORD startTime = GetTickCount();
    char timeStr[12] = {0};
    char etaStr[12] = {0};

    if (sectorsToRead == 0)
        return TRUE;

    buffer = (unsigned char *)VirtualAlloc(NULL, batchSize * 2048, MEM_COMMIT, PAGE_READWRITE);
    if (!buffer)
    {
        printf("\n[FATAL] Could not allocate dump buffer for %s range.\n", phaseName);
        return FALSE;
    }

    printf("\n--- STARTING %s RANGE ---\n", phaseName);
    printf("Source LBA: %u | Output LBA: %u | Sectors: %u\n", sourceStartLba, outputBaseLba, sectorsToRead);

    RecoveryKick(hDevice, authRecovery);

    while (sectorsDone < sectorsToRead)
    {
        uint32_t currentLba = sourceStartLba + sectorsDone;
        const char *currentLayerStr = "L0";
        uint32_t burstLimit = batchSize;
        uint32_t toRead;
        BOOL success = FALSE;

        if ((outputBaseLba + sectorsDone) >= LAYER_BREAK)
            currentLayerStr = "L1";

        // Layer-boundary safety: do not let one READ(10) span the Xbox layer break.
        if (sectorsDone == 0 || (outputBaseLba + sectorsDone) == LAYER_BREAK)
        {
            burstLimit = 1;
        }
        else if ((outputBaseLba + sectorsDone) < LAYER_BREAK &&
                 (outputBaseLba + sectorsDone + batchSize) > LAYER_BREAK)
        {
            burstLimit = LAYER_BREAK - (outputBaseLba + sectorsDone);
        }

        toRead = (sectorsToRead - sectorsDone > burstLimit) ? burstLimit : (sectorsToRead - sectorsDone);

        if ((outputBaseLba + sectorsDone) == LAYER_BREAK)
        {
            printf("\n[INFO] Redump/XGD1 output layer break at LBA %u. Reducing burst size to 1 sector for safety.\n", LAYER_BREAK);
        }

        for (int retry = 0; retry <= MAX_RETRIES; retry++)
        {
            if (ScsiReadSectors(hDevice, currentLba, (uint16_t)toRead, buffer))
            {
                if (!WriteOutputBytes(outFile, buffer, (size_t)toRead * 2048U, phaseName, currentLba, outputBaseLba + sectorsDone))
                {
                    VirtualFree(buffer, 0, MEM_RELEASE);
                    return FALSE;
                }
                CryptHashData(hHash, buffer, toRead * 2048, 0);
                sectorsDone += toRead;
                success = TRUE;
                break;
            }

            RecoveryKick(hDevice, authRecovery);
            Sleep(500);
        }

        if (!success)
        {
            unsigned char *smallBuffer = NULL;
            printf("\n[!] Batch failed in %s range at source LBA %u. Recovering sectors individually.\n", phaseName, currentLba);

            smallBuffer = (unsigned char *)VirtualAlloc(NULL, 2048, MEM_COMMIT, PAGE_READWRITE);
            if (!smallBuffer)
            {
                printf("\n[FATAL] Could not allocate single-sector recovery buffer.\n");
                VirtualFree(buffer, 0, MEM_RELEASE);
                return FALSE;
            }

            for (uint32_t i = 0; i < toRead; i++)
            {
                BOOL sectorSuccess = FALSE;

                for (int sRetry = 0; sRetry <= MAX_RETRIES; sRetry++)
                {
                    if (ScsiReadSectors(hDevice, currentLba + i, 1, smallBuffer))
                    {
                        if (!WriteOutputBytes(outFile, smallBuffer, 2048, phaseName, currentLba + i, outputBaseLba + sectorsDone))
                        {
                            VirtualFree(smallBuffer, 0, MEM_RELEASE);
                            VirtualFree(buffer, 0, MEM_RELEASE);
                            return FALSE;
                        }
                        CryptHashData(hHash, smallBuffer, 2048, 0);
                        sectorsDone++;
                        sectorSuccess = TRUE;
                        break;
                    }

                    RecoveryKick(hDevice, authRecovery);
                    Sleep(500);
                }

                if (!sectorSuccess)
                {
                    printf("\n[FATAL] Unrecoverable %s sector at source LBA %u. Output hash is invalid.\n", phaseName, currentLba + i);
                    VirtualFree(smallBuffer, 0, MEM_RELEASE);
                    VirtualFree(buffer, 0, MEM_RELEASE);
                    return FALSE;
                }
            }

            VirtualFree(smallBuffer, 0, MEM_RELEASE);
        }

        if (sectorsDone > 0)
        {
            DWORD elapsedMs = GetTickCount() - startTime;
            uint32_t sectorsLeft = sectorsToRead - sectorsDone;
            DWORD etaMs = (DWORD)(((double)elapsedMs / sectorsDone) * sectorsLeft);
            float percent = ((float)sectorsDone / sectorsToRead) * 100.0f;
            float mbDone = (float)(outputBaseLba + sectorsDone) * 2048 / 1024 / 1024;
            float speed = (elapsedMs > 0) ? (((float)sectorsDone * 2048 / 1024 / 1024) / (elapsedMs / 1000.0f)) : 0.0f;

            FormatElapsedTime(elapsedMs, timeStr);
            FormatElapsedTime(etaMs, etaStr);

            xbox_ref_console_printf("\rProgress [%s/%s]: %3.1f%% | %.1f MB | Speed: %.2f MB/s | sourceLba: %u | outputLba: %u | Time: %s | ETA: %s    ",
                   phaseName, currentLayerStr, percent, mbDone, speed, currentLba, outputBaseLba + sectorsDone, timeStr, etaStr);
            fflush(stdout);
        }
    }

    printf("\n[OK] Completed %s range.\n", phaseName);
    VirtualFree(buffer, 0, MEM_RELEASE);
    return TRUE;
}




static BOOL WriteZeroSectorsOutput(FILE *outFile,
                                   HCRYPTHASH hHash,
                                   uint32_t sectorCount,
                                   uint32_t outputBaseLba,
                                   const char *phaseName)
{
    const uint32_t batchSectors = 32;
    unsigned char *zeroBuffer = NULL;
    uint32_t sectorsDone = 0;
    DWORD startTime = GetTickCount();
    char timeStr[12] = {0};
    char etaStr[12] = {0};

    if (sectorCount == 0)
        return TRUE;

    zeroBuffer = (unsigned char *)VirtualAlloc(NULL, batchSectors * 2048, MEM_COMMIT, PAGE_READWRITE);
    if (!zeroBuffer)
    {
        printf("\n[FATAL] Could not allocate zero-fill buffer for %s range.\n", phaseName ? phaseName : "padding");
        return FALSE;
    }
    memset(zeroBuffer, 0, batchSectors * 2048);

    printf("\n--- STARTING %s RANGE ---\n", phaseName ? phaseName : "ZERO");
    printf("Output LBA: %u | Sectors: %u | Fill: synthetic zero-fill (not drive-captured)\n", outputBaseLba, sectorCount);

    while (sectorsDone < sectorCount)
    {
        uint32_t toWrite = (sectorCount - sectorsDone > batchSectors) ? batchSectors : (sectorCount - sectorsDone);
        uint32_t outputLba = outputBaseLba + sectorsDone;

        if (!WriteOutputBytes(outFile, zeroBuffer, (size_t)toWrite * 2048U, phaseName ? phaseName : "ZERO", 0, outputLba))
        {
            VirtualFree(zeroBuffer, 0, MEM_RELEASE);
            return FALSE;
        }
        if (hHash)
            CryptHashData(hHash, zeroBuffer, toWrite * 2048, 0);

        sectorsDone += toWrite;

        if (sectorsDone > 0)
        {
            DWORD elapsedMs = GetTickCount() - startTime;
            uint32_t sectorsLeft = sectorCount - sectorsDone;
            DWORD etaMs = (DWORD)(((double)elapsedMs / sectorsDone) * sectorsLeft);
            float percent = ((float)sectorsDone / sectorCount) * 100.0f;
            float mbDone = (float)(outputBaseLba + sectorsDone) * 2048 / 1024 / 1024;
            float speed = (elapsedMs > 0) ? (((float)sectorsDone * 2048 / 1024 / 1024) / (elapsedMs / 1000.0f)) : 0.0f;

            FormatElapsedTime(elapsedMs, timeStr);
            FormatElapsedTime(etaMs, etaStr);
            xbox_ref_console_printf("\rProgress [%s]: %3.1f%% | %.1f MB | Speed: %.2f MB/s | outputLba: %u | Time: %s | ETA: %s    ",
                   phaseName ? phaseName : "ZERO", percent, mbDone, speed, outputBaseLba + sectorsDone, timeStr, etaStr);
            fflush(stdout);
        }
    }

    printf("\n[OK] Completed %s range.\n", phaseName ? phaseName : "ZERO");
    VirtualFree(zeroBuffer, 0, MEM_RELEASE);
    return TRUE;
}

static BOOL ReadSectorsToMemory(HANDLE hDevice,
                                uint32_t sourceStartLba,
                                uint32_t sectorCount,
                                unsigned char *outBuffer,
                                const char *phaseName)
{
    const uint32_t batchSectors = 32;
    uint32_t sectorsDone = 0;

    if (sectorCount == 0)
        return TRUE;
    if (!outBuffer)
        return FALSE;

    printf("[RAW] Capturing %s to memory: source LBA %u..%u (%u sectors).\n",
           phaseName ? phaseName : "sector range",
           sourceStartLba,
           sourceStartLba + sectorCount - 1,
           sectorCount);

    while (sectorsDone < sectorCount)
    {
        uint32_t toRead = (sectorCount - sectorsDone > batchSectors) ? batchSectors : (sectorCount - sectorsDone);
        uint32_t currentLba = sourceStartLba + sectorsDone;
        BOOL success = FALSE;

        for (int retry = 0; retry <= MAX_RETRIES; retry++)
        {
            if (ScsiReadSectors(hDevice, currentLba, (uint16_t)toRead, outBuffer + ((size_t)sectorsDone * 2048U)))
            {
                sectorsDone += toRead;
                success = TRUE;
                break;
            }
            Sleep(500);
        }

        if (!success)
        {
            printf("\n[FATAL] Could not capture %s at source LBA %u.\n", phaseName ? phaseName : "sector range", currentLba);
            return FALSE;
        }
    }

    return TRUE;
}

static BOOL WriteMemorySectorsOutput(FILE *outFile,
                                     HCRYPTHASH hHash,
                                     const unsigned char *buffer,
                                     uint32_t sectorCount,
                                     uint32_t outputBaseLba,
                                     const char *phaseName)
{
    const uint32_t batchSectors = 32;
    uint32_t sectorsDone = 0;

    if (sectorCount == 0)
        return TRUE;
    if (!buffer)
        return FALSE;

    printf("\n--- STARTING %s RANGE ---\n", phaseName ? phaseName : "MEMORY");
    printf("Output LBA: %u | Sectors: %u | Source: captured memory\n", outputBaseLba, sectorCount);

    while (sectorsDone < sectorCount)
    {
        uint32_t toWrite = (sectorCount - sectorsDone > batchSectors) ? batchSectors : (sectorCount - sectorsDone);
        uint32_t outputLba = outputBaseLba + sectorsDone;
        const unsigned char *src = buffer + ((size_t)sectorsDone * 2048U);

        if (!WriteOutputBytes(outFile, src, (size_t)toWrite * 2048U, phaseName ? phaseName : "MEMORY", 0, outputLba))
            return FALSE;
        if (hHash)
            CryptHashData(hHash, src, toWrite * 2048, 0);

        sectorsDone += toWrite;
    }

    printf("[OK] Completed %s range.\n", phaseName ? phaseName : "MEMORY");
    return TRUE;
}

typedef struct _XboxDvdSidecarCapture
{
    BOOL hasLockedCapacity;
    BOOL hasLockedModeSense3E;
    BOOL hasUnlockedCapacity;
    BOOL hasUnlockedModeSense3E;
    BOOL hasAdC0;
    BOOL hasPfi;
    BOOL hasDmi;

    unsigned char lockedCapacity[8];
    unsigned char lockedModeSense3E[28];
    unsigned char unlockedCapacity[8];
    unsigned char unlockedModeSense3E[28];

    unsigned char adC0[0x664];
    unsigned char pfi[2048];
    unsigned char dmi[2048];
} XboxDvdSidecarCapture;

static void StripKnownExtension(const char *filename, char *outBase, size_t outBaseSize)
{
    char *dot;
    char *slash1;
    char *slash2;
    char *slash;

    if (!outBase || outBaseSize == 0)
        return;

    outBase[0] = '\0';
    if (!filename)
        return;

    strncpy(outBase, filename, outBaseSize - 1);
    outBase[outBaseSize - 1] = '\0';

    dot = strrchr(outBase, '.');
    slash1 = strrchr(outBase, '\\');
    slash2 = strrchr(outBase, '/');
    slash = slash1 > slash2 ? slash1 : slash2;

    if (dot && (!slash || dot > slash))
        *dot = '\0';
}

static void MakeSidecarPath(const char *filename, const char *suffix, char *outPath, size_t outPathSize)
{
    char base[MAX_PATH];

    if (!outPath || outPathSize == 0)
        return;

    StripKnownExtension(filename, base, sizeof(base));
    snprintf(outPath, outPathSize, "%s%s", base, suffix);
    outPath[outPathSize - 1] = '\0';
}

static BOOL WriteBinaryFile(const char *path, const unsigned char *data, size_t len)
{
    FILE *f;

    if (!path || !data || len == 0)
        return FALSE;

    f = fopen(path, "wb");
    if (!f)
        return FALSE;

    if (fwrite(data, 1, len, f) != len)
    {
        fclose(f);
        return FALSE;
    }

    fclose(f);
    return TRUE;
}

static void JsonWriteEscapedString(FILE *f, const char *s)
{
    fputc('"', f);
    if (s)
    {
        while (*s)
        {
            unsigned char c = (unsigned char)*s++;
            if (c == '"' || c == '\\')
            {
                fputc('\\', f);
                fputc(c, f);
            }
            else if (c == '\n')
            {
                fputs("\\n", f);
            }
            else if (c == '\r')
            {
                fputs("\\r", f);
            }
            else if (c == '\t')
            {
                fputs("\\t", f);
            }
            else if (c < 0x20)
            {
                fprintf(f, "\\u%04x", c);
            }
            else
            {
                fputc(c, f);
            }
        }
    }
    fputc('"', f);
}

static void JsonWriteHexString(FILE *f, const unsigned char *data, size_t len)
{
    fputc('"', f);
    if (data)
    {
        for (size_t i = 0; i < len; i++)
            fprintf(f, "%02X", data[i]);
    }
    fputc('"', f);
}


static const char *PathLeaf(const char *path)
{
    const char *slash1;
    const char *slash2;

    if (!path)
        return "";

    slash1 = strrchr(path, '\\');
    slash2 = strrchr(path, '/');

    if (slash1 && slash2)
        return (slash1 > slash2 ? slash1 : slash2) + 1;
    if (slash1)
        return slash1 + 1;
    if (slash2)
        return slash2 + 1;
    return path;
}

static void TrimTrailingSpaces(char *s)
{
    size_t len;

    if (!s)
        return;

    len = strlen(s);
    while (len > 0 && (s[len - 1] == ' ' || s[len - 1] == '\t'))
    {
        s[len - 1] = '\0';
        len--;
    }
}

static void CopyBounded(char *dst, size_t dstSize, const char *src, size_t srcLen)
{
    size_t n;

    if (!dst || dstSize == 0)
        return;

    dst[0] = '\0';
    if (!src)
        return;

    n = srcLen;
    if (n >= dstSize)
        n = dstSize - 1;

    memcpy(dst, src, n);
    dst[n] = '\0';
}

static void ExtractMediaProfileNames(const char *isoFilename,
                                     char *titleHint,
                                     size_t titleHintSize,
                                     char *mediaId,
                                     size_t mediaIdSize)
{
    char base[MAX_PATH];
    const char *leaf;
    const char *openBracket;
    const char *closeBracket;

    if (titleHint && titleHintSize > 0)
        titleHint[0] = '\0';
    if (mediaId && mediaIdSize > 0)
        mediaId[0] = '\0';

    if (!isoFilename)
        return;

    StripKnownExtension(isoFilename, base, sizeof(base));
    leaf = PathLeaf(base);

    openBracket = strrchr(leaf, '[');
    closeBracket = openBracket ? strchr(openBracket, ']') : NULL;

    if (openBracket && closeBracket && closeBracket > openBracket)
    {
        CopyBounded(titleHint, titleHintSize, leaf, (size_t)(openBracket - leaf));
        CopyBounded(mediaId, mediaIdSize, openBracket + 1, (size_t)(closeBracket - openBracket - 1));
    }
    else
    {
        CopyBounded(titleHint, titleHintSize, leaf, strlen(leaf));
    }

    TrimTrailingSpaces(titleHint);
}

static void JsonWriteValidationWarnings(FILE *f, const XboxDvdSidecarCapture *cap, BOOL payloadFilesPresent, BOOL redumpStyleZeroFilledPadding)
{
    BOOL wrote = FALSE;

    fprintf(f, "[");

#define WRITE_WARNING(w) do { \
        if (wrote) fprintf(f, ", "); \
        JsonWriteEscapedString(f, (w)); \
        wrote = TRUE; \
    } while (0)

    if (!cap || !cap->hasLockedCapacity)
        WRITE_WARNING("missing_locked_read_capacity_10");
    if (!cap || !cap->hasLockedModeSense3E)
        WRITE_WARNING("missing_locked_mode_sense_3e");
    if (!cap || !cap->hasUnlockedCapacity)
        WRITE_WARNING("missing_unlocked_read_capacity_10");
    if (!cap || !cap->hasUnlockedModeSense3E)
        WRITE_WARNING("missing_unlocked_mode_sense_3e");
    if (!cap || !cap->hasAdC0)
        WRITE_WARNING("missing_ad_c0_payload");
    if (!cap || !cap->hasPfi)
        WRITE_WARNING("missing_pfi_payload");
    if (!cap || !cap->hasDmi)
        WRITE_WARNING("missing_dmi_payload");
    if (!payloadFilesPresent)
        WRITE_WARNING("payload_files_not_fully_present");
    if (redumpStyleZeroFilledPadding)
        WRITE_WARNING("redump_style_padding_zero_filled_unresolved_content");

#undef WRITE_WARNING

    fprintf(f, "]");
}

static BOOL ScsiDataInCommand(HANDLE hDevice,
                              const unsigned char *cdb,
                              BYTE cdbLen,
                              DWORD dataLen,
                              unsigned char *buffer)
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    DWORD bytesReturned = 0;

    if (!hDevice || !cdb || !buffer || dataLen == 0 || cdbLen == 0 || cdbLen > 16)
        return FALSE;

    memset(&sptd, 0, sizeof(sptd));
    memset(buffer, 0, dataLen);

    sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
    sptd.CdbLength = cdbLen;
    sptd.DataIn = SCSI_IOCTL_DATA_IN;
    sptd.DataTransferLength = dataLen;
    sptd.TimeOutValue = 30;
    sptd.DataBuffer = buffer;
    memcpy(sptd.Cdb, cdb, cdbLen);

    return DeviceIoControl(hDevice,
                           IOCTL_SCSI_PASS_THROUGH_DIRECT,
                           &sptd,
                           sizeof(sptd),
                           &sptd,
                           sizeof(sptd),
                           &bytesReturned,
                           NULL);
}

static BOOL CaptureReadCapacity10(HANDLE hDevice, unsigned char out8[8])
{
    static const unsigned char cdb[10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    return ScsiDataInCommand(hDevice, cdb, 10, 8, out8);
}

static BOOL CaptureModeSense3E(HANDLE hDevice, unsigned char out28[28])
{
    static const unsigned char cdb[10] = {0x5A, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00};
    return ScsiDataInCommand(hDevice, cdb, 10, 28, out28);
}

static BOOL CaptureReadDvdStructureXboxC0(HANDLE hDevice, unsigned char out1664[0x664])
{
    static const unsigned char cdb[12] = {0xAD, 0x00, 0xFF, 0x02, 0xFD, 0xFF, 0xFE, 0x00, 0x06, 0x64, 0x00, 0xC0};
    return ScsiDataInCommand(hDevice, cdb, 12, 0x664, out1664);
}

static BOOL CaptureReadDvdStructurePfi(HANDLE hDevice, unsigned char out2048[2048])
{
    static const unsigned char cdb[12] = {0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00};
    return ScsiDataInCommand(hDevice, cdb, 12, 2048, out2048);
}

static BOOL CaptureReadDvdStructureDmi(HANDLE hDevice, unsigned char out2048[2048])
{
    static const unsigned char cdb[12] = {0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x00, 0x00, 0x00};
    return ScsiDataInCommand(hDevice, cdb, 12, 2048, out2048);
}

static void CaptureLockedSidecarState(HANDLE hDevice, XboxDvdSidecarCapture *cap)
{
    if (!cap)
        return;

    cap->hasLockedCapacity = CaptureReadCapacity10(hDevice, cap->lockedCapacity);
    cap->hasLockedModeSense3E = CaptureModeSense3E(hDevice, cap->lockedModeSense3E);

    printf("[META] Locked READ CAPACITY: %s\n", cap->hasLockedCapacity ? "captured" : "failed");
    printf("[META] Locked MODE SENSE 0x3E: %s\n", cap->hasLockedModeSense3E ? "captured" : "failed");
}

static void CaptureUnlockedSidecarState(HANDLE hDevice, XboxDvdSidecarCapture *cap)
{
    if (!cap)
        return;

    cap->hasUnlockedCapacity = CaptureReadCapacity10(hDevice, cap->unlockedCapacity);
    cap->hasUnlockedModeSense3E = CaptureModeSense3E(hDevice, cap->unlockedModeSense3E);
    cap->hasAdC0 = CaptureReadDvdStructureXboxC0(hDevice, cap->adC0);
    cap->hasPfi = CaptureReadDvdStructurePfi(hDevice, cap->pfi);
    cap->hasDmi = CaptureReadDvdStructureDmi(hDevice, cap->dmi);

    printf("[META] Unlocked READ CAPACITY: %s\n", cap->hasUnlockedCapacity ? "captured" : "failed");
    printf("[META] Unlocked MODE SENSE 0x3E: %s\n", cap->hasUnlockedModeSense3E ? "captured" : "failed");
    printf("[META] READ DVD STRUCTURE Xbox C0 block: %s\n", cap->hasAdC0 ? "captured" : "failed");
    printf("[META] READ DVD STRUCTURE PFI: %s\n", cap->hasPfi ? "captured" : "failed");
    printf("[META] READ DVD STRUCTURE DMI: %s\n", cap->hasDmi ? "captured" : "failed");
}

static uint32_t CapacitySectorsFromReadCapacity10(const unsigned char data[8])
{
    uint32_t maxLba;

    if (!data)
        return 0;

    maxLba = ((uint32_t)data[0] << 24) |
             ((uint32_t)data[1] << 16) |
             ((uint32_t)data[2] << 8) |
             ((uint32_t)data[3]);
    return maxLba + 1;
}


static uint32_t NormalizeRawIsoTargetSectors(uint32_t reportedSectors, BOOL isDualLayer)
{
    // Option 1 targets a Redump-style reconstructed 2048-byte-sector image.
    // The GDR-8050L's unlocked READ CAPACITY reports the game/XISO view length
    // (3,431,264 sectors), while the full Original Xbox/XGD1 reconstructed image
    // is larger (3,820,880 sectors) because it also includes video L0/L1 and
    // padding around the game region.
    if (isDualLayer || reportedSectors > LAYER_THRESHOLD)
        return XGD1_FULL_REDUMP_SECTORS;

    return reportedSectors;
}

static unsigned long long GetFileSizeBytes64(const char *filename)
{
    FILE *f;
    __int64 pos;

    if (!filename)
        return 0ULL;

    f = fopen(filename, "rb");
    if (!f)
        return 0ULL;

    if (_fseeki64(f, 0, SEEK_END) != 0)
    {
        fclose(f);
        return 0ULL;
    }

    pos = _ftelli64(f);
    fclose(f);

    if (pos < 0)
        return 0ULL;

    return (unsigned long long)pos;
}

static BOOL VerifyOutputByteCount(const char *filename, unsigned long long expectedBytes, const char *label)
{
    unsigned long long actualBytes = GetFileSizeBytes64(filename);

    if (expectedBytes == 0)
        return TRUE;

    if (actualBytes != expectedBytes)
    {
        printf("\n[FATAL] %s byte-count mismatch.\n", label ? label : "Output");
        printf("        Expected: %llu bytes\n", expectedBytes);
        printf("        Actual:   %llu bytes\n", actualBytes);
        printf("        Refusing to mark this dump complete.\n");
        return FALSE;
    }

    printf("[OK] %s byte count verified: %llu bytes.\n", label ? label : "Output", actualBytes);
    return TRUE;
}

static void JsonWriteNull(FILE *f)
{
    fprintf(f, "null");
}

static void HexBytesToString(const BYTE *bytes, DWORD byteCount, char *outHex, size_t outHexSize)
{
    DWORD i;

    if (!outHex || outHexSize == 0)
        return;

    outHex[0] = '\0';
    if (!bytes || outHexSize < ((size_t)byteCount * 2U + 1U))
        return;

    for (i = 0; i < byteCount; i++)
        sprintf(&outHex[i * 2], "%02x", bytes[i]);
}

static DWORD Crc32Update(DWORD crc, const unsigned char *buf, size_t len)
{
    static DWORD table[256];
    static BOOL tableReady = FALSE;
    size_t i;

    if (!tableReady)
    {
        DWORD n;
        for (n = 0; n < 256; n++)
        {
            DWORD c = n;
            int k;
            for (k = 0; k < 8; k++)
                c = (c & 1U) ? (0xEDB88320U ^ (c >> 1)) : (c >> 1);
            table[n] = c;
        }
        tableReady = TRUE;
    }

    for (i = 0; i < len; i++)
        crc = table[(crc ^ buf[i]) & 0xFFU] ^ (crc >> 8);

    return crc;
}

static BOOL CalculateFileHashes(const char *filename,
                                char *outCrc32,
                                size_t outCrc32Size,
                                char *outMd5,
                                size_t outMd5Size,
                                char *outSha1,
                                size_t outSha1Size,
                                char *outSha256,
                                size_t outSha256Size)
{
    FILE *f;
    unsigned char *buf;
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hMd5 = 0;
    HCRYPTHASH hSha1 = 0;
    HCRYPTHASH hSha256 = 0;
    DWORD crc = 0xFFFFFFFFU;
    BOOL ok = FALSE;
    size_t readBytes;
    unsigned long long totalBytes = 0ULL;
    unsigned long long doneBytes = 0ULL;
    DWORD startTick = 0;
    DWORD lastPrintTick = 0;
    DWORD nowTick = 0;
    DWORD elapsedMs = 0;
    DWORD etaMs = 0;
    char timeStr[12] = {0};
    char etaStr[12] = {0};

    if (outCrc32 && outCrc32Size) outCrc32[0] = '\0';
    if (outMd5 && outMd5Size) outMd5[0] = '\0';
    if (outSha1 && outSha1Size) outSha1[0] = '\0';
    if (outSha256 && outSha256Size) outSha256[0] = '\0';

    if (!filename)
        return FALSE;

    totalBytes = GetFileSizeBytes64(filename);

    f = fopen(filename, "rb");
    if (!f)
        return FALSE;

    buf = (unsigned char *)malloc(1024 * 1024);
    if (!buf)
    {
        fclose(f);
        return FALSE;
    }

    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT) &&
        !CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
        goto cleanup;
    if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hMd5))
        goto cleanup;
    if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hSha1))
        goto cleanup;
    if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hSha256))
        goto cleanup;

    startTick = GetTickCount();
    lastPrintTick = startTick;
    printf("[HASH] Calculating full-file CRC32/MD5/SHA-1/SHA-256 for %s (%llu bytes)...\n",
           filename,
           totalBytes);

    while ((readBytes = fread(buf, 1, 1024 * 1024, f)) > 0)
    {
        crc = Crc32Update(crc, buf, readBytes);
        if (!CryptHashData(hMd5, buf, (DWORD)readBytes, 0))
            goto cleanup;
        if (!CryptHashData(hSha1, buf, (DWORD)readBytes, 0))
            goto cleanup;
        if (!CryptHashData(hSha256, buf, (DWORD)readBytes, 0))
            goto cleanup;

        doneBytes += (unsigned long long)readBytes;
        nowTick = GetTickCount();
        if (totalBytes > 0 && (doneBytes >= totalBytes || (nowTick - lastPrintTick) >= 1000))
        {
            double percent = ((double)doneBytes / (double)totalBytes) * 100.0;
            double mbDone = (double)doneBytes / (1024.0 * 1024.0);
            double speed = 0.0;
            unsigned long long bytesLeft = totalBytes - doneBytes;

            elapsedMs = nowTick - startTick;
            if (elapsedMs > 0)
                speed = mbDone / ((double)elapsedMs / 1000.0);
            etaMs = (doneBytes > 0 && elapsedMs > 0)
                    ? (DWORD)(((double)elapsedMs / (double)doneBytes) * (double)bytesLeft)
                    : 0;
            FormatElapsedTime(elapsedMs, timeStr);
            FormatElapsedTime(etaMs, etaStr);
            xbox_ref_console_printf("\r[HASH] Full-file: %5.1f%% | %.1f MB | Speed: %.2f MB/s | Time: %s | ETA: %s    ",
                   percent,
                   mbDone,
                   speed,
                   timeStr,
                   etaStr);
            fflush(stdout);
            lastPrintTick = nowTick;
        }
    }

    if (ferror(f))
        goto cleanup;

    elapsedMs = GetTickCount() - startTick;
    FormatElapsedTime(elapsedMs, timeStr);
    if (totalBytes > 0)
        xbox_ref_console_printf("\r[HASH] Full-file: 100.0%% | %.1f MB | Time: %s                         \n",
               (double)totalBytes / (1024.0 * 1024.0),
               timeStr);
    printf("[OK] Full-file CRC32/MD5/SHA-1/SHA-256 complete in %s.\n", timeStr);

    crc ^= 0xFFFFFFFFU;
    if (outCrc32 && outCrc32Size >= 9)
        sprintf(outCrc32, "%08x", crc);

    if (outMd5 && outMd5Size >= 33)
    {
        BYTE md5Bytes[16];
        DWORD md5Len = sizeof(md5Bytes);
        if (!CryptGetHashParam(hMd5, HP_HASHVAL, md5Bytes, &md5Len, 0))
            goto cleanup;
        HexBytesToString(md5Bytes, md5Len, outMd5, outMd5Size);
    }

    if (outSha1 && outSha1Size >= 41)
    {
        BYTE sha1Bytes[20];
        DWORD sha1Len = sizeof(sha1Bytes);
        if (!CryptGetHashParam(hSha1, HP_HASHVAL, sha1Bytes, &sha1Len, 0))
            goto cleanup;
        HexBytesToString(sha1Bytes, sha1Len, outSha1, outSha1Size);
    }

    if (outSha256 && outSha256Size >= 65)
    {
        BYTE sha256Bytes[32];
        DWORD sha256Len = sizeof(sha256Bytes);
        if (!CryptGetHashParam(hSha256, HP_HASHVAL, sha256Bytes, &sha256Len, 0))
            goto cleanup;
        HexBytesToString(sha256Bytes, sha256Len, outSha256, outSha256Size);
    }

    ok = TRUE;

cleanup:
    if (!ok && startTick)
    {
        elapsedMs = GetTickCount() - startTick;
        FormatElapsedTime(elapsedMs, timeStr);
        printf("\n[WARN] Full-file CRC32/MD5/SHA-1/SHA-256 calculation failed after %s.\n", timeStr);
    }
    if (hSha256) CryptDestroyHash(hSha256);
    if (hSha1) CryptDestroyHash(hSha1);
    if (hMd5) CryptDestroyHash(hMd5);
    if (hProv) CryptReleaseContext(hProv, 0);
    free(buf);
    fclose(f);
    return ok;
}

static void WritePressedDvdRomWriteMediaState(FILE *json, const char *isoFilename, uint32_t totalDiscSectors)
{
    if (!json)
        return;

    fprintf(json, "  \"write_media_state\": {\n");
    fprintf(json, "    \"media_class\": \"pressed_dvd_rom\",\n");
    fprintf(json, "    \"writable\": false,\n");
    fprintf(json, "    \"erasable\": false,\n");
    fprintf(json, "    \"finalized\": true,\n");

    fprintf(json, "    \"backing_image\": {\n");
    fprintf(json, "      \"file\": "); JsonWriteEscapedString(json, isoFilename ? isoFilename : ""); fprintf(json, ",\n");
    fprintf(json, "      \"sector_size\": 2048,\n");
    fprintf(json, "      \"initial_sector_count\": %u,\n", totalDiscSectors);
    fprintf(json, "      \"max_sector_count\": %u,\n", totalDiscSectors);
    fprintf(json, "      \"growth_policy\": \"fixed_read_only\"\n");
    fprintf(json, "    },\n");

    fprintf(json, "    \"sessions\": [\n");
    fprintf(json, "      {\n");
    fprintf(json, "        \"session_number\": 1,\n");
    fprintf(json, "        \"state\": \"closed\",\n");
    fprintf(json, "        \"first_track_number\": 1,\n");
    fprintf(json, "        \"last_track_number\": 1\n");
    fprintf(json, "      }\n");
    fprintf(json, "    ],\n");

    fprintf(json, "    \"tracks\": [\n");
    fprintf(json, "      {\n");
    fprintf(json, "        \"track_number\": 1,\n");
    fprintf(json, "        \"state\": \"complete\",\n");
    fprintf(json, "        \"mode\": \"data\",\n");
    fprintf(json, "        \"packet_or_track_mode\": \"pressed_read_only\",\n");
    fprintf(json, "        \"start_lba\": 0,\n");
    fprintf(json, "        \"next_writable_lba\": "); JsonWriteNull(json); fprintf(json, ",\n");
    fprintf(json, "        \"free_blocks\": 0,\n");
    fprintf(json, "        \"written_blocks\": %u\n", totalDiscSectors);
    fprintf(json, "      }\n");
    fprintf(json, "    ],\n");

    fprintf(json, "    \"unwritten_read_policy\": \"not_applicable_read_only_media\",\n");
    fprintf(json, "    \"flush_policy\": \"read_only_noop\"\n");
    fprintf(json, "  },\n");
}

static BOOL WriteXboxDvdMediaProfileFile(const char *isoFilename,
                                         const XboxDvdSidecarCapture *cap,
                                         uint32_t totalDiscSectors,
                                         BOOL isDualLayer,
                                         uint32_t videoSectors,
                                         uint32_t gameSourceLba,
                                         uint32_t gameSectors,
                                         const char *isoSha1,
                                         const char *isoMd5,
                                         const char *isoCrc32,
                                         const char *isoSha256,
                                         const char *adC0Path,
                                         const char *pfiPath,
                                         const char *dmiPath,
                                         BOOL payloadFilesPresent)
{
    char profilePath[MAX_PATH];
    char titleHint[256];
    char mediaId[64];
    BOOL redumpStyle;
    FILE *json;

    if (!isoFilename || !cap)
        return FALSE;

    MakeSidecarPath(isoFilename, ".media.json", profilePath, sizeof(profilePath));
    ExtractMediaProfileNames(isoFilename, titleHint, sizeof(titleHint), mediaId, sizeof(mediaId));
    redumpStyle = (totalDiscSectors == XGD1_FULL_REDUMP_SECTORS && gameSectors == REDUMP_SECTORS);

    json = fopen(profilePath, "wb");
    if (!json)
        return FALSE;

    fprintf(json, "{\n");
    fprintf(json, "  \"format\": \"xdvd-media-profile\",\n");
    fprintf(json, "  \"version\": 1,\n");
    fprintf(json, "  \"media_id\": "); JsonWriteEscapedString(json, mediaId); fprintf(json, ",\n");
    fprintf(json, "  \"title_hint\": "); JsonWriteEscapedString(json, titleHint); fprintf(json, ",\n");
    fprintf(json, "  \"image\": {\n");
    fprintf(json, "    \"file\": "); JsonWriteEscapedString(json, isoFilename); fprintf(json, ",\n");
    fprintf(json, "    \"sector_size\": 2048,\n");
    fprintf(json, "    \"sector_count\": %u,\n", totalDiscSectors);
    fprintf(json, "    \"byte_count\": %llu,\n", (unsigned long long)totalDiscSectors * 2048ULL);
    fprintf(json, "    \"sha1\": "); JsonWriteEscapedString(json, isoSha1 ? isoSha1 : ""); fprintf(json, ",\n");
    fprintf(json, "    \"hashes\": {\n");
    fprintf(json, "      \"crc32\": "); JsonWriteEscapedString(json, isoCrc32 ? isoCrc32 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"md5\": "); JsonWriteEscapedString(json, isoMd5 ? isoMd5 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"sha1\": "); JsonWriteEscapedString(json, isoSha1 ? isoSha1 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"sha256\": "); JsonWriteEscapedString(json, isoSha256 ? isoSha256 : ""); fprintf(json, "\n");
    fprintf(json, "    }\n");
    fprintf(json, "  },\n");

    {
        uint32_t gameOutputLba = redumpStyle ? XGD1_GAME_OUTPUT_START_LBA : videoSectors;

        fprintf(json, "  \"layout\": {\n");
        fprintf(json, "    \"layout_type\": "); JsonWriteEscapedString(json, redumpStyle ? "original_xbox_xgd1_redump_style_2048" : "contiguous_logical_dump"); fprintf(json, ",\n");
        fprintf(json, "    \"layers\": %u,\n", isDualLayer ? 2U : 1U);
        fprintf(json, "    \"layer_break_lba\": %u,\n", isDualLayer ? LAYER_BREAK : 0U);
        fprintf(json, "    \"video_l0_start_lba\": 0,\n");
        fprintf(json, "    \"video_l0_sector_count\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : videoSectors);
        fprintf(json, "    \"pregame_padding_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : 0U);
        fprintf(json, "    \"pregame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) : 0U);
        fprintf(json, "    \"game_output_start_lba\": %u,\n", gameOutputLba);
        fprintf(json, "    \"game_leadin_unlocked_source_start_lba\": %u,\n", redumpStyle ? 0U : gameSourceLba);
        fprintf(json, "    \"game_leadin_source_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U);
        fprintf(json, "    \"game_leadin_source\": "); JsonWriteEscapedString(json, redumpStyle ? "drive_read10" : "not_applicable"); fprintf(json, ",\n");
        fprintf(json, "    \"game_unlocked_source_start_lba\": %u,\n", gameSourceLba);
        fprintf(json, "    \"game_unlocked_source_sector_count\": %u,\n", redumpStyle ? XGD1_GAME_SOURCE_SECTORS : gameSectors);
        fprintf(json, "    \"game_xiso_leadin_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U);
        fprintf(json, "    \"xdfs_volume_lba_within_game_region\": 32,\n");
        fprintf(json, "    \"game_sector_count\": %u,\n", gameSectors);
        fprintf(json, "    \"postgame_padding_start_lba\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS) : 0U);
        fprintf(json, "    \"postgame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_VIDEO_L1_OUTPUT_START_LBA - (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS)) : 0U);
        fprintf(json, "    \"video_l1_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L1_OUTPUT_START_LBA : 0U);
        fprintf(json, "    \"video_l1_sector_count\": %u,\n", redumpStyle ? XGD1_VIDEO_L1_SECTORS : 0U);
        fprintf(json, "    \"legacy_contiguous_visible_sector_count\": %u,\n", videoSectors);
        fprintf(json, "    \"drive_locked_visible_sector_count\": %u,\n", cap->hasLockedCapacity ? CapacitySectorsFromReadCapacity10(cap->lockedCapacity) : 0);
        fprintf(json, "    \"drive_reported_unlocked_sector_count\": %u,\n", cap->hasUnlockedCapacity ? CapacitySectorsFromReadCapacity10(cap->unlockedCapacity) : 0);
        fprintf(json, "    \"reconstructed_output_sector_count\": %u\n", totalDiscSectors);
        fprintf(json, "  },\n");
    }

    fprintf(json, "  \"reconstruction\": {\n");
    fprintf(json, "    \"is_reconstructed_layout\": %s,\n", redumpStyle ? "true" : "false");
    fprintf(json, "    \"filler_policy\": "); JsonWriteEscapedString(json, redumpStyle ? "pregame_and_postgame_zero_fill_content_placeholder" : "not_applicable"); fprintf(json, ",\n");
    fprintf(json, "    \"filler_geometry_verified\": %s,\n", redumpStyle ? "true" : "false");
    fprintf(json, "    \"filler_verified_from_disc\": false,\n");
    fprintf(json, "    \"filler_byte_value\": %s,\n", redumpStyle ? "0" : "null");
    fprintf(json, "    \"pending_hardware_capture\": false,\n");
    fprintf(json, "    \"unresolved_filler_content\": %s,\n", redumpStyle ? "true" : "false");
    fprintf(json, "    \"filler_ranges\": [\n");
    if (redumpStyle)
    {
        fprintf(json, "      {\n");
        fprintf(json, "        \"name\": \"pregame_padding\",\n");
        fprintf(json, "        \"start_lba\": %u,\n", XGD1_VIDEO_L0_SECTORS);
        fprintf(json, "        \"sector_count\": %u,\n", XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS);
        fprintf(json, "        \"source\": \"synthetic_zero_fill\"\n");
        fprintf(json, "      },\n");
        fprintf(json, "      {\n");
        fprintf(json, "        \"name\": \"postgame_padding\",\n");
        fprintf(json, "        \"start_lba\": %u,\n", XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS);
        fprintf(json, "        \"sector_count\": %u,\n", XGD1_VIDEO_L1_OUTPUT_START_LBA - (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS));
        fprintf(json, "        \"source\": \"synthetic_zero_fill\"\n");
        fprintf(json, "      }\n");
    }
    fprintf(json, "    ],\n");
    fprintf(json, "    \"note\": \"Pregame and postgame physical locations are verified by cache-aligned raw sector IDs, but their inaccessible contents remain zero-filled placeholders. The 32-sector game lead-in is drive-captured from unlocked LBA 0..31.\"\n");
    fprintf(json, "  },\n");

    fprintf(json, "  \"dvd_structures\": {\n");
    fprintf(json, "    \"ad_c0\": "); JsonWriteEscapedString(json, cap->hasAdC0 ? adC0Path : ""); fprintf(json, ",\n");
    fprintf(json, "    \"pfi\": "); JsonWriteEscapedString(json, cap->hasPfi ? pfiPath : ""); fprintf(json, ",\n");
    fprintf(json, "    \"dmi\": "); JsonWriteEscapedString(json, cap->hasDmi ? dmiPath : ""); fprintf(json, "\n");
    fprintf(json, "  },\n");

    fprintf(json, "  \"non_lba_physical_metadata\": {\n");
    fprintf(json, "    \"pfi_storage\": \"sidecar_bin\",\n");
    fprintf(json, "    \"dmi_storage\": \"sidecar_bin\",\n");
    fprintf(json, "    \"lead_in_storage\": \"not_in_iso_stream\",\n");
    fprintf(json, "    \"lead_out_storage\": \"not_in_iso_stream\",\n");
    fprintf(json, "    \"note\": \"A standard 2048-byte-sector ISO contains READ(10) user-data sectors only. PFI/DMI/lead-in/lead-out are represented by sidecar/profile metadata.\"\n");
    fprintf(json, "  },\n");

    fprintf(json, "  \"drive_state_observations\": {\n");
    fprintf(json, "    \"locked\": {\n");
    fprintf(json, "      \"read_capacity_10_hex\": "); JsonWriteHexString(json, cap->hasLockedCapacity ? cap->lockedCapacity : NULL, cap->hasLockedCapacity ? 8 : 0); fprintf(json, ",\n");
    fprintf(json, "      \"mode_sense_3e_hex\": "); JsonWriteHexString(json, cap->hasLockedModeSense3E ? cap->lockedModeSense3E : NULL, cap->hasLockedModeSense3E ? 28 : 0); fprintf(json, "\n");
    fprintf(json, "    },\n");
    fprintf(json, "    \"unlocked\": {\n");
    fprintf(json, "      \"read_capacity_10_hex\": "); JsonWriteHexString(json, cap->hasUnlockedCapacity ? cap->unlockedCapacity : NULL, cap->hasUnlockedCapacity ? 8 : 0); fprintf(json, ",\n");
    fprintf(json, "      \"mode_sense_3e_hex\": "); JsonWriteHexString(json, cap->hasUnlockedModeSense3E ? cap->unlockedModeSense3E : NULL, cap->hasUnlockedModeSense3E ? 28 : 0); fprintf(json, "\n");
    fprintf(json, "    }\n");
    fprintf(json, "  },\n");

    WritePressedDvdRomWriteMediaState(json, isoFilename, totalDiscSectors);

    fprintf(json, "  \"validation\": {\n");
    fprintf(json, "    \"byte_count_matches_sector_count\": true,\n");
    fprintf(json, "    \"payload_files_present\": %s,\n", payloadFilesPresent ? "true" : "false");
    fprintf(json, "    \"warnings\": "); JsonWriteValidationWarnings(json, cap, payloadFilesPresent, redumpStyle); fprintf(json, "\n");
    fprintf(json, "  }\n");
    fprintf(json, "}\n");

    fclose(json);
    printf("[META] Wrote media profile: %s\n", profilePath);
    return TRUE;
}

static BOOL WriteXboxDvdSidecarFiles(const char *isoFilename,
                                      const XboxDvdSidecarCapture *cap,
                                      uint32_t totalDiscSectors,
                                      BOOL isDualLayer,
                                      uint32_t videoSectors,
                                      uint32_t gameSourceLba,
                                      uint32_t gameSectors,
                                      const char *isoSha1,
                                      const char *isoMd5,
                                      const char *isoCrc32,
                                      const char *isoSha256)
{
    char jsonPath[MAX_PATH];
    char adC0Path[MAX_PATH];
    char pfiPath[MAX_PATH];
    char dmiPath[MAX_PATH];
    FILE *json;
    BOOL ok = TRUE;
    BOOL payloadFilesPresent = FALSE;
    BOOL redumpStyle = FALSE;

    if (!isoFilename || !cap)
        return FALSE;

    MakeSidecarPath(isoFilename, ".xdvd.json", jsonPath, sizeof(jsonPath));
    MakeSidecarPath(isoFilename, ".ad_c0.bin", adC0Path, sizeof(adC0Path));
    MakeSidecarPath(isoFilename, ".pfi.bin", pfiPath, sizeof(pfiPath));
    MakeSidecarPath(isoFilename, ".dmi.bin", dmiPath, sizeof(dmiPath));

    if (cap->hasAdC0 && !WriteBinaryFile(adC0Path, cap->adC0, 0x664))
        ok = FALSE;
    if (cap->hasPfi && !WriteBinaryFile(pfiPath, cap->pfi, 2048))
        ok = FALSE;
    if (cap->hasDmi && !WriteBinaryFile(dmiPath, cap->dmi, 2048))
        ok = FALSE;

    payloadFilesPresent = cap->hasAdC0 && cap->hasPfi && cap->hasDmi && ok;
    redumpStyle = (totalDiscSectors == XGD1_FULL_REDUMP_SECTORS && gameSectors == REDUMP_SECTORS);

    json = fopen(jsonPath, "wb");
    if (!json)
        return FALSE;

    fprintf(json, "{\n");
    fprintf(json, "  \"format\": \"xdvd-sidecar\",\n");
    fprintf(json, "  \"version\": 1,\n");
    fprintf(json, "  \"image\": {\n");
    fprintf(json, "    \"file\": "); JsonWriteEscapedString(json, isoFilename); fprintf(json, ",\n");
    fprintf(json, "    \"sector_size\": 2048,\n");
    fprintf(json, "    \"sector_count\": %u,\n", totalDiscSectors);
    fprintf(json, "    \"byte_count\": %llu,\n", (unsigned long long)totalDiscSectors * 2048ULL);
    fprintf(json, "    \"sha1\": "); JsonWriteEscapedString(json, isoSha1 ? isoSha1 : ""); fprintf(json, ",\n");
    fprintf(json, "    \"hashes\": {\n");
    fprintf(json, "      \"crc32\": "); JsonWriteEscapedString(json, isoCrc32 ? isoCrc32 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"md5\": "); JsonWriteEscapedString(json, isoMd5 ? isoMd5 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"sha1\": "); JsonWriteEscapedString(json, isoSha1 ? isoSha1 : ""); fprintf(json, ",\n");
    fprintf(json, "      \"sha256\": "); JsonWriteEscapedString(json, isoSha256 ? isoSha256 : ""); fprintf(json, "\n");
    fprintf(json, "    },\n");
    {
        uint32_t gameOutputLba = redumpStyle ? XGD1_GAME_OUTPUT_START_LBA : videoSectors;

        fprintf(json, "    \"layout\": {\n");
        fprintf(json, "      \"layout_type\": "); JsonWriteEscapedString(json, redumpStyle ? "original_xbox_xgd1_redump_style_2048" : "contiguous_logical_dump"); fprintf(json, ",\n");
        fprintf(json, "      \"legacy_contiguous_visible_start_lba\": 0,\n");
        fprintf(json, "      \"legacy_contiguous_visible_sector_count\": %u,\n", redumpStyle ? XGD1_GAME_OUTPUT_START_LBA : videoSectors);
        fprintf(json, "      \"drive_locked_visible_sector_count\": %u,\n", cap->hasLockedCapacity ? CapacitySectorsFromReadCapacity10(cap->lockedCapacity) : 0);
        fprintf(json, "      \"video_l0_start_lba\": 0,\n");
        fprintf(json, "      \"video_l0_sector_count\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : videoSectors);
        fprintf(json, "      \"pregame_padding_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L0_SECTORS : 0U);
        fprintf(json, "      \"pregame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS) : 0U);
        fprintf(json, "      \"game_output_start_lba\": %u,\n", gameOutputLba);
        fprintf(json, "      \"game_leadin_unlocked_source_start_lba\": %u,\n", redumpStyle ? 0U : gameSourceLba);
        fprintf(json, "      \"game_leadin_source_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U);
        fprintf(json, "      \"game_leadin_source\": "); JsonWriteEscapedString(json, redumpStyle ? "drive_read10" : "not_applicable"); fprintf(json, ",\n");
        fprintf(json, "      \"game_unlocked_source_start_lba\": %u,\n", gameSourceLba);
        fprintf(json, "      \"game_unlocked_source_sector_count\": %u,\n", redumpStyle ? XGD1_GAME_SOURCE_SECTORS : gameSectors);
        fprintf(json, "      \"game_xiso_leadin_sector_count\": %u,\n", redumpStyle ? XGD1_XISO_LEADIN_SECTORS : 0U);
        fprintf(json, "      \"xdfs_volume_lba_within_game_region\": 32,\n");
        fprintf(json, "      \"game_sector_count\": %u,\n", gameSectors);
        fprintf(json, "      \"postgame_padding_start_lba\": %u,\n", redumpStyle ? (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS) : 0U);
        fprintf(json, "      \"postgame_padding_sector_count\": %u,\n", redumpStyle ? (XGD1_VIDEO_L1_OUTPUT_START_LBA - (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS)) : 0U);
        fprintf(json, "      \"video_l1_start_lba\": %u,\n", redumpStyle ? XGD1_VIDEO_L1_OUTPUT_START_LBA : 0U);
        fprintf(json, "      \"video_l1_sector_count\": %u,\n", redumpStyle ? XGD1_VIDEO_L1_SECTORS : 0U);
        fprintf(json, "      \"layer_break_lba\": %u\n", LAYER_BREAK);
        fprintf(json, "    }\n");
    }
    fprintf(json, "  },\n");

    fprintf(json, "  \"disc\": {\n");
    fprintf(json, "    \"layers\": %u,\n", isDualLayer ? 2U : 1U);
    fprintf(json, "    \"reconstructed_output_sectors\": %u,\n", totalDiscSectors);
    fprintf(json, "    \"reconstructed_output_byte_count\": %llu,\n", (unsigned long long)totalDiscSectors * 2048ULL);
    fprintf(json, "    \"drive_reported_unlocked_sectors\": %u,\n", cap->hasUnlockedCapacity ? CapacitySectorsFromReadCapacity10(cap->unlockedCapacity) : 0);
    fprintf(json, "    \"drive_reported_locked_sectors\": %u,\n", cap->hasLockedCapacity ? CapacitySectorsFromReadCapacity10(cap->lockedCapacity) : 0);
    fprintf(json, "    \"unlocked_game_view_sectors\": %u\n", gameSectors);
    fprintf(json, "  },\n");

    fprintf(json, "  \"drive_states\": {\n");
    fprintf(json, "    \"locked\": {\n");
    fprintf(json, "      \"read_capacity_10_cdb_hex\": \"25000000000000000000\",\n");
    fprintf(json, "      \"read_capacity_10_hex\": "); JsonWriteHexString(json, cap->hasLockedCapacity ? cap->lockedCapacity : NULL, cap->hasLockedCapacity ? 8 : 0); fprintf(json, ",\n");
    fprintf(json, "      \"visible_sector_count\": %u,\n", cap->hasLockedCapacity ? CapacitySectorsFromReadCapacity10(cap->lockedCapacity) : 0);
    fprintf(json, "      \"mode_sense_3e_cdb_hex\": \"5A003E00000000001C00\",\n");
    fprintf(json, "      \"mode_sense_3e_hex\": "); JsonWriteHexString(json, cap->hasLockedModeSense3E ? cap->lockedModeSense3E : NULL, cap->hasLockedModeSense3E ? 28 : 0); fprintf(json, "\n");
    fprintf(json, "    },\n");
    fprintf(json, "    \"unlocked\": {\n");
    fprintf(json, "      \"read_capacity_10_cdb_hex\": \"25000000000000000000\",\n");
    fprintf(json, "      \"read_capacity_10_hex\": "); JsonWriteHexString(json, cap->hasUnlockedCapacity ? cap->unlockedCapacity : NULL, cap->hasUnlockedCapacity ? 8 : 0); fprintf(json, ",\n");
    fprintf(json, "      \"visible_sector_count\": %u,\n", cap->hasUnlockedCapacity ? CapacitySectorsFromReadCapacity10(cap->unlockedCapacity) : 0);
    fprintf(json, "      \"mode_sense_3e_cdb_hex\": \"5A003E00000000001C00\",\n");
    fprintf(json, "      \"mode_sense_3e_hex\": "); JsonWriteHexString(json, cap->hasUnlockedModeSense3E ? cap->unlockedModeSense3E : NULL, cap->hasUnlockedModeSense3E ? 28 : 0); fprintf(json, "\n");
    fprintf(json, "    }\n");
    fprintf(json, "  },\n");

    fprintf(json, "  \"scsi_responses\": [\n");
    fprintf(json, "    {\n");
    fprintf(json, "      \"name\": \"read_dvd_structure_xbox_control_block\",\n");
    fprintf(json, "      \"cdb_hex\": \"AD00FF02FDFFFE00066400C0\",\n");
    fprintf(json, "      \"data_in\": true,\n");
    fprintf(json, "      \"data_len\": 1636,\n");
    fprintf(json, "      \"response_file\": "); JsonWriteEscapedString(json, cap->hasAdC0 ? adC0Path : ""); fprintf(json, ",\n");
    fprintf(json, "      \"captured\": %s\n", cap->hasAdC0 ? "true" : "false");
    fprintf(json, "    },\n");
    fprintf(json, "    {\n");
    fprintf(json, "      \"name\": \"read_dvd_structure_pfi\",\n");
    fprintf(json, "      \"cdb_hex\": \"AD0000000000000008000000\",\n");
    fprintf(json, "      \"data_in\": true,\n");
    fprintf(json, "      \"data_len\": 2048,\n");
    fprintf(json, "      \"response_file\": "); JsonWriteEscapedString(json, cap->hasPfi ? pfiPath : ""); fprintf(json, ",\n");
    fprintf(json, "      \"captured\": %s\n", cap->hasPfi ? "true" : "false");
    fprintf(json, "    },\n");
    fprintf(json, "    {\n");
    fprintf(json, "      \"name\": \"read_dvd_structure_dmi\",\n");
    fprintf(json, "      \"cdb_hex\": \"AD0000000000000408000000\",\n");
    fprintf(json, "      \"data_in\": true,\n");
    fprintf(json, "      \"data_len\": 2048,\n");
    fprintf(json, "      \"response_file\": "); JsonWriteEscapedString(json, cap->hasDmi ? dmiPath : ""); fprintf(json, ",\n");
    fprintf(json, "      \"captured\": %s\n", cap->hasDmi ? "true" : "false");
    fprintf(json, "    }\n");
    fprintf(json, "  ],\n");

    fprintf(json, "  \"auth\": {\n");
    fprintf(json, "    \"requires_media_transition\": false,\n");
    fprintf(json, "    \"unlock_requires_media_transition\": false,\n");
    fprintf(json, "    \"locked_video_view_restore_requires_media_transition\": true,\n");
    fprintf(json, "    \"state_detection\": \"READ CAPACITY (10) visible-sector count\",\n");
    fprintf(json, "    \"mode_page\": \"0x3E\",\n");
    fprintf(json, "    \"challenge_table_source\": \"read_dvd_structure_xbox_control_block\",\n");
    fprintf(json, "    \"challenge_table_response_offset\": 774,\n");
    fprintf(json, "    \"challenge_table_hash_offset\": 1187,\n");
    fprintf(json, "    \"challenge_table_hash_length\": 44\n");
    fprintf(json, "  }\n");
    fprintf(json, "}\n");

    fclose(json);

    if (!WriteXboxDvdMediaProfileFile(isoFilename,
                                      cap,
                                      totalDiscSectors,
                                      isDualLayer,
                                      videoSectors,
                                      gameSourceLba,
                                      gameSectors,
                                      isoSha1,
                                      isoMd5,
                                      isoCrc32,
                                      isoSha256,
                                      adC0Path,
                                      pfiPath,
                                      dmiPath,
                                      payloadFilesPresent))
    {
        ok = FALSE;
        printf("[WARN] Failed to write XDVD media profile.\n");
    }

    printf("[META] Wrote XDVD sidecar: %s\n", jsonPath);
    if (cap->hasAdC0) printf("[META] Wrote Xbox control block: %s\n", adC0Path);
    if (cap->hasPfi) printf("[META] Wrote PFI: %s\n", pfiPath);
    if (cap->hasDmi) printf("[META] Wrote DMI: %s\n", dmiPath);

    return ok;
}

BOOL DumpXboxGameDisc(HANDLE hDevice, const char *filename, char xisoFormat, uint32_t totalDiscSectors, bool isDualLayer, bool EjectOnSuccess, xbox_ref_dump_result *result)
{
    HCRYPTPROV hProv = 0;
    HCRYPTHASH hHash = 0;
    FILE *outFile = NULL;
    BYTE rgbHash[20];
    char sha1String[41] = {0};
    char md5String[33] = {0};
    char crc32String[9] = {0};
    char fileSha1String[41] = {0};
    char fileSha256String[65] = {0};
    BOOL dumpOk = FALSE;
    XboxDvdSidecarCapture sidecarCapture;
    BOOL rawSidecarAvailable = FALSE;
    uint32_t rawVideoSectors = START_LBA_MAGIC;
    uint32_t rawGameSourceLba = 0xFFFFFFFFu;
    uint32_t rawGameSectors = 0;
    uint32_t rawTargetSectors = 0;
    unsigned long long expectedOutputBytes = 0ULL;
    const char *outputLabel = "Output";
    DWORD operationStartTick = GetTickCount();
    DWORD cbHash = 20;
    char operationTimeStr[12] = {0};

    memset(&sidecarCapture, 0, sizeof(sidecarCapture));

    if (result) {
        result->attempted = 1;
        result->mode = xisoFormat;
        if (filename && filename[0]) {
            strncpy(result->output_path, filename, sizeof(result->output_path) - 1);
            result->output_path[sizeof(result->output_path) - 1] = '\0';
        }
    }

    if (totalDiscSectors == 0)
        totalDiscSectors = GetTotalSectors(hDevice);
    if (totalDiscSectors == 0)
        totalDiscSectors = REDUMP_SECTORS;

    rawTargetSectors = NormalizeRawIsoTargetSectors(totalDiscSectors, isDualLayer);

    if (!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
        return FALSE;
    if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
    {
        CryptReleaseContext(hProv, 0);
        return FALSE;
    }

    EnsureDriveReady(hDevice, 30000);
    SetDriveSpeedMax(hDevice);

    if (xisoFormat == '1')
    {
        DWORD bytesReturned;
        uint32_t gameSourceLba = 0xFFFFFFFFu;
        uint32_t videoSectors = START_LBA_MAGIC;
        uint32_t gameSectors = 0;
        BOOL lockedViewIsAlreadyXdfs = FALSE;

        outputLabel = "RAW ISO";
        expectedOutputBytes = (unsigned long long)rawTargetSectors * 2048ULL;
        if (result)
            result->output_sectors = rawTargetSectors;

        if (rawTargetSectors != totalDiscSectors)
        {
            printf("[RAW] Drive-reported unlocked sectors %u normalized to Redump-style output target %u.\n",
                   totalDiscSectors, rawTargetSectors);
        }

        printf("[RAW] Full-disc target: %u sectors (%llu bytes).\n",
               rawTargetSectors,
               expectedOutputBytes);
        if (rawTargetSectors > LAYER_BREAK)
        {
            printf("[RAW] Expected Redump/XGD1 layer break at output LBA %u.\n", LAYER_BREAK);
        }
        printf("[RAW] Media-transition-preserving mode is enabled.\n");

        if (!CheckOutputFreeSpace(filename, expectedOutputBytes, outputLabel))
            goto cleanup;

        outFile = fopen(filename, "wb");
        if (!outFile)
        {
            printf("\n[FATAL] Could not create output file '%s'.\n", filename);
            if (errno)
                printf("        errno: %d (%s)\n", errno, strerror(errno));
            goto cleanup;
        }

        // Raw mode must capture the visible/video view first. The caller normally reaches this
        // point after the drive has already been authenticated for metadata, so reset the drive
        // state with a real media transition before reading LBA 0.
        DeviceIoControl(hDevice, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL);

        printf("[RAW] Cycling tray to restore locked/video view before dumping sector 0.\n");
        AutomateTrayCycle(hDevice);
        RefreshVolume(hDevice);
        SetDriveSpeedMax(hDevice);

        DeviceIoControl(hDevice, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL);

        CaptureLockedSidecarState(hDevice, &sidecarCapture);

        lockedViewIsAlreadyXdfs = ProbeXboxVolumeAt(hDevice, 0x20);

        if (lockedViewIsAlreadyXdfs && rawTargetSectors > START_LBA_MAGIC)
        {
            printf("\n[FATAL] Option 1 requires a full raw/video-front source image.\n");
            printf("        This source exposes XDFS at LBA 0x20 after the media-reset step,\n");
            printf("        which looks like an XISO/game-partition view, not a full raw disc view.\n");
            printf("        Use option 2 for this source, or mount/create a 7.29 GiB Redump-style option-1 ISO.\n");
            goto cleanup;
        }

        if (rawTargetSectors <= START_LBA_MAGIC)
        {
            printf("[RAW] Non-retail-sized source; dumping visible LBA 0..%u directly.\n",
                   rawTargetSectors - 1);
            rawVideoSectors = rawTargetSectors;
            rawGameSourceLba = 0;
            rawGameSectors = 0;
            CaptureUnlockedSidecarState(hDevice, &sidecarCapture);
            rawSidecarAvailable = TRUE;
            dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, rawTargetSectors, 0, "RAW", FALSE);
        }
        else if (rawTargetSectors == XGD1_FULL_REDUMP_SECTORS)
        {
            unsigned char *videoL1Buffer = NULL;
            uint32_t detectedXdfsLba;
            uint32_t pregamePaddingSectors = XGD1_GAME_OUTPUT_START_LBA - XGD1_VIDEO_L0_SECTORS;
            uint32_t postgamePaddingSectors = XGD1_VIDEO_L1_OUTPUT_START_LBA - (XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS);

            printf("[RAW] Using Original Xbox/XGD1 Redump-style 2048-byte-sector layout.\n");
            printf("[RAW] Layout: VIDEO_L0=%u sectors, pregame zero-fill padding=%u sectors, game/XISO=%u sectors, postgame zero-fill padding=%u sectors, VIDEO_L1=%u sectors.\n",
                   XGD1_VIDEO_L0_SECTORS,
                   pregamePaddingSectors,
                   REDUMP_SECTORS,
                   postgamePaddingSectors,
                   XGD1_VIDEO_L1_SECTORS);
            printf("[RAW] Note: filler/padding ranges are synthetic zero-fill placeholders until readable from hardware.\n");
            printf("[RAW] Note: PFI/DMI/lead-in/lead-out are MMC/physical metadata, not READ(10) user-data sectors; they are emitted in sidecar/profile files.\n");

            videoL1Buffer = (unsigned char *)VirtualAlloc(NULL, XGD1_VIDEO_L1_SECTORS * 2048U, MEM_COMMIT, PAGE_READWRITE);
            if (!videoL1Buffer)
            {
                printf("\n[FATAL] Could not allocate VIDEO_L1 capture buffer.\n");
                goto cleanup;
            }

            // The locked-visible Xbox video ISO is 6,992 sectors.  In the Redump-style
            // image, its L0 portion is placed at the beginning and its L1 tail is placed
            // at the end of the reconstructed image.  Capture the L1 tail while the drive
            // is still in the locked/video state, before authenticating for the game view.
            if (!ReadSectorsToMemory(hDevice,
                                     XGD1_VIDEO_L0_SECTORS,
                                     XGD1_VIDEO_L1_SECTORS,
                                     videoL1Buffer,
                                     "VIDEO-L1 tail"))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            printf("[RAW] Writing video L0 from locked source LBA 0..%u.\n", XGD1_VIDEO_L0_SECTORS - 1);
            if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, XGD1_VIDEO_L0_SECTORS, 0, "VIDEO-L0", FALSE))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            if (!WriteZeroSectorsOutput(outFile, hHash, pregamePaddingSectors, XGD1_VIDEO_L0_SECTORS, "PREGAME-PAD"))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            printf("[RAW] Re-applying full Xbox handshake for unlocked game/XISO view.\n");
            UnlockDrive(hDevice);
            RefreshVolume(hDevice);
            Sleep(2000);
            EnsureDriveReady(hDevice, 30000);
            SetDriveSpeedMax(hDevice);
            KickXboxMediaAuth(hDevice);
            RecoveryKick(hDevice, TRUE);
            CaptureUnlockedSidecarState(hDevice, &sidecarCapture);
            rawSidecarAvailable = TRUE;

            detectedXdfsLba = DetectXboxVolumeStart(hDevice);
            if (detectedXdfsLba == 0xFFFFFFFFu)
            {
                printf("\n[FATAL] Could not locate XDFS after unlock at LBA 0x20 or 0x%X.\n", START_LBA_MAGIC);
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }
            if (detectedXdfsLba != 0x20)
            {
                printf("[WARN] XDFS was detected at unlocked source LBA %u, not the expected XISO header LBA 32.\n", detectedXdfsLba);
            }

            // FriiDump 0.5.3.13 cache-aligned raw-ID validation proved that
            // unlocked source LBA 0..31 is the physical 32-sector game-region
            // lead-in and that XDVDFS begins at source LBA 32. Preserve the
            // drive-captured lead-in instead of synthesizing zero sectors.
            gameSourceLba = XGD1_GAME_SOURCE_START_LBA;
            gameSectors = REDUMP_SECTORS;
            rawVideoSectors = XGD1_GAME_OUTPUT_START_LBA;
            rawGameSourceLba = gameSourceLba;
            rawGameSectors = gameSectors;

            printf("[RAW] Capturing %u-sector game lead-in from unlocked source LBA 0..%u at output LBA %u.\n",
                   XGD1_XISO_LEADIN_SECTORS,
                   XGD1_XISO_LEADIN_SECTORS - 1,
                   XGD1_GAME_OUTPUT_START_LBA);
            if (!DumpSectorRangeWithRetry(hDevice,
                                          outFile,
                                          hHash,
                                          0,
                                          XGD1_XISO_LEADIN_SECTORS,
                                          XGD1_GAME_OUTPUT_START_LBA,
                                          "GAME-XISO-LEADIN",
                                          TRUE))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            printf("[RAW] Writing unlocked XDFS/game data from source LBA %u for %u sectors at output LBA %u.\n",
                   XGD1_GAME_SOURCE_START_LBA,
                   XGD1_GAME_SOURCE_SECTORS,
                   XGD1_GAME_OUTPUT_START_LBA + XGD1_XISO_LEADIN_SECTORS);
            if (!DumpSectorRangeWithRetry(hDevice,
                                          outFile,
                                          hHash,
                                          XGD1_GAME_SOURCE_START_LBA,
                                          XGD1_GAME_SOURCE_SECTORS,
                                          XGD1_GAME_OUTPUT_START_LBA + XGD1_XISO_LEADIN_SECTORS,
                                          "GAME-XISO",
                                          TRUE))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            if (!WriteZeroSectorsOutput(outFile, hHash, postgamePaddingSectors, XGD1_GAME_OUTPUT_START_LBA + REDUMP_SECTORS, "POSTGAME-PAD"))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            if (!WriteMemorySectorsOutput(outFile, hHash, videoL1Buffer, XGD1_VIDEO_L1_SECTORS, XGD1_VIDEO_L1_OUTPUT_START_LBA, "VIDEO-L1"))
            {
                VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
                goto cleanup;
            }

            VirtualFree(videoL1Buffer, 0, MEM_RELEASE);
            dumpOk = TRUE;
        }
        else
        {
            if (videoSectors > rawTargetSectors)
                videoSectors = rawTargetSectors;
            rawVideoSectors = videoSectors;

            printf("[RAW] Dumping contiguous visible/video area first: source LBA 0..%u.\n", videoSectors - 1);
            if (!DumpSectorRangeWithRetry(hDevice, outFile, hHash, 0, videoSectors, 0, "VIDEO", FALSE))
                goto cleanup;

            printf("[RAW] Re-applying full Xbox handshake after media transition for hidden game/data area.\n");
            UnlockDrive(hDevice);
            RefreshVolume(hDevice);
            Sleep(2000);
            EnsureDriveReady(hDevice, 30000);
            SetDriveSpeedMax(hDevice);
            KickXboxMediaAuth(hDevice);
            RecoveryKick(hDevice, TRUE);
            CaptureUnlockedSidecarState(hDevice, &sidecarCapture);
            rawSidecarAvailable = TRUE;

            gameSourceLba = DetectXboxVolumeStart(hDevice);
            if (gameSourceLba == 0xFFFFFFFFu)
            {
                printf("\n[FATAL] Could not locate XDFS after unlock at LBA 0x20 or 0x%X.\n", START_LBA_MAGIC);
                goto cleanup;
            }

            gameSectors = rawTargetSectors - videoSectors;
            rawGameSourceLba = gameSourceLba;
            rawGameSectors = gameSectors;
            printf("[RAW] Appending hidden game/data area from unlocked source LBA %u for %u sectors.\n",
                   gameSourceLba, gameSectors);
            dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, gameSourceLba, gameSectors, videoSectors, "GAME", TRUE);
        }
    }
    else if (xisoFormat == '2')
    {
        uint32_t startLba = 0;
        uint32_t sectorsToRead = 0;
        unsigned char *sectorBuffer = (unsigned char *)VirtualAlloc(NULL, 2048, MEM_COMMIT, PAGE_READWRITE);
        XDFS_VOLUME_DESCRIPTOR *vol = NULL;
        uint32_t xgd2EndLba = 1913920;
        unsigned char zeroSector[2048] = {0};

        if (!sectorBuffer)
            goto cleanup;

        SetDriveSpeedMax(hDevice);
        KickXboxMediaAuth(hDevice);
        RecoveryKick(hDevice, TRUE);

        vol = (XDFS_VOLUME_DESCRIPTOR *)sectorBuffer;

        if (ScsiReadSectors(hDevice, START_LBA_MAGIC, 1, sectorBuffer) && memcmp(vol->Identifier, "MICROSOFT", 9) == 0)
        {
            startLba = START_LBA_MAGIC;
            printf("[INFO] XGD2 Game Partition identified at LBA %u\n", startLba);
        }
        else if (ScsiReadSectors(hDevice, 0x20, 1, sectorBuffer) && memcmp(vol->Identifier, "MICROSOFT", 9) == 0)
        {
            startLba = 0x20;
            printf("[INFO] Standard Game Partition identified at LBA 32\n");
        }
        else
        {
            printf("[ERROR] No Xbox Game Partition found. Disc may be non-standard.\n");
            VirtualFree(sectorBuffer, 0, MEM_RELEASE);
            goto cleanup;
        }

        if (isDualLayer || totalDiscSectors > 3300000)
        {
            sectorsToRead = xgd2EndLba - startLba;
            printf("[INFO] Dual Layer disc detected. Calculating span across layers...\n");
        }
        else
        {
            sectorsToRead = vol->VolumeSize / 2048;
            printf("[INFO] Single Layer disc detected. Using header-reported size.\n");
        }

        outputLabel = "XISO";
        expectedOutputBytes = ((unsigned long long)sectorsToRead + 32ULL) * 2048ULL;
        if (result)
            result->output_sectors = sectorsToRead + 32U;

        printf("[SUCCESS] Final XISO target: %u sectors plus 32-sector lead-in (%llu bytes).\n",
               sectorsToRead,
               expectedOutputBytes);

        if (!CheckOutputFreeSpace(filename, expectedOutputBytes, outputLabel))
        {
            VirtualFree(sectorBuffer, 0, MEM_RELEASE);
            goto cleanup;
        }

        outFile = fopen(filename, "wb");
        if (!outFile)
        {
            printf("\n[FATAL] Could not create output file '%s'.\n", filename);
            if (errno)
                printf("        errno: %d (%s)\n", errno, strerror(errno));
            VirtualFree(sectorBuffer, 0, MEM_RELEASE);
            goto cleanup;
        }

        VirtualFree(sectorBuffer, 0, MEM_RELEASE);

        printf("Writing 64KB XISO lead-in padding...\n");
        for (int p = 0; p < 32; p++)
        {
            if (!WriteOutputBytes(outFile, zeroSector, 2048, "XISO-PAD", 0, (uint32_t)p))
                goto cleanup;
            CryptHashData(hHash, zeroSector, 2048, 0);
        }

        dumpOk = DumpSectorRangeWithRetry(hDevice, outFile, hHash, startLba, sectorsToRead, 32, "XISO", TRUE);
    }
    else
    {
        printf("[ERROR] Unsupported dump mode '%c'.\n", xisoFormat);
        goto cleanup;
    }

    if (!dumpOk)
        goto cleanup;

    if (!FlushAndCommitOutput(outFile, outputLabel))
    {
        dumpOk = FALSE;
        goto cleanup;
    }

    if (fclose(outFile) != 0)
    {
        printf("\n[FATAL] fclose failed for %s output.\n", outputLabel ? outputLabel : "dump");
        if (errno)
            printf("        errno: %d (%s)\n", errno, strerror(errno));
        outFile = NULL;
        dumpOk = FALSE;
        goto cleanup;
    }
    outFile = NULL;

    if (!VerifyOutputByteCount(filename, expectedOutputBytes, outputLabel))
    {
        dumpOk = FALSE;
        goto cleanup;
    }

    FormatElapsedTime(GetTickCount() - operationStartTick, operationTimeStr);
    printf("\nDump/write phase complete at elapsed %s. Finalizing hashes...\n", operationTimeStr);

    if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0))
    {
        HexBytesToString(rgbHash, cbHash, sha1String, sizeof(sha1String));
    }

    if (CalculateFileHashes(filename, crc32String, sizeof(crc32String), md5String, sizeof(md5String), fileSha1String, sizeof(fileSha1String), fileSha256String, sizeof(fileSha256String)))
    {
        if (fileSha1String[0] && sha1String[0] && strcmp(fileSha1String, sha1String) != 0)
        {
            printf("\n[WARN] Streaming SHA-1 differs from file SHA-1. Using file SHA-1 in metadata.\n");
            printf("       Streaming SHA-1: %s\n", sha1String);
            printf("       File SHA-1:      %s\n", fileSha1String);
        }
        if (fileSha1String[0])
            strcpy(sha1String, fileSha1String);
    }
    else
    {
        printf("\n[WARN] Could not calculate CRC32/MD5/SHA-1/SHA-256 from finalized output file.\n");
    }

    if (result) {
        result->output_size = GetFileSizeBytes64(filename);
        strncpy(result->crc32, crc32String, sizeof(result->crc32) - 1);
        result->crc32[sizeof(result->crc32) - 1] = '\0';
        strncpy(result->md5, md5String, sizeof(result->md5) - 1);
        result->md5[sizeof(result->md5) - 1] = '\0';
        strncpy(result->sha1, sha1String, sizeof(result->sha1) - 1);
        result->sha1[sizeof(result->sha1) - 1] = '\0';
        strncpy(result->sha256, fileSha256String, sizeof(result->sha256) - 1);
        result->sha256[sizeof(result->sha256) - 1] = '\0';
        result->hashes_complete = result->crc32[0] && result->md5[0] && result->sha1[0] && result->sha256[0];
    }

    if (xisoFormat == '1')
    {
        printf("Final RAW ISO Sector Count: %u\n", rawTargetSectors);
        printf("Final RAW ISO Byte Count: %llu\n", (unsigned long long)rawTargetSectors * 2048ULL);
        printf("CRC32: %s\n", crc32String);
        printf("MD5:   %s\n", md5String);
        printf("SHA-1: %s\n", sha1String);
        printf("SHA-256: %s\n", fileSha256String);
        PrintGamePartitionHash(filename);
        if (rawSidecarAvailable)
        {
            if (!WriteXboxDvdSidecarFiles(filename,
                                          &sidecarCapture,
                                          rawTargetSectors,
                                          isDualLayer,
                                          rawVideoSectors,
                                          rawGameSourceLba,
                                          rawGameSectors,
                                          sha1String,
                                          md5String,
                                          crc32String,
                                          fileSha256String))
            {
                printf("[WARN] Failed to write one or more XDVD sidecar metadata files.\n");
            }
        }
        else
        {
            printf("[WARN] XDVD sidecar metadata was not captured for this raw dump.\n");
        }
    }
    else
    {
        printf("Final XISO Byte Count: see progress target above.\n");
        printf("CRC32: %s\n", crc32String);
        printf("MD5:   %s\n", md5String);
        printf("SHA-1: %s\n", sha1String);
        printf("SHA-256: %s\n", fileSha256String);
    }
    FormatElapsedTime(GetTickCount() - operationStartTick, operationTimeStr);
    printf("\nOperation Complete! Total elapsed: %s\n", operationTimeStr);

    /* The caller owns final drive cleanup.  Embedded FriiDump runs issue one
     * STOP UNIT after Redump verification; standalone bridge runs stop the
     * drive in xbox_ref_gdr8050l_dump_core() before closing the handle. */
    if (EjectOnSuccess)
        ControlTray(hDevice, TRUE);

cleanup:
    if (outFile)
        fclose(outFile);
    if (hHash)
        CryptDestroyHash(hHash);
    if (hProv)
        CryptReleaseContext(hProv, 0);
    if (result) {
        result->dump_success = dumpOk ? 1 : 0;
        result->elapsed_seconds = (double)(GetTickCount() - operationStartTick) / 1000.0;
        if (dumpOk && result->output_size == 0)
            result->output_size = GetFileSizeBytes64(filename);
    }
    return dumpOk;
}
