#include "dvd_drive.h"
#include "disc.h"
#include "sha1.h"
+#include "xbox_ref/xbox_ref_log.h"
#ifdef WIN32
#include <windows.h>
#include <ntddscsi.h>
#else
#include <linux/cdrom.h>
+#include <scsi/sg.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
const char *hlds_e7_record_id;
const char *hlds_e7_notes;
+ /* Last transport command evidence for release-build diagnostics. */
+ dvd_command_diagnostic last_command;
+
/* Device-dependent internal memory dump function */
/*! The intended area should start where sector data is stored upon a READ command. Here we assume that sectors are
* stored one after the other, as heuristics showed it is the case for the Hitachi MN103-based drives, but this model
}
+static void dvd_record_command_diagnostic (
+ dvd_drive *dvd,
+ mmc_command *mmc,
+ int transport_result,
+ int os_error,
+ int scsi_status,
+ int sense_key,
+ int asc,
+ int ascq
+) {
+ if (!dvd || !mmc)
+ return;
+ memset (&dvd -> last_command, 0, sizeof (dvd -> last_command));
+ dvd -> last_command.valid = true;
+ dvd -> last_command.transport_result = transport_result;
+ dvd -> last_command.os_error = os_error;
+ dvd -> last_command.scsi_status = scsi_status;
+ dvd -> last_command.sense_key = sense_key & 0x0f;
+ dvd -> last_command.asc = asc & 0xff;
+ dvd -> last_command.ascq = ascq & 0xff;
+ dvd -> last_command.cdb_length = mmc -> cmdlen;
+ if (dvd -> last_command.cdb_length < 0)
+ dvd -> last_command.cdb_length = 0;
+ if (dvd -> last_command.cdb_length > (int) sizeof (dvd -> last_command.cdb))
+ dvd -> last_command.cdb_length = (int) sizeof (dvd -> last_command.cdb);
+ memcpy (dvd -> last_command.cdb, mmc -> cmd, sizeof (dvd -> last_command.cdb));
+}
+
+bool dvd_get_last_command_diagnostic (dvd_drive *dvd, dvd_command_diagnostic *out) {
+ if (!dvd || !out || !dvd -> last_command.valid)
+ return false;
+ *out = dvd -> last_command;
+ return true;
+}
+
#ifdef WIN32
/* Doc is under the UNIX function */
SCSI_PASS_THROUGH_DIRECT *sptd;
unsigned char sptd_sense[sizeof (*sptd) + 18], *sense;
DWORD bytes;
+ DWORD win_error;
+ BOOL ioctl_ok;
int out;
sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
else
sptd -> DataIn = SCSI_IOCTL_DATA_IN;
sptd -> DataBuffer = mmc -> buffer;
- // Quick hack: Windows hates sptd->DataTransferLength = 1, so we set it to 2 and ignore the second byte.
- if (mmc -> buflen == 1) // TODO
+ /* Quick hack: Windows hates DataTransferLength = 1. */
+ if (mmc -> buflen == 1)
sptd -> DataTransferLength = 2;
else
sptd -> DataTransferLength = mmc -> buflen;
sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
sptd -> SenseInfoOffset = sizeof (*sptd);
- //fprintf (stdout,"mmc->cmd[00] = %d \n",mmc->cmd[00]);
- //Set streaming hack
- if (mmc->cmd[00]==0xB6) {
+ if (mmc -> cmd[0] == 0xB6) {
sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
sptd -> DataTransferLength = 28;
}
- {
- BOOL ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
- sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
- /* DeviceIoControl may succeed while the drive returns CHECK CONDITION.
- * Treat any non-GOOD SCSI status as command failure. ignore_errors only
- * suppresses diagnostics; it must not turn failed commands into success. */
- if (!ioctl_ok || sptd -> ScsiStatus != 0) {
- out = -1; /* Failure */
- if (!ignore_errors) {
- error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
- (unsigned long) GetLastError (), sptd -> ScsiStatus);
- debug ("Command was: ");
- hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
- debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
- }
- } else {
- out = 0;
+ ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
+ sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
+ win_error = ioctl_ok ? ERROR_SUCCESS : GetLastError ();
+ /* DeviceIoControl may succeed while the drive returns CHECK CONDITION. */
+ if (!ioctl_ok || sptd -> ScsiStatus != 0) {
+ out = -1;
+ if (!ignore_errors) {
+ error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
+ (unsigned long) win_error, sptd -> ScsiStatus);
+ debug ("Command was: ");
+ hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
+ debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
}
+ } else {
+ out = 0;
}
if (mmc -> sense) {
mmc -> sense -> asc = sense[12];
mmc -> sense -> ascq = sense[13];
}
+ dvd_record_command_diagnostic (dvd, mmc, out, (int) win_error,
+ (int) sptd -> ScsiStatus, sense[2], sense[12], sense[13]);
- return (out);
+ return out;
}
#else
*/
int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
int out;
+ int saved_errno;
struct cdrom_generic_command cgc;
struct request_sense sense;
hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
#endif
- /* Init Linux-format MMC command */
- memset (&cgc, 0, sizeof (struct cdrom_generic_command));
+ memset (&sense, 0, sizeof (sense));
+ memset (&cgc, 0, sizeof (cgc));
memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd));
cgc.buffer = (unsigned char *) mmc -> buffer;
cgc.buflen = mmc -> buflen;
cgc.data_direction = CGC_DATA_NONE;
else
cgc.data_direction = CGC_DATA_READ;
- cgc.timeout = MMC_CMD_TIMEOUT * 1000; /* Linux uses milliseconds */
+ cgc.timeout = MMC_CMD_TIMEOUT * 1000;
cgc.sense = &sense;
if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
- out = -1; /* Failure */
+ saved_errno = errno;
+ out = -1;
if (!ignore_errors) {
- error ("Execution of MMC command failed: %s", strerror (errno));
+ error ("Execution of MMC command failed: %s", strerror (saved_errno));
debug ("Command was:");
hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd));
debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq);
}
} else {
+ saved_errno = 0;
out = 0;
}
mmc -> sense -> asc = sense.asc;
mmc -> sense -> ascq = sense.ascq;
}
+ dvd_record_command_diagnostic (dvd, mmc, out, saved_errno,
+ (int) cgc.stat, sense.sense_key, sense.asc, sense.ascq);
- return (out);
+ return out;
+}
+#endif
+
+#ifndef WIN32
+/*
+ * Linux equivalent of the copied Windows SCSI_PASS_THROUGH_DIRECT transport
+ * used by UnlockDrive(). CDROM_SEND_PACKET does not expose an explicit CDB
+ * length and applies a single generic timeout. The GDR-8050L handshake uses
+ * 6-, 10-, and 12-byte CDBs plus 120-second command timeouts (10 seconds only
+ * for sticky descrambling). SG_IO preserves those boundaries exactly.
+ */
+static int dvd_xbox_sgio_exact (dvd_drive *dvd,
+ const char *step,
+ const u_int8_t *cdb,
+ int cdb_len,
+ u_int8_t *buf,
+ u_int32_t buf_len,
+ dvd_data_direction direction,
+ unsigned int timeout_ms) {
+ sg_io_hdr_t io;
+ u_int8_t sense[32];
+ mmc_command diagnostic;
+ int rc;
+ int saved_errno = 0;
+ int transport_result;
+ int i;
+
+ if (!dvd || !cdb || cdb_len < 1 || cdb_len > 12)
+ return -1;
+
+ memset (&io, 0, sizeof (io));
+ memset (sense, 0, sizeof (sense));
+ memset (&diagnostic, 0, sizeof (diagnostic));
+
+ io.interface_id = 'S';
+ io.cmdp = (unsigned char *) cdb;
+ io.cmd_len = (unsigned char) cdb_len;
+ io.sbp = sense;
+ io.mx_sb_len = sizeof (sense);
+ io.timeout = timeout_ms;
+ io.dxferp = buf;
+ io.dxfer_len = buf_len;
+
+ switch (direction) {
+ case DVD_DATA_OUT:
+ io.dxfer_direction = SG_DXFER_TO_DEV;
+ break;
+ case DVD_DATA_NONE:
+ io.dxfer_direction = SG_DXFER_NONE;
+ io.dxferp = NULL;
+ io.dxfer_len = 0;
+ break;
+ case DVD_DATA_IN:
+ default:
+ io.dxfer_direction = SG_DXFER_FROM_DEV;
+ break;
+ }
+
+ errno = 0;
+ rc = ioctl (dvd -> fd, SG_IO, &io);
+ if (rc < 0)
+ saved_errno = errno;
+
+ transport_result =
+ (rc == 0 &&
+ io.status == 0 &&
+ io.host_status == 0 &&
+ io.driver_status == 0) ? 0 : -1;
+
+ diagnostic.cmdlen = cdb_len;
+ diagnostic.direction = direction;
+ diagnostic.buffer = buf;
+ diagnostic.buflen = (int) buf_len;
+ memcpy (diagnostic.cmd, cdb, (size_t) cdb_len);
+ dvd_record_command_diagnostic (
+ dvd,
+ &diagnostic,
+ transport_result,
+ saved_errno,
+ (int) io.status,
+ sense[2],
+ sense[12],
+ sense[13]);
+
+ xbox_ref_log_fprintf (
+ stderr,
+ "[XBOX-SGIO] step=%s rc=%d errno=%d status=0x%02X host=0x%04X driver=0x%04X sense=%02X/%02X/%02X resid=%d timeout_ms=%u cdb=[",
+ step ? step : "unnamed",
+ rc,
+ saved_errno,
+ (unsigned int) io.status,
+ (unsigned int) io.host_status,
+ (unsigned int) io.driver_status,
+ (unsigned int) (sense[2] & 0x0F),
+ (unsigned int) sense[12],
+ (unsigned int) sense[13],
+ io.resid,
+ timeout_ms);
+ for (i = 0; i < cdb_len; i++)
+ xbox_ref_log_fprintf (
+ stderr,
+ "%s%02X",
+ i ? " " : "",
+ (unsigned int) cdb[i]);
+ xbox_ref_log_fprintf (
+ stderr,
+ "] xfer=%u direction=%s result=%s\n",
+ buf_len,
+ direction == DVD_DATA_OUT ? "out" :
+ direction == DVD_DATA_NONE ? "none" : "in",
+ transport_result == 0 ? "PASS" : "FAIL");
+
+ return transport_result;
+}
+
+static int dvd_xbox_exact_read_capacity (dvd_drive *dvd,
+ const char *step,
+ u_int32_t *sectors,
+ u_int32_t *sector_size) {
+ u_int8_t cdb[10];
+ u_int8_t buf[8];
+ u_int32_t max_lba;
+
+ memset (cdb, 0, sizeof (cdb));
+ memset (buf, 0, sizeof (buf));
+ cdb[0] = 0x25;
+
+ if (dvd_xbox_sgio_exact (
+ dvd, step, cdb, sizeof (cdb), buf, sizeof (buf),
+ DVD_DATA_IN, 120000) < 0)
+ return -1;
+
+ max_lba =
+ ((u_int32_t) buf[0] << 24) |
+ ((u_int32_t) buf[1] << 16) |
+ ((u_int32_t) buf[2] << 8) |
+ (u_int32_t) buf[3];
+
+ if (sectors)
+ *sectors = max_lba + 1;
+ if (sector_size)
+ *sector_size =
+ ((u_int32_t) buf[4] << 24) |
+ ((u_int32_t) buf[5] << 16) |
+ ((u_int32_t) buf[6] << 8) |
+ (u_int32_t) buf[7];
+ return 0;
+}
+
+static int dvd_xbox_exact_mode_sense_10 (dvd_drive *dvd,
+ const char *step,
+ u_int8_t page,
+ u_int8_t *buf,
+ size_t buf_len) {
+ u_int8_t cdb[10];
+
+ if (!buf || buf_len > 0xFFFF)
+ return -1;
+ memset (cdb, 0, sizeof (cdb));
+ memset (buf, 0, buf_len);
+ cdb[0] = 0x5A;
+ cdb[2] = page;
+ cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
+ cdb[8] = (u_int8_t) (buf_len & 0xFF);
+
+ return dvd_xbox_sgio_exact (
+ dvd, step, cdb, sizeof (cdb), buf, (u_int32_t) buf_len,
+ DVD_DATA_IN, 120000);
+}
+
+static int dvd_xbox_exact_mode_select_10 (dvd_drive *dvd,
+ const char *step,
+ const u_int8_t *buf,
+ size_t buf_len) {
+ u_int8_t cdb[10];
+ u_int8_t tmp[256];
+
+ if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFFFF)
+ return -1;
+ memset (cdb, 0, sizeof (cdb));
+ memset (tmp, 0, sizeof (tmp));
+ memcpy (tmp, buf, buf_len);
+ cdb[0] = 0x55;
+ cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
+ cdb[8] = (u_int8_t) (buf_len & 0xFF);
+
+ return dvd_xbox_sgio_exact (
+ dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
+ DVD_DATA_OUT, 120000);
+}
+
+static int dvd_xbox_exact_mode_select_6 (dvd_drive *dvd,
+ const char *step,
+ const u_int8_t *buf,
+ size_t buf_len) {
+ u_int8_t cdb[6];
+ u_int8_t tmp[64];
+
+ if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFF)
+ return -1;
+ memset (cdb, 0, sizeof (cdb));
+ memset (tmp, 0, sizeof (tmp));
+ memcpy (tmp, buf, buf_len);
+ cdb[0] = 0x15;
+ cdb[1] = 0x11;
+ cdb[4] = (u_int8_t) buf_len;
+
+ return dvd_xbox_sgio_exact (
+ dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
+ DVD_DATA_OUT, 10000);
}
#endif
return dvd_start_stop_unit (dvd, start, false, sense);
}
+int dvd_set_door_lock (dvd_drive *dvd, bool locked) {
+#ifdef WIN32
+ (void) dvd;
+ (void) locked;
+ return 0;
+#else
+ if (!dvd)
+ return -EINVAL;
+ if (ioctl (dvd -> fd, CDROM_LOCKDOOR, locked ? 1 : 0) < 0)
+ return -errno;
+ return 0;
+#endif
+}
+
int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
unsigned int waited = 0;
if (!dvd)
int i;
int out;
+#ifndef WIN32
+ /* Linux commonly applies CDO_LOCK while an optical device is open. A raw
+ * START STOP UNIT eject then fails immediately even though the process owns
+ * the only intentional handle. Release that kernel/drive door lock before
+ * the GDR-8050L software tray cycle, then restore it after the tray is loaded
+ * and ready. CDROM_LOCKDOOR is the documented Linux optical-door API. */
+ out = dvd_set_door_lock (dvd, false);
+ if (out < 0) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX][FATAL] Linux media-cycle could not release the optical door lock: %s (errno=%d).\n",
+ strerror (-out), -out);
+ return out;
+ }
+ xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle released the optical door lock before software eject.\n");
+#endif
+
/* Match the original GDR-8050L dumper's AutomateTrayCycle(): eject, wait
* long enough for the tray to extend, close, poll readiness, then settle. */
out = dvd_start_stop_unit (dvd, false, true, sense); /* LoEj=1, Start=0: eject */
- if (out < 0)
+ if (out < 0) {
+ xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software eject command failed.\n");
return out;
+ }
dvd_sleep_ms (3000);
out = dvd_start_stop_unit (dvd, true, true, sense); /* LoEj=1, Start=1: load */
- if (out < 0)
+ if (out < 0) {
+ xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software load command failed; the door remains unlocked for recovery.\n");
return out;
+ }
for (i = 0; i < 90; i++) {
dvd_sleep_ms (500);
if (dvd_test_unit_ready (dvd, NULL) == 0) {
dvd_sleep_ms (1500);
+#ifndef WIN32
+ out = dvd_set_door_lock (dvd, true);
+ if (out < 0)
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX][WARN] Linux media-cycle completed, but the optical door could not be re-locked: %s (errno=%d).\n",
+ strerror (-out), -out);
+ else
+ xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle restored the optical door lock after load.\n");
+#endif
return 0;
}
}
/* Original dumper falls back to a fixed 10s settle delay if TUR never
- * reports ready after tray close. */
+ * reports ready after tray close. Keep the door unlocked on failure so the
+ * user can recover the media without power-cycling the external drive. */
dvd_sleep_ms (10000);
+ fprintf (stderr, "[XBOX][FATAL] Media-cycle tray closed, but the drive never became ready; the door remains unlocked for recovery.\n");
return -1;
}
dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
+ /* MMC READ DVD STRUCTURE places Format in CDB byte 7. Byte 11 is
+ * Control and must remain zero. The copied Windows GetMediaID() path
+ * uses the same byte-7 boundary for DMI format 0x04. */
mmc.cmd[6] = layer;
+ mmc.cmd[7] = format;
mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
mmc.cmd[9] = (u_int8_t) (extbufsize & 0x00FF);
- mmc.cmd[11] = format;
mmc.cmdlen = 12;
return dvd_execute_cmd (dvd, &mmc, false);
}
-int dvd_xbox_recovery_kick (dvd_drive *dvd, bool auth_recovery) {
- mmc_command mmc;
- u_int8_t dummy[2048];
- u_int8_t auth_probe_buf[8];
- int i;
+#define XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
+
+static int dvd_xbox_refresh_ready_capacity (dvd_drive *dvd,
+ u_int32_t *sectors,
+ u_int32_t *sector_size) {
+ u_int32_t observed_sectors = 0;
+ u_int32_t observed_sector_size = 0;
if (!dvd)
return -1;
- /* Mirrors the proven dumper recovery cadence: after the GDR-8050L
- * authentication/view switch, re-trigger the Xbox media auth command, set
- * maximum speed, then issue a few harmless READ(10) probes. All commands
- * deliberately ignore errors because the point is to wake/re-prime firmware
- * state, not to make any one probe authoritative.
- */
- if (auth_recovery) {
- /* GDR-8050L / HLDS auth kick: READ DVD STRUCTURE C0 magic trigger. */
- dvd_init_command (&mmc, auth_probe_buf, sizeof (auth_probe_buf), NULL);
- mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
- mmc.cmd[2] = 0xFF;
- mmc.cmd[3] = 0x02;
- mmc.cmd[4] = 0xFD;
- mmc.cmd[11] = 0xC0;
- mmc.cmdlen = 12;
- dvd_execute_cmd (dvd, &mmc, true);
-
- /* Hitachi/Kreon-style fallback auth kick: FF 08 01. */
- dvd_init_command (&mmc, NULL, 0, NULL);
- mmc.cmd[0] = 0xFF;
- mmc.cmd[1] = 0x08;
- mmc.cmd[2] = 0x01;
- mmc.cmdlen = 10;
- mmc.direction = DVD_DATA_NONE;
- dvd_execute_cmd (dvd, &mmc, true);
+ /* Exact portable equivalent of xbox_ref_refresh_ready_capacity():
+ * RefreshVolume(); Sleep(2000); EnsureDriveReady(30000); GetTotalSectors(). */
+ dvd_refresh_volume (dvd);
+ dvd_sleep_ms (2000);
+ if (dvd_wait_ready (dvd, 30000) < 0)
+ return -1;
+ if (dvd_read_capacity_10 (dvd, &observed_sectors, &observed_sector_size, NULL) < 0)
+ return -1;
+
+ if (sectors)
+ *sectors = observed_sectors;
+ if (sector_size)
+ *sector_size = observed_sector_size;
+ return 0;
+}
+
+int dvd_xbox_prepare_game_view (dvd_drive *dvd,
+ u_int32_t *sectors,
+ u_int32_t *sector_size) {
+ u_int32_t entry_sectors = 0;
+ u_int32_t observed_sectors = 0;
+ u_int32_t observed_sector_size = 0;
+
+ if (!dvd || !dvd_is_xbox_challenge_drive (dvd))
+ return -1;
+
+ /* Port xbox_ref_gdr8050l_dump_core() state preparation exactly, replacing
+ * Win32 handle/volume calls with FriiDump's portable Linux equivalents.
+ * No RecoveryKick, media-auth kick, or synthetic LBA-zero read cadence is
+ * part of this stock/cross-flashed GDR-8050L sequence. */
+ if (dvd_wait_ready (dvd, 30000) < 0) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][FATAL] Drive did not become ready before Xbox state preparation.\n");
+ return -1;
+ }
+ if (dvd_read_capacity_10 (dvd, &entry_sectors, &observed_sector_size, NULL) < 0) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][FATAL] Entry READ CAPACITY failed.\n");
+ return -1;
+ }
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Entry READ CAPACITY: %u sectors.\n",
+ entry_sectors);
+
+ if (entry_sectors > XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
+ if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
+ return -1;
+ } else {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
+ if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][WARN] Direct UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
+ if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
+ return -1;
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Direct-handshake READ CAPACITY: %u sectors.\n",
+ observed_sectors);
+
+ if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
+ if (dvd_media_cycle (dvd, NULL) < 0)
+ return -1;
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Re-applying the full handshake after recovery media change.\n");
+ if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][WARN] Recovery UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
+ if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
+ return -1;
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ] Recovery-handshake READ CAPACITY: %u sectors.\n",
+ observed_sectors);
+
+ if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
+ xbox_ref_log_fprintf (stderr,
+ "[XBOX-WINSEQ][FATAL] Xbox game view was not established after direct and recovery handshakes.\n");
+ return -1;
+ }
+ }
}
+ /* Exact next Windows step after state preparation. */
dvd_set_speed (dvd, 0xFFFF, NULL);
- for (i = 0; i < 10; i++)
- dvd_read_10 (dvd, 0, 1, NULL, dummy, sizeof (dummy));
-
+ if (sectors)
+ *sectors = observed_sectors;
+ if (sector_size)
+ *sector_size = observed_sector_size;
return 0;
}
#ifdef WIN32
DWORD bytesReturned = 0;
if (!dvd)
- return -1;
- return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL) ? 0 : -1;
+ return DVD_VOLUME_LOCK_FAILED;
+ return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)
+ ? DVD_VOLUME_LOCK_OK
+ : DVD_VOLUME_LOCK_FAILED;
#else
+ /* Linux CDROM_SEND_PACKET has no FSCTL_LOCK_VOLUME equivalent here.
+ * Return a distinct result instead of falsely reporting an exclusive lock.
+ * The caller may continue after warning about automount/media polling. */
(void) dvd;
- return 0;
+ return DVD_VOLUME_LOCK_UNAVAILABLE;
#endif
}
if (!table || table_len < 0x664)
return -1;
+#ifdef WIN32
/* GDR-8050L / Xbox READ DVD STRUCTURE format C0. */
dvd_init_command (&mmc, table, 0x664, NULL);
mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
mmc.cmdlen = 12;
out = dvd_execute_cmd (dvd, &mmc, true);
}
+#else
+ {
+ u_int8_t cdb[12];
+
+ memset (cdb, 0, sizeof (cdb));
+ memset (table, 0, table_len);
+ cdb[0] = 0xAD;
+ cdb[2] = 0xFF;
+ cdb[3] = 0x02;
+ cdb[4] = 0xFD;
+ cdb[5] = 0xFF;
+ cdb[6] = 0xFE;
+ cdb[8] = 0x06;
+ cdb[9] = 0x64;
+ cdb[11] = 0xC0;
+ out = dvd_xbox_sgio_exact (
+ dvd,
+ "3-read-dvd-structure-c0",
+ cdb,
+ sizeof (cdb),
+ table,
+ 0x664,
+ DVD_DATA_IN,
+ 120000);
+
+ if (out < 0 || table[772] != 1 || table[773] == 0) {
+ memset (cdb, 0, sizeof (cdb));
+ memset (table, 0, table_len);
+ cdb[0] = 0xFD;
+ cdb[1] = 0x01;
+ cdb[8] = 0x06;
+ cdb[9] = 0x64;
+ out = dvd_xbox_sgio_exact (
+ dvd,
+ "3-read-host-table-fallback-fd",
+ cdb,
+ sizeof (cdb),
+ table,
+ 0x664,
+ DVD_DATA_IN,
+ 120000);
+ }
+ }
+#endif
if (out < 0 || table[772] != 1)
return -1;
+
+ xbox_ref_log_fprintf (
+ stderr,
+ "[XBOX-SGIO] challenge-table marker=%u entries-byte=%u result=PASS\n",
+ (unsigned int) table[772],
+ (unsigned int) table[773]);
return 0;
}
int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
int i, k, l;
+ int out;
int chalpos[24];
u_int8_t table[0x664];
u_int8_t restable[261];
/* Step 1/2: read current capacity and the Xbox mode page. If the drive is
* already unlocked, this is harmless; the final capacity check below becomes
* the authority. */
+#ifdef WIN32
dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
+#else
+ dvd_xbox_exact_read_capacity (
+ dvd, "1-initial-read-capacity", §ors, §or_size);
+ dvd_xbox_exact_mode_sense_10 (
+ dvd, "2-mode-sense-3e", 0x3E, page, sizeof (page));
+#endif
/* Step 3: retrieve and decode the host challenge table. */
if (dvd_xbox_read_host_challenge_table (dvd, table, sizeof (table)) < 0) {
/* Match the original dumper: send the challenge and continue even if
* Windows reports a transport failure. The later XDVDFS probe is the
* authority for whether the drive actually entered the game view. */
+#ifdef WIN32
dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
+#else
+ dvd_xbox_exact_mode_select_10 (
+ dvd, "4-mode-select-challenge-1", page, sizeof (page));
+ dvd_xbox_exact_mode_sense_10 (
+ dvd, "5-mode-sense-verify-1", 0x3E, page, sizeof (page));
+#endif
/* Step 6: second host challenge. */
memset (page, 0, sizeof (page));
page[9] = 0x12;
page[12] = 0x01;
memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
+#ifdef WIN32
dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
+#else
+ dvd_xbox_exact_mode_select_10 (
+ dvd, "6-mode-select-challenge-2", page, sizeof (page));
+ dvd_xbox_exact_mode_sense_10 (
+ dvd, "7-mode-sense-verify-2", 0x3E, page, sizeof (page));
+#endif
/* Step 8: unlock partition 1. */
memset (page, 0, sizeof (page));
page[13] = 0xD1;
page[14] = 0x01;
memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
+#ifdef WIN32
dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
+#else
+ dvd_xbox_exact_mode_select_10 (
+ dvd, "8-mode-select-partition-1-unlock", page, sizeof (page));
+#endif
/* Step 9: sticky descrambling, mode page 0x31. */
memset (sticky, 0, sizeof (sticky));
sticky[4] = 0x31;
sticky[5] = 0x06;
sticky[6] = 0x01;
+#ifdef WIN32
dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
+#else
+ dvd_xbox_exact_mode_select_6 (
+ dvd, "9-mode-select-sticky-descrambling", sticky, sizeof (sticky));
+#endif
/* Step 10: final capacity observation. The original UnlockDrive() only
* prints this verification and does not fail if the capacity has not changed
* prime the drive before the required media-change event. */
sectors = 0;
sector_size = 0;
- if (dvd_read_capacity_10 (dvd, §ors, §or_size, NULL) == 0) {
+#ifdef WIN32
+ out = dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
+#else
+ out = dvd_xbox_exact_read_capacity (
+ dvd, "10-final-read-capacity", §ors, §or_size);
+#endif
+ if (out == 0) {
if (unlocked_sectors)
*unlocked_sectors = sectors;
- fprintf (stderr, "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n", sectors, sector_size);
+ xbox_ref_log_fprintf (
+ stderr,
+ "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n",
+ sectors,
+ sector_size);
} else {
- fprintf (stderr, "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
+ xbox_ref_log_fprintf (
+ stderr,
+ "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
}
return 0;