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 hlds_product_has (const char *product_id, const char *needle) {
204 return product_id && needle && strstr (product_id, needle) != NULL;
207 static bool hlds_product_has_any (
208 const char *product_id,
209 const char **needles,
213 for (i = 0; i < count; i++) {
214 if (hlds_product_has (product_id, needles[i]))
220 static bool dvd_is_hlds_gcc4243_4244_drive (dvd_drive *dvd) {
221 static const char *names[] = {
222 "GCC-4243N", "GCC4243N", "GCC4243",
223 "GCC-4244N", "GCC4244N", "GCC4244"
225 return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
228 static bool dvd_is_hlds_gdr8050l_drive (dvd_drive *dvd) {
229 static const char *names[] = {
230 "GDR8050L", "GDR-8050L"
232 return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
238 const char *firmware;
240 u_int32_t cache_base;
241 u_int32_t mem_blocks;
242 int preferred_method;
244 const char *support_tier;
247 const char *record_id;
248 u_int32_t static_cdb_base;
249 u_int32_t static_gate;
251 } hlds_e7_profile_desc;
253 static const hlds_e7_profile_desc hlds_e7_profiles[] = {
254 { "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" },
255 { "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" },
256 { "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" },
257 { "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" },
258 { "GCC4243", "A102", 3, 0x80000000U, 5, 8, "GCC-4243N A102 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0439", 0x880U, 0x9003731dU, "Live INQUIRY alias for GCC4243/A102; exact profile selection, 20-block seed retrieval, full GameCube dump, STOP UNIT, and exact Redump match validated with Sonic Mega Collection (US)" },
259 { "GCC-4243N", "A102", 3, 0x80000000U, 5, 8, "GCC-4243N A102 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0439", 0x880U, 0x9003731dU, "Canonical model alias for GCC-4243N A102; exact profile selection, 20-block seed retrieval, full GameCube dump, STOP UNIT, and exact Redump match validated with Sonic Mega Collection (US)" },
260 { "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" },
261 { "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" },
262 { "GCC4244", "B101", 3, 0x80000000U, 5, 8, "GCC-4244N B101 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0426", 0x894U, 0x900386d2U, "Live INQUIRY alias for GCC4244/B101; exact profile selection, 20-block GameCube seed retrieval, and STOP UNIT validated; the earlier legacy Type3 fallback full dump matched Redump" },
263 { "GCC-4244N", "B101", 3, 0x80000000U, 5, 8, "GCC-4244N B101 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0426", 0x894U, 0x900386d2U, "Canonical model alias for GCC-4244N B101; exact profile selection, 20-block GameCube seed retrieval, and STOP UNIT validated; the earlier legacy Type3 fallback full dump matched Redump" },
264 { "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" },
265 { "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" },
266 { "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" },
267 { "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" },
268 { "GDR-8050L", "0012", 44, 0x80000000U, 1, 8, "GDR-8050L 0012 hybrid cross-flash E7 parser profile", "hybrid_crossflash_modified_firmware_only", "GDR_8050x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "hybrid_identity_v3:gdr8050l_0012", 0x5d0U, 0x90026160U, "Operating identity is GDR-8050L 0012 on GDR-8163B physical hardware; package compatibility identity is GDR-8163B 0L23; GC/Wii 0xE7 access requires modified firmware; native GDR-8050L hardware is not owned" },
269 { "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" },
270 { "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" },
271 { "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" },
272 { "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" },
273 { "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" },
274 { "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" },
275 { "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" },
276 { "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" },
277 { "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" },
278 { "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" },
279 { "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" },
280 { NULL, NULL, 0, 0, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }
283 static void hlds_normalize_model (const char *src, char *dst, size_t dst_size) {
285 if (!dst || dst_size == 0)
290 for (i = 0, j = 0; src[i] && j + 1 < dst_size; i++) {
291 unsigned char c = (unsigned char) src[i];
293 dst[j++] = (char) toupper (c);
298 static bool hlds_model_matches_value (const char *product_id, const char *model) {
301 if (!product_id || !model)
303 hlds_normalize_model (product_id, prod_norm, sizeof (prod_norm));
304 hlds_normalize_model (model, model_norm, sizeof (model_norm));
305 return prod_norm[0] && model_norm[0] && strstr (prod_norm, model_norm) != NULL;
308 static bool hlds_revision_matches_value (const char *actual, const char *expected) {
309 const unsigned char *a;
310 const unsigned char *b;
311 if (!expected || !expected[0])
315 a = (const unsigned char *) actual;
316 b = (const unsigned char *) expected;
318 if (toupper (*a) != toupper (*b))
323 return *a == 0 && *b == 0;
326 static const hlds_e7_profile_desc *hlds_find_e7_profile_for_identity (
328 const char *product_id,
331 const hlds_e7_profile_desc *p;
332 if (!vendor || strcmp (vendor, "HL-DT-ST") != 0)
334 for (p = hlds_e7_profiles; p -> model; p++) {
336 hlds_model_matches_value (product_id, p -> model) &&
337 hlds_revision_matches_value (revision, p -> firmware)
344 static const hlds_e7_profile_desc *dvd_find_hlds_e7_profile (dvd_drive *dvd) {
347 return hlds_find_e7_profile_for_identity (
354 bool dvd_lookup_hlds_e7_profile (
356 const char *product_id,
357 const char *revision,
358 dvd_hlds_e7_profile_info *out
360 const hlds_e7_profile_desc *p;
362 memset (out, 0, sizeof (*out));
363 p = hlds_find_e7_profile_for_identity (vendor, product_id, revision);
367 out -> type = p -> type;
368 out -> cache_base = p -> cache_base;
369 out -> mem_blocks = p -> mem_blocks;
370 out -> preferred_method = p -> preferred_method;
371 out -> label = p -> label;
372 out -> support_tier = p -> support_tier;
373 out -> family = p -> family;
374 out -> tokens = p -> tokens;
375 out -> record_id = p -> record_id;
376 out -> static_cdb_base = p -> static_cdb_base;
377 out -> static_gate = p -> static_gate;
378 out -> notes = p -> notes;
383 u_int32_t dvd_detect_hlds_e7_type_for_identity (
385 const char *product_id,
388 const hlds_e7_profile_desc *profile =
389 hlds_find_e7_profile_for_identity (vendor, product_id, revision);
390 static const char *type1[] = {
391 "GCC-4160N", "GCC4160N", "GCC4160",
392 "GCC-4240N", "GCC4240N", "GCC4240"
394 static const char *type2_1[] = {
395 "GCC-4241N", "GCC4241N", "GCC4241"
397 static const char *type2_2[] = {
398 "GCC-4242N", "GCC4242N", "GCC4242"
400 static const char *gdr8081n[] = {
401 "GDR8081N", "GDR-8081N"
403 static const char *type3[] = {
404 "GCC4244", "GCC4244N", "GCC-4244N",
405 "GCC4247", "GCC4247N", "GCC-4247N",
406 "GDR8083N", "GDR8084N",
407 "GCC-4243N", "GCC4243N", "GCC4243",
408 "GCC-4246N", "GCC4246N", "GCC4246"
410 static const char *type4[] = {
411 "DU10N", "GDR8082N", "GDR8161B", "GDR8162B",
412 "GDR8163B", "GDR8164B", "GDR-T10N",
413 /* Local project keeps GDR-3120L in the same transport family for
414 * classification, but Xbox dumping is still routed through the explicit
415 * Xbox paths rather than the GC/Wii Method 8/9 readers. */
416 "GDR3120L", "GDR-3120L"
419 if (!vendor || strcmp (vendor, "HL-DT-ST") != 0)
422 return profile -> type;
423 if (hlds_product_has_any (product_id, type1, sizeof (type1) / sizeof (type1[0])))
425 if (hlds_product_has_any (product_id, type2_1, sizeof (type2_1) / sizeof (type2_1[0])))
427 if (hlds_product_has_any (product_id, type2_2, sizeof (type2_2) / sizeof (type2_2[0])))
429 if (hlds_product_has_any (product_id, gdr8081n, sizeof (gdr8081n) / sizeof (gdr8081n[0])))
431 if (hlds_product_has_any (product_id, type3, sizeof (type3) / sizeof (type3[0])))
433 if (hlds_product_has (product_id, "GDR8050L") || hlds_product_has (product_id, "GDR-8050L"))
435 if (hlds_product_has_any (product_id, type4, sizeof (type4) / sizeof (type4[0])))
437 /* DIC labels GSA-4163B as an Xbox swap candidate, not a normal 0xE7
438 * Nintendo-disc cache dump profile, so keep it out of the profile layer. */
442 static u_int32_t dvd_hlds_e7_detect_type (dvd_drive *dvd) {
445 return dvd_detect_hlds_e7_type_for_identity (
452 static bool dvd_is_hlds_gc_wii_drive (dvd_drive *dvd) {
453 return dvd_hlds_e7_detect_type (dvd) != 0;
456 static const char *dvd_hlds_e7_profile_name_from_type (u_int32_t type) {
458 case 1: return "Type1";
459 case 21: return "Type2_1 experimental";
460 case 22: return "Type2_2 experimental";
461 case 3: return "Type3";
462 case 4: return "Type4";
463 case 44: return "GDR-8050L modified 0xE7 single-window proven fallback";
464 case 45: return "GDR-8050L modified 0xE7 speed-probe pending";
465 case 442: return "GDR-8050L modified 0xE7 probe B 2-window";
466 case 443: return "GDR-8050L modified 0xE7 probe A 3-window";
467 case 445: return "GDR-8050L modified 0xE7 probe C 5-window guarded";
468 case 81: return "GDR-8081N experimental 0xE7 probe";
469 case 811: return "GDR-8081N probe A Type4-derived";
470 case 812: return "GDR-8081N probe B single-window";
471 case 813: return "GDR-8081N probe C Type1-base";
472 case 814: return "GDR-8081N probe E exact-offset moving-cache candidate";
473 case 815: return "GDR-8081N probe A scan-guided Type4-derived";
474 default: return "none";
478 static void dvd_apply_hlds_e7_profile (dvd_drive *dvd) {
479 const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
480 dvd -> hlds_e7_type = dvd_hlds_e7_detect_type (dvd);
481 dvd -> hlds_e7_cache_base = 0x80000000U;
482 dvd -> hlds_e7_mem_blocks = 5;
483 dvd -> hlds_e7_static_cdb_base = 0;
484 dvd -> hlds_e7_static_gate = 0;
485 dvd -> hlds_e7_preferred_method = -1;
486 dvd -> hlds_e7_profile_label = NULL;
487 dvd -> hlds_e7_support_tier = NULL;
488 dvd -> hlds_e7_family = NULL;
489 dvd -> hlds_e7_tokens = NULL;
490 dvd -> hlds_e7_record_id = NULL;
491 dvd -> hlds_e7_notes = NULL;
493 switch (dvd -> hlds_e7_type) {
495 /* DIC Type1: GCC-4160N/GCC-4240N cache frames begin at 0x00a13000
496 * and only one 16-sector cache window is consumed per READ. */
497 dvd -> hlds_e7_cache_base = 0x00a13000U;
498 dvd -> hlds_e7_mem_blocks = 1;
502 /* DIC Type2 uses a moving 0x80000000-derived cache address. This
503 * branch logs/classifies it, but does not yet claim DIC parity. */
504 dvd -> hlds_e7_cache_base = 0x80000000U;
505 dvd -> hlds_e7_mem_blocks = 1;
508 /* GDR-8081N is not in the confirmed DIC dump list, but local firmware
509 * analysis suggests an 0xE7 command surface. Start with a Type4-derived
510 * candidate; disc.c probes and may switch to one of the 811..814 runtime
511 * profiles before seed cracking continues. */
512 dvd -> hlds_e7_cache_base = 0x80000000U;
513 dvd -> hlds_e7_mem_blocks = 5;
516 /* Stock GDR-8050L firmware does not expose the HIT 0xE7 memdump command.
517 * The local test unit is GDR-8163B hardware cross-flashed with modified
518 * GDR-8050L firmware where 0xE7 memdump was added. Single-window has
519 * completed and hash-matched Sonic, so it remains the proven fallback.
520 * Start in an explicit speed-probe-pending profile so the initial drive
521 * information does not look like the old static single-window build.
522 * disc.c promotes to 2/3/5 windows only after guarded validation, or
523 * settles back to the proven single-window profile. */
524 dvd -> hlds_e7_type = 45;
525 dvd -> hlds_e7_cache_base = 0x80000000U;
526 dvd -> hlds_e7_mem_blocks = 1;
531 dvd -> hlds_e7_cache_base = 0x80000000U;
532 dvd -> hlds_e7_mem_blocks = 5;
537 dvd -> hlds_e7_cache_base = profile -> cache_base;
538 dvd -> hlds_e7_mem_blocks = profile -> mem_blocks;
539 dvd -> hlds_e7_static_cdb_base = profile -> static_cdb_base;
540 dvd -> hlds_e7_static_gate = profile -> static_gate;
541 dvd -> hlds_e7_preferred_method = profile -> preferred_method;
542 dvd -> hlds_e7_profile_label = profile -> label;
543 dvd -> hlds_e7_support_tier = profile -> support_tier;
544 dvd -> hlds_e7_family = profile -> family;
545 dvd -> hlds_e7_tokens = profile -> tokens;
546 dvd -> hlds_e7_record_id = profile -> record_id;
547 dvd -> hlds_e7_notes = profile -> notes;
548 /* The modified GDR-8050L runtime profile still starts in the guarded
549 * speed-probe-pending state, but keeps its static parser evidence fields. */
550 if (profile -> type == 44) {
551 dvd -> hlds_e7_type = 45;
552 dvd -> hlds_e7_mem_blocks = 1;
557 void dvd_set_hlds_e7_runtime_profile (dvd_drive *dvd, u_int32_t type, u_int32_t cache_base, u_int32_t mem_blocks) {
560 dvd -> hlds_e7_type = type;
561 dvd -> hlds_e7_cache_base = cache_base;
562 dvd -> hlds_e7_mem_blocks = mem_blocks;
565 static bool dvd_is_tsst_kreon_candidate (dvd_drive *dvd) {
566 if (!(dvd_vendor_is (dvd, "TSSTcorp") || dvd_vendor_is (dvd, "SAMSUNG")))
569 dvd_prod_has (dvd, "TS-H352C") ||
570 dvd_prod_has (dvd, "TS-H353A") ||
571 dvd_prod_has (dvd, "SH-D162C") ||
572 dvd_prod_has (dvd, "SH-D162D") ||
573 dvd_prod_has (dvd, "SH-D163A") ||
574 dvd_prod_has (dvd, "SH-D163B");
579 * Initializes a structure representing an MMC command.
580 * @param mmc A pointer to the MMC command structure.
581 * @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.
582 * @param len The length of the buffer (ignored in case buf is NULL).
583 * @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.
585 void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) {
586 memset (mmc, 0, sizeof (mmc_command));
588 memset (buf, 0, len);
590 mmc -> direction = buf && len > 0 ? DVD_DATA_IN : DVD_DATA_NONE;
592 mmc -> buflen = buf ? len : 0;
593 mmc -> sense = sense;
599 static void dvd_record_command_diagnostic (
602 int transport_result,
611 memset (&dvd -> last_command, 0, sizeof (dvd -> last_command));
612 dvd -> last_command.valid = true;
613 dvd -> last_command.transport_result = transport_result;
614 dvd -> last_command.os_error = os_error;
615 dvd -> last_command.scsi_status = scsi_status;
616 dvd -> last_command.sense_key = sense_key & 0x0f;
617 dvd -> last_command.asc = asc & 0xff;
618 dvd -> last_command.ascq = ascq & 0xff;
619 dvd -> last_command.cdb_length = mmc -> cmdlen;
620 if (dvd -> last_command.cdb_length < 0)
621 dvd -> last_command.cdb_length = 0;
622 if (dvd -> last_command.cdb_length > (int) sizeof (dvd -> last_command.cdb))
623 dvd -> last_command.cdb_length = (int) sizeof (dvd -> last_command.cdb);
624 memcpy (dvd -> last_command.cdb, mmc -> cmd, sizeof (dvd -> last_command.cdb));
627 bool dvd_get_last_command_diagnostic (dvd_drive *dvd, dvd_command_diagnostic *out) {
628 if (!dvd || !out || !dvd -> last_command.valid)
630 *out = dvd -> last_command;
636 /* Doc is under the UNIX function */
637 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
638 SCSI_PASS_THROUGH_DIRECT *sptd;
639 unsigned char sptd_sense[sizeof (*sptd) + 18], *sense;
645 sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
646 sense = &sptd_sense[sizeof (*sptd)];
648 memset (sptd, 0, sizeof (sptd_sense));
649 memcpy (sptd -> Cdb, mmc -> cmd, sizeof (mmc -> cmd));
650 sptd -> Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
651 sptd -> CdbLength = mmc -> cmdlen;
652 sptd -> SenseInfoLength = 18;
653 if (mmc -> direction == DVD_DATA_OUT)
654 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
655 else if (mmc -> direction == DVD_DATA_NONE)
656 sptd -> DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
658 sptd -> DataIn = SCSI_IOCTL_DATA_IN;
659 sptd -> DataBuffer = mmc -> buffer;
660 /* Quick hack: Windows hates DataTransferLength = 1. */
661 if (mmc -> buflen == 1)
662 sptd -> DataTransferLength = 2;
664 sptd -> DataTransferLength = mmc -> buflen;
665 sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
666 sptd -> SenseInfoOffset = sizeof (*sptd);
668 if (mmc -> cmd[0] == 0xB6) {
669 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
670 sptd -> DataTransferLength = 28;
673 ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
674 sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
675 win_error = ioctl_ok ? ERROR_SUCCESS : GetLastError ();
676 /* DeviceIoControl may succeed while the drive returns CHECK CONDITION. */
677 if (!ioctl_ok || sptd -> ScsiStatus != 0) {
679 if (!ignore_errors) {
680 error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
681 (unsigned long) win_error, sptd -> ScsiStatus);
682 debug ("Command was: ");
683 hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
684 debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
691 mmc -> sense -> sense_key = sense[2];
692 mmc -> sense -> asc = sense[12];
693 mmc -> sense -> ascq = sense[13];
695 dvd_record_command_diagnostic (dvd, mmc, out, (int) win_error,
696 (int) sptd -> ScsiStatus, sense[2], sense[12], sense[13]);
704 * Executes an MMC command.
705 * @param dvd The DVD drive the command should be exectued on.
706 * @param mmc The command to be executed.
707 * @param ignore_errors If set to true, no error will be printed if the command fails.
708 * @return 0 if the command was executed successfully, < 0 otherwise.
710 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
713 struct cdrom_generic_command cgc;
714 struct request_sense sense;
717 debug ("Executing MMC command: ");
718 hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
721 memset (&sense, 0, sizeof (sense));
722 memset (&cgc, 0, sizeof (cgc));
723 memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd));
724 cgc.buffer = (unsigned char *) mmc -> buffer;
725 cgc.buflen = mmc -> buflen;
726 if (mmc -> direction == DVD_DATA_OUT)
727 cgc.data_direction = CGC_DATA_WRITE;
728 else if (mmc -> direction == DVD_DATA_NONE)
729 cgc.data_direction = CGC_DATA_NONE;
731 cgc.data_direction = CGC_DATA_READ;
732 cgc.timeout = MMC_CMD_TIMEOUT * 1000;
734 if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
737 if (!ignore_errors) {
738 error ("Execution of MMC command failed: %s", strerror (saved_errno));
739 debug ("Command was:");
740 hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd));
741 debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq);
749 mmc -> sense -> sense_key = sense.sense_key;
750 mmc -> sense -> asc = sense.asc;
751 mmc -> sense -> ascq = sense.ascq;
753 dvd_record_command_diagnostic (dvd, mmc, out, saved_errno,
754 (int) cgc.stat, sense.sense_key, sense.asc, sense.ascq);
762 * Linux equivalent of the copied Windows SCSI_PASS_THROUGH_DIRECT transport
763 * used by UnlockDrive(). CDROM_SEND_PACKET does not expose an explicit CDB
764 * length and applies a single generic timeout. The GDR-8050L handshake uses
765 * 6-, 10-, and 12-byte CDBs plus 120-second command timeouts (10 seconds only
766 * for sticky descrambling). SG_IO preserves those boundaries exactly.
768 static int dvd_xbox_sgio_exact (dvd_drive *dvd,
774 dvd_data_direction direction,
775 unsigned int timeout_ms) {
778 mmc_command diagnostic;
781 int transport_result;
784 if (!dvd || !cdb || cdb_len < 1 || cdb_len > 12)
787 memset (&io, 0, sizeof (io));
788 memset (sense, 0, sizeof (sense));
789 memset (&diagnostic, 0, sizeof (diagnostic));
791 io.interface_id = 'S';
792 io.cmdp = (unsigned char *) cdb;
793 io.cmd_len = (unsigned char) cdb_len;
795 io.mx_sb_len = sizeof (sense);
796 io.timeout = timeout_ms;
798 io.dxfer_len = buf_len;
802 io.dxfer_direction = SG_DXFER_TO_DEV;
805 io.dxfer_direction = SG_DXFER_NONE;
811 io.dxfer_direction = SG_DXFER_FROM_DEV;
816 rc = ioctl (dvd -> fd, SG_IO, &io);
823 io.host_status == 0 &&
824 io.driver_status == 0) ? 0 : -1;
826 diagnostic.cmdlen = cdb_len;
827 diagnostic.direction = direction;
828 diagnostic.buffer = buf;
829 diagnostic.buflen = (int) buf_len;
830 memcpy (diagnostic.cmd, cdb, (size_t) cdb_len);
831 dvd_record_command_diagnostic (
841 xbox_ref_log_fprintf (
843 "[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=[",
844 step ? step : "unnamed",
847 (unsigned int) io.status,
848 (unsigned int) io.host_status,
849 (unsigned int) io.driver_status,
850 (unsigned int) (sense[2] & 0x0F),
851 (unsigned int) sense[12],
852 (unsigned int) sense[13],
855 for (i = 0; i < cdb_len; i++)
856 xbox_ref_log_fprintf (
860 (unsigned int) cdb[i]);
861 xbox_ref_log_fprintf (
863 "] xfer=%u direction=%s result=%s\n",
865 direction == DVD_DATA_OUT ? "out" :
866 direction == DVD_DATA_NONE ? "none" : "in",
867 transport_result == 0 ? "PASS" : "FAIL");
869 return transport_result;
872 static int dvd_xbox_exact_read_capacity (dvd_drive *dvd,
875 u_int32_t *sector_size) {
880 memset (cdb, 0, sizeof (cdb));
881 memset (buf, 0, sizeof (buf));
884 if (dvd_xbox_sgio_exact (
885 dvd, step, cdb, sizeof (cdb), buf, sizeof (buf),
886 DVD_DATA_IN, 120000) < 0)
890 ((u_int32_t) buf[0] << 24) |
891 ((u_int32_t) buf[1] << 16) |
892 ((u_int32_t) buf[2] << 8) |
896 *sectors = max_lba + 1;
899 ((u_int32_t) buf[4] << 24) |
900 ((u_int32_t) buf[5] << 16) |
901 ((u_int32_t) buf[6] << 8) |
906 static int dvd_xbox_exact_mode_sense_10 (dvd_drive *dvd,
913 if (!buf || buf_len > 0xFFFF)
915 memset (cdb, 0, sizeof (cdb));
916 memset (buf, 0, buf_len);
919 cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
920 cdb[8] = (u_int8_t) (buf_len & 0xFF);
922 return dvd_xbox_sgio_exact (
923 dvd, step, cdb, sizeof (cdb), buf, (u_int32_t) buf_len,
924 DVD_DATA_IN, 120000);
927 static int dvd_xbox_exact_mode_select_10 (dvd_drive *dvd,
934 if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFFFF)
936 memset (cdb, 0, sizeof (cdb));
937 memset (tmp, 0, sizeof (tmp));
938 memcpy (tmp, buf, buf_len);
940 cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
941 cdb[8] = (u_int8_t) (buf_len & 0xFF);
943 return dvd_xbox_sgio_exact (
944 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
945 DVD_DATA_OUT, 120000);
948 static int dvd_xbox_exact_mode_select_6 (dvd_drive *dvd,
955 if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFF)
957 memset (cdb, 0, sizeof (cdb));
958 memset (tmp, 0, sizeof (tmp));
959 memcpy (tmp, buf, buf_len);
962 cdb[4] = (u_int8_t) buf_len;
964 return dvd_xbox_sgio_exact (
965 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
966 DVD_DATA_OUT, 10000);
972 * Sends an INQUIRY command to the drive to retrieve drive identification strings.
973 * @param dvd The DVD drive the command should be exectued on.
974 * @return 0 if the command was executed successfully, < 0 otherwise.
976 static int dvd_get_drive_info (dvd_drive *dvd) {
982 dvd_init_command (&mmc, buf, sizeof (buf), NULL);
983 mmc.cmd[0] = SPC_INQUIRY;
984 mmc.cmd[4] = sizeof (buf);
985 if ((out = dvd_execute_cmd (dvd, &mmc, false)) >= 0) {
986 my_strndup (dvd -> vendor, buf + 8, 8);
987 strtrimr (dvd -> vendor);
988 my_strndup (dvd -> prod_id, buf + 16, 16);
989 strtrimr (dvd -> prod_id);
990 my_strndup (dvd -> prod_rev, buf + 32, 4);
991 strtrimr (dvd -> prod_rev);
992 snprintf (tmp, sizeof (tmp), "%s/%s/%s", dvd -> vendor, dvd -> prod_id, dvd -> prod_rev);
993 my_strdup (dvd -> model_string, tmp);
995 debug ("DVD drive is \"%s\"", dvd -> model_string);
997 error ("Cannot identify DVD drive\n");
1005 * Assigns the proper memory dump functions to a dvd_drive object, according to vendor, model and other parameters. Actually this scheme probably needs to
1006 * to be improved, but it is enough for the moment.
1007 * @param dvd The DVD drive the command should be exectued on.
1009 static void dvd_assign_functions (dvd_drive *dvd, u_int32_t command) {
1010 dvd -> def_method = 0;
1011 if (dvd_is_hlds_gc_wii_drive (dvd)) {
1012 dvd_apply_hlds_e7_profile (dvd);
1013 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",
1014 dvd_get_hlds_e7_profile_name (dvd),
1015 dvd_get_hlds_e7_support_tier (dvd),
1016 dvd_get_hlds_e7_family (dvd),
1017 dvd -> hlds_e7_cache_base, dvd -> hlds_e7_mem_blocks,
1018 dvd -> hlds_e7_static_cdb_base, dvd -> hlds_e7_static_gate);
1019 dvd -> memdump = &hitachi_dvd_dump_mem;
1021 dvd -> supported = true;
1022 /* DIC Type1/Type3/Type4 use READ12 + HIT 0xE7 cache extraction.
1023 * GCC-4244N is validated with Sonic Mega Collection and GCC-4243N is
1024 * actively under test, so both remain Method 8. Type1 drives get the
1025 * DIC-derived 0x00a13000 / one-window cache profile and also default to
1026 * Method 8 so GCC-4160N/GCC-4240N can be tested without forcing a method.
1027 * GDR-8050L gets a proven single-window fallback plus guarded speed probes
1028 * for cross-flashed/modified firmware with 0xE7 memdump added; stock
1029 * GDR-8050L firmware does not expose this GC/Wii memdump path. GDR-8081N
1030 * is an experimental probe target and needs Method 8 so seed probing can run.
1031 * Type2 is still classified only; leave it on the older Method 9 path. */
1032 if (dvd -> hlds_e7_preferred_method >= 0)
1033 dvd -> def_method = (u_int32_t) dvd -> hlds_e7_preferred_method;
1034 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))
1035 dvd -> def_method = 8;
1037 dvd -> def_method = 9;
1039 } else if (strcmp (dvd -> vendor, "LITE-ON") == 0 && (
1040 strcmp (dvd ->prod_id, "DVDRW LH-18A1H") == 0 ||
1041 strcmp (dvd ->prod_id, "DVDRW LH-18A1P") == 0 ||
1042 strcmp (dvd ->prod_id, "DVDRW LH-20A1H") == 0 ||
1043 strcmp (dvd ->prod_id, "DVDRW LH-20A1P") == 0
1045 debug ("Lite-On DVD drive detected, using Lite-On memory dump command");
1046 dvd -> memdump = &liteon_dvd_dump_mem;
1048 dvd -> supported = true;
1049 dvd -> def_method = 5;
1051 } else if (dvd_is_tsst_kreon_candidate (dvd) || (strcmp (dvd -> vendor, "TSSTcorp") == 0 && (
1052 strcmp (dvd ->prod_id, "DVD-ROM SH-D162A") == 0 ||
1053 strcmp (dvd ->prod_id, "DVD-ROM SH-D162B") == 0
1055 debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command");
1056 dvd -> memdump = &vanilla_2384_dvd_dump_mem;
1058 dvd -> supported = true;
1059 dvd -> def_method = 0;
1061 } else if (strcmp (dvd -> vendor, "PLEXTOR") == 0) {
1062 debug ("Plextor DVD drive detected, using vanilla 2064 memory dump command");
1063 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
1065 dvd -> supported = true;
1066 dvd -> def_method = 2;
1069 /* This is an unsupported drive (yet). */
1070 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
1072 dvd -> supported = false;
1076 dvd -> command = command;
1077 if (command == 0) dvd -> memdump = &vanilla_2064_dvd_dump_mem;
1078 else if (command == 1) dvd -> memdump = &vanilla_2384_dvd_dump_mem;
1079 else if (command == 2) dvd -> memdump = &hitachi_dvd_dump_mem;
1080 else if (command == 3) dvd -> memdump = &liteon_dvd_dump_mem;
1081 else if (command == 4) dvd -> memdump = &renesas_dvd_dump_mem;
1085 //init Reed-Solomon for Lite-On
1094 * Creates a new structure representing a CD/DVD-ROM drive.
1095 * @param device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows).
1096 * @return The newly-created structure, to be used with the other commands, or NULL if the drive could not be initialized.
1098 dvd_drive *dvd_drive_new (char *device, u_int32_t command) {
1107 /* Force the dropping of privileges: in our model, privileges are only used to execute memory dump commands, the user
1108 must gain access to the device somehow else (i. e. get added to the "cdrom" group or similar things) */
1111 debug ("Trying to open DVD device %s", device);
1113 sprintf (dev, "\\\\.\\%c:", device[0]);
1114 if ((fd = CreateFile (dev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
1115 error ("Cannot open drive: %d", GetLastError ());
1117 if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) {
1118 perror ("Cannot open drive");
1122 debug ("Opened successfully");
1124 dvd = (dvd_drive *) malloc (sizeof (dvd_drive));
1126 fprintf (stderr, "malloc() failed\n");
1129 memset (dvd, 0, sizeof (dvd_drive));
1130 my_strdup (dvd -> device, device);
1132 dvd_get_drive_info (dvd);
1133 dvd_assign_functions (dvd, command);
1141 * Frees resources used by a DVD drive structure and destroys it.
1142 * @param dvd The DVD drive structure to be destroyed.
1145 void *dvd_drive_destroy (dvd_drive *dvd) {
1148 CloseHandle (dvd -> fd);
1152 my_free (dvd -> device);
1153 my_free (dvd -> vendor);
1154 my_free (dvd -> prod_id);
1155 my_free (dvd -> prod_rev);
1164 * Executes the drive-dependent function to dump the drive sector cache, and returns the dumped data.
1165 * @param dvd The DVD drive the command should be exectued on.
1166 * @param block_off The offset to start dumping, WRT the beginning of the sector cache.
1167 * @param block_len The number of blocks to dump.
1168 * @param block_size The block size to be used for dumping.
1169 * @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.
1170 * @return 0 if the command was executed successfully, < 0 otherwise.
1172 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) {
1175 /* Upgrade privileges and call actual dump functions */
1177 out = dvd -> memdump (dvd, block_off, block_len, block_size, buf);
1185 * 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
1186 * at the beginning of the cache and can be dumped later.
1187 * @param dvd The DVD drive the command should be exectued on.
1188 * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs.
1189 * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1190 * @return 0 if the command was executed successfully, < 0 otherwise.
1192 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) {
1195 u_int8_t intbuf[64 * 1024], *buf;
1198 /* We need some buffer, be it provided externally or not */
1201 bufsize = extbufsize;
1204 bufsize = sizeof (intbuf);
1207 dvd_init_command (&mmc, buf, bufsize, sense);
1208 mmc.cmd[0] = MMC_READ_12;
1209 mmc.cmd[1] = 0x08; /* FUA bit set */
1210 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1211 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1212 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1213 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1214 mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1215 mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1216 mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1217 mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF);
1218 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1225 * Issues a READ(12) command using the STREAMING bit, which causes the requested 16-sector block to be read into memory,
1226 * together with the following four. This way we will be able to dump 5 sector with a single READ request.
1228 * Note the strange need for a big buffer even though we must only pass 0x10 as the transfer length, otherwise the drive will hang (!?).
1229 * @param dvd The DVD drive the command should be exectued on.
1230 * @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.
1231 * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1232 * @param extbuf A buffer where to store the read data, or NULL.
1233 * @param extbufsize The size of the buffer.
1236 int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1239 u_int8_t intbuf[2048 * 16], *buf;
1242 /* We need some buffer, be it provided externally or not */
1245 bufsize = extbufsize;
1248 bufsize = sizeof (intbuf);
1251 dvd_init_command (&mmc, buf, bufsize, sense);
1252 mmc.cmd[0] = MMC_READ_12;
1253 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1254 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1255 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1256 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1261 mmc.cmd[10] = 0x80; /* STREAMING bit set */
1262 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1268 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) {
1271 u_int8_t intbuf[64 * 1024], *buf;
1274 /* We need some buffer, be it provided externally or not */
1277 bufsize = extbufsize;
1280 bufsize = sizeof (intbuf);
1283 dvd_init_command (&mmc, buf, bufsize, sense);
1284 mmc.cmd[0] = MMC_READ_12;
1285 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1286 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1287 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1288 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1289 mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1290 mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1291 mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1292 mmc.cmd[9] = (u_int8_t) (sectors & 0x000000FF);
1293 mmc.cmd[10] = 0x80; /* STREAMING bit set */
1294 out = dvd_execute_cmd (dvd, &mmc, true); /* Ignore errors! */
1300 int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) {
1303 u_int8_t intbuf[64], *buf;
1309 dvd_init_command (&mmc, buf, bufsize, sense);
1310 mmc.cmd[0] = MMC_READ_12;
1312 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24); /* LBA from MSB to LSB */
1313 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1314 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1315 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1316 out = dvd_execute_cmd (dvd, &mmc, true);
1321 static void dvd_sleep_ms (unsigned int ms) {
1325 usleep ((useconds_t) ms * 1000);
1329 int dvd_start_stop_unit (dvd_drive *dvd, bool start, bool load_eject, req_sense *sense) {
1332 u_int8_t intbuf[64], *buf;
1338 dvd_init_command (&mmc, buf, bufsize, sense);
1340 /* START STOP UNIT byte 4: bit 1 = LoEj, bit 0 = Start. */
1341 mmc.cmd[4] = (load_eject ? 0x02 : 0x00) | (start ? 0x01 : 0x00);
1342 out = dvd_execute_cmd (dvd, &mmc, true);
1347 int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) {
1348 return dvd_start_stop_unit (dvd, start, false, sense);
1351 int dvd_set_door_lock (dvd_drive *dvd, bool locked) {
1359 if (ioctl (dvd -> fd, CDROM_LOCKDOOR, locked ? 1 : 0) < 0)
1365 int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
1366 unsigned int waited = 0;
1369 while (waited <= timeout_ms) {
1370 if (dvd_test_unit_ready (dvd, NULL) == 0)
1373 dvd_stop_unit (dvd, true, NULL); /* START UNIT, like EnsureDriveReady() */
1380 int dvd_media_cycle (dvd_drive *dvd, req_sense *sense) {
1385 /* Linux commonly applies CDO_LOCK while an optical device is open. A raw
1386 * START STOP UNIT eject then fails immediately even though the process owns
1387 * the only intentional handle. Release that kernel/drive door lock before
1388 * the GDR-8050L software tray cycle, then restore it after the tray is loaded
1389 * and ready. CDROM_LOCKDOOR is the documented Linux optical-door API. */
1390 out = dvd_set_door_lock (dvd, false);
1392 xbox_ref_log_fprintf (stderr,
1393 "[XBOX][FATAL] Linux media-cycle could not release the optical door lock: %s (errno=%d).\n",
1394 strerror (-out), -out);
1397 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle released the optical door lock before software eject.\n");
1400 /* Match the original GDR-8050L dumper's AutomateTrayCycle(): eject, wait
1401 * long enough for the tray to extend, close, poll readiness, then settle. */
1402 out = dvd_start_stop_unit (dvd, false, true, sense); /* LoEj=1, Start=0: eject */
1404 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software eject command failed.\n");
1407 dvd_sleep_ms (3000);
1409 out = dvd_start_stop_unit (dvd, true, true, sense); /* LoEj=1, Start=1: load */
1411 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software load command failed; the door remains unlocked for recovery.\n");
1415 for (i = 0; i < 90; i++) {
1417 if (dvd_test_unit_ready (dvd, NULL) == 0) {
1418 dvd_sleep_ms (1500);
1420 out = dvd_set_door_lock (dvd, true);
1422 xbox_ref_log_fprintf (stderr,
1423 "[XBOX][WARN] Linux media-cycle completed, but the optical door could not be re-locked: %s (errno=%d).\n",
1424 strerror (-out), -out);
1426 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle restored the optical door lock after load.\n");
1432 /* Original dumper falls back to a fixed 10s settle delay if TUR never
1433 * reports ready after tray close. Keep the door unlocked on failure so the
1434 * user can recover the media without power-cycling the external drive. */
1435 dvd_sleep_ms (10000);
1436 fprintf (stderr, "[XBOX][FATAL] Media-cycle tray closed, but the drive never became ready; the door remains unlocked for recovery.\n");
1440 int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1443 u_int8_t intbuf[64], *buf;
1449 dvd_init_command (&mmc, buf, bufsize, sense);
1451 mmc.cmd[2] = (u_int8_t) ((speed & 0x0000FF00) >> 8);
1452 mmc.cmd[3] = (u_int8_t) (speed & 0x000000FF);
1453 out = dvd_execute_cmd (dvd, &mmc, true);
1458 int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) {
1461 u_int8_t intbuf[64], *buf;
1467 dvd_init_command (&mmc, buf, bufsize, sense);
1472 out = dvd_execute_cmd (dvd, &mmc, true);
1474 *(size)=*(size) << 8 | intbuf[0x18];
1475 *(size)=*(size) << 8 | intbuf[0x19];
1476 *(size)=*(size) << 8 | intbuf[0x1a];
1477 *(size)=*(size) << 8 | intbuf[0x1b];
1482 int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) {
1485 u_int8_t intbuf[2052], *buf;
1491 dvd_init_command (&mmc, buf, bufsize, sense);
1495 out = dvd_execute_cmd (dvd, &mmc, true);
1497 *(layerbreak)=*(layerbreak) << 8;
1498 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x11];
1499 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x12];
1500 *(layerbreak)=*(layerbreak) << 8 | intbuf[0x13];
1501 if (*(layerbreak) > 0) *(layerbreak)=*(layerbreak) - 0x30000 + 1;
1506 int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1509 DeviceIoControl : \Device\CdRom5
1510 Command : IOCTL_SCSI_PASS_THROUGH_DIRECT
1511 Length : 44 (0x002C)
1516 CdbLength : 12 (0x0C)
1517 SenseInfoLength : 24 (0x18)
1518 DataTransferLength : 28 (0x0000001C)
1523 00000000 B6 00 00 00 00 00 00 00 00 00 1C 00 ...........
1526 00000000 00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF ............____
1527 00000010 00 00 03 E8 FF FF FF FF 00 00 03 E8 ...____...
1531 u_int8_t inbuf[28], *buf;
1537 dvd_init_command (&mmc, buf, bufsize, sense);
1550 *(buf+ 8)=0xff; //MSB
1553 *(buf+11)=0xff; //LSB
1555 *(buf+12)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1556 *(buf+13)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1557 *(buf+14)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1558 *(buf+15)=(u_int8_t) (speed & 0x000000FF);
1560 *(buf+16)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1561 *(buf+17)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1562 *(buf+18)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1563 *(buf+19)=(u_int8_t) (1000 & 0x000000FF);
1565 *(buf+20)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1566 *(buf+21)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1567 *(buf+22)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1568 *(buf+23)=(u_int8_t) (speed & 0x000000FF);
1570 *(buf+24)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1571 *(buf+25)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1572 *(buf+26)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1573 *(buf+27)=(u_int8_t) (1000 & 0x000000FF);
1575 out = dvd_execute_cmd (dvd, &mmc, true);
1581 int dvd_test_unit_ready (dvd_drive *dvd, req_sense *sense) {
1585 dvd_init_command (&mmc, intbuf, 0, sense);
1586 mmc.cmd[0] = SPC_TEST_UNIT_READY;
1588 mmc.direction = DVD_DATA_NONE;
1589 return dvd_execute_cmd (dvd, &mmc, true);
1592 int dvd_read_capacity_10 (dvd_drive *dvd, u_int32_t *sectors, u_int32_t *sector_size, req_sense *sense) {
1596 u_int32_t max_lba, block_len;
1598 dvd_init_command (&mmc, buf, sizeof (buf), sense);
1599 mmc.cmd[0] = MMC_READ_CAPACITY_10;
1601 out = dvd_execute_cmd (dvd, &mmc, false);
1603 max_lba = ((u_int32_t) buf[0] << 24) | ((u_int32_t) buf[1] << 16) | ((u_int32_t) buf[2] << 8) | buf[3];
1604 block_len = ((u_int32_t) buf[4] << 24) | ((u_int32_t) buf[5] << 16) | ((u_int32_t) buf[6] << 8) | buf[7];
1606 *sectors = max_lba + 1;
1608 *sector_size = block_len;
1614 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) {
1616 u_int8_t intbuf[64 * 1024], *buf;
1617 size_t need, bufsize;
1619 need = (size_t) sectors * 2048;
1622 bufsize = extbufsize;
1625 bufsize = sizeof (intbuf);
1628 if (need > bufsize) {
1629 error ("dvd_read_10 buffer too small (%u sectors need %lu bytes)", sectors, (unsigned long) need);
1633 dvd_init_command (&mmc, buf, (int) need, sense);
1634 mmc.cmd[0] = MMC_READ_10;
1636 mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);
1637 mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1638 mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1639 mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1640 mmc.cmd[7] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1641 mmc.cmd[8] = (u_int8_t) (sectors & 0x000000FF);
1643 return dvd_execute_cmd (dvd, &mmc, true);
1646 int dvd_mode_sense_10 (dvd_drive *dvd, u_int8_t page, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1649 if (!extbuf || extbufsize > 0xFFFF)
1652 dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1653 mmc.cmd[0] = SPC_MODE_SENSE_10;
1655 mmc.cmd[7] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1656 mmc.cmd[8] = (u_int8_t) (extbufsize & 0x00FF);
1659 return dvd_execute_cmd (dvd, &mmc, false);
1662 int dvd_mode_select_10 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1666 if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFFFF)
1668 memset (tmp, 0, sizeof (tmp));
1669 memcpy (tmp, buf, bufsize);
1671 dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1672 mmc.direction = DVD_DATA_OUT;
1673 mmc.cmd[0] = SPC_MODE_SELECT_10;
1674 mmc.cmd[7] = (u_int8_t) ((bufsize & 0xFF00) >> 8);
1675 mmc.cmd[8] = (u_int8_t) (bufsize & 0x00FF);
1678 return dvd_execute_cmd (dvd, &mmc, false);
1681 int dvd_mode_select_6 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1685 if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFF)
1687 memset (tmp, 0, sizeof (tmp));
1688 memcpy (tmp, buf, bufsize);
1690 dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1691 mmc.direction = DVD_DATA_OUT;
1692 mmc.cmd[0] = SPC_MODE_SELECT_6;
1694 mmc.cmd[4] = (u_int8_t) (bufsize & 0xFF);
1697 return dvd_execute_cmd (dvd, &mmc, false);
1700 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) {
1703 if (!extbuf || extbufsize > 0xFFFF)
1706 dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1707 mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1708 /* MMC READ DVD STRUCTURE places Format in CDB byte 7. Byte 11 is
1709 * Control and must remain zero. The copied Windows GetMediaID() path
1710 * uses the same byte-7 boundary for DMI format 0x04. */
1712 mmc.cmd[7] = format;
1713 mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1714 mmc.cmd[9] = (u_int8_t) (extbufsize & 0x00FF);
1717 return dvd_execute_cmd (dvd, &mmc, false);
1721 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) {
1724 dvd_init_command (&mmc, buf, (int) bufsize, NULL);
1728 mmc.cmd[3] = subcommand;
1731 mmc.direction = direction;
1732 if (direction == DVD_DATA_NONE) {
1736 return dvd_execute_cmd (dvd, &mmc, false);
1739 static bool dvd_xbox_feature_list_has (const u_int16_t *features, size_t count, u_int16_t needle) {
1743 for (i = 0; i < count && features[i] != 0; i++) {
1744 if (features[i] == needle)
1750 int dvd_xbox_vendor_get_feature_list (dvd_drive *dvd, u_int16_t *features, size_t max_features) {
1754 if (!dvd || !features || max_features == 0)
1756 memset (features, 0, max_features * sizeof (features[0]));
1757 memset (buf, 0, sizeof (buf));
1758 if (dvd_xbox_vendor_command (dvd, 0x10, 0, buf, sizeof (buf), DVD_DATA_IN) < 0)
1760 if ((((u_int16_t) buf[0] << 8) | buf[1]) != 0xA55A ||
1761 (((u_int16_t) buf[2] << 8) | buf[3]) != 0x5AA5) {
1762 error ("Xbox vendor feature-list signature is invalid");
1765 count = sizeof (buf) / 2;
1766 if (count > max_features)
1767 count = max_features;
1768 for (i = 0; i < count; i++)
1769 features[i] = ((u_int16_t) buf[i * 2] << 8) | buf[i * 2 + 1];
1773 int dvd_xbox_vendor_lock (dvd_drive *dvd) {
1774 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1776 return dvd_xbox_vendor_command (dvd, 0x11, 0x00, NULL, 0, DVD_DATA_NONE);
1779 int dvd_xbox_vendor_set_error_skip (dvd_drive *dvd, bool enabled) {
1780 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1782 return dvd_xbox_vendor_command (dvd, 0x15, enabled ? 0x01 : 0x00, NULL, 0, DVD_DATA_NONE);
1785 int dvd_xbox_vendor_unlock_wxripper (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1786 u_int16_t features[13];
1787 u_int32_t sectors = 0, sector_size = 0;
1789 if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1792 if (dvd_xbox_vendor_get_feature_list (dvd, features, sizeof (features) / sizeof (features[0])) < 0)
1794 if (!dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0201) &&
1795 !dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0221))
1796 warning ("Xbox vendor feature list did not advertise Xbox unlock state 2/full challenge support; trying wxripper state anyway");
1798 if (dvd_xbox_vendor_command (dvd, 0x11, 0x02, NULL, 0, DVD_DATA_NONE) < 0)
1801 /* DiscImageCreator disables error-skip before dumping; keep that behavior so
1802 * real read errors are visible to FriiDump unless a future option says otherwise. */
1803 dvd_xbox_vendor_set_error_skip (dvd, false);
1805 if (dvd_read_capacity_10 (dvd, §ors, §or_size, NULL) < 0)
1807 if (sector_size != 2048 || sectors < 1000000) {
1808 error ("Xbox vendor unlock did not expose the expected 2048-byte view");
1811 if (unlocked_sectors)
1812 *unlocked_sectors = sectors;
1817 #define XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
1819 static int dvd_xbox_refresh_ready_capacity (dvd_drive *dvd,
1821 u_int32_t *sector_size) {
1822 u_int32_t observed_sectors = 0;
1823 u_int32_t observed_sector_size = 0;
1828 /* Exact portable equivalent of xbox_ref_refresh_ready_capacity():
1829 * RefreshVolume(); Sleep(2000); EnsureDriveReady(30000); GetTotalSectors(). */
1830 dvd_refresh_volume (dvd);
1831 dvd_sleep_ms (2000);
1832 if (dvd_wait_ready (dvd, 30000) < 0)
1834 if (dvd_read_capacity_10 (dvd, &observed_sectors, &observed_sector_size, NULL) < 0)
1838 *sectors = observed_sectors;
1840 *sector_size = observed_sector_size;
1844 int dvd_xbox_prepare_game_view (dvd_drive *dvd,
1846 u_int32_t *sector_size) {
1847 u_int32_t entry_sectors = 0;
1848 u_int32_t observed_sectors = 0;
1849 u_int32_t observed_sector_size = 0;
1851 if (!dvd || !dvd_is_xbox_challenge_drive (dvd))
1854 /* Port xbox_ref_gdr8050l_dump_core() state preparation exactly, replacing
1855 * Win32 handle/volume calls with FriiDump's portable Linux equivalents.
1856 * No RecoveryKick, media-auth kick, or synthetic LBA-zero read cadence is
1857 * part of this stock/cross-flashed GDR-8050L sequence. */
1858 if (dvd_wait_ready (dvd, 30000) < 0) {
1859 xbox_ref_log_fprintf (stderr,
1860 "[XBOX-WINSEQ][FATAL] Drive did not become ready before Xbox state preparation.\n");
1863 if (dvd_read_capacity_10 (dvd, &entry_sectors, &observed_sector_size, NULL) < 0) {
1864 xbox_ref_log_fprintf (stderr,
1865 "[XBOX-WINSEQ][FATAL] Entry READ CAPACITY failed.\n");
1868 xbox_ref_log_fprintf (stderr,
1869 "[XBOX-WINSEQ] Entry READ CAPACITY: %u sectors.\n",
1872 if (entry_sectors > XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1873 xbox_ref_log_fprintf (stderr,
1874 "[XBOX-WINSEQ] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
1875 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1878 xbox_ref_log_fprintf (stderr,
1879 "[XBOX-WINSEQ] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
1880 if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1881 xbox_ref_log_fprintf (stderr,
1882 "[XBOX-WINSEQ][WARN] Direct UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1883 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1885 xbox_ref_log_fprintf (stderr,
1886 "[XBOX-WINSEQ] Direct-handshake READ CAPACITY: %u sectors.\n",
1889 if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1890 xbox_ref_log_fprintf (stderr,
1891 "[XBOX-WINSEQ][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
1892 if (dvd_media_cycle (dvd, NULL) < 0)
1894 xbox_ref_log_fprintf (stderr,
1895 "[XBOX-WINSEQ] Re-applying the full handshake after recovery media change.\n");
1896 if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1897 xbox_ref_log_fprintf (stderr,
1898 "[XBOX-WINSEQ][WARN] Recovery UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1899 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1901 xbox_ref_log_fprintf (stderr,
1902 "[XBOX-WINSEQ] Recovery-handshake READ CAPACITY: %u sectors.\n",
1905 if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1906 xbox_ref_log_fprintf (stderr,
1907 "[XBOX-WINSEQ][FATAL] Xbox game view was not established after direct and recovery handshakes.\n");
1913 /* Exact next Windows step after state preparation. */
1914 dvd_set_speed (dvd, 0xFFFF, NULL);
1917 *sectors = observed_sectors;
1919 *sector_size = observed_sector_size;
1923 int dvd_refresh_volume (dvd_drive *dvd) {
1925 DWORD bytesReturned = 0;
1928 /* Match RefreshVolume() from the reference dumper: update properties only;
1929 * do not dismount here because that can reset drive state. This is now
1930 * shared by Xbox and HLDS 0xE7 GC/Wii paths so Windows is less likely to
1931 * keep stale filesystem/probe state attached to odd discs. */
1932 DeviceIoControl (dvd -> fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL);
1933 dvd_sleep_ms (1000);
1941 int dvd_lock_volume (dvd_drive *dvd) {
1943 DWORD bytesReturned = 0;
1945 return DVD_VOLUME_LOCK_FAILED;
1946 return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)
1947 ? DVD_VOLUME_LOCK_OK
1948 : DVD_VOLUME_LOCK_FAILED;
1950 /* Linux CDROM_SEND_PACKET has no FSCTL_LOCK_VOLUME equivalent here.
1951 * Return a distinct result instead of falsely reporting an exclusive lock.
1952 * The caller may continue after warning about automount/media polling. */
1954 return DVD_VOLUME_LOCK_UNAVAILABLE;
1958 int dvd_xbox_refresh_volume (dvd_drive *dvd) {
1959 return dvd_refresh_volume (dvd);
1962 int dvd_xbox_lock_volume (dvd_drive *dvd) {
1963 return dvd_lock_volume (dvd);
1966 static int dvd_xbox_read_host_challenge_table (dvd_drive *dvd, u_int8_t *table, size_t table_len) {
1970 if (!table || table_len < 0x664)
1974 /* GDR-8050L / Xbox READ DVD STRUCTURE format C0. */
1975 dvd_init_command (&mmc, table, 0x664, NULL);
1976 mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1986 out = dvd_execute_cmd (dvd, &mmc, true);
1988 /* Some Hitachi-family drives expose the same table via vendor command 0xFD. */
1989 if (out < 0 || table[772] != 1 || table[773] == 0) {
1990 dvd_init_command (&mmc, table, 0x664, NULL);
1996 out = dvd_execute_cmd (dvd, &mmc, true);
2002 memset (cdb, 0, sizeof (cdb));
2003 memset (table, 0, table_len);
2013 out = dvd_xbox_sgio_exact (
2015 "3-read-dvd-structure-c0",
2023 if (out < 0 || table[772] != 1 || table[773] == 0) {
2024 memset (cdb, 0, sizeof (cdb));
2025 memset (table, 0, table_len);
2030 out = dvd_xbox_sgio_exact (
2032 "3-read-host-table-fallback-fd",
2043 if (out < 0 || table[772] != 1)
2046 xbox_ref_log_fprintf (
2048 "[XBOX-SGIO] challenge-table marker=%u entries-byte=%u result=PASS\n",
2049 (unsigned int) table[772],
2050 (unsigned int) table[773]);
2054 int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
2058 u_int8_t table[0x664];
2059 u_int8_t restable[261];
2060 u_int8_t hash[0x2C];
2063 u_int8_t sticky[12];
2064 u_int32_t sectors = 0, sector_size = 0;
2067 if (!dvd || !dvd_is_xbox_unlock_drive (dvd))
2070 /* Step 1/2: read current capacity and the Xbox mode page. If the drive is
2071 * already unlocked, this is harmless; the final capacity check below becomes
2074 dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
2075 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2077 dvd_xbox_exact_read_capacity (
2078 dvd, "1-initial-read-capacity", §ors, §or_size);
2079 dvd_xbox_exact_mode_sense_10 (
2080 dvd, "2-mode-sense-3e", 0x3E, page, sizeof (page));
2083 /* Step 3: retrieve and decode the host challenge table. */
2084 if (dvd_xbox_read_host_challenge_table (dvd, table, sizeof (table)) < 0) {
2085 error ("Cannot retrieve Xbox host challenge table");
2089 for (i = 0; i < 0x2C; i++)
2090 hash[i] = table[0x4A3 + i];
2091 Sha1Calculate (hash, 0x2C, &digest);
2093 for (i = 0; i <= 260; i++)
2094 restable[i] = table[774 + i];
2095 xbox_rc4_init (&rc4, digest.bytes, 7);
2096 xbox_rc4_crypt (&rc4, restable, restable, 0xFD);
2099 for (l = 0; l <= 23; l++) {
2100 if (restable[l * 11] == 1) {
2102 if (k == (int) (sizeof (chalpos) / sizeof (chalpos[0])))
2107 error ("Xbox challenge table does not contain enough usable entries");
2111 /* Step 4: first host challenge. */
2112 memset (page, 0, sizeof (page));
2119 memcpy (&page[15], &restable[1 + chalpos[k - 2] * 11], 5);
2120 /* Match the original dumper: send the challenge and continue even if
2121 * Windows reports a transport failure. The later XDVDFS probe is the
2122 * authority for whether the drive actually entered the game view. */
2124 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2125 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2127 dvd_xbox_exact_mode_select_10 (
2128 dvd, "4-mode-select-challenge-1", page, sizeof (page));
2129 dvd_xbox_exact_mode_sense_10 (
2130 dvd, "5-mode-sense-verify-1", 0x3E, page, sizeof (page));
2133 /* Step 6: second host challenge. */
2134 memset (page, 0, sizeof (page));
2139 memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2141 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2142 dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2144 dvd_xbox_exact_mode_select_10 (
2145 dvd, "6-mode-select-challenge-2", page, sizeof (page));
2146 dvd_xbox_exact_mode_sense_10 (
2147 dvd, "7-mode-sense-verify-2", 0x3E, page, sizeof (page));
2150 /* Step 8: unlock partition 1. */
2151 memset (page, 0, sizeof (page));
2160 memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2162 dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2164 dvd_xbox_exact_mode_select_10 (
2165 dvd, "8-mode-select-partition-1-unlock", page, sizeof (page));
2168 /* Step 9: sticky descrambling, mode page 0x31. */
2169 memset (sticky, 0, sizeof (sticky));
2174 dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
2176 dvd_xbox_exact_mode_select_6 (
2177 dvd, "9-mode-select-sticky-descrambling", sticky, sizeof (sticky));
2180 /* Step 10: final capacity observation. The original UnlockDrive() only
2181 * prints this verification and does not fail if the capacity has not changed
2182 * yet. This matters for the first GDR-8050L handshake, whose purpose is to
2183 * prime the drive before the required media-change event. */
2187 out = dvd_read_capacity_10 (dvd, §ors, §or_size, NULL);
2189 out = dvd_xbox_exact_read_capacity (
2190 dvd, "10-final-read-capacity", §ors, §or_size);
2193 if (unlocked_sectors)
2194 *unlocked_sectors = sectors;
2195 xbox_ref_log_fprintf (
2197 "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n",
2201 xbox_ref_log_fprintf (
2203 "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
2209 bool dvd_is_xbox_challenge_drive (dvd_drive *dvd) {
2210 return dvd_is_hlds_drive (dvd) && dvd_prod_has (dvd, "GDR8050L");
2213 bool dvd_is_xbox_vendor_unlock_drive (dvd_drive *dvd) {
2214 return (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L"))) ||
2215 dvd_is_tsst_kreon_candidate (dvd);
2218 bool dvd_is_xbox_unlock_drive (dvd_drive *dvd) {
2219 return dvd_is_xbox_challenge_drive (dvd) || dvd_is_xbox_vendor_unlock_drive (dvd);
2222 bool dvd_is_xbox_drive (dvd_drive *dvd) {
2223 /* Autodetect only the two native Xbox profiles currently wired into the
2224 * Xbox dump planner. Other candidate drives keep FriiDump's normal GC/Wii/DVD
2225 * behavior unless the user explicitly forces Xbox mode with -T 4. */
2226 return dvd_is_xbox_challenge_drive (dvd) ||
2227 (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L")));
2232 const char *dvd_get_hlds_e7_profile_name (dvd_drive *dvd) {
2233 if (!dvd) return "none";
2234 if (dvd -> hlds_e7_profile_label) return dvd -> hlds_e7_profile_label;
2235 return dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2238 const char *dvd_get_hlds_e7_support_tier (dvd_drive *dvd) {
2239 if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2240 return dvd -> hlds_e7_support_tier ? dvd -> hlds_e7_support_tier : "legacy_detected";
2243 const char *dvd_get_hlds_e7_family (dvd_drive *dvd) {
2244 if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2245 return dvd -> hlds_e7_family ? dvd -> hlds_e7_family : dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2248 const char *dvd_get_hlds_e7_tokens (dvd_drive *dvd) {
2249 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2250 return dvd -> hlds_e7_tokens ? dvd -> hlds_e7_tokens : "";
2253 const char *dvd_get_hlds_e7_record_id (dvd_drive *dvd) {
2254 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2255 return dvd -> hlds_e7_record_id ? dvd -> hlds_e7_record_id : "";
2258 const char *dvd_get_hlds_e7_notes (dvd_drive *dvd) {
2259 if (!dvd || dvd -> hlds_e7_type == 0) return "";
2260 return dvd -> hlds_e7_notes ? dvd -> hlds_e7_notes : "";
2263 u_int32_t dvd_get_hlds_e7_static_cdb_base (dvd_drive *dvd) {
2264 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_cdb_base : 0;
2267 u_int32_t dvd_get_hlds_e7_static_gate (dvd_drive *dvd) {
2268 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_gate : 0;
2271 int dvd_get_hlds_e7_preferred_method (dvd_drive *dvd) {
2272 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_preferred_method : -1;
2275 u_int32_t dvd_get_hlds_e7_type (dvd_drive *dvd) {
2276 return dvd ? dvd -> hlds_e7_type : 0;
2279 u_int32_t dvd_get_hlds_e7_cache_base (dvd_drive *dvd) {
2280 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_cache_base : 0;
2283 u_int32_t dvd_get_hlds_e7_mem_blocks (dvd_drive *dvd) {
2284 return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_mem_blocks : 0;
2287 char *dvd_get_vendor (dvd_drive *dvd) {
2288 return (dvd -> vendor);
2292 char *dvd_get_product_id (dvd_drive *dvd) {
2293 return (dvd -> prod_id);
2297 char *dvd_get_product_revision (dvd_drive *dvd) {
2298 return (dvd -> prod_rev);
2302 char *dvd_get_model_string (dvd_drive *dvd) {
2303 return (dvd -> model_string);
2307 char *dvd_get_device (dvd_drive *dvd) {
2308 return (dvd -> device);
2311 void *dvd_get_native_handle (dvd_drive *dvd) {
2312 if (!dvd) return NULL;
2314 return (void *) dvd -> fd;
2321 bool dvd_get_support_status (dvd_drive *dvd) {
2322 return (dvd -> supported);
2325 u_int32_t dvd_get_def_method (dvd_drive *dvd){
2326 return (dvd -> def_method);
2329 u_int32_t dvd_get_command (dvd_drive *dvd){
2330 return (dvd -> command);