]> FriiDump Source - friidump.git/blob - libfriidump/dvd_drive.c
b77a95d3a0c4697c6b2e982501c3133dd06813da
[friidump.git] / libfriidump / dvd_drive.c
1 /***************************************************************************
2  *   Copyright (C) 2007 by Arep                                            *
3  *   Support is provided through the forums at                             *
4  *   http://wii.console-tribe.com                                          *
5  *                                                                         *
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.                                   *
10  *                                                                         *
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.                          *
15  *                                                                         *
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  ***************************************************************************/
21
22 /*! \file
23  * \brief A class to send raw MMC commands to a CD/DVD-ROM drive.
24  *
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.
30  *
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.
33  */
34
35 #include "rs.h"
36 #include "misc.h"
37 #include <stdio.h>
38 #include <sys/types.h>
39 //#include <sys/time.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include "dvd_drive.h"
45 #include "disc.h"
46 #include "sha1.h"
47 #include "xbox_ref/xbox_ref_log.h"
48
49 #ifdef WIN32
50 #include <windows.h>
51 #include <ntddscsi.h>
52 #else
53 #include <linux/cdrom.h>
54 #include <scsi/sg.h>
55 #include <sys/ioctl.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58 #include <unistd.h>
59 #endif
60
61
62 /*! \brief Timeout for MMC commands.
63  *
64  * This must be expressed in seconds (Windows uses seconds, right?).
65  */
66 #define MMC_CMD_TIMEOUT 10
67
68
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);
76
77
78 /*! \brief A structure that represents a CD/DVD-ROM drive.
79  */
80 struct dvd_drive_s {
81         /* Device special file */
82         char *device;                   //!< The path to the drive (i.e.: /dev/something on Unix, x: on Windows).
83
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.
89         u_int32_t def_method;
90         u_int32_t command;
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;
103
104         /* Last transport command evidence for release-build diagnostics. */
105         dvd_command_diagnostic last_command;
106
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.
111          */
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.
114
115
116         /* File descriptor & stuff used to access drive */
117 #ifdef WIN32
118         HANDLE fd;                      //!< The HANDLE to interact with the drive on Windows.
119 #else
120         int fd;                         //!< The file descriptor to interact with the drive on Unix.
121 #endif
122 };
123
124
125 /** \brief Supported MMC commands.
126  */
127 enum mmc_commands_e {
128         SPC_TEST_UNIT_READY = 0x00,
129         SPC_INQUIRY = 0x12,
130         SPC_MODE_SELECT_6 = 0x15,
131         MMC_START_STOP_UNIT = 0x1B,
132         MMC_READ_CAPACITY_10 = 0x25,
133         MMC_READ_10 = 0x28,
134         SPC_MODE_SENSE_10 = 0x5A,
135         SPC_MODE_SELECT_10 = 0x55,
136         MMC_READ_12 = 0xA8,
137         MMC_READ_DVD_STRUCTURE = 0xAD,
138 };
139
140 typedef struct {
141         u_int8_t s[256];
142         u_int8_t i;
143         u_int8_t j;
144 } xbox_rc4_ctx;
145
146 static void xbox_rc4_init (xbox_rc4_ctx *ctx, const u_int8_t *key, size_t keylen) {
147         u_int32_t i;
148         u_int8_t j, tmp;
149
150         for (i = 0; i < 256; i++)
151                 ctx -> s[i] = (u_int8_t) i;
152         ctx -> i = 0;
153         ctx -> j = 0;
154
155         if (keylen == 0)
156                 return;
157
158         j = 0;
159         for (i = 0; i < 256; i++) {
160                 j = (u_int8_t) (j + ctx -> s[i] + key[i % keylen]);
161                 tmp = ctx -> s[i];
162                 ctx -> s[i] = ctx -> s[j];
163                 ctx -> s[j] = tmp;
164         }
165 }
166
167 static void xbox_rc4_crypt (xbox_rc4_ctx *ctx, const u_int8_t *in, u_int8_t *out, size_t len) {
168         size_t n;
169         u_int8_t tmp, k;
170
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])];
178                 out[n] = in[n] ^ k;
179         }
180 }
181
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;
184 }
185
186 static bool dvd_vendor_is (dvd_drive *dvd, const char *vendor) {
187         return dvd && dvd -> vendor && vendor && strcmp (dvd -> vendor, vendor) == 0;
188 }
189
190 static bool dvd_is_hlds_drive (dvd_drive *dvd) {
191         return dvd_vendor_is (dvd, "HL-DT-ST");
192 }
193
194 static bool dvd_prod_has_any (dvd_drive *dvd, const char **needles, size_t count) {
195         size_t i;
196         for (i = 0; i < count; i++) {
197                 if (dvd_prod_has (dvd, needles[i]))
198                         return true;
199         }
200         return false;
201 }
202
203 static bool hlds_product_has (const char *product_id, const char *needle) {
204         return product_id && needle && strstr (product_id, needle) != NULL;
205 }
206
207 static bool hlds_product_has_any (
208         const char *product_id,
209         const char **needles,
210         size_t count
211 ) {
212         size_t i;
213         for (i = 0; i < count; i++) {
214                 if (hlds_product_has (product_id, needles[i]))
215                         return true;
216         }
217         return false;
218 }
219
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"
224         };
225         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
226 }
227
228 static bool dvd_is_hlds_gdr8050l_drive (dvd_drive *dvd) {
229         static const char *names[] = {
230                 "GDR8050L", "GDR-8050L"
231         };
232         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
233 }
234
235
236 typedef struct {
237         const char *model;
238         const char *firmware;
239         u_int32_t type;
240         u_int32_t cache_base;
241         u_int32_t mem_blocks;
242         int preferred_method;
243         const char *label;
244         const char *support_tier;
245         const char *family;
246         const char *tokens;
247         const char *record_id;
248         u_int32_t static_cdb_base;
249         u_int32_t static_gate;
250         const char *notes;
251 } hlds_e7_profile_desc;
252
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", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0439", 0x880U, 0x9003731dU, "Live INQUIRY alias for GCC4243/A102; analyzer-derived exact parser metadata; read-only profile hardening target; not yet live-validated with the exact profile" },
259         { "GCC-4243N", "A102", 3, 0x80000000U, 5, 8, "GCC-4243N A102 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0439", 0x880U, 0x9003731dU, "Canonical model alias for GCC-4243N A102; analyzer-derived exact parser metadata; read-only profile hardening target; not yet live-validated with the exact profile" },
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", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0426", 0x894U, 0x900386d2U, "Live INQUIRY alias for GCC4244/B101; legacy Type3 fallback completed a Redump-matching GameCube dump; exact parser metadata is analyzer-derived and is the regression oracle for A102 hardening" },
263         { "GCC-4244N", "B101", 3, 0x80000000U, 5, 8, "GCC-4244N B101 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "hybrid_identity_v3:stage5b_0426", 0x894U, 0x900386d2U, "Canonical model alias for GCC-4244N B101; legacy Type3 fallback completed a Redump-matching GameCube dump; exact parser metadata is analyzer-derived and is the regression oracle for A102 hardening" },
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 }
281 };
282
283 static void hlds_normalize_model (const char *src, char *dst, size_t dst_size) {
284         size_t i, j;
285         if (!dst || dst_size == 0)
286                 return;
287         dst[0] = 0;
288         if (!src)
289                 return;
290         for (i = 0, j = 0; src[i] && j + 1 < dst_size; i++) {
291                 unsigned char c = (unsigned char) src[i];
292                 if (isalnum (c))
293                         dst[j++] = (char) toupper (c);
294         }
295         dst[j] = 0;
296 }
297
298 static bool hlds_model_matches_value (const char *product_id, const char *model) {
299         char prod_norm[64];
300         char model_norm[64];
301         if (!product_id || !model)
302                 return false;
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;
306 }
307
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])
312                 return true;
313         if (!actual)
314                 return false;
315         a = (const unsigned char *) actual;
316         b = (const unsigned char *) expected;
317         while (*a && *b) {
318                 if (toupper (*a) != toupper (*b))
319                         return false;
320                 a++;
321                 b++;
322         }
323         return *a == 0 && *b == 0;
324 }
325
326 static const hlds_e7_profile_desc *hlds_find_e7_profile_for_identity (
327         const char *vendor,
328         const char *product_id,
329         const char *revision
330 ) {
331         const hlds_e7_profile_desc *p;
332         if (!vendor || strcmp (vendor, "HL-DT-ST") != 0)
333                 return NULL;
334         for (p = hlds_e7_profiles; p -> model; p++) {
335                 if (
336                         hlds_model_matches_value (product_id, p -> model) &&
337                         hlds_revision_matches_value (revision, p -> firmware)
338                 )
339                         return p;
340         }
341         return NULL;
342 }
343
344 static const hlds_e7_profile_desc *dvd_find_hlds_e7_profile (dvd_drive *dvd) {
345         if (!dvd)
346                 return NULL;
347         return hlds_find_e7_profile_for_identity (
348                 dvd -> vendor,
349                 dvd -> prod_id,
350                 dvd -> prod_rev
351         );
352 }
353
354 bool dvd_lookup_hlds_e7_profile (
355         const char *vendor,
356         const char *product_id,
357         const char *revision,
358         dvd_hlds_e7_profile_info *out
359 ) {
360         const hlds_e7_profile_desc *p;
361         if (out)
362                 memset (out, 0, sizeof (*out));
363         p = hlds_find_e7_profile_for_identity (vendor, product_id, revision);
364         if (!p)
365                 return false;
366         if (out) {
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;
379         }
380         return true;
381 }
382
383 u_int32_t dvd_detect_hlds_e7_type_for_identity (
384         const char *vendor,
385         const char *product_id,
386         const char *revision
387 ) {
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"
393         };
394         static const char *type2_1[] = {
395                 "GCC-4241N", "GCC4241N", "GCC4241"
396         };
397         static const char *type2_2[] = {
398                 "GCC-4242N", "GCC4242N", "GCC4242"
399         };
400         static const char *gdr8081n[] = {
401                 "GDR8081N", "GDR-8081N"
402         };
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"
409         };
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"
417         };
418
419         if (!vendor || strcmp (vendor, "HL-DT-ST") != 0)
420                 return 0;
421         if (profile)
422                 return profile -> type;
423         if (hlds_product_has_any (product_id, type1, sizeof (type1) / sizeof (type1[0])))
424                 return 1;
425         if (hlds_product_has_any (product_id, type2_1, sizeof (type2_1) / sizeof (type2_1[0])))
426                 return 21;
427         if (hlds_product_has_any (product_id, type2_2, sizeof (type2_2) / sizeof (type2_2[0])))
428                 return 22;
429         if (hlds_product_has_any (product_id, gdr8081n, sizeof (gdr8081n) / sizeof (gdr8081n[0])))
430                 return 81;
431         if (hlds_product_has_any (product_id, type3, sizeof (type3) / sizeof (type3[0])))
432                 return 3;
433         if (hlds_product_has (product_id, "GDR8050L") || hlds_product_has (product_id, "GDR-8050L"))
434                 return 44;
435         if (hlds_product_has_any (product_id, type4, sizeof (type4) / sizeof (type4[0])))
436                 return 4;
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. */
439         return 0;
440 }
441
442 static u_int32_t dvd_hlds_e7_detect_type (dvd_drive *dvd) {
443         if (!dvd)
444                 return 0;
445         return dvd_detect_hlds_e7_type_for_identity (
446                 dvd -> vendor,
447                 dvd -> prod_id,
448                 dvd -> prod_rev
449         );
450 }
451
452 static bool dvd_is_hlds_gc_wii_drive (dvd_drive *dvd) {
453         return dvd_hlds_e7_detect_type (dvd) != 0;
454 }
455
456 static const char *dvd_hlds_e7_profile_name_from_type (u_int32_t type) {
457         switch (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";
475         }
476 }
477
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;
492
493         switch (dvd -> hlds_e7_type) {
494                 case 1:
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;
499                         break;
500                 case 21:
501                 case 22:
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;
506                         break;
507                 case 81:
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;
514                         break;
515                 case 44:
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;
527                         break;
528                 case 3:
529                 case 4:
530                 default:
531                         dvd -> hlds_e7_cache_base = 0x80000000U;
532                         dvd -> hlds_e7_mem_blocks = 5;
533                         break;
534         }
535
536         if (profile) {
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;
553                 }
554         }
555 }
556
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) {
558         if (!dvd)
559                 return;
560         dvd -> hlds_e7_type = type;
561         dvd -> hlds_e7_cache_base = cache_base;
562         dvd -> hlds_e7_mem_blocks = mem_blocks;
563 }
564
565 static bool dvd_is_tsst_kreon_candidate (dvd_drive *dvd) {
566         if (!(dvd_vendor_is (dvd, "TSSTcorp") || dvd_vendor_is (dvd, "SAMSUNG")))
567                 return false;
568         return
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");
575 }
576
577
578 /**
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.
584  */
585 void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) {
586         memset (mmc, 0, sizeof (mmc_command));
587         if (buf)
588                 memset (buf, 0, len);
589         mmc -> cmdlen = 12;
590         mmc -> direction = buf && len > 0 ? DVD_DATA_IN : DVD_DATA_NONE;
591         mmc -> buffer = buf;
592         mmc -> buflen = buf ? len : 0;
593         mmc -> sense = sense;
594         
595         return;
596 }
597
598
599 static void dvd_record_command_diagnostic (
600         dvd_drive *dvd,
601         mmc_command *mmc,
602         int transport_result,
603         int os_error,
604         int scsi_status,
605         int sense_key,
606         int asc,
607         int ascq
608 ) {
609         if (!dvd || !mmc)
610                 return;
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));
625 }
626
627 bool dvd_get_last_command_diagnostic (dvd_drive *dvd, dvd_command_diagnostic *out) {
628         if (!dvd || !out || !dvd -> last_command.valid)
629                 return false;
630         *out = dvd -> last_command;
631         return true;
632 }
633
634 #ifdef WIN32
635
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;
640         DWORD bytes;
641         DWORD win_error;
642         BOOL ioctl_ok;
643         int out;
644
645         sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
646         sense = &sptd_sense[sizeof (*sptd)];
647         
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;
657         else
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;
663         else
664                 sptd -> DataTransferLength = mmc -> buflen;
665         sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
666         sptd -> SenseInfoOffset = sizeof (*sptd);
667
668         if (mmc -> cmd[0] == 0xB6) {
669                 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
670                 sptd -> DataTransferLength = 28;
671         }
672
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) {
678                 out = -1;
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]);
685                 }
686         } else {
687                 out = 0;
688         }
689         
690         if (mmc -> sense) {
691                 mmc -> sense -> sense_key = sense[2];
692                 mmc -> sense -> asc = sense[12];
693                 mmc -> sense -> ascq = sense[13];
694         }
695         dvd_record_command_diagnostic (dvd, mmc, out, (int) win_error,
696                 (int) sptd -> ScsiStatus, sense[2], sense[12], sense[13]);
697         
698         return out;
699 }
700
701 #else
702
703 /**
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.
709  */
710 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
711         int out;
712         int saved_errno;
713         struct cdrom_generic_command cgc;
714         struct request_sense sense;
715         
716 #if 0
717         debug ("Executing MMC command: ");
718         hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
719 #endif
720
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;
730         else
731                 cgc.data_direction = CGC_DATA_READ;
732         cgc.timeout = MMC_CMD_TIMEOUT * 1000;
733         cgc.sense = &sense;
734         if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
735                 saved_errno = errno;
736                 out = -1;
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);
742                 }
743         } else {
744                 saved_errno = 0;
745                 out = 0;
746         }
747         
748         if (mmc -> sense) {
749                 mmc -> sense -> sense_key = sense.sense_key;
750                 mmc -> sense -> asc = sense.asc;
751                 mmc -> sense -> ascq = sense.ascq;
752         }
753         dvd_record_command_diagnostic (dvd, mmc, out, saved_errno,
754                 (int) cgc.stat, sense.sense_key, sense.asc, sense.ascq);
755         
756         return out;
757 }
758 #endif
759
760 #ifndef WIN32
761 /*
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.
767  */
768 static int dvd_xbox_sgio_exact (dvd_drive *dvd,
769                 const char *step,
770                 const u_int8_t *cdb,
771                 int cdb_len,
772                 u_int8_t *buf,
773                 u_int32_t buf_len,
774                 dvd_data_direction direction,
775                 unsigned int timeout_ms) {
776         sg_io_hdr_t io;
777         u_int8_t sense[32];
778         mmc_command diagnostic;
779         int rc;
780         int saved_errno = 0;
781         int transport_result;
782         int i;
783
784         if (!dvd || !cdb || cdb_len < 1 || cdb_len > 12)
785                 return -1;
786
787         memset (&io, 0, sizeof (io));
788         memset (sense, 0, sizeof (sense));
789         memset (&diagnostic, 0, sizeof (diagnostic));
790
791         io.interface_id = 'S';
792         io.cmdp = (unsigned char *) cdb;
793         io.cmd_len = (unsigned char) cdb_len;
794         io.sbp = sense;
795         io.mx_sb_len = sizeof (sense);
796         io.timeout = timeout_ms;
797         io.dxferp = buf;
798         io.dxfer_len = buf_len;
799
800         switch (direction) {
801         case DVD_DATA_OUT:
802                 io.dxfer_direction = SG_DXFER_TO_DEV;
803                 break;
804         case DVD_DATA_NONE:
805                 io.dxfer_direction = SG_DXFER_NONE;
806                 io.dxferp = NULL;
807                 io.dxfer_len = 0;
808                 break;
809         case DVD_DATA_IN:
810         default:
811                 io.dxfer_direction = SG_DXFER_FROM_DEV;
812                 break;
813         }
814
815         errno = 0;
816         rc = ioctl (dvd -> fd, SG_IO, &io);
817         if (rc < 0)
818                 saved_errno = errno;
819
820         transport_result =
821                 (rc == 0 &&
822                  io.status == 0 &&
823                  io.host_status == 0 &&
824                  io.driver_status == 0) ? 0 : -1;
825
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 (
832                 dvd,
833                 &diagnostic,
834                 transport_result,
835                 saved_errno,
836                 (int) io.status,
837                 sense[2],
838                 sense[12],
839                 sense[13]);
840
841         xbox_ref_log_fprintf (
842                 stderr,
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",
845                 rc,
846                 saved_errno,
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],
853                 io.resid,
854                 timeout_ms);
855         for (i = 0; i < cdb_len; i++)
856                 xbox_ref_log_fprintf (
857                         stderr,
858                         "%s%02X",
859                         i ? " " : "",
860                         (unsigned int) cdb[i]);
861         xbox_ref_log_fprintf (
862                 stderr,
863                 "] xfer=%u direction=%s result=%s\n",
864                 buf_len,
865                 direction == DVD_DATA_OUT ? "out" :
866                 direction == DVD_DATA_NONE ? "none" : "in",
867                 transport_result == 0 ? "PASS" : "FAIL");
868
869         return transport_result;
870 }
871
872 static int dvd_xbox_exact_read_capacity (dvd_drive *dvd,
873                 const char *step,
874                 u_int32_t *sectors,
875                 u_int32_t *sector_size) {
876         u_int8_t cdb[10];
877         u_int8_t buf[8];
878         u_int32_t max_lba;
879
880         memset (cdb, 0, sizeof (cdb));
881         memset (buf, 0, sizeof (buf));
882         cdb[0] = 0x25;
883
884         if (dvd_xbox_sgio_exact (
885                         dvd, step, cdb, sizeof (cdb), buf, sizeof (buf),
886                         DVD_DATA_IN, 120000) < 0)
887                 return -1;
888
889         max_lba =
890                 ((u_int32_t) buf[0] << 24) |
891                 ((u_int32_t) buf[1] << 16) |
892                 ((u_int32_t) buf[2] << 8) |
893                 (u_int32_t) buf[3];
894
895         if (sectors)
896                 *sectors = max_lba + 1;
897         if (sector_size)
898                 *sector_size =
899                         ((u_int32_t) buf[4] << 24) |
900                         ((u_int32_t) buf[5] << 16) |
901                         ((u_int32_t) buf[6] << 8) |
902                         (u_int32_t) buf[7];
903         return 0;
904 }
905
906 static int dvd_xbox_exact_mode_sense_10 (dvd_drive *dvd,
907                 const char *step,
908                 u_int8_t page,
909                 u_int8_t *buf,
910                 size_t buf_len) {
911         u_int8_t cdb[10];
912
913         if (!buf || buf_len > 0xFFFF)
914                 return -1;
915         memset (cdb, 0, sizeof (cdb));
916         memset (buf, 0, buf_len);
917         cdb[0] = 0x5A;
918         cdb[2] = page;
919         cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
920         cdb[8] = (u_int8_t) (buf_len & 0xFF);
921
922         return dvd_xbox_sgio_exact (
923                 dvd, step, cdb, sizeof (cdb), buf, (u_int32_t) buf_len,
924                 DVD_DATA_IN, 120000);
925 }
926
927 static int dvd_xbox_exact_mode_select_10 (dvd_drive *dvd,
928                 const char *step,
929                 const u_int8_t *buf,
930                 size_t buf_len) {
931         u_int8_t cdb[10];
932         u_int8_t tmp[256];
933
934         if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFFFF)
935                 return -1;
936         memset (cdb, 0, sizeof (cdb));
937         memset (tmp, 0, sizeof (tmp));
938         memcpy (tmp, buf, buf_len);
939         cdb[0] = 0x55;
940         cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
941         cdb[8] = (u_int8_t) (buf_len & 0xFF);
942
943         return dvd_xbox_sgio_exact (
944                 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
945                 DVD_DATA_OUT, 120000);
946 }
947
948 static int dvd_xbox_exact_mode_select_6 (dvd_drive *dvd,
949                 const char *step,
950                 const u_int8_t *buf,
951                 size_t buf_len) {
952         u_int8_t cdb[6];
953         u_int8_t tmp[64];
954
955         if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFF)
956                 return -1;
957         memset (cdb, 0, sizeof (cdb));
958         memset (tmp, 0, sizeof (tmp));
959         memcpy (tmp, buf, buf_len);
960         cdb[0] = 0x15;
961         cdb[1] = 0x11;
962         cdb[4] = (u_int8_t) buf_len;
963
964         return dvd_xbox_sgio_exact (
965                 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
966                 DVD_DATA_OUT, 10000);
967 }
968 #endif
969
970
971 /**
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.
975  */
976 static int dvd_get_drive_info (dvd_drive *dvd) {
977         mmc_command mmc;
978         int out;
979         u_int8_t buf[36];
980         char tmp[36 * 4];
981         
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);
994                 
995                 debug ("DVD drive is \"%s\"", dvd -> model_string);
996         } else {
997                 error ("Cannot identify DVD drive\n");
998         }
999
1000         return (out);
1001 }
1002
1003
1004 /**
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.
1008  */
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;
1020                 dvd -> command = 2;
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;
1036                 else
1037                         dvd -> def_method = 9;
1038
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
1044         )) {
1045                 debug ("Lite-On DVD drive detected, using Lite-On memory dump command");
1046                 dvd -> memdump = &liteon_dvd_dump_mem;
1047                 dvd -> command = 3;
1048                 dvd -> supported = true;
1049                 dvd -> def_method = 5;
1050
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
1054         ))) {
1055                 debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command");
1056                 dvd -> memdump = &vanilla_2384_dvd_dump_mem;
1057                 dvd -> command = 1;
1058                 dvd -> supported = true;
1059                 dvd -> def_method = 0;
1060
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;
1064                 dvd -> command = 0;
1065                 dvd -> supported = true;
1066                 dvd -> def_method = 2;
1067
1068         } else {
1069                 /* This is an unsupported drive (yet). */
1070                 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
1071                 dvd -> command = 0;
1072                 dvd -> supported = false;
1073         }
1074
1075         if (command!=-1) {
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;
1082         }
1083
1084
1085         //init Reed-Solomon for Lite-On
1086         generate_gf();
1087         gen_poly();
1088
1089         return;
1090 }
1091
1092
1093 /**
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.
1097  */
1098 dvd_drive *dvd_drive_new (char *device, u_int32_t command) {
1099         dvd_drive *dvd;
1100 #ifdef WIN32
1101         HANDLE fd;
1102         char dev[40];
1103 #else
1104         int fd;
1105 #endif
1106
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) */
1109         drop_euid ();
1110         
1111         debug ("Trying to open DVD device %s", device);
1112 #ifdef WIN32
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 ());
1116 #else
1117         if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) {
1118                 perror ("Cannot open drive");
1119 #endif
1120                 dvd = NULL;
1121         } else {
1122                 debug ("Opened successfully");
1123                 drop_euid ();
1124                 dvd = (dvd_drive *) malloc (sizeof (dvd_drive));
1125                 if (!dvd) {
1126                         fprintf (stderr, "malloc() failed\n");
1127                         exit (100);
1128                 }
1129                 memset (dvd, 0, sizeof (dvd_drive));
1130                 my_strdup (dvd -> device, device);
1131                 dvd -> fd = fd;
1132                 dvd_get_drive_info (dvd);
1133                 dvd_assign_functions (dvd, command);
1134         }
1135
1136         return (dvd);
1137 }
1138
1139
1140 /**
1141  * Frees resources used by a DVD drive structure and destroys it.
1142  * @param dvd The DVD drive structure to be destroyed.
1143  * @return NULL.
1144  */
1145 void *dvd_drive_destroy (dvd_drive *dvd) {
1146         if (dvd) {
1147 #ifdef WIN32
1148                 CloseHandle (dvd -> fd);
1149 #else
1150                 close (dvd -> fd);
1151 #endif
1152                 my_free (dvd -> device);
1153                 my_free (dvd -> vendor);
1154                 my_free (dvd -> prod_id);
1155                 my_free (dvd -> prod_rev);
1156                 my_free (dvd);
1157         }
1158
1159         return (NULL);
1160 }
1161
1162
1163 /**
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.
1171  */
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) {
1173         int out;
1174
1175         /* Upgrade privileges and call actual dump functions */
1176         upgrade_euid ();
1177         out = dvd -> memdump (dvd, block_off, block_len, block_size, buf);
1178         drop_euid ();
1179
1180         return (out);
1181 }
1182
1183
1184 /**
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.
1191  */
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) {
1193         mmc_command mmc;
1194         int out;
1195         u_int8_t intbuf[64 * 1024], *buf;
1196         size_t bufsize;
1197
1198         /* We need some buffer, be it provided externally or not */
1199         if (extbuf) {
1200                 buf = extbuf;
1201                 bufsize = extbufsize;
1202         } else {
1203                 buf = intbuf;
1204                 bufsize = sizeof (intbuf);
1205         }
1206
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! */
1219
1220         return (out);
1221 }
1222
1223
1224 /**
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.
1227  *
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.
1234  * @return 
1235  */
1236 int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1237         mmc_command mmc;
1238         int out;
1239         u_int8_t intbuf[2048 * 16], *buf;
1240         size_t bufsize;
1241
1242         /* We need some buffer, be it provided externally or not */
1243         if (extbuf) {
1244                 buf = extbuf;
1245                 bufsize = extbufsize;
1246         } else {
1247                 buf = intbuf;
1248                 bufsize = sizeof (intbuf);
1249         }
1250         
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);
1257         mmc.cmd[6] = 0;
1258         mmc.cmd[7] = 0;
1259         mmc.cmd[8] = 0;
1260         mmc.cmd[9] = 0x10;
1261         mmc.cmd[10] = 0x80;     /* STREAMING bit set */
1262         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
1263         
1264         return (out);
1265 }
1266
1267
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) {
1269         mmc_command mmc;
1270         int out;
1271         u_int8_t intbuf[64 * 1024], *buf;
1272         size_t bufsize;
1273
1274         /* We need some buffer, be it provided externally or not */
1275         if (extbuf) {
1276                 buf = extbuf;
1277                 bufsize = extbufsize;
1278         } else {
1279                 buf = intbuf;
1280                 bufsize = sizeof (intbuf);
1281         }
1282         
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! */
1295         
1296         return (out);
1297 }
1298
1299
1300 int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) {
1301         mmc_command mmc;
1302         int out;
1303         u_int8_t intbuf[64], *buf;
1304         size_t bufsize;
1305
1306         buf = intbuf;
1307         bufsize = 0;
1308         
1309         dvd_init_command (&mmc, buf, bufsize, sense);
1310         mmc.cmd[0] = MMC_READ_12;
1311         mmc.cmd[1] = 0x08;
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);
1317         
1318         return (out);
1319 }
1320
1321 static void dvd_sleep_ms (unsigned int ms) {
1322 #ifdef WIN32
1323         Sleep (ms);
1324 #else
1325         usleep ((useconds_t) ms * 1000);
1326 #endif
1327 }
1328
1329 int dvd_start_stop_unit (dvd_drive *dvd, bool start, bool load_eject, req_sense *sense) {
1330         mmc_command mmc;
1331         int out;
1332         u_int8_t intbuf[64], *buf;
1333         size_t bufsize;
1334
1335         buf = intbuf;
1336         bufsize = 0;
1337         
1338         dvd_init_command (&mmc, buf, bufsize, sense);
1339         mmc.cmd[0] = 0x1B;
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);
1343         
1344         return (out);
1345 }
1346
1347 int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) {
1348         return dvd_start_stop_unit (dvd, start, false, sense);
1349 }
1350
1351 int dvd_set_door_lock (dvd_drive *dvd, bool locked) {
1352 #ifdef WIN32
1353         (void) dvd;
1354         (void) locked;
1355         return 0;
1356 #else
1357         if (!dvd)
1358                 return -EINVAL;
1359         if (ioctl (dvd -> fd, CDROM_LOCKDOOR, locked ? 1 : 0) < 0)
1360                 return -errno;
1361         return 0;
1362 #endif
1363 }
1364
1365 int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
1366         unsigned int waited = 0;
1367         if (!dvd)
1368                 return -1;
1369         while (waited <= timeout_ms) {
1370                 if (dvd_test_unit_ready (dvd, NULL) == 0)
1371                         return 0;
1372                 if (waited == 0)
1373                         dvd_stop_unit (dvd, true, NULL); /* START UNIT, like EnsureDriveReady() */
1374                 dvd_sleep_ms (500);
1375                 waited += 500;
1376         }
1377         return -1;
1378 }
1379
1380 int dvd_media_cycle (dvd_drive *dvd, req_sense *sense) {
1381         int i;
1382         int out;
1383
1384 #ifndef WIN32
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);
1391         if (out < 0) {
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);
1395                 return out;
1396         }
1397         xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle released the optical door lock before software eject.\n");
1398 #endif
1399
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 */
1403         if (out < 0) {
1404                 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software eject command failed.\n");
1405                 return out;
1406         }
1407         dvd_sleep_ms (3000);
1408
1409         out = dvd_start_stop_unit (dvd, true, true, sense);  /* LoEj=1, Start=1: load */
1410         if (out < 0) {
1411                 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software load command failed; the door remains unlocked for recovery.\n");
1412                 return out;
1413         }
1414
1415         for (i = 0; i < 90; i++) {
1416                 dvd_sleep_ms (500);
1417                 if (dvd_test_unit_ready (dvd, NULL) == 0) {
1418                         dvd_sleep_ms (1500);
1419 #ifndef WIN32
1420                         out = dvd_set_door_lock (dvd, true);
1421                         if (out < 0)
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);
1425                         else
1426                                 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle restored the optical door lock after load.\n");
1427 #endif
1428                         return 0;
1429                 }
1430         }
1431
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");
1437         return -1;
1438 }
1439
1440 int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1441         mmc_command mmc;
1442         int out;
1443         u_int8_t intbuf[64], *buf;
1444         size_t bufsize;
1445
1446         buf = intbuf;
1447         bufsize = 0;
1448         
1449         dvd_init_command (&mmc, buf, bufsize, sense);
1450         mmc.cmd[0] = 0xBB;
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);
1454         
1455         return (out);
1456 }
1457
1458 int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) {
1459         mmc_command mmc;
1460         int out;
1461         u_int8_t intbuf[64], *buf;
1462         size_t bufsize;
1463
1464         buf = intbuf;
1465         bufsize = 0x22;
1466         
1467         dvd_init_command (&mmc, buf, bufsize, sense);
1468         mmc.cmd[0] = 0x52;
1469         mmc.cmd[1] = 0x01;
1470         mmc.cmd[5] = 0x01;
1471         mmc.cmd[8] = 0x22;
1472         out = dvd_execute_cmd (dvd, &mmc, true);
1473
1474         *(size)=*(size) << 8 | intbuf[0x18];
1475         *(size)=*(size) << 8 | intbuf[0x19];
1476         *(size)=*(size) << 8 | intbuf[0x1a];
1477         *(size)=*(size) << 8 | intbuf[0x1b];
1478
1479         return (out);
1480 }
1481
1482 int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) {
1483         mmc_command mmc;
1484         int out;
1485         u_int8_t intbuf[2052], *buf;
1486         size_t bufsize;
1487
1488         buf = intbuf;
1489         bufsize = 2052;
1490         
1491         dvd_init_command (&mmc, buf, bufsize, sense);
1492         mmc.cmd[0] = 0xad;
1493         mmc.cmd[8] = 0x08;
1494         mmc.cmd[9] = 0x04;
1495         out = dvd_execute_cmd (dvd, &mmc, true);
1496
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;
1502
1503         return (out);
1504 }
1505
1506 int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1507 /*
1508 DVD Decrypter->
1509 DeviceIoControl    : \Device\CdRom5
1510 Command            : IOCTL_SCSI_PASS_THROUGH_DIRECT
1511 Length             : 44 (0x002C)
1512 ScsiStatus         : 0
1513 PathId             : 0
1514 TargedId           : 0
1515 Lun                : 0
1516 CdbLength          : 12 (0x0C)
1517 SenseInfoLength    : 24 (0x18)
1518 DataTransferLength : 28 (0x0000001C)
1519 DataIn             : 0
1520 TimeOutValue       : 5000
1521
1522 CDB:
1523 00000000  B6 00 00 00 00 00 00 00 00 00 1C 00               ...........    
1524
1525 Data Sent:
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               ...____...    
1528 */
1529         mmc_command mmc;
1530         int out;
1531         u_int8_t inbuf[28], *buf;
1532         size_t bufsize;
1533
1534         buf = inbuf;
1535         bufsize = 28;
1536         
1537         dvd_init_command (&mmc, buf, bufsize, sense);
1538         mmc.cmd[00] = 0xB6;
1539         mmc.cmd[10] = 28;
1540
1541         *(buf+ 0)=0;//2
1542         *(buf+ 1)=0;
1543         *(buf+ 2)=0;
1544         *(buf+ 3)=0;
1545         *(buf+ 4)=0; //MSB
1546         *(buf+ 5)=0; //
1547         *(buf+ 6)=0; //
1548         *(buf+ 7)=0; //LSB
1549
1550         *(buf+ 8)=0xff; //MSB
1551         *(buf+ 9)=0xff; //
1552         *(buf+10)=0xff; //
1553         *(buf+11)=0xff; //LSB
1554
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);
1559
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);
1564
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);
1569
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);
1574
1575         out = dvd_execute_cmd (dvd, &mmc, true);
1576
1577         return (out);
1578 }
1579
1580
1581 int dvd_test_unit_ready (dvd_drive *dvd, req_sense *sense) {
1582         mmc_command mmc;
1583         u_int8_t intbuf[1];
1584
1585         dvd_init_command (&mmc, intbuf, 0, sense);
1586         mmc.cmd[0] = SPC_TEST_UNIT_READY;
1587         mmc.cmdlen = 6;
1588         mmc.direction = DVD_DATA_NONE;
1589         return dvd_execute_cmd (dvd, &mmc, true);
1590 }
1591
1592 int dvd_read_capacity_10 (dvd_drive *dvd, u_int32_t *sectors, u_int32_t *sector_size, req_sense *sense) {
1593         mmc_command mmc;
1594         u_int8_t buf[8];
1595         int out;
1596         u_int32_t max_lba, block_len;
1597
1598         dvd_init_command (&mmc, buf, sizeof (buf), sense);
1599         mmc.cmd[0] = MMC_READ_CAPACITY_10;
1600         mmc.cmdlen = 10;
1601         out = dvd_execute_cmd (dvd, &mmc, false);
1602         if (out >= 0) {
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];
1605                 if (sectors)
1606                         *sectors = max_lba + 1;
1607                 if (sector_size)
1608                         *sector_size = block_len;
1609         }
1610
1611         return out;
1612 }
1613
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) {
1615         mmc_command mmc;
1616         u_int8_t intbuf[64 * 1024], *buf;
1617         size_t need, bufsize;
1618
1619         need = (size_t) sectors * 2048;
1620         if (extbuf) {
1621                 buf = extbuf;
1622                 bufsize = extbufsize;
1623         } else {
1624                 buf = intbuf;
1625                 bufsize = sizeof (intbuf);
1626         }
1627
1628         if (need > bufsize) {
1629                 error ("dvd_read_10 buffer too small (%u sectors need %lu bytes)", sectors, (unsigned long) need);
1630                 return -1;
1631         }
1632
1633         dvd_init_command (&mmc, buf, (int) need, sense);
1634         mmc.cmd[0] = MMC_READ_10;
1635         mmc.cmdlen = 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);
1642
1643         return dvd_execute_cmd (dvd, &mmc, true);
1644 }
1645
1646 int dvd_mode_sense_10 (dvd_drive *dvd, u_int8_t page, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1647         mmc_command mmc;
1648
1649         if (!extbuf || extbufsize > 0xFFFF)
1650                 return -1;
1651
1652         dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1653         mmc.cmd[0] = SPC_MODE_SENSE_10;
1654         mmc.cmd[2] = page;
1655         mmc.cmd[7] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1656         mmc.cmd[8] = (u_int8_t)  (extbufsize & 0x00FF);
1657         mmc.cmdlen = 10;
1658
1659         return dvd_execute_cmd (dvd, &mmc, false);
1660 }
1661
1662 int dvd_mode_select_10 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1663         mmc_command mmc;
1664         u_int8_t tmp[256];
1665
1666         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFFFF)
1667                 return -1;
1668         memset (tmp, 0, sizeof (tmp));
1669         memcpy (tmp, buf, bufsize);
1670
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);
1676         mmc.cmdlen = 10;
1677
1678         return dvd_execute_cmd (dvd, &mmc, false);
1679 }
1680
1681 int dvd_mode_select_6 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1682         mmc_command mmc;
1683         u_int8_t tmp[64];
1684
1685         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFF)
1686                 return -1;
1687         memset (tmp, 0, sizeof (tmp));
1688         memcpy (tmp, buf, bufsize);
1689
1690         dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1691         mmc.direction = DVD_DATA_OUT;
1692         mmc.cmd[0] = SPC_MODE_SELECT_6;
1693         mmc.cmd[1] = 0x11;
1694         mmc.cmd[4] = (u_int8_t) (bufsize & 0xFF);
1695         mmc.cmdlen = 6;
1696
1697         return dvd_execute_cmd (dvd, &mmc, false);
1698 }
1699
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) {
1701         mmc_command mmc;
1702
1703         if (!extbuf || extbufsize > 0xFFFF)
1704                 return -1;
1705
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. */
1711         mmc.cmd[6] = layer;
1712         mmc.cmd[7] = format;
1713         mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1714         mmc.cmd[9] = (u_int8_t)  (extbufsize & 0x00FF);
1715         mmc.cmdlen = 12;
1716
1717         return dvd_execute_cmd (dvd, &mmc, false);
1718 }
1719
1720
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) {
1722         mmc_command mmc;
1723
1724         dvd_init_command (&mmc, buf, (int) bufsize, NULL);
1725         mmc.cmd[0] = 0xFF;
1726         mmc.cmd[1] = 0x08;
1727         mmc.cmd[2] = 0x01;
1728         mmc.cmd[3] = subcommand;
1729         mmc.cmd[4] = value;
1730         mmc.cmdlen = 10;
1731         mmc.direction = direction;
1732         if (direction == DVD_DATA_NONE) {
1733                 mmc.buffer = NULL;
1734                 mmc.buflen = 0;
1735         }
1736         return dvd_execute_cmd (dvd, &mmc, false);
1737 }
1738
1739 static bool dvd_xbox_feature_list_has (const u_int16_t *features, size_t count, u_int16_t needle) {
1740         size_t i;
1741         if (!features)
1742                 return false;
1743         for (i = 0; i < count && features[i] != 0; i++) {
1744                 if (features[i] == needle)
1745                         return true;
1746         }
1747         return false;
1748 }
1749
1750 int dvd_xbox_vendor_get_feature_list (dvd_drive *dvd, u_int16_t *features, size_t max_features) {
1751         u_int8_t buf[26];
1752         size_t i, count;
1753
1754         if (!dvd || !features || max_features == 0)
1755                 return -1;
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)
1759                 return -1;
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");
1763                 return -1;
1764         }
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];
1770         return 0;
1771 }
1772
1773 int dvd_xbox_vendor_lock (dvd_drive *dvd) {
1774         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1775                 return -1;
1776         return dvd_xbox_vendor_command (dvd, 0x11, 0x00, NULL, 0, DVD_DATA_NONE);
1777 }
1778
1779 int dvd_xbox_vendor_set_error_skip (dvd_drive *dvd, bool enabled) {
1780         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1781                 return -1;
1782         return dvd_xbox_vendor_command (dvd, 0x15, enabled ? 0x01 : 0x00, NULL, 0, DVD_DATA_NONE);
1783 }
1784
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;
1788
1789         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1790                 return -1;
1791
1792         if (dvd_xbox_vendor_get_feature_list (dvd, features, sizeof (features) / sizeof (features[0])) < 0)
1793                 return -1;
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");
1797
1798         if (dvd_xbox_vendor_command (dvd, 0x11, 0x02, NULL, 0, DVD_DATA_NONE) < 0)
1799                 return -1;
1800
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);
1804
1805         if (dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL) < 0)
1806                 return -1;
1807         if (sector_size != 2048 || sectors < 1000000) {
1808                 error ("Xbox vendor unlock did not expose the expected 2048-byte view");
1809                 return -1;
1810         }
1811         if (unlocked_sectors)
1812                 *unlocked_sectors = sectors;
1813         return 0;
1814 }
1815
1816
1817 #define XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
1818
1819 static int dvd_xbox_refresh_ready_capacity (dvd_drive *dvd,
1820                 u_int32_t *sectors,
1821                 u_int32_t *sector_size) {
1822         u_int32_t observed_sectors = 0;
1823         u_int32_t observed_sector_size = 0;
1824
1825         if (!dvd)
1826                 return -1;
1827
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)
1833                 return -1;
1834         if (dvd_read_capacity_10 (dvd, &observed_sectors, &observed_sector_size, NULL) < 0)
1835                 return -1;
1836
1837         if (sectors)
1838                 *sectors = observed_sectors;
1839         if (sector_size)
1840                 *sector_size = observed_sector_size;
1841         return 0;
1842 }
1843
1844 int dvd_xbox_prepare_game_view (dvd_drive *dvd,
1845                 u_int32_t *sectors,
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;
1850
1851         if (!dvd || !dvd_is_xbox_challenge_drive (dvd))
1852                 return -1;
1853
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");
1861                 return -1;
1862         }
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");
1866                 return -1;
1867         }
1868         xbox_ref_log_fprintf (stderr,
1869                 "[XBOX-WINSEQ] Entry READ CAPACITY: %u sectors.\n",
1870                 entry_sectors);
1871
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)
1876                         return -1;
1877         } else {
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)
1884                         return -1;
1885                 xbox_ref_log_fprintf (stderr,
1886                         "[XBOX-WINSEQ] Direct-handshake READ CAPACITY: %u sectors.\n",
1887                         observed_sectors);
1888
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)
1893                                 return -1;
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)
1900                                 return -1;
1901                         xbox_ref_log_fprintf (stderr,
1902                                 "[XBOX-WINSEQ] Recovery-handshake READ CAPACITY: %u sectors.\n",
1903                                 observed_sectors);
1904
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");
1908                                 return -1;
1909                         }
1910                 }
1911         }
1912
1913         /* Exact next Windows step after state preparation. */
1914         dvd_set_speed (dvd, 0xFFFF, NULL);
1915
1916         if (sectors)
1917                 *sectors = observed_sectors;
1918         if (sector_size)
1919                 *sector_size = observed_sector_size;
1920         return 0;
1921 }
1922
1923 int dvd_refresh_volume (dvd_drive *dvd) {
1924 #ifdef WIN32
1925         DWORD bytesReturned = 0;
1926         if (!dvd)
1927                 return -1;
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);
1934         return 0;
1935 #else
1936         (void) dvd;
1937         return 0;
1938 #endif
1939 }
1940
1941 int dvd_lock_volume (dvd_drive *dvd) {
1942 #ifdef WIN32
1943         DWORD bytesReturned = 0;
1944         if (!dvd)
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;
1949 #else
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. */
1953         (void) dvd;
1954         return DVD_VOLUME_LOCK_UNAVAILABLE;
1955 #endif
1956 }
1957
1958 int dvd_xbox_refresh_volume (dvd_drive *dvd) {
1959         return dvd_refresh_volume (dvd);
1960 }
1961
1962 int dvd_xbox_lock_volume (dvd_drive *dvd) {
1963         return dvd_lock_volume (dvd);
1964 }
1965
1966 static int dvd_xbox_read_host_challenge_table (dvd_drive *dvd, u_int8_t *table, size_t table_len) {
1967         mmc_command mmc;
1968         int out;
1969
1970         if (!table || table_len < 0x664)
1971                 return -1;
1972
1973 #ifdef WIN32
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;
1977         mmc.cmd[2] = 0xFF;
1978         mmc.cmd[3] = 0x02;
1979         mmc.cmd[4] = 0xFD;
1980         mmc.cmd[5] = 0xFF;
1981         mmc.cmd[6] = 0xFE;
1982         mmc.cmd[8] = 0x06;
1983         mmc.cmd[9] = 0x64;
1984         mmc.cmd[11] = 0xC0;
1985         mmc.cmdlen = 12;
1986         out = dvd_execute_cmd (dvd, &mmc, true);
1987
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);
1991                 mmc.cmd[0] = 0xFD;
1992                 mmc.cmd[1] = 0x01;
1993                 mmc.cmd[8] = 0x06;
1994                 mmc.cmd[9] = 0x64;
1995                 mmc.cmdlen = 12;
1996                 out = dvd_execute_cmd (dvd, &mmc, true);
1997         }
1998 #else
1999         {
2000                 u_int8_t cdb[12];
2001
2002                 memset (cdb, 0, sizeof (cdb));
2003                 memset (table, 0, table_len);
2004                 cdb[0] = 0xAD;
2005                 cdb[2] = 0xFF;
2006                 cdb[3] = 0x02;
2007                 cdb[4] = 0xFD;
2008                 cdb[5] = 0xFF;
2009                 cdb[6] = 0xFE;
2010                 cdb[8] = 0x06;
2011                 cdb[9] = 0x64;
2012                 cdb[11] = 0xC0;
2013                 out = dvd_xbox_sgio_exact (
2014                         dvd,
2015                         "3-read-dvd-structure-c0",
2016                         cdb,
2017                         sizeof (cdb),
2018                         table,
2019                         0x664,
2020                         DVD_DATA_IN,
2021                         120000);
2022
2023                 if (out < 0 || table[772] != 1 || table[773] == 0) {
2024                         memset (cdb, 0, sizeof (cdb));
2025                         memset (table, 0, table_len);
2026                         cdb[0] = 0xFD;
2027                         cdb[1] = 0x01;
2028                         cdb[8] = 0x06;
2029                         cdb[9] = 0x64;
2030                         out = dvd_xbox_sgio_exact (
2031                                 dvd,
2032                                 "3-read-host-table-fallback-fd",
2033                                 cdb,
2034                                 sizeof (cdb),
2035                                 table,
2036                                 0x664,
2037                                 DVD_DATA_IN,
2038                                 120000);
2039                 }
2040         }
2041 #endif
2042
2043         if (out < 0 || table[772] != 1)
2044                 return -1;
2045
2046         xbox_ref_log_fprintf (
2047                 stderr,
2048                 "[XBOX-SGIO] challenge-table marker=%u entries-byte=%u result=PASS\n",
2049                 (unsigned int) table[772],
2050                 (unsigned int) table[773]);
2051         return 0;
2052 }
2053
2054 int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
2055         int i, k, l;
2056         int out;
2057         int chalpos[24];
2058         u_int8_t table[0x664];
2059         u_int8_t restable[261];
2060         u_int8_t hash[0x2C];
2061         SHA1_HASH digest;
2062         u_int8_t page[28];
2063         u_int8_t sticky[12];
2064         u_int32_t sectors = 0, sector_size = 0;
2065         xbox_rc4_ctx rc4;
2066
2067         if (!dvd || !dvd_is_xbox_unlock_drive (dvd))
2068                 return -1;
2069
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
2072          * the authority. */
2073 #ifdef WIN32
2074         dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL);
2075         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2076 #else
2077         dvd_xbox_exact_read_capacity (
2078                 dvd, "1-initial-read-capacity", &sectors, &sector_size);
2079         dvd_xbox_exact_mode_sense_10 (
2080                 dvd, "2-mode-sense-3e", 0x3E, page, sizeof (page));
2081 #endif
2082
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");
2086                 return -1;
2087         }
2088
2089         for (i = 0; i < 0x2C; i++)
2090                 hash[i] = table[0x4A3 + i];
2091         Sha1Calculate (hash, 0x2C, &digest);
2092
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);
2097
2098         k = 0;
2099         for (l = 0; l <= 23; l++) {
2100                 if (restable[l * 11] == 1) {
2101                         chalpos[k++] = l;
2102                         if (k == (int) (sizeof (chalpos) / sizeof (chalpos[0])))
2103                                 break;
2104                 }
2105         }
2106         if (k < 2) {
2107                 error ("Xbox challenge table does not contain enough usable entries");
2108                 return -1;
2109         }
2110
2111         /* Step 4: first host challenge. */
2112         memset (page, 0, sizeof (page));
2113         page[1] = 0x1A;
2114         page[8] = 0x3E;
2115         page[9] = 0x12;
2116         page[11] = 0x01;
2117         page[13] = 0xD1;
2118         page[14] = 0x01;
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. */
2123 #ifdef WIN32
2124         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2125         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2126 #else
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));
2131 #endif
2132
2133         /* Step 6: second host challenge. */
2134         memset (page, 0, sizeof (page));
2135         page[1] = 0x1A;
2136         page[8] = 0x3E;
2137         page[9] = 0x12;
2138         page[12] = 0x01;
2139         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2140 #ifdef WIN32
2141         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2142         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2143 #else
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));
2148 #endif
2149
2150         /* Step 8: unlock partition 1. */
2151         memset (page, 0, sizeof (page));
2152         page[1] = 0x1A;
2153         page[8] = 0x3E;
2154         page[9] = 0x12;
2155         page[10] = 0x01;
2156         page[11] = 0x01;
2157         page[12] = 0x01;
2158         page[13] = 0xD1;
2159         page[14] = 0x01;
2160         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2161 #ifdef WIN32
2162         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2163 #else
2164         dvd_xbox_exact_mode_select_10 (
2165                 dvd, "8-mode-select-partition-1-unlock", page, sizeof (page));
2166 #endif
2167
2168         /* Step 9: sticky descrambling, mode page 0x31. */
2169         memset (sticky, 0, sizeof (sticky));
2170         sticky[4] = 0x31;
2171         sticky[5] = 0x06;
2172         sticky[6] = 0x01;
2173 #ifdef WIN32
2174         dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
2175 #else
2176         dvd_xbox_exact_mode_select_6 (
2177                 dvd, "9-mode-select-sticky-descrambling", sticky, sizeof (sticky));
2178 #endif
2179
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. */
2184         sectors = 0;
2185         sector_size = 0;
2186 #ifdef WIN32
2187         out = dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL);
2188 #else
2189         out = dvd_xbox_exact_read_capacity (
2190                 dvd, "10-final-read-capacity", &sectors, &sector_size);
2191 #endif
2192         if (out == 0) {
2193                 if (unlocked_sectors)
2194                         *unlocked_sectors = sectors;
2195                 xbox_ref_log_fprintf (
2196                         stderr,
2197                         "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n",
2198                         sectors,
2199                         sector_size);
2200         } else {
2201                 xbox_ref_log_fprintf (
2202                         stderr,
2203                         "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
2204         }
2205
2206         return 0;
2207 }
2208
2209 bool dvd_is_xbox_challenge_drive (dvd_drive *dvd) {
2210         return dvd_is_hlds_drive (dvd) && dvd_prod_has (dvd, "GDR8050L");
2211 }
2212
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);
2216 }
2217
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);
2220 }
2221
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")));
2228 }
2229
2230
2231
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);
2236 }
2237
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";
2241 }
2242
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);
2246 }
2247
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 : "";
2251 }
2252
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 : "";
2256 }
2257
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 : "";
2261 }
2262
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;
2265 }
2266
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;
2269 }
2270
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;
2273 }
2274
2275 u_int32_t dvd_get_hlds_e7_type (dvd_drive *dvd) {
2276         return dvd ? dvd -> hlds_e7_type : 0;
2277 }
2278
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;
2281 }
2282
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;
2285 }
2286
2287 char *dvd_get_vendor (dvd_drive *dvd) {
2288         return (dvd -> vendor);
2289 }
2290
2291
2292 char *dvd_get_product_id (dvd_drive *dvd) {
2293         return (dvd -> prod_id);
2294 }
2295
2296
2297 char *dvd_get_product_revision (dvd_drive *dvd) {
2298         return (dvd -> prod_rev);
2299 }
2300
2301
2302 char *dvd_get_model_string (dvd_drive *dvd) {
2303         return (dvd -> model_string);
2304 }
2305
2306
2307 char *dvd_get_device (dvd_drive *dvd) {
2308         return (dvd -> device);
2309 }
2310
2311 void *dvd_get_native_handle (dvd_drive *dvd) {
2312         if (!dvd) return NULL;
2313 #ifdef WIN32
2314         return (void *) dvd -> fd;
2315 #else
2316         return NULL;
2317 #endif
2318 }
2319
2320
2321 bool dvd_get_support_status (dvd_drive *dvd) {
2322         return (dvd -> supported);
2323 }
2324
2325 u_int32_t dvd_get_def_method (dvd_drive *dvd){
2326         return (dvd -> def_method);
2327 }
2328
2329 u_int32_t dvd_get_command (dvd_drive *dvd){
2330         return (dvd -> command);
2331 }