1 /***************************************************************************
2 * Copyright (C) 2007 by Arep *
3 * Support is provided through the forums at *
4 * http://wii.console-tribe.com *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the *
18 * Free Software Foundation, Inc., *
19 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
20 ***************************************************************************/
23 * \brief A class to send raw MMC commands to a CD/DVD-ROM drive.
25 * This class can be used to send raw MMC commands to a CD/DVD-ROM drive. It uses own structures and data types to represent the commands, which are
26 * then transformed in the proper OS-dependent structures when the command is executed, achieving portability. Currently Linux and Windows are supported, but
27 * all that is needed to add support to a new OS is a proper <code>dvd_execute_cmd()</code> function, so it should be very easy. I hope that someone can add
28 * compatibility with MacOS X and *BSD: libcdio is a good place to understand how it should be done :). Actally, we could have used libcdio right from the start,
29 * but I didn't want to add a dependency on a library that cannot be easily found in binary format for all the target OS's.
31 * This file contains code derived from the work of Kevin East (SeventhSon), kev@kev.nu, http://www.kev.nu/360/ , which, in turn, derives from work by
32 * a lot of other people. See his page for full details.
38 #include <sys/types.h>
39 //#include <sys/time.h>
44 #include "dvd_drive.h"
47 #include "xbox_ref/xbox_ref_log.h"
53 #include <linux/cdrom.h>
55 #include <sys/ioctl.h>
62 /*! \brief Timeout for MMC commands.
64 * This must be expressed in seconds (Windows uses seconds, right?).
66 #define MMC_CMD_TIMEOUT 10
69 /* Imported drive-specific functions */
70 int vanilla_2064_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
71 int vanilla_2384_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
72 int hitachi_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
73 int hitachi_dvd_dump_mem_type1 (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
74 int liteon_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
75 int renesas_dvd_dump_mem (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf);
78 /*! \brief A structure that represents a CD/DVD-ROM drive.
81 /* Device special file */
82 char *device; //!< The path to the drive (i.e.: /dev/something on Unix, x: on Windows).
84 /* Data about the drive */
85 char *vendor; //!< The drive vendor.
86 char *prod_id; //!< The drive product ID.
87 char *prod_rev; //!< The drive product revision (Usually firmware version).
88 char *model_string; //!< The above three strings, joined in a single one.
91 u_int32_t hlds_e7_type;
92 u_int32_t hlds_e7_cache_base;
93 u_int32_t hlds_e7_mem_blocks;
94 u_int32_t hlds_e7_static_cdb_base;
95 u_int32_t hlds_e7_static_gate;
96 int hlds_e7_preferred_method;
97 const char *hlds_e7_profile_label;
98 const char *hlds_e7_support_tier;
99 const char *hlds_e7_family;
100 const char *hlds_e7_tokens;
101 const char *hlds_e7_record_id;
102 const char *hlds_e7_notes;
104 /* Last transport command evidence for release-build diagnostics. */
105 dvd_command_diagnostic last_command;
107 /* Device-dependent internal memory dump function */
108 /*! The intended area should start where sector data is stored upon a READ command. Here we assume that sectors are
109 * stored one after the other, as heuristics showed it is the case for the Hitachi MN103-based drives, but this model
110 * might be changed in the future, if we get support for other drives.
112 dvd_drive_memdump_func memdump; //!< A pointer to a function that is able to dump the drive's internal memory area.
113 bool supported; //!< True if the drive is a supported model, false otherwise.
116 /* File descriptor & stuff used to access drive */
118 HANDLE fd; //!< The HANDLE to interact with the drive on Windows.
120 int fd; //!< The file descriptor to interact with the drive on Unix.
125 /** \brief Supported MMC commands.
127 enum mmc_commands_e {
128 SPC_TEST_UNIT_READY = 0x00,
130 SPC_MODE_SELECT_6 = 0x15,
131 MMC_START_STOP_UNIT = 0x1B,
132 MMC_READ_CAPACITY_10 = 0x25,
134 SPC_MODE_SENSE_10 = 0x5A,
135 SPC_MODE_SELECT_10 = 0x55,
137 MMC_READ_DVD_STRUCTURE = 0xAD,
146 static void xbox_rc4_init (xbox_rc4_ctx *ctx, const u_int8_t *key, size_t keylen) {
150 for (i = 0; i < 256; i++)
151 ctx -> s[i] = (u_int8_t) i;
159 for (i = 0; i < 256; i++) {
160 j = (u_int8_t) (j + ctx -> s[i] + key[i % keylen]);
162 ctx -> s[i] = ctx -> s[j];
167 static void xbox_rc4_crypt (xbox_rc4_ctx *ctx, const u_int8_t *in, u_int8_t *out, size_t len) {
171 for (n = 0; n < len; n++) {
172 ctx -> i = (u_int8_t) (ctx -> i + 1);
173 ctx -> j = (u_int8_t) (ctx -> j + ctx -> s[ctx -> i]);
174 tmp = ctx -> s[ctx -> i];
175 ctx -> s[ctx -> i] = ctx -> s[ctx -> j];
176 ctx -> s[ctx -> j] = tmp;
177 k = ctx -> s[(u_int8_t) (ctx -> s[ctx -> i] + ctx -> s[ctx -> j])];
182 static bool dvd_prod_has (dvd_drive *dvd, const char *needle) {
183 return dvd && dvd -> prod_id && needle && strstr (dvd -> prod_id, needle) != NULL;
186 static bool dvd_vendor_is (dvd_drive *dvd, const char *vendor) {
187 return dvd && dvd -> vendor && vendor && strcmp (dvd -> vendor, vendor) == 0;
190 static bool dvd_is_hlds_drive (dvd_drive *dvd) {
191 return dvd_vendor_is (dvd, "HL-DT-ST");
194 static bool dvd_prod_has_any (dvd_drive *dvd, const char **needles, size_t count) {
196 for (i = 0; i < count; i++) {
197 if (dvd_prod_has (dvd, needles[i]))
203 static bool dvd_is_hlds_gcc4243_4244_drive (dvd_drive *dvd) {
204 static const char *names[] = {
205 "GCC-4243N", "GCC4243N", "GCC4243",
206 "GCC-4244N", "GCC4244N", "GCC4244"
208 return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
211 static bool dvd_is_hlds_gdr8050l_drive (dvd_drive *dvd) {
212 static const char *names[] = {
213 "GDR8050L", "GDR-8050L"
215 return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
221 const char *firmware;
223 u_int32_t cache_base;
224 u_int32_t mem_blocks;
225 int preferred_method;
227 const char *support_tier;
230 const char *record_id;
231 u_int32_t static_cdb_base;
232 u_int32_t static_gate;
234 } hlds_e7_profile_desc;
236 static const hlds_e7_profile_desc hlds_e7_profiles[] = {
237 { "GCC-4241N", "A101", 21, 0x80000000U, 1, 8, "GCC-4241N A101 promoted E7 parser profile", "error_prone_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0064", 0x85cU, 0x900356b7U, "capable but error-prone; conservative Method 8 one-window validation profile" },
238 { "GCC-4242N", "0J06", 22, 0x80000000U, 1, 8, "GCC-4242N 0J06 promoted E7 parser profile", "error_prone_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0066", 0x824U, 0x90038621U, "capable but error-prone; conservative Method 8 one-window validation profile" },
239 { "GCC-4243N", "0000", 3, 0x80000000U, 5, 8, "GCC-4243N 0000 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0069", 0x884U, 0x90037929U, "known-supported GCC_424x profile hardening target" },
240 { "GCC-4243N", "1.08", 3, 0x80000000U, 5, 8, "GCC-4243N 1.08 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0071", 0x880U, 0x90036813U, "known-supported GCC_424x profile hardening target" },
241 { "GCC-4244N", "1.03", 3, 0x80000000U, 5, 8, "GCC-4244N 1.03 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0073", 0x88cU, 0x90037d06U, "P1 owned GCC_424x profile hardening target" },
242 { "GCC-4244N", "103", 3, 0x80000000U, 5, 8, "GCC-4244N 103 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0076", 0x894U, 0x900386fbU, "P1 owned GCC_424x profile hardening target" },
243 { "GCC4244", "B103", 3, 0x80000000U, 5, 8, "GCC-4244N B103 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "promoted_parser_signature_v23", 0x894U, 0x900386fbU, "Live INQUIRY alias for HL-DT-ST CDRW/DVD GCC4244 B103; case label B101; exact Stage5B parser signature recovered; media preflight, seed retrieval, full GameCube dump, STOP UNIT, and Redump hash match validated" },
244 { "GCC-4244N", "B103", 3, 0x80000000U, 5, 8, "GCC-4244N B103 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "promoted_parser_signature_v23", 0x894U, 0x900386fbU, "B103 shares the promoted parser signature gate/CDB with 103/104; live GCC4244/B103 hardware completed a Redump-matching GameCube dump" },
245 { "GCC-4244N", "104", 3, 0x80000000U, 5, 8, "GCC-4244N 104 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0078", 0x894U, 0x900386fbU, "P1 owned GCC_424x profile hardening target" },
246 { "GDR-3120L", "0046", 4, 0x80000000U, 5, 8, "GDR-3120L 0046 experimental GC/Wii E7 parser profile", "experimental_gc_wii_candidate", "GDR_3120x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0355", 0x5b8U, 0x90025bceU, "historically Xbox/reference; allow read-only GC/Wii Method 8 experiment, not proven support until dump validates" },
247 { "GDR-8050L", "0L23", 44, 0x80000000U, 1, 8, "GDR-8050L 0L23 donor/modified-firmware E7 parser profile", "donor_reference_modified_firmware_only", "GDR_8050x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0099", 0x5d0U, 0x90026160U, "stock firmware is Xbox-only here; GC/Wii 0xE7 path requires modified firmware with memdump support" },
248 { "GDR-8082N", "0120", 4, 0x80000000U, 5, 9, "GDR-8082N 0120 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_808x", "HL;IT;RPC;RPC_SUFFIX", "stage5b_0103", 0x638U, 0x900282daU, "known-supported reference profile" },
249 { "GDR-8083N", "0K04", 4, 0x80000000U, 5, 9, "GDR-8083N 0K04 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_808x", "HL;IT;RPC;RPC_SUFFIX", "stage5b_0104", 0x638U, 0x900291fcU, "known-supported reference profile" },
250 { "GDR-8161B", "0102", 4, 0x80000000U, 5, 9, "GDR-8161B 0102 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_816x", "IT;RPC;RPC_SUFFIX", "stage5b_0109", 0x5a8U, 0x90025010U, "known-supported reference profile" },
251 { "GDR-8163B", "0L23", 4, 0x80000000U, 5, 9, "GDR-8163B 0L23 promoted E7 parser profile", "known_supported_profile_hardening_p0", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0110", 0x5e0U, 0x90024d5aU, "P0 owned profile hardening target" },
252 { "GDR-8163B", "0L30", 4, 0x80000000U, 5, 8, "GDR-8163B 0L30 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90025021U, "Germany-batch variant; Method 8 seed retrieval and full GameCube dump OK; exact Stage5B parser signature recovered" },
253 { "GDR-8163B", "0L20", 4, 0x80000000U, 5, -1, "GDR-8163B 0L20 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024c8fU, "Germany-batch variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
254 { "GDR-8163B", "0D20", 4, 0x80000000U, 5, -1, "GDR-8163B 0D20 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024ae7U, "Germany-batch variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
255 { "GDR-8163B", "0B30", 4, 0x80000000U, 5, -1, "GDR-8163B 0B30 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90025030U, "Germany-batch HP/OEM variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
256 { "GDR-8163B", "0E15", 4, 0x80000000U, 5, -1, "GDR-8163B 0E15 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5d8U, 0x900247d1U, "Germany-batch HP/OEM variant; exact Stage5B parser signature recovered with CDB base 0x5D8; full GameCube dump matches Redump" },
257 { "GDR-8163B", "0M26", 4, 0x80000000U, 5, -1, "GDR-8163B 0M26 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024ff7U, "Germany-batch Lenovo/OEM Malaysia variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
258 { "GDR-8164B", "0L06", 4, 0x80000000U, 5, 9, "GDR-8164B 0L06 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0111/stage5b_0113", 0x5ccU, 0x90025bbeU, "known-supported reference profile; two firmware records agree" },
259 { NULL, NULL, 0, 0, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }
262 static void hlds_normalize_model (const char *src, char *dst, size_t dst_size) {
264 if (!dst || dst_size == 0)
269 for (i = 0, j = 0; src[i] && j + 1 < dst_size; i++) {
270 unsigned char c = (unsigned char) src[i];
272 dst[j++] = (char) toupper (c);
277 static bool hlds_model_matches (dvd_drive *dvd, const char *model) {
280 if (!dvd || !dvd -> prod_id || !model)
282 hlds_normalize_model (dvd -> prod_id, prod_norm, sizeof (prod_norm));
283 hlds_normalize_model (model, model_norm, sizeof (model_norm));
284 return prod_norm[0] && model_norm[0] && strstr (prod_norm, model_norm) != NULL;
287 static bool hlds_revision_matches (dvd_drive *dvd, const char *revision) {
288 const unsigned char *a;
289 const unsigned char *b;
290 if (!revision || !revision[0])
292 if (!dvd || !dvd -> prod_rev)
294 a = (const unsigned char *) dvd -> prod_rev;
295 b = (const unsigned char *) revision;
297 if (toupper (*a) != toupper (*b))
302 return *a == 0 && *b == 0;
305 static const hlds_e7_profile_desc *dvd_find_hlds_e7_profile (dvd_drive *dvd) {
306 const hlds_e7_profile_desc *p;
307 if (!dvd_is_hlds_drive (dvd))
309 for (p = hlds_e7_profiles; p -> model; p++) {
310 if (hlds_model_matches (dvd, p -> model) && hlds_revision_matches (dvd, p -> firmware))
316 static u_int32_t dvd_hlds_e7_detect_type (dvd_drive *dvd) {
317 const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
318 static const char *type1[] = {
319 "GCC-4160N", "GCC4160N", "GCC4160",
320 "GCC-4240N", "GCC4240N", "GCC4240"
322 static const char *type2_1[] = {
323 "GCC-4241N", "GCC4241N", "GCC4241"
325 static const char *type2_2[] = {
326 "GCC-4242N", "GCC4242N", "GCC4242"
328 static const char *gdr8081n[] = {
329 "GDR8081N", "GDR-8081N"
331 static const char *type3[] = {
332 "GCC4244", "GCC4244N", "GCC-4244N",
333 "GCC4247", "GCC4247N", "GCC-4247N",
334 "GDR8083N", "GDR8084N",
335 "GCC-4243N", "GCC4243N", "GCC4243",
336 "GCC-4246N", "GCC4246N", "GCC4246"
338 static const char *type4[] = {
339 "DU10N", "GDR8082N", "GDR8161B", "GDR8162B",
340 "GDR8163B", "GDR8164B", "GDR-T10N",
341 /* Local project keeps GDR-3120L in the same transport family for
342 * classification, but Xbox dumping is still routed through the explicit
343 * Xbox paths rather than the GC/Wii Method 8/9 readers. */
344 "GDR3120L", "GDR-3120L"
347 if (!dvd_is_hlds_drive (dvd))
350 return profile -> type;
351 if (dvd_prod_has_any (dvd, type1, sizeof (type1) / sizeof (type1[0])))
353 if (dvd_prod_has_any (dvd, type2_1, sizeof (type2_1) / sizeof (type2_1[0])))
355 if (dvd_prod_has_any (dvd, type2_2, sizeof (type2_2) / sizeof (type2_2[0])))
357 if (dvd_prod_has_any (dvd, gdr8081n, sizeof (gdr8081n) / sizeof (gdr8081n[0])))
359 if (dvd_prod_has_any (dvd, type3, sizeof (type3) / sizeof (type3[0])))
361 if (dvd_is_hlds_gdr8050l_drive (dvd))
363 if (dvd_prod_has_any (dvd, type4, sizeof (type4) / sizeof (type4[0])))
365 /* DIC labels GSA-4163B as an Xbox swap candidate, not a normal 0xE7
366 * Nintendo-disc cache dump profile, so keep it out of the profile layer. */
370 static bool dvd_is_hlds_gc_wii_drive (dvd_drive *dvd) {
371 return dvd_hlds_e7_detect_type (dvd) != 0;
374 static const char *dvd_hlds_e7_profile_name_from_type (u_int32_t type) {
376 case 1: return "Type1";
377 case 21: return "Type2_1 experimental";
378 case 22: return "Type2_2 experimental";
379 case 3: return "Type3";
380 case 4: return "Type4";
381 case 44: return "GDR-8050L modified 0xE7 single-window proven fallback";
382 case 45: return "GDR-8050L modified 0xE7 speed-probe pending";
383 case 442: return "GDR-8050L modified 0xE7 probe B 2-window";
384 case 443: return "GDR-8050L modified 0xE7 probe A 3-window";
385 case 445: return "GDR-8050L modified 0xE7 probe C 5-window guarded";
386 case 81: return "GDR-8081N experimental 0xE7 probe";
387 case 811: return "GDR-8081N probe A Type4-derived";
388 case 812: return "GDR-8081N probe B single-window";
389 case 813: return "GDR-8081N probe C Type1-base";
390 case 814: return "GDR-8081N probe E exact-offset moving-cache candidate";
391 case 815: return "GDR-8081N probe A scan-guided Type4-derived";
392 default: return "none";
396 static void dvd_apply_hlds_e7_profile (dvd_drive *dvd) {
397 const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
398 dvd -> hlds_e7_type = dvd_hlds_e7_detect_type (dvd);
399 dvd -> hlds_e7_cache_base = 0x80000000U;
400 dvd -> hlds_e7_mem_blocks = 5;
401 dvd -> hlds_e7_static_cdb_base = 0;
402 dvd -> hlds_e7_static_gate = 0;
403 dvd -> hlds_e7_preferred_method = -1;
404 dvd -> hlds_e7_profile_label = NULL;
405 dvd -> hlds_e7_support_tier = NULL;
406 dvd -> hlds_e7_family = NULL;
407 dvd -> hlds_e7_tokens = NULL;
408 dvd -> hlds_e7_record_id = NULL;
409 dvd -> hlds_e7_notes = NULL;
411 switch (dvd -> hlds_e7_type) {
413 /* DIC Type1: GCC-4160N/GCC-4240N cache frames begin at 0x00a13000
414 * and only one 16-sector cache window is consumed per READ. */
415 dvd -> hlds_e7_cache_base = 0x00a13000U;
416 dvd -> hlds_e7_mem_blocks = 1;
420 /* DIC Type2 uses a moving 0x80000000-derived cache address. This
421 * branch logs/classifies it, but does not yet claim DIC parity. */
422 dvd -> hlds_e7_cache_base = 0x80000000U;
423 dvd -> hlds_e7_mem_blocks = 1;
426 /* GDR-8081N is not in the confirmed DIC dump list, but local firmware
427 * analysis suggests an 0xE7 command surface. Start with a Type4-derived
428 * candidate; disc.c probes and may switch to one of the 811..814 runtime
429 * profiles before seed cracking continues. */
430 dvd -> hlds_e7_cache_base = 0x80000000U;
431 dvd -> hlds_e7_mem_blocks = 5;
434 /* Stock GDR-8050L firmware does not expose the HIT 0xE7 memdump command.
435 * The local test unit is GDR-8163B hardware cross-flashed with modified
436 * GDR-8050L firmware where 0xE7 memdump was added. Single-window has
437 * completed and hash-matched Sonic, so it remains the proven fallback.
438 * Start in an explicit speed-probe-pending profile so the initial drive
439 * information does not look like the old static single-window build.
440 * disc.c promotes to 2/3/5 windows only after guarded validation, or
441 * settles back to the proven single-window profile. */
442 dvd -> hlds_e7_type = 45;
443 dvd -> hlds_e7_cache_base = 0x80000000U;
444 dvd -> hlds_e7_mem_blocks = 1;
449 dvd -> hlds_e7_cache_base = 0x80000000U;
450 dvd -> hlds_e7_mem_blocks = 5;
455 dvd -> hlds_e7_cache_base = profile -> cache_base;
456 dvd -> hlds_e7_mem_blocks = profile -> mem_blocks;
457 dvd -> hlds_e7_static_cdb_base = profile -> static_cdb_base;
458 dvd -> hlds_e7_static_gate = profile -> static_gate;
459 dvd -> hlds_e7_preferred_method = profile -> preferred_method;
460 dvd -> hlds_e7_profile_label = profile -> label;
461 dvd -> hlds_e7_support_tier = profile -> support_tier;
462 dvd -> hlds_e7_family = profile -> family;
463 dvd -> hlds_e7_tokens = profile -> tokens;
464 dvd -> hlds_e7_record_id = profile -> record_id;
465 dvd -> hlds_e7_notes = profile -> notes;
466 /* The modified GDR-8050L runtime profile still starts in the guarded
467 * speed-probe-pending state, but keeps its static parser evidence fields. */
468 if (profile -> type == 44) {
469 dvd -> hlds_e7_type = 45;
470 dvd -> hlds_e7_mem_blocks = 1;
475 void dvd_set_hlds_e7_runtime_profile (dvd_drive *dvd, u_int32_t type, u_int32_t cache_base, u_int32_t mem_blocks) {
478 dvd -> hlds_e7_type = type;
479 dvd -> hlds_e7_cache_base = cache_base;
480 dvd -> hlds_e7_mem_blocks = mem_blocks;
483 static bool dvd_is_tsst_kreon_candidate (dvd_drive *dvd) {
484 if (!(dvd_vendor_is (dvd, "TSSTcorp") || dvd_vendor_is (dvd, "SAMSUNG")))
487 dvd_prod_has (dvd, "TS-H352C") ||
488 dvd_prod_has (dvd, "TS-H353A") ||
489 dvd_prod_has (dvd, "SH-D162C") ||
490 dvd_prod_has (dvd, "SH-D162D") ||
491 dvd_prod_has (dvd, "SH-D163A") ||
492 dvd_prod_has (dvd, "SH-D163B");
497 * Initializes a structure representing an MMC command.
498 * @param mmc A pointer to the MMC command structure.
499 * @param buf The buffer where results of the MMC command execution provided by the drive should be stored, or NULL if no buffer will be provided.
500 * @param len The length of the buffer (ignored in case buf is NULL).
501 * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed, or NULL.
503 void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) {
504 memset (mmc, 0, sizeof (mmc_command));
506 memset (buf, 0, len);
508 mmc -> direction = buf && len > 0 ? DVD_DATA_IN : DVD_DATA_NONE;
510 mmc -> buflen = buf ? len : 0;
511 mmc -> sense = sense;
517 static void dvd_record_command_diagnostic (
520 int transport_result,
529 memset (&dvd -> last_command, 0, sizeof (dvd -> last_command));
530 dvd -> last_command.valid = true;
531 dvd -> last_command.transport_result = transport_result;
532 dvd -> last_command.os_error = os_error;
533 dvd -> last_command.scsi_status = scsi_status;
534 dvd -> last_command.sense_key = sense_key & 0x0f;
535 dvd -> last_command.asc = asc & 0xff;
536 dvd -> last_command.ascq = ascq & 0xff;
537 dvd -> last_command.cdb_length = mmc -> cmdlen;
538 if (dvd -> last_command.cdb_length < 0)
539 dvd -> last_command.cdb_length = 0;
540 if (dvd -> last_command.cdb_length > (int) sizeof (dvd -> last_command.cdb))
541 dvd -> last_command.cdb_length = (int) sizeof (dvd -> last_command.cdb);
542 memcpy (dvd -> last_command.cdb, mmc -> cmd, sizeof (dvd -> last_command.cdb));
545 bool dvd_get_last_command_diagnostic (dvd_drive *dvd, dvd_command_diagnostic *out) {
546 if (!dvd || !out || !dvd -> last_command.valid)
548 *out = dvd -> last_command;
554 /* Doc is under the UNIX function */
555 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
556 SCSI_PASS_THROUGH_DIRECT *sptd;
557 unsigned char sptd_sense[sizeof (*sptd) + 18], *sense;
563 sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
564 sense = &sptd_sense[sizeof (*sptd)];
566 memset (sptd, 0, sizeof (sptd_sense));
567 memcpy (sptd -> Cdb, mmc -> cmd, sizeof (mmc -> cmd));
568 sptd -> Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
569 sptd -> CdbLength = mmc -> cmdlen;
570 sptd -> SenseInfoLength = 18;
571 if (mmc -> direction == DVD_DATA_OUT)
572 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
573 else if (mmc -> direction == DVD_DATA_NONE)
574 sptd -> DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
576 sptd -> DataIn = SCSI_IOCTL_DATA_IN;
577 sptd -> DataBuffer = mmc -> buffer;
578 /* Quick hack: Windows hates DataTransferLength = 1. */
579 if (mmc -> buflen == 1)
580 sptd -> DataTransferLength = 2;
582 sptd -> DataTransferLength = mmc -> buflen;
583 sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
584 sptd -> SenseInfoOffset = sizeof (*sptd);
586 if (mmc -> cmd[0] == 0xB6) {
587 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
588 sptd -> DataTransferLength = 28;
591 ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
592 sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
593 win_error = ioctl_ok ? ERROR_SUCCESS : GetLastError ();
594 /* DeviceIoControl may succeed while the drive returns CHECK CONDITION. */
595 if (!ioctl_ok || sptd -> ScsiStatus != 0) {
597 if (!ignore_errors) {
598 error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
599 (unsigned long) win_error, sptd -> ScsiStatus);
600 debug ("Command was: ");
601 hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
602 debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
609 mmc -> sense -> sense_key = sense[2];
610 mmc -> sense -> asc = sense[12];
611 mmc -> sense -> ascq = sense[13];
613 dvd_record_command_diagnostic (dvd, mmc, out, (int) win_error,
614 (int) sptd -> ScsiStatus, sense[2], sense[12], sense[13]);
622 * Executes an MMC command.
623 * @param dvd The DVD drive the command should be exectued on.
624 * @param mmc The command to be executed.
625 * @param ignore_errors If set to true, no error will be printed if the command fails.
626 * @return 0 if the command was executed successfully, < 0 otherwise.
628 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
631 struct cdrom_generic_command cgc;
632 struct request_sense sense;
635 debug ("Executing MMC command: ");
636 hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
639 memset (&sense, 0, sizeof (sense));
640 memset (&cgc, 0, sizeof (cgc));
641 memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd));
642 cgc.buffer = (unsigned char *) mmc -> buffer;
643 cgc.buflen = mmc -> buflen;
644 if (mmc -> direction == DVD_DATA_OUT)
645 cgc.data_direction = CGC_DATA_WRITE;
646 else if (mmc -> direction == DVD_DATA_NONE)
647 cgc.data_direction = CGC_DATA_NONE;
649 cgc.data_direction = CGC_DATA_READ;
650 cgc.timeout = MMC_CMD_TIMEOUT * 1000;
652 if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
655 if (!ignore_errors) {
656 error ("Execution of MMC command failed: %s", strerror (saved_errno));
657 debug ("Command was:");
658 hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd));
659 debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq);
667 mmc -> sense -> sense_key = sense.sense_key;
668 mmc -> sense -> asc = sense.asc;
669 mmc -> sense -> ascq = sense.ascq;
671 dvd_record_command_diagnostic (dvd, mmc, out, saved_errno,
672 (int) cgc.stat, sense.sense_key, sense.asc, sense.ascq);
680 * Linux equivalent of the copied Windows SCSI_PASS_THROUGH_DIRECT transport
681 * used by UnlockDrive(). CDROM_SEND_PACKET does not expose an explicit CDB
682 * length and applies a single generic timeout. The GDR-8050L handshake uses
683 * 6-, 10-, and 12-byte CDBs plus 120-second command timeouts (10 seconds only
684 * for sticky descrambling). SG_IO preserves those boundaries exactly.
686 static int dvd_xbox_sgio_exact (dvd_drive *dvd,
692 dvd_data_direction direction,
693 unsigned int timeout_ms) {
696 mmc_command diagnostic;
699 int transport_result;
702 if (!dvd || !cdb || cdb_len < 1 || cdb_len > 12)
705 memset (&io, 0, sizeof (io));
706 memset (sense, 0, sizeof (sense));
707 memset (&diagnostic, 0, sizeof (diagnostic));
709 io.interface_id = 'S';
710 io.cmdp = (unsigned char *) cdb;
711 io.cmd_len = (unsigned char) cdb_len;
713 io.mx_sb_len = sizeof (sense);
714 io.timeout = timeout_ms;
716 io.dxfer_len = buf_len;
720 io.dxfer_direction = SG_DXFER_TO_DEV;
723 io.dxfer_direction = SG_DXFER_NONE;
729 io.dxfer_direction = SG_DXFER_FROM_DEV;
734 rc = ioctl (dvd -> fd, SG_IO, &io);
741 io.host_status == 0 &&
742 io.driver_status == 0) ? 0 : -1;
744 diagnostic.cmdlen = cdb_len;
745 diagnostic.direction = direction;
746 diagnostic.buffer = buf;
747 diagnostic.buflen = (int) buf_len;
748 memcpy (diagnostic.cmd, cdb, (size_t) cdb_len);
749 dvd_record_command_diagnostic (
759 xbox_ref_log_fprintf (
761 "[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=[",
762 step ? step : "unnamed",
765 (unsigned int) io.status,
766 (unsigned int) io.host_status,
767 (unsigned int) io.driver_status,
768 (unsigned int) (sense[2] & 0x0F),
769 (unsigned int) sense[12],
770 (unsigned int) sense[13],
773 for (i = 0; i < cdb_len; i++)
774 xbox_ref_log_fprintf (
778 (unsigned int) cdb[i]);
779 xbox_ref_log_fprintf (
781 "] xfer=%u direction=%s result=%s\n",
783 direction == DVD_DATA_OUT ? "out" :
784 direction == DVD_DATA_NONE ? "none" : "in",
785 transport_result == 0 ? "PASS" : "FAIL");
787 return transport_result;
790 static int dvd_xbox_exact_read_capacity (dvd_drive *dvd,
793 u_int32_t *sector_size) {
798 memset (cdb, 0, sizeof (cdb));
799 memset (buf, 0, sizeof (buf));
802 if (dvd_xbox_sgio_exact (
803 dvd, step, cdb, sizeof (cdb), buf, sizeof (buf),
804 DVD_DATA_IN, 120000) < 0)
808 ((u_int32_t) buf[0] << 24) |
809 ((u_int32_t) buf[1] << 16) |
810 ((u_int32_t) buf[2] << 8) |
814 *sectors = max_lba + 1;
817 ((u_int32_t) buf[4] << 24) |
818 ((u_int32_t) buf[5] << 16) |
819 ((u_int32_t) buf[6] << 8) |
824 static int dvd_xbox_exact_mode_sense_10 (dvd_drive *dvd,
831 if (!buf || buf_len > 0xFFFF)
833 memset (cdb, 0, sizeof (cdb));
834 memset (buf, 0, buf_len);
837 cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
838 cdb[8] = (u_int8_t) (buf_len & 0xFF);
840 return dvd_xbox_sgio_exact (
841 dvd, step, cdb, sizeof (cdb), buf, (u_int32_t) buf_len,
842 DVD_DATA_IN, 120000);
845 static int dvd_xbox_exact_mode_select_10 (dvd_drive *dvd,
852 if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFFFF)
854 memset (cdb, 0, sizeof (cdb));
855 memset (tmp, 0, sizeof (tmp));
856 memcpy (tmp, buf, buf_len);
858 cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
859 cdb[8] = (u_int8_t) (buf_len & 0xFF);
861 return dvd_xbox_sgio_exact (
862 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
863 DVD_DATA_OUT, 120000);
866 static int dvd_xbox_exact_mode_select_6 (dvd_drive *dvd,
873 if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFF)
875 memset (cdb, 0, sizeof (cdb));
876 memset (tmp, 0, sizeof (tmp));
877 memcpy (tmp, buf, buf_len);
880 cdb[4] = (u_int8_t) buf_len;
882 return dvd_xbox_sgio_exact (
883 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
884 DVD_DATA_OUT, 10000);
890 * Sends an INQUIRY command to the drive to retrieve drive identification strings.
891 * @param dvd The DVD drive the command should be exectued on.
892 * @return 0 if the command was executed successfully, < 0 otherwise.
894 static int dvd_get_drive_info (dvd_drive *dvd) {
900 dvd_init_command (&mmc, buf, sizeof (buf), NULL);
901 mmc.cmd[0] = SPC_INQUIRY;
902 mmc.cmd[4] = sizeof (buf);
903 if ((out = dvd_execute_cmd (dvd, &mmc, false)) >= 0) {
904 my_strndup (dvd -> vendor, buf + 8, 8);
905 strtrimr (dvd -> vendor);
906 my_strndup (dvd -> prod_id, buf + 16, 16);
907 strtrimr (dvd -> prod_id);
908 my_strndup (dvd -> prod_rev, buf + 32, 4);
909 strtrimr (dvd -> prod_rev);
910 snprintf (tmp, sizeof (tmp), "%s/%s/%s", dvd -> vendor, dvd -> prod_id, dvd -> prod_rev);
911 my_strdup (dvd -> model_string, tmp);
913 debug ("DVD drive is \"%s\"", dvd -> model_string);
915 error ("Cannot identify DVD drive\n");
923 * Assigns the proper memory dump functions to a dvd_drive object, according to vendor, model and other parameters. Actually this scheme probably needs to
924 * to be improved, but it is enough for the moment.
925 * @param dvd The DVD drive the command should be exectued on.
927 static void dvd_assign_functions (dvd_drive *dvd, u_int32_t command) {
928 dvd -> def_method = 0;
929 if (dvd_is_hlds_gc_wii_drive (dvd)) {
930 dvd_apply_hlds_e7_profile (dvd);
931 debug ("Hitachi-LG MN103-family 0xE7 drive detected: profile=%s tier=%s family=%s base=0x%08x windows=%u cdb=0x%03x gate=0x%08x",
932 dvd_get_hlds_e7_profile_name (dvd),
933 dvd_get_hlds_e7_support_tier (dvd),
934 dvd_get_hlds_e7_family (dvd),
935 dvd -> hlds_e7_cache_base, dvd -> hlds_e7_mem_blocks,
936 dvd -> hlds_e7_static_cdb_base, dvd -> hlds_e7_static_gate);
937 dvd -> memdump = &hitachi_dvd_dump_mem;
939 dvd -> supported = true;
940 /* DIC Type1/Type3/Type4 use READ12 + HIT 0xE7 cache extraction.
941 * GCC-4244N is validated with Sonic Mega Collection and GCC-4243N is
942 * actively under test, so both remain Method 8. Type1 drives get the
943 * DIC-derived 0x00a13000 / one-window cache profile and also default to
944 * Method 8 so GCC-4160N/GCC-4240N can be tested without forcing a method.
945 * GDR-8050L gets a proven single-window fallback plus guarded speed probes
946 * for cross-flashed/modified firmware with 0xE7 memdump added; stock
947 * GDR-8050L firmware does not expose this GC/Wii memdump path. GDR-8081N
948 * is an experimental probe target and needs Method 8 so seed probing can run.
949 * Type2 is still classified only; leave it on the older Method 9 path. */
950 if (dvd -> hlds_e7_preferred_method >= 0)
951 dvd -> def_method = (u_int32_t) dvd -> hlds_e7_preferred_method;
952 else if (dvd -> hlds_e7_type == 1 || dvd -> hlds_e7_type == 44 || dvd -> hlds_e7_type == 45 || dvd -> hlds_e7_type == 81 || dvd_is_hlds_gcc4243_4244_drive (dvd))
953 dvd -> def_method = 8;
955 dvd -> def_method = 9;
957 } else if (strcmp (dvd -> vendor, "LITE-ON") == 0 && (
958 strcmp (dvd ->prod_id, "DVDRW LH-18A1H") == 0 ||
959 strcmp (dvd ->prod_id, "DVDRW LH-18A1P") == 0 ||
960 strcmp (dvd ->prod_id, "DVDRW LH-20A1H") == 0 ||
961 strcmp (dvd ->prod_id, "DVDRW LH-20A1P") == 0
963 debug ("Lite-On DVD drive detected, using Lite-On memory dump command");
964 dvd -> memdump = &liteon_dvd_dump_mem;
966 dvd -> supported = true;
967 dvd -> def_method = 5;
969 } else if (dvd_is_tsst_kreon_candidate (dvd) || (strcmp (dvd -> vendor, "TSSTcorp") == 0 && (
970 strcmp (dvd ->prod_id, "DVD-ROM SH-D162A") == 0 ||
971 strcmp (dvd ->prod_id, "DVD-ROM SH-D162B") == 0
973 debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command");
974 dvd -> memdump = &vanilla_2384_dvd_dump_mem;
976 dvd -> supported = true;
977 dvd -> def_method = 0;
979 } else if (strcmp (dvd -> vendor, "PLEXTOR") == 0) {
980 debug ("Plextor DVD drive detected, using vanilla 2064 memory dump command");
981 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
983 dvd -> supported = true;
984 dvd -> def_method = 2;
987 /* This is an unsupported drive (yet). */
988 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
990 dvd -> supported = false;
994 dvd -> command = command;
995 if (command == 0) dvd -> memdump = &vanilla_2064_dvd_dump_mem;
996 else if (command == 1) dvd -> memdump = &vanilla_2384_dvd_dump_mem;
997 else if (command == 2) dvd -> memdump = &hitachi_dvd_dump_mem;
998 else if (command == 3) dvd -> memdump = &liteon_dvd_dump_mem;
999 else if (command == 4) dvd -> memdump = &renesas_dvd_dump_mem;
1003 //init Reed-Solomon for Lite-On
1012 * Creates a new structure representing a CD/DVD-ROM drive.
1013 * @param device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows).
1014 * @return The newly-created structure, to be used with the other commands, or NULL if the drive could not be initialized.
1016 dvd_drive *dvd_drive_new (char *device, u_int32_t command) {
1025 /* Force the dropping of privileges: in our model, privileges are only used to execute memory dump commands, the user
1026 must gain access to the device somehow else (i. e. get added to the "cdrom" group or similar things) */
1029 debug ("Trying to open DVD device %s", device);
1031 sprintf (dev, "\\\\.\\%c:", device[0]);
1032 if ((fd = CreateFile (dev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
1033 error ("Cannot open drive: %d", GetLastError ());
1035 if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) {
1036 perror ("Cannot open drive");
1040 debug ("Opened successfully");
1042 dvd = (dvd_drive *) malloc (sizeof (dvd_drive));
1044 fprintf (stderr, "malloc() failed\n");
1047 memset (dvd, 0, sizeof (dvd_drive));
1048 my_strdup (dvd -> device, device);
1050 dvd_get_drive_info (dvd);
1051 dvd_assign_functions (dvd, command);
1059 * Frees resources used by a DVD drive structure and destroys it.
1060 * @param dvd The DVD drive structure to be destroyed.
1063 void *dvd_drive_destroy (dvd_drive *dvd) {
1066 CloseHandle (dvd -> fd);
1070 my_free (dvd -> device);
1071 my_free (dvd -> vendor);
1072 my_free (dvd -> prod_id);
1073 my_free (dvd -> prod_rev);
1082 * Executes the drive-dependent function to dump the drive sector cache, and returns the dumped data.
1083 * @param dvd The DVD drive the command should be exectued on.
1084 * @param block_off The offset to start dumping, WRT the beginning of the sector cache.
1085 * @param block_len The number of blocks to dump.
1086 * @param block_size The block size to be used for dumping.
1087 * @param buf A buffer where to store the dumped data. Note that this must be able to hold at least block_len * block_size bytes.
1088 * @return 0 if the command was executed successfully, < 0 otherwise.
1090 int dvd_memdump (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) {
1093 /* Upgrade privileges and call actual dump functions */
1095 out = dvd -> memdump (dvd, block_off, block_len, block_size, buf);
1103 * Issues a READ(12) command without bothering to return the results. Uses the FUA (Force Unit Access bit) so that the requested sectors are actually read
1104 * at the beginning of the cache and can be dumped later.
1105 * @param dvd The DVD drive the command should be exectued on.
1106 * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs.
1107 * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1108 * @return 0 if the command was executed successfully, < 0 otherwise.
1110 int dvd_read_sector_dummy (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1113 u_int8_t intbuf[64 * 1024], *buf;
1116 /* We need some buffer, be it provided externally or not */
1119 bufsize = extbufsize;
1122 bufsize = sizeof (intbuf);
1125 dvd_init_command (&mmc, buf, bufsize, sense);
1126 mmc.cmd[0] = MMC_READ_12;
1127 mmc.cmd[1] = 0x08; /* FUA bit set */
1128 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1129 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1130 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1131 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1132 mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1133 mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1134 mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1135 mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF);
1136 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1143 * Issues a READ(12) command using the STREAMING bit, which causes the requested 16-sector block to be read into memory,
1144 * together with the following four. This way we will be able to dump 5 sector with a single READ request.
1146 * Note the strange need for a big buffer even though we must only pass 0x10 as the transfer length, otherwise the drive will hang (!?).
1147 * @param dvd The DVD drive the command should be exectued on.
1148 * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs, and the following 4 blocks.
1149 * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1150 * @param extbuf A buffer where to store the read data, or NULL.
1151 * @param extbufsize The size of the buffer.
1154 int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1157 u_int8_t intbuf[2048 * 16], *buf;
1160 /* We need some buffer, be it provided externally or not */
1163 bufsize = extbufsize;
1166 bufsize = sizeof (intbuf);
1169 dvd_init_command (&mmc, buf, bufsize, sense);
1170 mmc.cmd[0] = MMC_READ_12;
1171 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1172 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1173 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1174 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1179 mmc.cmd[10] = 0x80; /* STREAMING bit set */
1180 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1186 int dvd_read_streaming (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1189 u_int8_t intbuf[64 * 1024], *buf;
1192 /* We need some buffer, be it provided externally or not */
1195 bufsize = extbufsize;
1198 bufsize = sizeof (intbuf);
1201 dvd_init_command (&mmc, buf, bufsize, sense);
1202 mmc.cmd[0] = MMC_READ_12;
1203 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1204 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1205 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1206 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1207 mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1208 mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1209 mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1210 mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF);
1211 mmc.cmd[10] = 0x80; /* STREAMING bit set */
1212 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1218 int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) {
1221 u_int8_t intbuf[64], *buf;
1227 dvd_init_command (&mmc, buf, bufsize, sense);
1228 mmc.cmd[0] = MMC_READ_12;
1230 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1231 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1232 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1233 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1234 out = dvd_execute_cmd (dvd, &mmc, true);
1239 static void dvd_sleep_ms (unsigned int ms) {
1243 usleep ((useconds_t) ms * 1000);
1247 int dvd_start_stop_unit (dvd_drive *dvd, bool start, bool load_eject, req_sense *sense) {
1250 u_int8_t intbuf[64], *buf;
1256 dvd_init_command (&mmc, buf, bufsize, sense);
1258 /* START STOP UNIT byte 4: bit 1 = LoEj, bit 0 = Start. */
1259 mmc.cmd[4] = (load_eject ? 0x02 : 0x00) | (start ? 0x01 : 0x00);
1260 out = dvd_execute_cmd (dvd, &mmc, true);
1265 int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) {
1266 return dvd_start_stop_unit (dvd, start, false, sense);
1269 int dvd_set_door_lock (dvd_drive *dvd, bool locked) {
1277 if (ioctl (dvd -> fd, CDROM_LOCKDOOR, locked ? 1 : 0) < 0)
1283 int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
1284 unsigned int waited = 0;
1287 while (waited <= timeout_ms) {
1288 if (dvd_test_unit_ready (dvd, NULL) == 0)
1291 dvd_stop_unit (dvd, true, NULL); /* START UNIT, like EnsureDriveReady() */
1298 int dvd_media_cycle (dvd_drive *dvd, req_sense *sense) {
1303 /* Linux commonly applies CDO_LOCK while an optical device is open. A raw
1304 * START STOP UNIT eject then fails immediately even though the process owns
1305 * the only intentional handle. Release that kernel/drive door lock before
1306 * the GDR-8050L software tray cycle, then restore it after the tray is loaded
1307 * and ready. CDROM_LOCKDOOR is the documented Linux optical-door API. */
1308 out = dvd_set_door_lock (dvd, false);
1310 xbox_ref_log_fprintf (stderr,
1311 "[XBOX][FATAL] Linux media-cycle could not release the optical door lock: %s (errno=%d).\n",
1312 strerror (-out), -out);
1315 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle released the optical door lock before software eject.\n");
1318 /* Match the original GDR-8050L dumper's AutomateTrayCycle(): eject, wait
1319 * long enough for the tray to extend, close, poll readiness, then settle. */
1320 out = dvd_start_stop_unit (dvd, false, true, sense); /* LoEj=1, Start=0: eject */
1322 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software eject command failed.\n");
1325 dvd_sleep_ms (3000);
1327 out = dvd_start_stop_unit (dvd, true, true, sense); /* LoEj=1, Start=1: load */
1329 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software load command failed; the door remains unlocked for recovery.\n");
1333 for (i = 0; i < 90; i++) {
1335 if (dvd_test_unit_ready (dvd, NULL) == 0) {
1336 dvd_sleep_ms (1500);
1338 out = dvd_set_door_lock (dvd, true);
1340 xbox_ref_log_fprintf (stderr,
1341 "[XBOX][WARN] Linux media-cycle completed, but the optical door could not be re-locked: %s (errno=%d).\n",
1342 strerror (-out), -out);
1344 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle restored the optical door lock after load.\n");
1350 /* Original dumper falls back to a fixed 10s settle delay if TUR never
1351 * reports ready after tray close. Keep the door unlocked on failure so the
1352 * user can recover the media without power-cycling the external drive. */
1353 dvd_sleep_ms (10000);
1354 fprintf (stderr, "[XBOX][FATAL] Media-cycle tray closed, but the drive never became ready; the door remains unlocked for recovery.\n");
1358 int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1361 u_int8_t intbuf[64], *buf;
1367 dvd_init_command (&mmc, buf, bufsize, sense);
1369 mmc.cmd[2] = (u_int8_t) ((speed & 0x0000FF00) >> 8);
1370 mmc.cmd[3] = (u_int8_t) (speed & 0x000000FF);
1371 out = dvd_execute_cmd (dvd, &mmc, true);
1376 int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) {
1379 u_int8_t intbuf[64], *buf;
1385 dvd_init_command (&mmc, buf, bufsize, sense);
1390 out = dvd_execute_cmd (dvd, &mmc, true);
1392 *(size)=*(size) << 8 | intbuf[0x18];
1393 *(size)=*(size) << 8 | intbuf[0x19];
1394 *(size)=*(size) << 8 | intbuf[0x1a];
1395 *(size)=*(size) << 8 | intbuf[0x1b];
1400 int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) {
1403 u_int8_t intbuf[2052], *buf;
1409 dvd_init_command (&mmc, buf, bufsize, sense);
1413 out = dvd_execute_cmd (dvd, &mmc, true);
1415 *(layerbreak)=*(layerbreak) << 8;
1416 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x11];
1417 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x12];
1418 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x13];
1419 if (*(layerbreak) > 0) *(layerbreak)=*(layerbreak) - 0x30000 + 1;
1424 int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1427 DeviceIoControl : \Device\CdRom5
1428 Command : IOCTL_SCSI_PASS_THROUGH_DIRECT
1429 Length : 44 (0x002C)
1434 CdbLength : 12 (0x0C)
1435 SenseInfoLength : 24 (0x18)
1436 DataTransferLength : 28 (0x0000001C)
1441 00000000 B6 00 00 00 00 00 00 00 00 00 1C 00 ...........
1444 00000000 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF ............____
1445 00000010 00 00 03 E8 FF FF FF FF 00 00 03 E8 ...____...
1449 u_int8_t inbuf[28], *buf;
1455 dvd_init_command (&mmc, buf, bufsize, sense);
1468 *(buf+ 8)=0xff; //MSB
1471 *(buf+11)=0xff; //LSB
1473 *(buf+12)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1474 *(buf+13)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1475 *(buf+14)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1476 *(buf+15)=(u_int8_t) (speed & 0x000000FF);
1478 *(buf+16)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1479 *(buf+17)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1480 *(buf+18)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1481 *(buf+19)=(u_int8_t) (1000 & 0x000000FF);
1483 *(buf+20)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1484 *(buf+21)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1485 *(buf+22)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1486 *(buf+23)=(u_int8_t) (speed & 0x000000FF);
1488 *(buf+24)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1489 *(buf+25)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1490 *(buf+26)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1491 *(buf+27)=(u_int8_t) (1000 & 0x000000FF);
1493 out = dvd_execute_cmd (dvd, &mmc, true);
1499 int dvd_test_unit_ready (dvd_drive *dvd, req_sense *sense) {
1503 dvd_init_command (&mmc, intbuf, 0, sense);
1504 mmc.cmd[0] = SPC_TEST_UNIT_READY;
1506 mmc.direction = DVD_DATA_NONE;
1507 return dvd_execute_cmd (dvd, &mmc, true);
1510 int dvd_read_capacity_10 (dvd_drive *dvd, u_int32_t *sectors, u_int32_t *sector_size, req_sense *sense) {
1514 u_int32_t max_lba, block_len;
1516 dvd_init_command (&mmc, buf, sizeof (buf), sense);
1517 mmc.cmd[0] = MMC_READ_CAPACITY_10;
1519 out = dvd_execute_cmd (dvd, &mmc, false);
1521 max_lba = ((u_int32_t) buf[0] << 24) | ((u_int32_t) buf[1] << 16) | ((u_int32_t) buf[2] << 8) | buf[3];
1522 block_len = ((u_int32_t) buf[4] << 24) | ((u_int32_t) buf[5] << 16) | ((u_int32_t) buf[6] << 8) | buf[7];
1524 *sectors = max_lba + 1;
1526 *sector_size = block_len;
1532 int dvd_read_10 (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1534 u_int8_t intbuf[64 * 1024], *buf;
1535 size_t need, bufsize;
1537 need = (size_t) sectors * 2048;
1540 bufsize = extbufsize;
1543 bufsize = sizeof (intbuf);
1546 if (need > bufsize) {
1547 error ("dvd_read_10 buffer too small (%u sectors need %lu bytes)", sectors, (unsigned long) need);
1551 dvd_init_command (&mmc, buf, (int) need, sense);
1552 mmc.cmd[0] = MMC_READ_10;
1554 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);
1555 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1556 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1557 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1558 mmc.cmd[7] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1559 mmc.cmd[8] = (u_int8_t) (sectors & 0x000000FF);
1561 return dvd_execute_cmd (dvd, &mmc, true);
1564 int dvd_mode_sense_10 (dvd_drive *dvd, u_int8_t page, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1567 if (!extbuf || extbufsize > 0xFFFF)
1570 dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1571 mmc.cmd[0] = SPC_MODE_SENSE_10;
1573 mmc.cmd[7] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1574 mmc.cmd[8] = (u_int8_t) (extbufsize & 0x00FF);
1577 return dvd_execute_cmd (dvd, &mmc, false);
1580 int dvd_mode_select_10 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1584 if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFFFF)
1586 memset (tmp, 0, sizeof (tmp));
1587 memcpy (tmp, buf, bufsize);
1589 dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1590 mmc.direction = DVD_DATA_OUT;
1591 mmc.cmd[0] = SPC_MODE_SELECT_10;
1592 mmc.cmd[7] = (u_int8_t) ((bufsize & 0xFF00) >> 8);
1593 mmc.cmd[8] = (u_int8_t) (bufsize & 0x00FF);
1596 return dvd_execute_cmd (dvd, &mmc, false);
1599 int dvd_mode_select_6 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1603 if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFF)
1605 memset (tmp, 0, sizeof (tmp));
1606 memcpy (tmp, buf, bufsize);
1608 dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1609 mmc.direction = DVD_DATA_OUT;
1610 mmc.cmd[0] = SPC_MODE_SELECT_6;
1612 mmc.cmd[4] = (u_int8_t) (bufsize & 0xFF);
1615 return dvd_execute_cmd (dvd, &mmc, false);
1618 int dvd_read_dvd_structure (dvd_drive *dvd, u_int8_t format, u_int8_t layer, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1621 if (!extbuf || extbufsize > 0xFFFF)
1624 dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1625 mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1626 /* MMC READ DVD STRUCTURE places Format in CDB byte 7. Byte 11 is
1627 * Control and must remain zero. The copied Windows GetMediaID() path
1628 * uses the same byte-7 boundary for DMI format 0x04. */
1630 mmc.cmd[7] = format;
1631 mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1632 mmc.cmd[9] = (u_int8_t) (extbufsize & 0x00FF);
1635 return dvd_execute_cmd (dvd, &mmc, false);
1639 static int dvd_xbox_vendor_command (dvd_drive *dvd, u_int8_t subcommand, u_int8_t value, u_int8_t *buf, size_t bufsize, dvd_data_direction direction) {
1642 dvd_init_command (&mmc, buf, (int) bufsize, NULL);
1646 mmc.cmd[3] = subcommand;
1649 mmc.direction = direction;
1650 if (direction == DVD_DATA_NONE) {
1654 return dvd_execute_cmd (dvd, &mmc, false);
1657 static bool dvd_xbox_feature_list_has (const u_int16_t *features, size_t count, u_int16_t needle) {
1661 for (i = 0; i < count && features[i] != 0; i++) {
1662 if (features[i] == needle)
1668 int dvd_xbox_vendor_get_feature_list (dvd_drive *dvd, u_int16_t *features, size_t max_features) {
1672 if (!dvd || !features || max_features == 0)
1674 memset (features, 0, max_features * sizeof (features[0]));
1675 memset (buf, 0, sizeof (buf));
1676 if (dvd_xbox_vendor_command (dvd, 0x10, 0, buf, sizeof (buf), DVD_DATA_IN) < 0)
1678 if ((((u_int16_t) buf[0] << 8) | buf[1]) != 0xA55A ||
1679 (((u_int16_t) buf[2] << 8) | buf[3]) != 0x5AA5) {
1680 error ("Xbox vendor feature-list signature is invalid");
1683 count = sizeof (buf) / 2;
1684 if (count > max_features)
1685 count = max_features;
1686 for (i = 0; i < count; i++)
1687 features[i] = ((u_int16_t) buf[i * 2] << 8) | buf[i * 2 + 1];
1691 int dvd_xbox_vendor_lock (dvd_drive *dvd) {
1692 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1694 return dvd_xbox_vendor_command (dvd, 0x11, 0x00, NULL, 0, DVD_DATA_NONE);
1697 int dvd_xbox_vendor_set_error_skip (dvd_drive *dvd, bool enabled) {
1698 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1700 return dvd_xbox_vendor_command (dvd, 0x15, enabled ? 0x01 : 0x00, NULL, 0, DVD_DATA_NONE);
1703 int dvd_xbox_vendor_unlock_wxripper (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1704 u_int16_t features[13];
1705 u_int32_t sectors = 0, sector_size = 0;
1707 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1710 if (dvd_xbox_vendor_get_feature_list (dvd, features, sizeof (features) / sizeof (features[0])) < 0)
1712 if (!dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0201) &&
1713 !dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0221))
1714 warning ("Xbox vendor feature list did not advertise Xbox unlock state 2/full challenge support; trying wxripper state anyway");
1716 if (dvd_xbox_vendor_command (dvd, 0x11, 0x02, NULL, 0, DVD_DATA_NONE) < 0)
1719 /* DiscImageCreator disables error-skip before dumping; keep that behavior so
1720 * real read errors are visible to FriiDump unless a future option says otherwise. */
1721 dvd_xbox_vendor_set_error_skip (dvd, false);
1723 if (dvd_read_capacity_10 (dvd, §ors, §or_size, NULL) < 0)
1725 if (sector_size != 2048 || sectors < 1000000) {
1726 error ("Xbox vendor unlock did not expose the expected 2048-byte view");
1729 if (unlocked_sectors)
1730 *unlocked_sectors = sectors;
1735 #define XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
1737 static int dvd_xbox_refresh_ready_capacity (dvd_drive *dvd,
1739 u_int32_t *sector_size) {
1740 u_int32_t observed_sectors = 0;
1741 u_int32_t observed_sector_size = 0;
1746 /* Exact portable equivalent of xbox_ref_refresh_ready_capacity():
1747 * RefreshVolume(); Sleep(2000); EnsureDriveReady(30000); GetTotalSectors(). */
1748 dvd_refresh_volume (dvd);
1749 dvd_sleep_ms (2000);
1750 if (dvd_wait_ready (dvd, 30000) < 0)
1752 if (dvd_read_capacity_10 (dvd, &observed_sectors, &observed_sector_size, NULL) < 0)
1756 *sectors = observed_sectors;
1758 *sector_size = observed_sector_size;
1762 int dvd_xbox_prepare_game_view (dvd_drive *dvd,
1764 u_int32_t *sector_size) {
1765 u_int32_t entry_sectors = 0;
1766 u_int32_t observed_sectors = 0;
1767 u_int32_t observed_sector_size = 0;
1769 if (!dvd || !dvd_is_xbox_challenge_drive (dvd))
1772 /* Port xbox_ref_gdr8050l_dump_core() state preparation exactly, replacing
1773 * Win32 handle/volume calls with FriiDump's portable Linux equivalents.
1774 * No RecoveryKick, media-auth kick, or synthetic LBA-zero read cadence is
1775 * part of this stock/cross-flashed GDR-8050L sequence. */
1776 if (dvd_wait_ready (dvd, 30000) < 0) {
1777 xbox_ref_log_fprintf (stderr,
1778 "[XBOX-WINSEQ][FATAL] Drive did not become ready before Xbox state preparation.\n");
1781 if (dvd_read_capacity_10 (dvd, &entry_sectors, &observed_sector_size, NULL) < 0) {
1782 xbox_ref_log_fprintf (stderr,
1783 "[XBOX-WINSEQ][FATAL] Entry READ CAPACITY failed.\n");
1786 xbox_ref_log_fprintf (stderr,
1787 "[XBOX-WINSEQ] Entry READ CAPACITY: %u sectors.\n",
1790 if (entry_sectors > XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1791 xbox_ref_log_fprintf (stderr,
1792 "[XBOX-WINSEQ] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
1793 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1796 xbox_ref_log_fprintf (stderr,
1797 "[XBOX-WINSEQ] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
1798 if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1799 xbox_ref_log_fprintf (stderr,
1800 "[XBOX-WINSEQ][WARN] Direct UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1801 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1803 xbox_ref_log_fprintf (stderr,
1804 "[XBOX-WINSEQ] Direct-handshake READ CAPACITY: %u sectors.\n",
1807 if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1808 xbox_ref_log_fprintf (stderr,
1809 "[XBOX-WINSEQ][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
1810 if (dvd_media_cycle (dvd, NULL) < 0)
1812 xbox_ref_log_fprintf (stderr,
1813 "[XBOX-WINSEQ] Re-applying the full handshake after recovery media change.\n");
1814 if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1815 xbox_ref_log_fprintf (stderr,
1816 "[XBOX-WINSEQ][WARN] Recovery UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1817 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1819 xbox_ref_log_fprintf (stderr,
1820 "[XBOX-WINSEQ] Recovery-handshake READ CAPACITY: %u sectors.\n",
1823 if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1824 xbox_ref_log_fprintf (stderr,
1825 "[XBOX-WINSEQ][FATAL] Xbox game view was not established after direct and recovery handshakes.\n");
1831 /* Exact next Windows step after state preparation. */
1832 dvd_set_speed (dvd, 0xFFFF, NULL);
1835 *sectors = observed_sectors;
1837 *sector_size = observed_sector_size;
1841 int dvd_refresh_volume (dvd_drive *dvd) {
1843 DWORD bytesReturned = 0;
1846 /* Match RefreshVolume() from the reference dumper: update properties only;
1847 * do not dismount here because that can reset drive state. This is now
1848 * shared by Xbox and HLDS 0xE7 GC/Wii paths so Windows is less likely to
1849 * keep stale filesystem/probe state attached to odd discs. */
1850 DeviceIoControl (dvd -> fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL);
1851 dvd_sleep_ms (1000);
1859 int dvd_lock_volume (dvd_drive *dvd) {
1861 DWORD bytesReturned = 0;
1863 return DVD_VOLUME_LOCK_FAILED;
1864 return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)
1865 ? DVD_VOLUME_LOCK_OK
1866 : DVD_VOLUME_LOCK_FAILED;
1868 /* Linux CDROM_SEND_PACKET has no FSCTL_LOCK_VOLUME equivalent here.
1869 * Return a distinct result instead of falsely reporting an exclusive lock.
1870 * The caller may continue after warning about automount/media polling. */
1872 return DVD_VOLUME_LOCK_UNAVAILABLE;
1876 int dvd_xbox_refresh_volume (dvd_drive *dvd) {
1877 return dvd_refresh_volume (dvd);
1880 int dvd_xbox_lock_volume (dvd_drive *dvd) {
1881 return dvd_lock_volume (dvd);
1884 static int dvd_xbox_read_host_challenge_table (dvd_drive *dvd, u_int8_t *table, size_t table_len) {
1888 if (!table || table_len < 0x664)
1892 /* GDR-8050L / Xbox READ DVD STRUCTURE format C0. */
1893 dvd_init_command (&mmc, table, 0x664, NULL);
1894 mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1904 out = dvd_execute_cmd (dvd, &mmc, true);
1906 /* Some Hitachi-family drives expose the same table via vendor command 0xFD. */
1907 if (out < 0 || table[772] != 1 || table[773] == 0) {
1908 dvd_init_command (&mmc, table, 0x664, NULL);
1914 out = dvd_execute_cmd (dvd, &mmc, true);
1920 memset (cdb, 0, sizeof (cdb));
1921 memset (table, 0, table_len);
1931 out = dvd_xbox_sgio_exact (
1933 "3-read-dvd-structure-c0",
1941 if (out < 0 || table[772] != 1 || table[773] == 0) {
1942 memset (cdb, 0, sizeof (cdb));
1943 memset (table, 0, table_len);
1948 out = dvd_xbox_sgio_exact (
1950 "3-read-host-table-fallback-fd",
1961 if (out < 0 || table[772] != 1)
1964 xbox_ref_log_fprintf (
1966 "[XBOX-SGIO] challenge-table marker=%u entries-byte=%u result=PASS\n",
1967 (unsigned int) table[772],
1968 (unsigned int) table[773]);
1972 int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1976 u_int8_t table[0x664];
1977 u_int8_t restable[261];
1978 u_int8_t hash[0x2C];
1981 u_int8_t sticky[12];
1982 u_int32_t sectors = 0, sector_size = 0;
1985 if (!dvd || !dvd_is_xbox_unlock_drive (dvd))
1988 /* Step 1/2: read current capacity and the Xbox mode page. If the drive is
1989 * already unlocked, this is harmless; the final capacity check below becomes
1992 dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
1993 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
1995 dvd_xbox_exact_read_capacity (
1996 dvd, "1-initial-read-capacity", §ors, §or_size);
1997 dvd_xbox_exact_mode_sense_10 (
1998 dvd, "2-mode-sense-3e", 0x3E, page, sizeof (page));
2001 /* Step 3: retrieve and decode the host challenge table. */
2002 if (dvd_xbox_read_host_challenge_table (dvd, table, sizeof (table)) < 0) {
2003 error ("Cannot retrieve Xbox host challenge table");
2007 for (i = 0; i < 0x2C; i++)
2008 hash[i] = table[0x4A3 + i];
2009 Sha1Calculate (hash, 0x2C, &digest);
2011 for (i = 0; i <= 260; i++)
2012 restable[i] = table[774 + i];
2013 xbox_rc4_init (&rc4, digest.bytes, 7);
2014 xbox_rc4_crypt (&rc4, restable, restable, 0xFD);
2017 for (l = 0; l <= 23; l++) {
2018 if (restable[l * 11] == 1) {
2020 if (k == (int) (sizeof (chalpos) / sizeof (chalpos[0])))
2025 error ("Xbox challenge table does not contain enough usable entries");
2029 /* Step 4: first host challenge. */
2030 memset (page, 0, sizeof (page));
2037 memcpy (&page[15], &restable[1 + chalpos[k - 2] * 11], 5);
2038 /* Match the original dumper: send the challenge and continue even if
2039 * Windows reports a transport failure. The later XDVDFS probe is the
2040 * authority for whether the drive actually entered the game view. */
2042 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2043 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2045 dvd_xbox_exact_mode_select_10 (
2046 dvd, "4-mode-select-challenge-1", page, sizeof (page));
2047 dvd_xbox_exact_mode_sense_10 (
2048 dvd, "5-mode-sense-verify-1", 0x3E, page, sizeof (page));
2051 /* Step 6: second host challenge. */
2052 memset (page, 0, sizeof (page));
2057 memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2059 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2060 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2062 dvd_xbox_exact_mode_select_10 (
2063 dvd, "6-mode-select-challenge-2", page, sizeof (page));
2064 dvd_xbox_exact_mode_sense_10 (
2065 dvd, "7-mode-sense-verify-2", 0x3E, page, sizeof (page));
2068 /* Step 8: unlock partition 1. */
2069 memset (page, 0, sizeof (page));
2078 memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2080 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2082 dvd_xbox_exact_mode_select_10 (
2083 dvd, "8-mode-select-partition-1-unlock", page, sizeof (page));
2086 /* Step 9: sticky descrambling, mode page 0x31. */
2087 memset (sticky, 0, sizeof (sticky));
2092 dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
2094 dvd_xbox_exact_mode_select_6 (
2095 dvd, "9-mode-select-sticky-descrambling", sticky, sizeof (sticky));
2098 /* Step 10: final capacity observation. The original UnlockDrive() only
2099 * prints this verification and does not fail if the capacity has not changed
2100 * yet. This matters for the first GDR-8050L handshake, whose purpose is to
2101 * prime the drive before the required media-change event. */
2105 out = dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
2107 out = dvd_xbox_exact_read_capacity (
2108 dvd, "10-final-read-capacity", §ors, §or_size);
2111 if (unlocked_sectors)
2112 *unlocked_sectors = sectors;
2113 xbox_ref_log_fprintf (
2115 "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n",
2119 xbox_ref_log_fprintf (
2121 "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
2127 bool dvd_is_xbox_challenge_drive (dvd_drive *dvd) {
2128 return dvd_is_hlds_drive (dvd) && dvd_prod_has (dvd, "GDR8050L");
2131 bool dvd_is_xbox_vendor_unlock_drive (dvd_drive *dvd) {
2132 return (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L"))) ||
2133 dvd_is_tsst_kreon_candidate (dvd);
2136 bool dvd_is_xbox_unlock_drive (dvd_drive *dvd) {
2137 return dvd_is_xbox_challenge_drive (dvd) || dvd_is_xbox_vendor_unlock_drive (dvd);
2140 bool dvd_is_xbox_drive (dvd_drive *dvd) {
2141 /* Autodetect only the two native Xbox profiles currently wired into the
2142 * Xbox dump planner. Other candidate drives keep FriiDump's normal GC/Wii/DVD
2143 * behavior unless the user explicitly forces Xbox mode with -T 4. */
2144 return dvd_is_xbox_challenge_drive (dvd) ||
2145 (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L")));
2150 const char *dvd_get_hlds_e7_profile_name (dvd_drive *dvd) {
2151 if (!dvd) return "none";
2152 if (dvd -> hlds_e7_profile_label) return dvd -> hlds_e7_profile_label;
2153 return dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2156 const char *dvd_get_hlds_e7_support_tier (dvd_drive *dvd) {
2157 if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2158 return dvd -> hlds_e7_support_tier ? dvd -> hlds_e7_support_tier : "legacy_detected";
2161 const char *dvd_get_hlds_e7_family (dvd_drive *dvd) {
2162 if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2163 return dvd -> hlds_e7_family ? dvd -> hlds_e7_family : dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2166 const char *dvd_get_hlds_e7_tokens (dvd_drive *dvd) {
2167 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2168 return dvd -> hlds_e7_tokens ? dvd -> hlds_e7_tokens : "";
2171 const char *dvd_get_hlds_e7_record_id (dvd_drive *dvd) {
2172 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2173 return dvd -> hlds_e7_record_id ? dvd -> hlds_e7_record_id : "";
2176 const char *dvd_get_hlds_e7_notes (dvd_drive *dvd) {
2177 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2178 return dvd -> hlds_e7_notes ? dvd -> hlds_e7_notes : "";
2181 u_int32_t dvd_get_hlds_e7_static_cdb_base (dvd_drive *dvd) {
2182 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_cdb_base : 0;
2185 u_int32_t dvd_get_hlds_e7_static_gate (dvd_drive *dvd) {
2186 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_gate : 0;
2189 int dvd_get_hlds_e7_preferred_method (dvd_drive *dvd) {
2190 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_preferred_method : -1;
2193 u_int32_t dvd_get_hlds_e7_type (dvd_drive *dvd) {
2194 return dvd ? dvd -> hlds_e7_type : 0;
2197 u_int32_t dvd_get_hlds_e7_cache_base (dvd_drive *dvd) {
2198 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_cache_base : 0;
2201 u_int32_t dvd_get_hlds_e7_mem_blocks (dvd_drive *dvd) {
2202 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_mem_blocks : 0;
2205 char *dvd_get_vendor (dvd_drive *dvd) {
2206 return (dvd -> vendor);
2210 char *dvd_get_product_id (dvd_drive *dvd) {
2211 return (dvd -> prod_id);
2215 char *dvd_get_product_revision (dvd_drive *dvd) {
2216 return (dvd -> prod_rev);
2220 char *dvd_get_model_string (dvd_drive *dvd) {
2221 return (dvd -> model_string);
2225 char *dvd_get_device (dvd_drive *dvd) {
2226 return (dvd -> device);
2229 void *dvd_get_native_handle (dvd_drive *dvd) {
2230 if (!dvd) return NULL;
2232 return (void *) dvd -> fd;
2239 bool dvd_get_support_status (dvd_drive *dvd) {
2240 return (dvd -> supported);
2243 u_int32_t dvd_get_def_method (dvd_drive *dvd){
2244 return (dvd -> def_method);
2247 u_int32_t dvd_get_command (dvd_drive *dvd){
2248 return (dvd -> command);