]> FriiDump Source - friidump.git/blob - libfriidump/dvd_drive.c
fe71f801931edd8db2ca89c97a3530d2a8e10c97
[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 dvd_is_hlds_gcc4243_4244_drive (dvd_drive *dvd) {
204         static const char *names[] = {
205                 "GCC-4243N", "GCC4243N", "GCC4243",
206                 "GCC-4244N", "GCC4244N", "GCC4244"
207         };
208         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
209 }
210
211 static bool dvd_is_hlds_gdr8050l_drive (dvd_drive *dvd) {
212         static const char *names[] = {
213                 "GDR8050L", "GDR-8050L"
214         };
215         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
216 }
217
218
219 typedef struct {
220         const char *model;
221         const char *firmware;
222         u_int32_t type;
223         u_int32_t cache_base;
224         u_int32_t mem_blocks;
225         int preferred_method;
226         const char *label;
227         const char *support_tier;
228         const char *family;
229         const char *tokens;
230         const char *record_id;
231         u_int32_t static_cdb_base;
232         u_int32_t static_gate;
233         const char *notes;
234 } hlds_e7_profile_desc;
235
236 static const hlds_e7_profile_desc hlds_e7_profiles[] = {
237         { "GCC-4241N", "A101", 21, 0x80000000U, 1, 8, "GCC-4241N A101 promoted E7 parser profile", "error_prone_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0064", 0x85cU, 0x900356b7U, "capable but error-prone; conservative Method 8 one-window validation profile" },
238         { "GCC-4242N", "0J06", 22, 0x80000000U, 1, 8, "GCC-4242N 0J06 promoted E7 parser profile", "error_prone_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0066", 0x824U, 0x90038621U, "capable but error-prone; conservative Method 8 one-window validation profile" },
239         { "GCC-4243N", "0000", 3, 0x80000000U, 5, 8, "GCC-4243N 0000 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0069", 0x884U, 0x90037929U, "known-supported GCC_424x profile hardening target" },
240         { "GCC-4243N", "1.08", 3, 0x80000000U, 5, 8, "GCC-4243N 1.08 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0071", 0x880U, 0x90036813U, "known-supported GCC_424x profile hardening target" },
241         { "GCC-4244N", "1.03", 3, 0x80000000U, 5, 8, "GCC-4244N 1.03 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0073", 0x88cU, 0x90037d06U, "P1 owned GCC_424x profile hardening target" },
242         { "GCC-4244N", "103", 3, 0x80000000U, 5, 8, "GCC-4244N 103 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0076", 0x894U, 0x900386fbU, "P1 owned GCC_424x profile hardening target" },
243         { "GCC4244", "B103", 3, 0x80000000U, 5, 8, "GCC-4244N B103 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "promoted_parser_signature_v23", 0x894U, 0x900386fbU, "Live INQUIRY alias for HL-DT-ST CDRW/DVD GCC4244 B103; case label B101; exact Stage5B parser signature recovered; media preflight, seed retrieval, full GameCube dump, STOP UNIT, and Redump hash match validated" },
244         { "GCC-4244N", "B103", 3, 0x80000000U, 5, 8, "GCC-4244N B103 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "promoted_parser_signature_v23", 0x894U, 0x900386fbU, "B103 shares the promoted parser signature gate/CDB with 103/104; live GCC4244/B103 hardware completed a Redump-matching GameCube dump" },
245         { "GCC-4244N", "104", 3, 0x80000000U, 5, 8, "GCC-4244N 104 promoted E7 parser profile", "known_supported_profile_hardening", "GCC_424x", "HL;IT;RPC;RPC_JCS3;RPC_SUFFIX", "stage5b_0078", 0x894U, 0x900386fbU, "P1 owned GCC_424x profile hardening target" },
246         { "GDR-3120L", "0046", 4, 0x80000000U, 5, 8, "GDR-3120L 0046 experimental GC/Wii E7 parser profile", "experimental_gc_wii_candidate", "GDR_3120x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0355", 0x5b8U, 0x90025bceU, "historically Xbox/reference; allow read-only GC/Wii Method 8 experiment, not proven support until dump validates" },
247         { "GDR-8050L", "0L23", 44, 0x80000000U, 1, 8, "GDR-8050L 0L23 donor/modified-firmware E7 parser profile", "donor_reference_modified_firmware_only", "GDR_8050x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0099", 0x5d0U, 0x90026160U, "stock firmware is Xbox-only here; GC/Wii 0xE7 path requires modified firmware with memdump support" },
248         { "GDR-8082N", "0120", 4, 0x80000000U, 5, 9, "GDR-8082N 0120 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_808x", "HL;IT;RPC;RPC_SUFFIX", "stage5b_0103", 0x638U, 0x900282daU, "known-supported reference profile" },
249         { "GDR-8083N", "0K04", 4, 0x80000000U, 5, 9, "GDR-8083N 0K04 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_808x", "HL;IT;RPC;RPC_SUFFIX", "stage5b_0104", 0x638U, 0x900291fcU, "known-supported reference profile" },
250         { "GDR-8161B", "0102", 4, 0x80000000U, 5, 9, "GDR-8161B 0102 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_816x", "IT;RPC;RPC_SUFFIX", "stage5b_0109", 0x5a8U, 0x90025010U, "known-supported reference profile" },
251         { "GDR-8163B", "0L23", 4, 0x80000000U, 5, 9, "GDR-8163B 0L23 promoted E7 parser profile", "known_supported_profile_hardening_p0", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0110", 0x5e0U, 0x90024d5aU, "P0 owned profile hardening target" },
252         { "GDR-8163B", "0L30", 4, 0x80000000U, 5, 8, "GDR-8163B 0L30 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90025021U, "Germany-batch variant; Method 8 seed retrieval and full GameCube dump OK; exact Stage5B parser signature recovered" },
253         { "GDR-8163B", "0L20", 4, 0x80000000U, 5, -1, "GDR-8163B 0L20 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024c8fU, "Germany-batch variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
254         { "GDR-8163B", "0D20", 4, 0x80000000U, 5, -1, "GDR-8163B 0D20 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024ae7U, "Germany-batch variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
255         { "GDR-8163B", "0B30", 4, 0x80000000U, 5, -1, "GDR-8163B 0B30 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90025030U, "Germany-batch HP/OEM variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
256         { "GDR-8163B", "0E15", 4, 0x80000000U, 5, -1, "GDR-8163B 0E15 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5d8U, 0x900247d1U, "Germany-batch HP/OEM variant; exact Stage5B parser signature recovered with CDB base 0x5D8; full GameCube dump matches Redump" },
257         { "GDR-8163B", "0M26", 4, 0x80000000U, 5, -1, "GDR-8163B 0M26 promoted E7 parser profile", "known_supported_profile_hardening_live_validated", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "promoted_parser_signature_v23", 0x5e0U, 0x90024ff7U, "Germany-batch Lenovo/OEM Malaysia variant; exact Stage5B parser signature recovered; full GameCube dump matches Redump" },
258         { "GDR-8164B", "0L06", 4, 0x80000000U, 5, 9, "GDR-8164B 0L06 promoted E7 parser profile", "known_supported_profile_hardening", "GDR_816x", "HL;IT;RPC;RPC_JD4_SPACE;RPC_SUFFIX", "stage5b_0111/stage5b_0113", 0x5ccU, 0x90025bbeU, "known-supported reference profile; two firmware records agree" },
259         { NULL, NULL, 0, 0, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }
260 };
261
262 static void hlds_normalize_model (const char *src, char *dst, size_t dst_size) {
263         size_t i, j;
264         if (!dst || dst_size == 0)
265                 return;
266         dst[0] = 0;
267         if (!src)
268                 return;
269         for (i = 0, j = 0; src[i] && j + 1 < dst_size; i++) {
270                 unsigned char c = (unsigned char) src[i];
271                 if (isalnum (c))
272                         dst[j++] = (char) toupper (c);
273         }
274         dst[j] = 0;
275 }
276
277 static bool hlds_model_matches (dvd_drive *dvd, const char *model) {
278         char prod_norm[64];
279         char model_norm[64];
280         if (!dvd || !dvd -> prod_id || !model)
281                 return false;
282         hlds_normalize_model (dvd -> prod_id, prod_norm, sizeof (prod_norm));
283         hlds_normalize_model (model, model_norm, sizeof (model_norm));
284         return prod_norm[0] && model_norm[0] && strstr (prod_norm, model_norm) != NULL;
285 }
286
287 static bool hlds_revision_matches (dvd_drive *dvd, const char *revision) {
288         const unsigned char *a;
289         const unsigned char *b;
290         if (!revision || !revision[0])
291                 return true;
292         if (!dvd || !dvd -> prod_rev)
293                 return false;
294         a = (const unsigned char *) dvd -> prod_rev;
295         b = (const unsigned char *) revision;
296         while (*a && *b) {
297                 if (toupper (*a) != toupper (*b))
298                         return false;
299                 a++;
300                 b++;
301         }
302         return *a == 0 && *b == 0;
303 }
304
305 static const hlds_e7_profile_desc *dvd_find_hlds_e7_profile (dvd_drive *dvd) {
306         const hlds_e7_profile_desc *p;
307         if (!dvd_is_hlds_drive (dvd))
308                 return NULL;
309         for (p = hlds_e7_profiles; p -> model; p++) {
310                 if (hlds_model_matches (dvd, p -> model) && hlds_revision_matches (dvd, p -> firmware))
311                         return p;
312         }
313         return NULL;
314 }
315
316 static u_int32_t dvd_hlds_e7_detect_type (dvd_drive *dvd) {
317         const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
318         static const char *type1[] = {
319                 "GCC-4160N", "GCC4160N", "GCC4160",
320                 "GCC-4240N", "GCC4240N", "GCC4240"
321         };
322         static const char *type2_1[] = {
323                 "GCC-4241N", "GCC4241N", "GCC4241"
324         };
325         static const char *type2_2[] = {
326                 "GCC-4242N", "GCC4242N", "GCC4242"
327         };
328         static const char *gdr8081n[] = {
329                 "GDR8081N", "GDR-8081N"
330         };
331         static const char *type3[] = {
332                 "GCC4244", "GCC4244N", "GCC-4244N",
333                 "GCC4247", "GCC4247N", "GCC-4247N",
334                 "GDR8083N", "GDR8084N",
335                 "GCC-4243N", "GCC4243N", "GCC4243",
336                 "GCC-4246N", "GCC4246N", "GCC4246"
337         };
338         static const char *type4[] = {
339                 "DU10N", "GDR8082N", "GDR8161B", "GDR8162B",
340                 "GDR8163B", "GDR8164B", "GDR-T10N",
341                 /* Local project keeps GDR-3120L in the same transport family for
342                  * classification, but Xbox dumping is still routed through the explicit
343                  * Xbox paths rather than the GC/Wii Method 8/9 readers. */
344                 "GDR3120L", "GDR-3120L"
345         };
346
347         if (!dvd_is_hlds_drive (dvd))
348                 return 0;
349         if (profile)
350                 return profile -> type;
351         if (dvd_prod_has_any (dvd, type1, sizeof (type1) / sizeof (type1[0])))
352                 return 1;
353         if (dvd_prod_has_any (dvd, type2_1, sizeof (type2_1) / sizeof (type2_1[0])))
354                 return 21;
355         if (dvd_prod_has_any (dvd, type2_2, sizeof (type2_2) / sizeof (type2_2[0])))
356                 return 22;
357         if (dvd_prod_has_any (dvd, gdr8081n, sizeof (gdr8081n) / sizeof (gdr8081n[0])))
358                 return 81;
359         if (dvd_prod_has_any (dvd, type3, sizeof (type3) / sizeof (type3[0])))
360                 return 3;
361         if (dvd_is_hlds_gdr8050l_drive (dvd))
362                 return 44;
363         if (dvd_prod_has_any (dvd, type4, sizeof (type4) / sizeof (type4[0])))
364                 return 4;
365         /* DIC labels GSA-4163B as an Xbox swap candidate, not a normal 0xE7
366          * Nintendo-disc cache dump profile, so keep it out of the profile layer. */
367         return 0;
368 }
369
370 static bool dvd_is_hlds_gc_wii_drive (dvd_drive *dvd) {
371         return dvd_hlds_e7_detect_type (dvd) != 0;
372 }
373
374 static const char *dvd_hlds_e7_profile_name_from_type (u_int32_t type) {
375         switch (type) {
376                 case 1: return "Type1";
377                 case 21: return "Type2_1 experimental";
378                 case 22: return "Type2_2 experimental";
379                 case 3: return "Type3";
380                 case 4: return "Type4";
381                 case 44: return "GDR-8050L modified 0xE7 single-window proven fallback";
382                 case 45: return "GDR-8050L modified 0xE7 speed-probe pending";
383                 case 442: return "GDR-8050L modified 0xE7 probe B 2-window";
384                 case 443: return "GDR-8050L modified 0xE7 probe A 3-window";
385                 case 445: return "GDR-8050L modified 0xE7 probe C 5-window guarded";
386                 case 81: return "GDR-8081N experimental 0xE7 probe";
387                 case 811: return "GDR-8081N probe A Type4-derived";
388                 case 812: return "GDR-8081N probe B single-window";
389                 case 813: return "GDR-8081N probe C Type1-base";
390                 case 814: return "GDR-8081N probe E exact-offset moving-cache candidate";
391                 case 815: return "GDR-8081N probe A scan-guided Type4-derived";
392                 default: return "none";
393         }
394 }
395
396 static void dvd_apply_hlds_e7_profile (dvd_drive *dvd) {
397         const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
398         dvd -> hlds_e7_type = dvd_hlds_e7_detect_type (dvd);
399         dvd -> hlds_e7_cache_base = 0x80000000U;
400         dvd -> hlds_e7_mem_blocks = 5;
401         dvd -> hlds_e7_static_cdb_base = 0;
402         dvd -> hlds_e7_static_gate = 0;
403         dvd -> hlds_e7_preferred_method = -1;
404         dvd -> hlds_e7_profile_label = NULL;
405         dvd -> hlds_e7_support_tier = NULL;
406         dvd -> hlds_e7_family = NULL;
407         dvd -> hlds_e7_tokens = NULL;
408         dvd -> hlds_e7_record_id = NULL;
409         dvd -> hlds_e7_notes = NULL;
410
411         switch (dvd -> hlds_e7_type) {
412                 case 1:
413                         /* DIC Type1: GCC-4160N/GCC-4240N cache frames begin at 0x00a13000
414                          * and only one 16-sector cache window is consumed per READ. */
415                         dvd -> hlds_e7_cache_base = 0x00a13000U;
416                         dvd -> hlds_e7_mem_blocks = 1;
417                         break;
418                 case 21:
419                 case 22:
420                         /* DIC Type2 uses a moving 0x80000000-derived cache address.  This
421                          * branch logs/classifies it, but does not yet claim DIC parity. */
422                         dvd -> hlds_e7_cache_base = 0x80000000U;
423                         dvd -> hlds_e7_mem_blocks = 1;
424                         break;
425                 case 81:
426                         /* GDR-8081N is not in the confirmed DIC dump list, but local firmware
427                          * analysis suggests an 0xE7 command surface.  Start with a Type4-derived
428                          * candidate; disc.c probes and may switch to one of the 811..814 runtime
429                          * profiles before seed cracking continues. */
430                         dvd -> hlds_e7_cache_base = 0x80000000U;
431                         dvd -> hlds_e7_mem_blocks = 5;
432                         break;
433                 case 44:
434                         /* Stock GDR-8050L firmware does not expose the HIT 0xE7 memdump command.
435                          * The local test unit is GDR-8163B hardware cross-flashed with modified
436                          * GDR-8050L firmware where 0xE7 memdump was added.  Single-window has
437                          * completed and hash-matched Sonic, so it remains the proven fallback.
438                          * Start in an explicit speed-probe-pending profile so the initial drive
439                          * information does not look like the old static single-window build.
440                          * disc.c promotes to 2/3/5 windows only after guarded validation, or
441                          * settles back to the proven single-window profile. */
442                         dvd -> hlds_e7_type = 45;
443                         dvd -> hlds_e7_cache_base = 0x80000000U;
444                         dvd -> hlds_e7_mem_blocks = 1;
445                         break;
446                 case 3:
447                 case 4:
448                 default:
449                         dvd -> hlds_e7_cache_base = 0x80000000U;
450                         dvd -> hlds_e7_mem_blocks = 5;
451                         break;
452         }
453
454         if (profile) {
455                 dvd -> hlds_e7_cache_base = profile -> cache_base;
456                 dvd -> hlds_e7_mem_blocks = profile -> mem_blocks;
457                 dvd -> hlds_e7_static_cdb_base = profile -> static_cdb_base;
458                 dvd -> hlds_e7_static_gate = profile -> static_gate;
459                 dvd -> hlds_e7_preferred_method = profile -> preferred_method;
460                 dvd -> hlds_e7_profile_label = profile -> label;
461                 dvd -> hlds_e7_support_tier = profile -> support_tier;
462                 dvd -> hlds_e7_family = profile -> family;
463                 dvd -> hlds_e7_tokens = profile -> tokens;
464                 dvd -> hlds_e7_record_id = profile -> record_id;
465                 dvd -> hlds_e7_notes = profile -> notes;
466                 /* The modified GDR-8050L runtime profile still starts in the guarded
467                  * speed-probe-pending state, but keeps its static parser evidence fields. */
468                 if (profile -> type == 44) {
469                         dvd -> hlds_e7_type = 45;
470                         dvd -> hlds_e7_mem_blocks = 1;
471                 }
472         }
473 }
474
475 void dvd_set_hlds_e7_runtime_profile (dvd_drive *dvd, u_int32_t type, u_int32_t cache_base, u_int32_t mem_blocks) {
476         if (!dvd)
477                 return;
478         dvd -> hlds_e7_type = type;
479         dvd -> hlds_e7_cache_base = cache_base;
480         dvd -> hlds_e7_mem_blocks = mem_blocks;
481 }
482
483 static bool dvd_is_tsst_kreon_candidate (dvd_drive *dvd) {
484         if (!(dvd_vendor_is (dvd, "TSSTcorp") || dvd_vendor_is (dvd, "SAMSUNG")))
485                 return false;
486         return
487                 dvd_prod_has (dvd, "TS-H352C") ||
488                 dvd_prod_has (dvd, "TS-H353A") ||
489                 dvd_prod_has (dvd, "SH-D162C") ||
490                 dvd_prod_has (dvd, "SH-D162D") ||
491                 dvd_prod_has (dvd, "SH-D163A") ||
492                 dvd_prod_has (dvd, "SH-D163B");
493 }
494
495
496 /**
497  * Initializes a structure representing an MMC command.
498  * @param mmc A pointer to the MMC command structure.
499  * @param buf The buffer where results of the MMC command execution provided by the drive should be stored, or NULL if no buffer will be provided.
500  * @param len The length of the buffer (ignored in case buf is NULL).
501  * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed, or NULL.
502  */
503 void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) {
504         memset (mmc, 0, sizeof (mmc_command));
505         if (buf)
506                 memset (buf, 0, len);
507         mmc -> cmdlen = 12;
508         mmc -> direction = buf && len > 0 ? DVD_DATA_IN : DVD_DATA_NONE;
509         mmc -> buffer = buf;
510         mmc -> buflen = buf ? len : 0;
511         mmc -> sense = sense;
512         
513         return;
514 }
515
516
517 static void dvd_record_command_diagnostic (
518         dvd_drive *dvd,
519         mmc_command *mmc,
520         int transport_result,
521         int os_error,
522         int scsi_status,
523         int sense_key,
524         int asc,
525         int ascq
526 ) {
527         if (!dvd || !mmc)
528                 return;
529         memset (&dvd -> last_command, 0, sizeof (dvd -> last_command));
530         dvd -> last_command.valid = true;
531         dvd -> last_command.transport_result = transport_result;
532         dvd -> last_command.os_error = os_error;
533         dvd -> last_command.scsi_status = scsi_status;
534         dvd -> last_command.sense_key = sense_key & 0x0f;
535         dvd -> last_command.asc = asc & 0xff;
536         dvd -> last_command.ascq = ascq & 0xff;
537         dvd -> last_command.cdb_length = mmc -> cmdlen;
538         if (dvd -> last_command.cdb_length < 0)
539                 dvd -> last_command.cdb_length = 0;
540         if (dvd -> last_command.cdb_length > (int) sizeof (dvd -> last_command.cdb))
541                 dvd -> last_command.cdb_length = (int) sizeof (dvd -> last_command.cdb);
542         memcpy (dvd -> last_command.cdb, mmc -> cmd, sizeof (dvd -> last_command.cdb));
543 }
544
545 bool dvd_get_last_command_diagnostic (dvd_drive *dvd, dvd_command_diagnostic *out) {
546         if (!dvd || !out || !dvd -> last_command.valid)
547                 return false;
548         *out = dvd -> last_command;
549         return true;
550 }
551
552 #ifdef WIN32
553
554 /* Doc is under the UNIX function */
555 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
556         SCSI_PASS_THROUGH_DIRECT *sptd;
557         unsigned char sptd_sense[sizeof (*sptd) + 18], *sense;
558         DWORD bytes;
559         DWORD win_error;
560         BOOL ioctl_ok;
561         int out;
562
563         sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
564         sense = &sptd_sense[sizeof (*sptd)];
565         
566         memset (sptd, 0, sizeof (sptd_sense));
567         memcpy (sptd -> Cdb, mmc -> cmd, sizeof (mmc -> cmd));
568         sptd -> Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
569         sptd -> CdbLength = mmc -> cmdlen;
570         sptd -> SenseInfoLength = 18;
571         if (mmc -> direction == DVD_DATA_OUT)
572                 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
573         else if (mmc -> direction == DVD_DATA_NONE)
574                 sptd -> DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
575         else
576                 sptd -> DataIn = SCSI_IOCTL_DATA_IN;
577         sptd -> DataBuffer = mmc -> buffer;
578         /* Quick hack: Windows hates DataTransferLength = 1. */
579         if (mmc -> buflen == 1)
580                 sptd -> DataTransferLength = 2;
581         else
582                 sptd -> DataTransferLength = mmc -> buflen;
583         sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
584         sptd -> SenseInfoOffset = sizeof (*sptd);
585
586         if (mmc -> cmd[0] == 0xB6) {
587                 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
588                 sptd -> DataTransferLength = 28;
589         }
590
591         ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
592                 sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
593         win_error = ioctl_ok ? ERROR_SUCCESS : GetLastError ();
594         /* DeviceIoControl may succeed while the drive returns CHECK CONDITION. */
595         if (!ioctl_ok || sptd -> ScsiStatus != 0) {
596                 out = -1;
597                 if (!ignore_errors) {
598                         error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
599                                 (unsigned long) win_error, sptd -> ScsiStatus);
600                         debug ("Command was: ");
601                         hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
602                         debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
603                 }
604         } else {
605                 out = 0;
606         }
607         
608         if (mmc -> sense) {
609                 mmc -> sense -> sense_key = sense[2];
610                 mmc -> sense -> asc = sense[12];
611                 mmc -> sense -> ascq = sense[13];
612         }
613         dvd_record_command_diagnostic (dvd, mmc, out, (int) win_error,
614                 (int) sptd -> ScsiStatus, sense[2], sense[12], sense[13]);
615         
616         return out;
617 }
618
619 #else
620
621 /**
622  * Executes an MMC command.
623  * @param dvd The DVD drive the command should be exectued on.
624  * @param mmc The command to be executed.
625  * @param ignore_errors If set to true, no error will be printed if the command fails.
626  * @return 0 if the command was executed successfully, < 0 otherwise.
627  */
628 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
629         int out;
630         int saved_errno;
631         struct cdrom_generic_command cgc;
632         struct request_sense sense;
633         
634 #if 0
635         debug ("Executing MMC command: ");
636         hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
637 #endif
638
639         memset (&sense, 0, sizeof (sense));
640         memset (&cgc, 0, sizeof (cgc));
641         memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd));
642         cgc.buffer = (unsigned char *) mmc -> buffer;
643         cgc.buflen = mmc -> buflen;
644         if (mmc -> direction == DVD_DATA_OUT)
645                 cgc.data_direction = CGC_DATA_WRITE;
646         else if (mmc -> direction == DVD_DATA_NONE)
647                 cgc.data_direction = CGC_DATA_NONE;
648         else
649                 cgc.data_direction = CGC_DATA_READ;
650         cgc.timeout = MMC_CMD_TIMEOUT * 1000;
651         cgc.sense = &sense;
652         if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
653                 saved_errno = errno;
654                 out = -1;
655                 if (!ignore_errors) {
656                         error ("Execution of MMC command failed: %s", strerror (saved_errno));
657                         debug ("Command was:");
658                         hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd));
659                         debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq);
660                 }
661         } else {
662                 saved_errno = 0;
663                 out = 0;
664         }
665         
666         if (mmc -> sense) {
667                 mmc -> sense -> sense_key = sense.sense_key;
668                 mmc -> sense -> asc = sense.asc;
669                 mmc -> sense -> ascq = sense.ascq;
670         }
671         dvd_record_command_diagnostic (dvd, mmc, out, saved_errno,
672                 (int) cgc.stat, sense.sense_key, sense.asc, sense.ascq);
673         
674         return out;
675 }
676 #endif
677
678 #ifndef WIN32
679 /*
680  * Linux equivalent of the copied Windows SCSI_PASS_THROUGH_DIRECT transport
681  * used by UnlockDrive().  CDROM_SEND_PACKET does not expose an explicit CDB
682  * length and applies a single generic timeout.  The GDR-8050L handshake uses
683  * 6-, 10-, and 12-byte CDBs plus 120-second command timeouts (10 seconds only
684  * for sticky descrambling).  SG_IO preserves those boundaries exactly.
685  */
686 static int dvd_xbox_sgio_exact (dvd_drive *dvd,
687                 const char *step,
688                 const u_int8_t *cdb,
689                 int cdb_len,
690                 u_int8_t *buf,
691                 u_int32_t buf_len,
692                 dvd_data_direction direction,
693                 unsigned int timeout_ms) {
694         sg_io_hdr_t io;
695         u_int8_t sense[32];
696         mmc_command diagnostic;
697         int rc;
698         int saved_errno = 0;
699         int transport_result;
700         int i;
701
702         if (!dvd || !cdb || cdb_len < 1 || cdb_len > 12)
703                 return -1;
704
705         memset (&io, 0, sizeof (io));
706         memset (sense, 0, sizeof (sense));
707         memset (&diagnostic, 0, sizeof (diagnostic));
708
709         io.interface_id = 'S';
710         io.cmdp = (unsigned char *) cdb;
711         io.cmd_len = (unsigned char) cdb_len;
712         io.sbp = sense;
713         io.mx_sb_len = sizeof (sense);
714         io.timeout = timeout_ms;
715         io.dxferp = buf;
716         io.dxfer_len = buf_len;
717
718         switch (direction) {
719         case DVD_DATA_OUT:
720                 io.dxfer_direction = SG_DXFER_TO_DEV;
721                 break;
722         case DVD_DATA_NONE:
723                 io.dxfer_direction = SG_DXFER_NONE;
724                 io.dxferp = NULL;
725                 io.dxfer_len = 0;
726                 break;
727         case DVD_DATA_IN:
728         default:
729                 io.dxfer_direction = SG_DXFER_FROM_DEV;
730                 break;
731         }
732
733         errno = 0;
734         rc = ioctl (dvd -> fd, SG_IO, &io);
735         if (rc < 0)
736                 saved_errno = errno;
737
738         transport_result =
739                 (rc == 0 &&
740                  io.status == 0 &&
741                  io.host_status == 0 &&
742                  io.driver_status == 0) ? 0 : -1;
743
744         diagnostic.cmdlen = cdb_len;
745         diagnostic.direction = direction;
746         diagnostic.buffer = buf;
747         diagnostic.buflen = (int) buf_len;
748         memcpy (diagnostic.cmd, cdb, (size_t) cdb_len);
749         dvd_record_command_diagnostic (
750                 dvd,
751                 &diagnostic,
752                 transport_result,
753                 saved_errno,
754                 (int) io.status,
755                 sense[2],
756                 sense[12],
757                 sense[13]);
758
759         xbox_ref_log_fprintf (
760                 stderr,
761                 "[XBOX-SGIO] step=%s rc=%d errno=%d status=0x%02X host=0x%04X driver=0x%04X sense=%02X/%02X/%02X resid=%d timeout_ms=%u cdb=[",
762                 step ? step : "unnamed",
763                 rc,
764                 saved_errno,
765                 (unsigned int) io.status,
766                 (unsigned int) io.host_status,
767                 (unsigned int) io.driver_status,
768                 (unsigned int) (sense[2] & 0x0F),
769                 (unsigned int) sense[12],
770                 (unsigned int) sense[13],
771                 io.resid,
772                 timeout_ms);
773         for (i = 0; i < cdb_len; i++)
774                 xbox_ref_log_fprintf (
775                         stderr,
776                         "%s%02X",
777                         i ? " " : "",
778                         (unsigned int) cdb[i]);
779         xbox_ref_log_fprintf (
780                 stderr,
781                 "] xfer=%u direction=%s result=%s\n",
782                 buf_len,
783                 direction == DVD_DATA_OUT ? "out" :
784                 direction == DVD_DATA_NONE ? "none" : "in",
785                 transport_result == 0 ? "PASS" : "FAIL");
786
787         return transport_result;
788 }
789
790 static int dvd_xbox_exact_read_capacity (dvd_drive *dvd,
791                 const char *step,
792                 u_int32_t *sectors,
793                 u_int32_t *sector_size) {
794         u_int8_t cdb[10];
795         u_int8_t buf[8];
796         u_int32_t max_lba;
797
798         memset (cdb, 0, sizeof (cdb));
799         memset (buf, 0, sizeof (buf));
800         cdb[0] = 0x25;
801
802         if (dvd_xbox_sgio_exact (
803                         dvd, step, cdb, sizeof (cdb), buf, sizeof (buf),
804                         DVD_DATA_IN, 120000) < 0)
805                 return -1;
806
807         max_lba =
808                 ((u_int32_t) buf[0] << 24) |
809                 ((u_int32_t) buf[1] << 16) |
810                 ((u_int32_t) buf[2] << 8) |
811                 (u_int32_t) buf[3];
812
813         if (sectors)
814                 *sectors = max_lba + 1;
815         if (sector_size)
816                 *sector_size =
817                         ((u_int32_t) buf[4] << 24) |
818                         ((u_int32_t) buf[5] << 16) |
819                         ((u_int32_t) buf[6] << 8) |
820                         (u_int32_t) buf[7];
821         return 0;
822 }
823
824 static int dvd_xbox_exact_mode_sense_10 (dvd_drive *dvd,
825                 const char *step,
826                 u_int8_t page,
827                 u_int8_t *buf,
828                 size_t buf_len) {
829         u_int8_t cdb[10];
830
831         if (!buf || buf_len > 0xFFFF)
832                 return -1;
833         memset (cdb, 0, sizeof (cdb));
834         memset (buf, 0, buf_len);
835         cdb[0] = 0x5A;
836         cdb[2] = page;
837         cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
838         cdb[8] = (u_int8_t) (buf_len & 0xFF);
839
840         return dvd_xbox_sgio_exact (
841                 dvd, step, cdb, sizeof (cdb), buf, (u_int32_t) buf_len,
842                 DVD_DATA_IN, 120000);
843 }
844
845 static int dvd_xbox_exact_mode_select_10 (dvd_drive *dvd,
846                 const char *step,
847                 const u_int8_t *buf,
848                 size_t buf_len) {
849         u_int8_t cdb[10];
850         u_int8_t tmp[256];
851
852         if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFFFF)
853                 return -1;
854         memset (cdb, 0, sizeof (cdb));
855         memset (tmp, 0, sizeof (tmp));
856         memcpy (tmp, buf, buf_len);
857         cdb[0] = 0x55;
858         cdb[7] = (u_int8_t) ((buf_len >> 8) & 0xFF);
859         cdb[8] = (u_int8_t) (buf_len & 0xFF);
860
861         return dvd_xbox_sgio_exact (
862                 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
863                 DVD_DATA_OUT, 120000);
864 }
865
866 static int dvd_xbox_exact_mode_select_6 (dvd_drive *dvd,
867                 const char *step,
868                 const u_int8_t *buf,
869                 size_t buf_len) {
870         u_int8_t cdb[6];
871         u_int8_t tmp[64];
872
873         if (!buf || buf_len > sizeof (tmp) || buf_len > 0xFF)
874                 return -1;
875         memset (cdb, 0, sizeof (cdb));
876         memset (tmp, 0, sizeof (tmp));
877         memcpy (tmp, buf, buf_len);
878         cdb[0] = 0x15;
879         cdb[1] = 0x11;
880         cdb[4] = (u_int8_t) buf_len;
881
882         return dvd_xbox_sgio_exact (
883                 dvd, step, cdb, sizeof (cdb), tmp, (u_int32_t) buf_len,
884                 DVD_DATA_OUT, 10000);
885 }
886 #endif
887
888
889 /**
890  * Sends an INQUIRY command to the drive to retrieve drive identification strings.
891  * @param dvd The DVD drive the command should be exectued on.
892  * @return 0 if the command was executed successfully, < 0 otherwise.
893  */
894 static int dvd_get_drive_info (dvd_drive *dvd) {
895         mmc_command mmc;
896         int out;
897         u_int8_t buf[36];
898         char tmp[36 * 4];
899         
900         dvd_init_command (&mmc, buf, sizeof (buf), NULL);
901         mmc.cmd[0] = SPC_INQUIRY;
902         mmc.cmd[4] = sizeof (buf);
903         if ((out = dvd_execute_cmd (dvd, &mmc, false)) >= 0) {
904                 my_strndup (dvd -> vendor, buf + 8, 8);
905                 strtrimr (dvd -> vendor);
906                 my_strndup (dvd -> prod_id, buf + 16, 16);
907                 strtrimr (dvd -> prod_id);
908                 my_strndup (dvd -> prod_rev, buf + 32, 4);
909                 strtrimr (dvd -> prod_rev);
910                 snprintf (tmp, sizeof (tmp), "%s/%s/%s", dvd -> vendor, dvd -> prod_id, dvd -> prod_rev);
911                 my_strdup (dvd -> model_string, tmp);
912                 
913                 debug ("DVD drive is \"%s\"", dvd -> model_string);
914         } else {
915                 error ("Cannot identify DVD drive\n");
916         }
917
918         return (out);
919 }
920
921
922 /**
923  * Assigns the proper memory dump functions to a dvd_drive object, according to vendor, model and other parameters. Actually this scheme probably needs to
924  * to be improved, but it is enough for the moment.
925  * @param dvd The DVD drive the command should be exectued on.
926  */
927 static void dvd_assign_functions (dvd_drive *dvd, u_int32_t command) {
928         dvd -> def_method = 0;
929         if (dvd_is_hlds_gc_wii_drive (dvd)) {
930                 dvd_apply_hlds_e7_profile (dvd);
931                 debug ("Hitachi-LG MN103-family 0xE7 drive detected: profile=%s tier=%s family=%s base=0x%08x windows=%u cdb=0x%03x gate=0x%08x",
932                         dvd_get_hlds_e7_profile_name (dvd),
933                         dvd_get_hlds_e7_support_tier (dvd),
934                         dvd_get_hlds_e7_family (dvd),
935                         dvd -> hlds_e7_cache_base, dvd -> hlds_e7_mem_blocks,
936                         dvd -> hlds_e7_static_cdb_base, dvd -> hlds_e7_static_gate);
937                 dvd -> memdump = &hitachi_dvd_dump_mem;
938                 dvd -> command = 2;
939                 dvd -> supported = true;
940                 /* DIC Type1/Type3/Type4 use READ12 + HIT 0xE7 cache extraction.
941                  * GCC-4244N is validated with Sonic Mega Collection and GCC-4243N is
942                  * actively under test, so both remain Method 8.  Type1 drives get the
943                  * DIC-derived 0x00a13000 / one-window cache profile and also default to
944                  * Method 8 so GCC-4160N/GCC-4240N can be tested without forcing a method.
945                  * GDR-8050L gets a proven single-window fallback plus guarded speed probes
946                  * for cross-flashed/modified firmware with 0xE7 memdump added; stock
947                  * GDR-8050L firmware does not expose this GC/Wii memdump path. GDR-8081N
948                  * is an experimental probe target and needs Method 8 so seed probing can run.
949                  * Type2 is still classified only; leave it on the older Method 9 path. */
950                 if (dvd -> hlds_e7_preferred_method >= 0)
951                         dvd -> def_method = (u_int32_t) dvd -> hlds_e7_preferred_method;
952                 else if (dvd -> hlds_e7_type == 1 || dvd -> hlds_e7_type == 44 || dvd -> hlds_e7_type == 45 || dvd -> hlds_e7_type == 81 || dvd_is_hlds_gcc4243_4244_drive (dvd))
953                         dvd -> def_method = 8;
954                 else
955                         dvd -> def_method = 9;
956
957         } else if (strcmp (dvd -> vendor, "LITE-ON") == 0 && (
958                 strcmp (dvd ->prod_id, "DVDRW LH-18A1H") == 0 ||
959                 strcmp (dvd ->prod_id, "DVDRW LH-18A1P") == 0 ||
960                 strcmp (dvd ->prod_id, "DVDRW LH-20A1H") == 0 ||
961                 strcmp (dvd ->prod_id, "DVDRW LH-20A1P") == 0
962         )) {
963                 debug ("Lite-On DVD drive detected, using Lite-On memory dump command");
964                 dvd -> memdump = &liteon_dvd_dump_mem;
965                 dvd -> command = 3;
966                 dvd -> supported = true;
967                 dvd -> def_method = 5;
968
969         } else if (dvd_is_tsst_kreon_candidate (dvd) || (strcmp (dvd -> vendor, "TSSTcorp") == 0 && (
970                 strcmp (dvd ->prod_id, "DVD-ROM SH-D162A") == 0 ||
971                 strcmp (dvd ->prod_id, "DVD-ROM SH-D162B") == 0
972         ))) {
973                 debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command");
974                 dvd -> memdump = &vanilla_2384_dvd_dump_mem;
975                 dvd -> command = 1;
976                 dvd -> supported = true;
977                 dvd -> def_method = 0;
978
979         } else if (strcmp (dvd -> vendor, "PLEXTOR") == 0) {
980                 debug ("Plextor DVD drive detected, using vanilla 2064 memory dump command");
981                 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
982                 dvd -> command = 0;
983                 dvd -> supported = true;
984                 dvd -> def_method = 2;
985
986         } else {
987                 /* This is an unsupported drive (yet). */
988                 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
989                 dvd -> command = 0;
990                 dvd -> supported = false;
991         }
992
993         if (command!=-1) {
994                 dvd -> command = command;
995                 if          (command == 0) dvd -> memdump = &vanilla_2064_dvd_dump_mem;
996                 else if (command == 1) dvd -> memdump = &vanilla_2384_dvd_dump_mem;
997                 else if (command == 2) dvd -> memdump = &hitachi_dvd_dump_mem;
998                 else if (command == 3) dvd -> memdump = &liteon_dvd_dump_mem;
999                 else if (command == 4) dvd -> memdump = &renesas_dvd_dump_mem;
1000         }
1001
1002
1003         //init Reed-Solomon for Lite-On
1004         generate_gf();
1005         gen_poly();
1006
1007         return;
1008 }
1009
1010
1011 /**
1012  * Creates a new structure representing a CD/DVD-ROM drive.
1013  * @param device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows).
1014  * @return The newly-created structure, to be used with the other commands, or NULL if the drive could not be initialized.
1015  */
1016 dvd_drive *dvd_drive_new (char *device, u_int32_t command) {
1017         dvd_drive *dvd;
1018 #ifdef WIN32
1019         HANDLE fd;
1020         char dev[40];
1021 #else
1022         int fd;
1023 #endif
1024
1025         /* Force the dropping of privileges: in our model, privileges are only used to execute memory dump commands, the user
1026            must gain access to the device somehow else (i. e. get added to the "cdrom" group or similar things) */
1027         drop_euid ();
1028         
1029         debug ("Trying to open DVD device %s", device);
1030 #ifdef WIN32
1031         sprintf (dev, "\\\\.\\%c:", device[0]);
1032         if ((fd = CreateFile (dev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
1033                 error ("Cannot open drive: %d", GetLastError ());
1034 #else
1035         if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) {
1036                 perror ("Cannot open drive");
1037 #endif
1038                 dvd = NULL;
1039         } else {
1040                 debug ("Opened successfully");
1041                 drop_euid ();
1042                 dvd = (dvd_drive *) malloc (sizeof (dvd_drive));
1043                 if (!dvd) {
1044                         fprintf (stderr, "malloc() failed\n");
1045                         exit (100);
1046                 }
1047                 memset (dvd, 0, sizeof (dvd_drive));
1048                 my_strdup (dvd -> device, device);
1049                 dvd -> fd = fd;
1050                 dvd_get_drive_info (dvd);
1051                 dvd_assign_functions (dvd, command);
1052         }
1053
1054         return (dvd);
1055 }
1056
1057
1058 /**
1059  * Frees resources used by a DVD drive structure and destroys it.
1060  * @param dvd The DVD drive structure to be destroyed.
1061  * @return NULL.
1062  */
1063 void *dvd_drive_destroy (dvd_drive *dvd) {
1064         if (dvd) {
1065 #ifdef WIN32
1066                 CloseHandle (dvd -> fd);
1067 #else
1068                 close (dvd -> fd);
1069 #endif
1070                 my_free (dvd -> device);
1071                 my_free (dvd -> vendor);
1072                 my_free (dvd -> prod_id);
1073                 my_free (dvd -> prod_rev);
1074                 my_free (dvd);
1075         }
1076
1077         return (NULL);
1078 }
1079
1080
1081 /**
1082  * Executes the drive-dependent function to dump the drive sector cache, and returns the dumped data.
1083  * @param dvd The DVD drive the command should be exectued on.
1084  * @param block_off The offset to start dumping, WRT the beginning of the sector cache.
1085  * @param block_len The number of blocks to dump.
1086  * @param block_size The block size to be used for dumping.
1087  * @param buf A buffer where to store the dumped data. Note that this must be able to hold at least block_len * block_size bytes.
1088  * @return 0 if the command was executed successfully, < 0 otherwise.
1089  */
1090 int dvd_memdump (dvd_drive *dvd, u_int32_t block_off, u_int32_t block_len, u_int32_t block_size, u_int8_t *buf) {
1091         int out;
1092
1093         /* Upgrade privileges and call actual dump functions */
1094         upgrade_euid ();
1095         out = dvd -> memdump (dvd, block_off, block_len, block_size, buf);
1096         drop_euid ();
1097
1098         return (out);
1099 }
1100
1101
1102 /**
1103  * Issues a READ(12) command without bothering to return the results. Uses the FUA (Force Unit Access bit) so that the requested sectors are actually read
1104  * at the beginning of the cache and can be dumped later.
1105  * @param dvd The DVD drive the command should be exectued on.
1106  * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs.
1107  * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1108  * @return 0 if the command was executed successfully, < 0 otherwise.
1109  */
1110 int dvd_read_sector_dummy (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1111         mmc_command mmc;
1112         int out;
1113         u_int8_t intbuf[64 * 1024], *buf;
1114         size_t bufsize;
1115
1116         /* We need some buffer, be it provided externally or not */
1117         if (extbuf) {
1118                 buf = extbuf;
1119                 bufsize = extbufsize;
1120         } else {
1121                 buf = intbuf;
1122                 bufsize = sizeof (intbuf);
1123         }
1124
1125         dvd_init_command (&mmc, buf, bufsize, sense);
1126         mmc.cmd[0] = MMC_READ_12;
1127         mmc.cmd[1] = 0x08;      /* FUA bit set */
1128         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
1129         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1130         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1131         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
1132         mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1133         mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1134         mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1135         mmc.cmd[9] = (u_int8_t)  (sectors & 0x000000FF);
1136         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
1137
1138         return (out);
1139 }
1140
1141
1142 /**
1143  * Issues a READ(12) command using the STREAMING bit, which causes the requested 16-sector block to be read into memory,
1144  * together with the following four. This way we will be able to dump 5 sector with a single READ request.
1145  *
1146  * Note the strange need for a big buffer even though we must only pass 0x10 as the transfer length, otherwise the drive will hang (!?).
1147  * @param dvd The DVD drive the command should be exectued on.
1148  * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs, and the following 4 blocks.
1149  * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
1150  * @param extbuf A buffer where to store the read data, or NULL.
1151  * @param extbufsize The size of the buffer.
1152  * @return 
1153  */
1154 int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1155         mmc_command mmc;
1156         int out;
1157         u_int8_t intbuf[2048 * 16], *buf;
1158         size_t bufsize;
1159
1160         /* We need some buffer, be it provided externally or not */
1161         if (extbuf) {
1162                 buf = extbuf;
1163                 bufsize = extbufsize;
1164         } else {
1165                 buf = intbuf;
1166                 bufsize = sizeof (intbuf);
1167         }
1168         
1169         dvd_init_command (&mmc, buf, bufsize, sense);
1170         mmc.cmd[0] = MMC_READ_12;
1171         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
1172         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1173         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1174         mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
1175         mmc.cmd[6] = 0;
1176         mmc.cmd[7] = 0;
1177         mmc.cmd[8] = 0;
1178         mmc.cmd[9] = 0x10;
1179         mmc.cmd[10] = 0x80;     /* STREAMING bit set */
1180         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
1181         
1182         return (out);
1183 }
1184
1185
1186 int dvd_read_streaming (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1187         mmc_command mmc;
1188         int out;
1189         u_int8_t intbuf[64 * 1024], *buf;
1190         size_t bufsize;
1191
1192         /* We need some buffer, be it provided externally or not */
1193         if (extbuf) {
1194                 buf = extbuf;
1195                 bufsize = extbufsize;
1196         } else {
1197                 buf = intbuf;
1198                 bufsize = sizeof (intbuf);
1199         }
1200         
1201         dvd_init_command (&mmc, buf, bufsize, sense);
1202         mmc.cmd[0] = MMC_READ_12;
1203         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
1204         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1205         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1206         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
1207         mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
1208         mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
1209         mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1210         mmc.cmd[9] = (u_int8_t)  (sectors & 0x000000FF);
1211         mmc.cmd[10] = 0x80;     /* STREAMING bit set */
1212         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
1213         
1214         return (out);
1215 }
1216
1217
1218 int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) {
1219         mmc_command mmc;
1220         int out;
1221         u_int8_t intbuf[64], *buf;
1222         size_t bufsize;
1223
1224         buf = intbuf;
1225         bufsize = 0;
1226         
1227         dvd_init_command (&mmc, buf, bufsize, sense);
1228         mmc.cmd[0] = MMC_READ_12;
1229         mmc.cmd[1] = 0x08;
1230         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
1231         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1232         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1233         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
1234         out = dvd_execute_cmd (dvd, &mmc, true);
1235         
1236         return (out);
1237 }
1238
1239 static void dvd_sleep_ms (unsigned int ms) {
1240 #ifdef WIN32
1241         Sleep (ms);
1242 #else
1243         usleep ((useconds_t) ms * 1000);
1244 #endif
1245 }
1246
1247 int dvd_start_stop_unit (dvd_drive *dvd, bool start, bool load_eject, req_sense *sense) {
1248         mmc_command mmc;
1249         int out;
1250         u_int8_t intbuf[64], *buf;
1251         size_t bufsize;
1252
1253         buf = intbuf;
1254         bufsize = 0;
1255         
1256         dvd_init_command (&mmc, buf, bufsize, sense);
1257         mmc.cmd[0] = 0x1B;
1258         /* START STOP UNIT byte 4: bit 1 = LoEj, bit 0 = Start. */
1259         mmc.cmd[4] = (load_eject ? 0x02 : 0x00) | (start ? 0x01 : 0x00);
1260         out = dvd_execute_cmd (dvd, &mmc, true);
1261         
1262         return (out);
1263 }
1264
1265 int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) {
1266         return dvd_start_stop_unit (dvd, start, false, sense);
1267 }
1268
1269 int dvd_set_door_lock (dvd_drive *dvd, bool locked) {
1270 #ifdef WIN32
1271         (void) dvd;
1272         (void) locked;
1273         return 0;
1274 #else
1275         if (!dvd)
1276                 return -EINVAL;
1277         if (ioctl (dvd -> fd, CDROM_LOCKDOOR, locked ? 1 : 0) < 0)
1278                 return -errno;
1279         return 0;
1280 #endif
1281 }
1282
1283 int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
1284         unsigned int waited = 0;
1285         if (!dvd)
1286                 return -1;
1287         while (waited <= timeout_ms) {
1288                 if (dvd_test_unit_ready (dvd, NULL) == 0)
1289                         return 0;
1290                 if (waited == 0)
1291                         dvd_stop_unit (dvd, true, NULL); /* START UNIT, like EnsureDriveReady() */
1292                 dvd_sleep_ms (500);
1293                 waited += 500;
1294         }
1295         return -1;
1296 }
1297
1298 int dvd_media_cycle (dvd_drive *dvd, req_sense *sense) {
1299         int i;
1300         int out;
1301
1302 #ifndef WIN32
1303         /* Linux commonly applies CDO_LOCK while an optical device is open.  A raw
1304          * START STOP UNIT eject then fails immediately even though the process owns
1305          * the only intentional handle.  Release that kernel/drive door lock before
1306          * the GDR-8050L software tray cycle, then restore it after the tray is loaded
1307          * and ready.  CDROM_LOCKDOOR is the documented Linux optical-door API. */
1308         out = dvd_set_door_lock (dvd, false);
1309         if (out < 0) {
1310                 xbox_ref_log_fprintf (stderr,
1311                         "[XBOX][FATAL] Linux media-cycle could not release the optical door lock: %s (errno=%d).\n",
1312                         strerror (-out), -out);
1313                 return out;
1314         }
1315         xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle released the optical door lock before software eject.\n");
1316 #endif
1317
1318         /* Match the original GDR-8050L dumper's AutomateTrayCycle(): eject, wait
1319          * long enough for the tray to extend, close, poll readiness, then settle. */
1320         out = dvd_start_stop_unit (dvd, false, true, sense); /* LoEj=1, Start=0: eject */
1321         if (out < 0) {
1322                 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software eject command failed.\n");
1323                 return out;
1324         }
1325         dvd_sleep_ms (3000);
1326
1327         out = dvd_start_stop_unit (dvd, true, true, sense);  /* LoEj=1, Start=1: load */
1328         if (out < 0) {
1329                 xbox_ref_log_fprintf (stderr, "[XBOX][FATAL] Media-cycle software load command failed; the door remains unlocked for recovery.\n");
1330                 return out;
1331         }
1332
1333         for (i = 0; i < 90; i++) {
1334                 dvd_sleep_ms (500);
1335                 if (dvd_test_unit_ready (dvd, NULL) == 0) {
1336                         dvd_sleep_ms (1500);
1337 #ifndef WIN32
1338                         out = dvd_set_door_lock (dvd, true);
1339                         if (out < 0)
1340                                 xbox_ref_log_fprintf (stderr,
1341                                         "[XBOX][WARN] Linux media-cycle completed, but the optical door could not be re-locked: %s (errno=%d).\n",
1342                                         strerror (-out), -out);
1343                         else
1344                                 xbox_ref_log_fprintf (stderr, "[XBOX] Linux media-cycle restored the optical door lock after load.\n");
1345 #endif
1346                         return 0;
1347                 }
1348         }
1349
1350         /* Original dumper falls back to a fixed 10s settle delay if TUR never
1351          * reports ready after tray close.  Keep the door unlocked on failure so the
1352          * user can recover the media without power-cycling the external drive. */
1353         dvd_sleep_ms (10000);
1354         fprintf (stderr, "[XBOX][FATAL] Media-cycle tray closed, but the drive never became ready; the door remains unlocked for recovery.\n");
1355         return -1;
1356 }
1357
1358 int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1359         mmc_command mmc;
1360         int out;
1361         u_int8_t intbuf[64], *buf;
1362         size_t bufsize;
1363
1364         buf = intbuf;
1365         bufsize = 0;
1366         
1367         dvd_init_command (&mmc, buf, bufsize, sense);
1368         mmc.cmd[0] = 0xBB;
1369         mmc.cmd[2] = (u_int8_t) ((speed & 0x0000FF00) >> 8);
1370         mmc.cmd[3] = (u_int8_t)  (speed & 0x000000FF);
1371         out = dvd_execute_cmd (dvd, &mmc, true);
1372         
1373         return (out);
1374 }
1375
1376 int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) {
1377         mmc_command mmc;
1378         int out;
1379         u_int8_t intbuf[64], *buf;
1380         size_t bufsize;
1381
1382         buf = intbuf;
1383         bufsize = 0x22;
1384         
1385         dvd_init_command (&mmc, buf, bufsize, sense);
1386         mmc.cmd[0] = 0x52;
1387         mmc.cmd[1] = 0x01;
1388         mmc.cmd[5] = 0x01;
1389         mmc.cmd[8] = 0x22;
1390         out = dvd_execute_cmd (dvd, &mmc, true);
1391
1392         *(size)=*(size) << 8 | intbuf[0x18];
1393         *(size)=*(size) << 8 | intbuf[0x19];
1394         *(size)=*(size) << 8 | intbuf[0x1a];
1395         *(size)=*(size) << 8 | intbuf[0x1b];
1396
1397         return (out);
1398 }
1399
1400 int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) {
1401         mmc_command mmc;
1402         int out;
1403         u_int8_t intbuf[2052], *buf;
1404         size_t bufsize;
1405
1406         buf = intbuf;
1407         bufsize = 2052;
1408         
1409         dvd_init_command (&mmc, buf, bufsize, sense);
1410         mmc.cmd[0] = 0xad;
1411         mmc.cmd[8] = 0x08;
1412         mmc.cmd[9] = 0x04;
1413         out = dvd_execute_cmd (dvd, &mmc, true);
1414
1415         *(layerbreak)=*(layerbreak) << 8;
1416         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x11];
1417         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x12];
1418         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x13];
1419         if (*(layerbreak) > 0) *(layerbreak)=*(layerbreak) - 0x30000 + 1;
1420
1421         return (out);
1422 }
1423
1424 int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1425 /*
1426 DVD Decrypter->
1427 DeviceIoControl    : \Device\CdRom5
1428 Command            : IOCTL_SCSI_PASS_THROUGH_DIRECT
1429 Length             : 44 (0x002C)
1430 ScsiStatus         : 0
1431 PathId             : 0
1432 TargedId           : 0
1433 Lun                : 0
1434 CdbLength          : 12 (0x0C)
1435 SenseInfoLength    : 24 (0x18)
1436 DataTransferLength : 28 (0x0000001C)
1437 DataIn             : 0
1438 TimeOutValue       : 5000
1439
1440 CDB:
1441 00000000  B6 00 00 00 00 00 00 00 00 00 1C 00               ...........    
1442
1443 Data Sent:
1444 00000000  00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF   ............____
1445 00000010  00 00 03 E8 FF FF FF FF 00 00 03 E8               ...____...    
1446 */
1447         mmc_command mmc;
1448         int out;
1449         u_int8_t inbuf[28], *buf;
1450         size_t bufsize;
1451
1452         buf = inbuf;
1453         bufsize = 28;
1454         
1455         dvd_init_command (&mmc, buf, bufsize, sense);
1456         mmc.cmd[00] = 0xB6;
1457         mmc.cmd[10] = 28;
1458
1459         *(buf+ 0)=0;//2
1460         *(buf+ 1)=0;
1461         *(buf+ 2)=0;
1462         *(buf+ 3)=0;
1463         *(buf+ 4)=0; //MSB
1464         *(buf+ 5)=0; //
1465         *(buf+ 6)=0; //
1466         *(buf+ 7)=0; //LSB
1467
1468         *(buf+ 8)=0xff; //MSB
1469         *(buf+ 9)=0xff; //
1470         *(buf+10)=0xff; //
1471         *(buf+11)=0xff; //LSB
1472
1473         *(buf+12)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1474         *(buf+13)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1475         *(buf+14)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1476         *(buf+15)=(u_int8_t)  (speed & 0x000000FF);
1477
1478         *(buf+16)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1479         *(buf+17)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1480         *(buf+18)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1481         *(buf+19)=(u_int8_t)  (1000 & 0x000000FF);
1482
1483         *(buf+20)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1484         *(buf+21)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1485         *(buf+22)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1486         *(buf+23)=(u_int8_t)  (speed & 0x000000FF);
1487
1488         *(buf+24)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1489         *(buf+25)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1490         *(buf+26)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1491         *(buf+27)=(u_int8_t)  (1000 & 0x000000FF);
1492
1493         out = dvd_execute_cmd (dvd, &mmc, true);
1494
1495         return (out);
1496 }
1497
1498
1499 int dvd_test_unit_ready (dvd_drive *dvd, req_sense *sense) {
1500         mmc_command mmc;
1501         u_int8_t intbuf[1];
1502
1503         dvd_init_command (&mmc, intbuf, 0, sense);
1504         mmc.cmd[0] = SPC_TEST_UNIT_READY;
1505         mmc.cmdlen = 6;
1506         mmc.direction = DVD_DATA_NONE;
1507         return dvd_execute_cmd (dvd, &mmc, true);
1508 }
1509
1510 int dvd_read_capacity_10 (dvd_drive *dvd, u_int32_t *sectors, u_int32_t *sector_size, req_sense *sense) {
1511         mmc_command mmc;
1512         u_int8_t buf[8];
1513         int out;
1514         u_int32_t max_lba, block_len;
1515
1516         dvd_init_command (&mmc, buf, sizeof (buf), sense);
1517         mmc.cmd[0] = MMC_READ_CAPACITY_10;
1518         mmc.cmdlen = 10;
1519         out = dvd_execute_cmd (dvd, &mmc, false);
1520         if (out >= 0) {
1521                 max_lba = ((u_int32_t) buf[0] << 24) | ((u_int32_t) buf[1] << 16) | ((u_int32_t) buf[2] << 8) | buf[3];
1522                 block_len = ((u_int32_t) buf[4] << 24) | ((u_int32_t) buf[5] << 16) | ((u_int32_t) buf[6] << 8) | buf[7];
1523                 if (sectors)
1524                         *sectors = max_lba + 1;
1525                 if (sector_size)
1526                         *sector_size = block_len;
1527         }
1528
1529         return out;
1530 }
1531
1532 int dvd_read_10 (dvd_drive *dvd, u_int32_t sector, u_int32_t sectors, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
1533         mmc_command mmc;
1534         u_int8_t intbuf[64 * 1024], *buf;
1535         size_t need, bufsize;
1536
1537         need = (size_t) sectors * 2048;
1538         if (extbuf) {
1539                 buf = extbuf;
1540                 bufsize = extbufsize;
1541         } else {
1542                 buf = intbuf;
1543                 bufsize = sizeof (intbuf);
1544         }
1545
1546         if (need > bufsize) {
1547                 error ("dvd_read_10 buffer too small (%u sectors need %lu bytes)", sectors, (unsigned long) need);
1548                 return -1;
1549         }
1550
1551         dvd_init_command (&mmc, buf, (int) need, sense);
1552         mmc.cmd[0] = MMC_READ_10;
1553         mmc.cmdlen = 10;
1554         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);
1555         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1556         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1557         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
1558         mmc.cmd[7] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1559         mmc.cmd[8] = (u_int8_t)  (sectors & 0x000000FF);
1560
1561         return dvd_execute_cmd (dvd, &mmc, true);
1562 }
1563
1564 int dvd_mode_sense_10 (dvd_drive *dvd, u_int8_t page, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1565         mmc_command mmc;
1566
1567         if (!extbuf || extbufsize > 0xFFFF)
1568                 return -1;
1569
1570         dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1571         mmc.cmd[0] = SPC_MODE_SENSE_10;
1572         mmc.cmd[2] = page;
1573         mmc.cmd[7] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1574         mmc.cmd[8] = (u_int8_t)  (extbufsize & 0x00FF);
1575         mmc.cmdlen = 10;
1576
1577         return dvd_execute_cmd (dvd, &mmc, false);
1578 }
1579
1580 int dvd_mode_select_10 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1581         mmc_command mmc;
1582         u_int8_t tmp[256];
1583
1584         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFFFF)
1585                 return -1;
1586         memset (tmp, 0, sizeof (tmp));
1587         memcpy (tmp, buf, bufsize);
1588
1589         dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1590         mmc.direction = DVD_DATA_OUT;
1591         mmc.cmd[0] = SPC_MODE_SELECT_10;
1592         mmc.cmd[7] = (u_int8_t) ((bufsize & 0xFF00) >> 8);
1593         mmc.cmd[8] = (u_int8_t)  (bufsize & 0x00FF);
1594         mmc.cmdlen = 10;
1595
1596         return dvd_execute_cmd (dvd, &mmc, false);
1597 }
1598
1599 int dvd_mode_select_6 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1600         mmc_command mmc;
1601         u_int8_t tmp[64];
1602
1603         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFF)
1604                 return -1;
1605         memset (tmp, 0, sizeof (tmp));
1606         memcpy (tmp, buf, bufsize);
1607
1608         dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1609         mmc.direction = DVD_DATA_OUT;
1610         mmc.cmd[0] = SPC_MODE_SELECT_6;
1611         mmc.cmd[1] = 0x11;
1612         mmc.cmd[4] = (u_int8_t) (bufsize & 0xFF);
1613         mmc.cmdlen = 6;
1614
1615         return dvd_execute_cmd (dvd, &mmc, false);
1616 }
1617
1618 int dvd_read_dvd_structure (dvd_drive *dvd, u_int8_t format, u_int8_t layer, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1619         mmc_command mmc;
1620
1621         if (!extbuf || extbufsize > 0xFFFF)
1622                 return -1;
1623
1624         dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1625         mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1626         /* MMC READ DVD STRUCTURE places Format in CDB byte 7. Byte 11 is
1627          * Control and must remain zero. The copied Windows GetMediaID() path
1628          * uses the same byte-7 boundary for DMI format 0x04. */
1629         mmc.cmd[6] = layer;
1630         mmc.cmd[7] = format;
1631         mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1632         mmc.cmd[9] = (u_int8_t)  (extbufsize & 0x00FF);
1633         mmc.cmdlen = 12;
1634
1635         return dvd_execute_cmd (dvd, &mmc, false);
1636 }
1637
1638
1639 static int dvd_xbox_vendor_command (dvd_drive *dvd, u_int8_t subcommand, u_int8_t value, u_int8_t *buf, size_t bufsize, dvd_data_direction direction) {
1640         mmc_command mmc;
1641
1642         dvd_init_command (&mmc, buf, (int) bufsize, NULL);
1643         mmc.cmd[0] = 0xFF;
1644         mmc.cmd[1] = 0x08;
1645         mmc.cmd[2] = 0x01;
1646         mmc.cmd[3] = subcommand;
1647         mmc.cmd[4] = value;
1648         mmc.cmdlen = 10;
1649         mmc.direction = direction;
1650         if (direction == DVD_DATA_NONE) {
1651                 mmc.buffer = NULL;
1652                 mmc.buflen = 0;
1653         }
1654         return dvd_execute_cmd (dvd, &mmc, false);
1655 }
1656
1657 static bool dvd_xbox_feature_list_has (const u_int16_t *features, size_t count, u_int16_t needle) {
1658         size_t i;
1659         if (!features)
1660                 return false;
1661         for (i = 0; i < count && features[i] != 0; i++) {
1662                 if (features[i] == needle)
1663                         return true;
1664         }
1665         return false;
1666 }
1667
1668 int dvd_xbox_vendor_get_feature_list (dvd_drive *dvd, u_int16_t *features, size_t max_features) {
1669         u_int8_t buf[26];
1670         size_t i, count;
1671
1672         if (!dvd || !features || max_features == 0)
1673                 return -1;
1674         memset (features, 0, max_features * sizeof (features[0]));
1675         memset (buf, 0, sizeof (buf));
1676         if (dvd_xbox_vendor_command (dvd, 0x10, 0, buf, sizeof (buf), DVD_DATA_IN) < 0)
1677                 return -1;
1678         if ((((u_int16_t) buf[0] << 8) | buf[1]) != 0xA55A ||
1679             (((u_int16_t) buf[2] << 8) | buf[3]) != 0x5AA5) {
1680                 error ("Xbox vendor feature-list signature is invalid");
1681                 return -1;
1682         }
1683         count = sizeof (buf) / 2;
1684         if (count > max_features)
1685                 count = max_features;
1686         for (i = 0; i < count; i++)
1687                 features[i] = ((u_int16_t) buf[i * 2] << 8) | buf[i * 2 + 1];
1688         return 0;
1689 }
1690
1691 int dvd_xbox_vendor_lock (dvd_drive *dvd) {
1692         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1693                 return -1;
1694         return dvd_xbox_vendor_command (dvd, 0x11, 0x00, NULL, 0, DVD_DATA_NONE);
1695 }
1696
1697 int dvd_xbox_vendor_set_error_skip (dvd_drive *dvd, bool enabled) {
1698         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1699                 return -1;
1700         return dvd_xbox_vendor_command (dvd, 0x15, enabled ? 0x01 : 0x00, NULL, 0, DVD_DATA_NONE);
1701 }
1702
1703 int dvd_xbox_vendor_unlock_wxripper (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1704         u_int16_t features[13];
1705         u_int32_t sectors = 0, sector_size = 0;
1706
1707         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1708                 return -1;
1709
1710         if (dvd_xbox_vendor_get_feature_list (dvd, features, sizeof (features) / sizeof (features[0])) < 0)
1711                 return -1;
1712         if (!dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0201) &&
1713             !dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0221))
1714                 warning ("Xbox vendor feature list did not advertise Xbox unlock state 2/full challenge support; trying wxripper state anyway");
1715
1716         if (dvd_xbox_vendor_command (dvd, 0x11, 0x02, NULL, 0, DVD_DATA_NONE) < 0)
1717                 return -1;
1718
1719         /* DiscImageCreator disables error-skip before dumping; keep that behavior so
1720          * real read errors are visible to FriiDump unless a future option says otherwise. */
1721         dvd_xbox_vendor_set_error_skip (dvd, false);
1722
1723         if (dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL) < 0)
1724                 return -1;
1725         if (sector_size != 2048 || sectors < 1000000) {
1726                 error ("Xbox vendor unlock did not expose the expected 2048-byte view");
1727                 return -1;
1728         }
1729         if (unlocked_sectors)
1730                 *unlocked_sectors = sectors;
1731         return 0;
1732 }
1733
1734
1735 #define XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS 200000U
1736
1737 static int dvd_xbox_refresh_ready_capacity (dvd_drive *dvd,
1738                 u_int32_t *sectors,
1739                 u_int32_t *sector_size) {
1740         u_int32_t observed_sectors = 0;
1741         u_int32_t observed_sector_size = 0;
1742
1743         if (!dvd)
1744                 return -1;
1745
1746         /* Exact portable equivalent of xbox_ref_refresh_ready_capacity():
1747          * RefreshVolume(); Sleep(2000); EnsureDriveReady(30000); GetTotalSectors(). */
1748         dvd_refresh_volume (dvd);
1749         dvd_sleep_ms (2000);
1750         if (dvd_wait_ready (dvd, 30000) < 0)
1751                 return -1;
1752         if (dvd_read_capacity_10 (dvd, &observed_sectors, &observed_sector_size, NULL) < 0)
1753                 return -1;
1754
1755         if (sectors)
1756                 *sectors = observed_sectors;
1757         if (sector_size)
1758                 *sector_size = observed_sector_size;
1759         return 0;
1760 }
1761
1762 int dvd_xbox_prepare_game_view (dvd_drive *dvd,
1763                 u_int32_t *sectors,
1764                 u_int32_t *sector_size) {
1765         u_int32_t entry_sectors = 0;
1766         u_int32_t observed_sectors = 0;
1767         u_int32_t observed_sector_size = 0;
1768
1769         if (!dvd || !dvd_is_xbox_challenge_drive (dvd))
1770                 return -1;
1771
1772         /* Port xbox_ref_gdr8050l_dump_core() state preparation exactly, replacing
1773          * Win32 handle/volume calls with FriiDump's portable Linux equivalents.
1774          * No RecoveryKick, media-auth kick, or synthetic LBA-zero read cadence is
1775          * part of this stock/cross-flashed GDR-8050L sequence. */
1776         if (dvd_wait_ready (dvd, 30000) < 0) {
1777                 xbox_ref_log_fprintf (stderr,
1778                         "[XBOX-WINSEQ][FATAL] Drive did not become ready before Xbox state preparation.\n");
1779                 return -1;
1780         }
1781         if (dvd_read_capacity_10 (dvd, &entry_sectors, &observed_sector_size, NULL) < 0) {
1782                 xbox_ref_log_fprintf (stderr,
1783                         "[XBOX-WINSEQ][FATAL] Entry READ CAPACITY failed.\n");
1784                 return -1;
1785         }
1786         xbox_ref_log_fprintf (stderr,
1787                 "[XBOX-WINSEQ] Entry READ CAPACITY: %u sectors.\n",
1788                 entry_sectors);
1789
1790         if (entry_sectors > XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1791                 xbox_ref_log_fprintf (stderr,
1792                         "[XBOX-WINSEQ] Entry state already exposes the Xbox game view; skipping the redundant initial handshake and tray cycle.\n");
1793                 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1794                         return -1;
1795         } else {
1796                 xbox_ref_log_fprintf (stderr,
1797                         "[XBOX-WINSEQ] Entry state appears locked/video; attempting the full handshake directly without a media transition.\n");
1798                 if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1799                         xbox_ref_log_fprintf (stderr,
1800                                 "[XBOX-WINSEQ][WARN] Direct UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1801                 if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1802                         return -1;
1803                 xbox_ref_log_fprintf (stderr,
1804                         "[XBOX-WINSEQ] Direct-handshake READ CAPACITY: %u sectors.\n",
1805                         observed_sectors);
1806
1807                 if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1808                         xbox_ref_log_fprintf (stderr,
1809                                 "[XBOX-WINSEQ][WARN] Direct handshake did not expose the Xbox game view; performing one tray-cycle recovery and retry.\n");
1810                         if (dvd_media_cycle (dvd, NULL) < 0)
1811                                 return -1;
1812                         xbox_ref_log_fprintf (stderr,
1813                                 "[XBOX-WINSEQ] Re-applying the full handshake after recovery media change.\n");
1814                         if (dvd_xbox_gdr8050l_unlock (dvd, NULL) < 0)
1815                                 xbox_ref_log_fprintf (stderr,
1816                                         "[XBOX-WINSEQ][WARN] Recovery UnlockDrive transport returned failure; READ CAPACITY remains authoritative.\n");
1817                         if (dvd_xbox_refresh_ready_capacity (dvd, &observed_sectors, &observed_sector_size) < 0)
1818                                 return -1;
1819                         xbox_ref_log_fprintf (stderr,
1820                                 "[XBOX-WINSEQ] Recovery-handshake READ CAPACITY: %u sectors.\n",
1821                                 observed_sectors);
1822
1823                         if (observed_sectors <= XBOX_LOCKED_VIDEO_VIEW_MAX_SECTORS) {
1824                                 xbox_ref_log_fprintf (stderr,
1825                                         "[XBOX-WINSEQ][FATAL] Xbox game view was not established after direct and recovery handshakes.\n");
1826                                 return -1;
1827                         }
1828                 }
1829         }
1830
1831         /* Exact next Windows step after state preparation. */
1832         dvd_set_speed (dvd, 0xFFFF, NULL);
1833
1834         if (sectors)
1835                 *sectors = observed_sectors;
1836         if (sector_size)
1837                 *sector_size = observed_sector_size;
1838         return 0;
1839 }
1840
1841 int dvd_refresh_volume (dvd_drive *dvd) {
1842 #ifdef WIN32
1843         DWORD bytesReturned = 0;
1844         if (!dvd)
1845                 return -1;
1846         /* Match RefreshVolume() from the reference dumper: update properties only;
1847          * do not dismount here because that can reset drive state.  This is now
1848          * shared by Xbox and HLDS 0xE7 GC/Wii paths so Windows is less likely to
1849          * keep stale filesystem/probe state attached to odd discs. */
1850         DeviceIoControl (dvd -> fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL);
1851         dvd_sleep_ms (1000);
1852         return 0;
1853 #else
1854         (void) dvd;
1855         return 0;
1856 #endif
1857 }
1858
1859 int dvd_lock_volume (dvd_drive *dvd) {
1860 #ifdef WIN32
1861         DWORD bytesReturned = 0;
1862         if (!dvd)
1863                 return DVD_VOLUME_LOCK_FAILED;
1864         return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL)
1865                 ? DVD_VOLUME_LOCK_OK
1866                 : DVD_VOLUME_LOCK_FAILED;
1867 #else
1868         /* Linux CDROM_SEND_PACKET has no FSCTL_LOCK_VOLUME equivalent here.
1869          * Return a distinct result instead of falsely reporting an exclusive lock.
1870          * The caller may continue after warning about automount/media polling. */
1871         (void) dvd;
1872         return DVD_VOLUME_LOCK_UNAVAILABLE;
1873 #endif
1874 }
1875
1876 int dvd_xbox_refresh_volume (dvd_drive *dvd) {
1877         return dvd_refresh_volume (dvd);
1878 }
1879
1880 int dvd_xbox_lock_volume (dvd_drive *dvd) {
1881         return dvd_lock_volume (dvd);
1882 }
1883
1884 static int dvd_xbox_read_host_challenge_table (dvd_drive *dvd, u_int8_t *table, size_t table_len) {
1885         mmc_command mmc;
1886         int out;
1887
1888         if (!table || table_len < 0x664)
1889                 return -1;
1890
1891 #ifdef WIN32
1892         /* GDR-8050L / Xbox READ DVD STRUCTURE format C0. */
1893         dvd_init_command (&mmc, table, 0x664, NULL);
1894         mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1895         mmc.cmd[2] = 0xFF;
1896         mmc.cmd[3] = 0x02;
1897         mmc.cmd[4] = 0xFD;
1898         mmc.cmd[5] = 0xFF;
1899         mmc.cmd[6] = 0xFE;
1900         mmc.cmd[8] = 0x06;
1901         mmc.cmd[9] = 0x64;
1902         mmc.cmd[11] = 0xC0;
1903         mmc.cmdlen = 12;
1904         out = dvd_execute_cmd (dvd, &mmc, true);
1905
1906         /* Some Hitachi-family drives expose the same table via vendor command 0xFD. */
1907         if (out < 0 || table[772] != 1 || table[773] == 0) {
1908                 dvd_init_command (&mmc, table, 0x664, NULL);
1909                 mmc.cmd[0] = 0xFD;
1910                 mmc.cmd[1] = 0x01;
1911                 mmc.cmd[8] = 0x06;
1912                 mmc.cmd[9] = 0x64;
1913                 mmc.cmdlen = 12;
1914                 out = dvd_execute_cmd (dvd, &mmc, true);
1915         }
1916 #else
1917         {
1918                 u_int8_t cdb[12];
1919
1920                 memset (cdb, 0, sizeof (cdb));
1921                 memset (table, 0, table_len);
1922                 cdb[0] = 0xAD;
1923                 cdb[2] = 0xFF;
1924                 cdb[3] = 0x02;
1925                 cdb[4] = 0xFD;
1926                 cdb[5] = 0xFF;
1927                 cdb[6] = 0xFE;
1928                 cdb[8] = 0x06;
1929                 cdb[9] = 0x64;
1930                 cdb[11] = 0xC0;
1931                 out = dvd_xbox_sgio_exact (
1932                         dvd,
1933                         "3-read-dvd-structure-c0",
1934                         cdb,
1935                         sizeof (cdb),
1936                         table,
1937                         0x664,
1938                         DVD_DATA_IN,
1939                         120000);
1940
1941                 if (out < 0 || table[772] != 1 || table[773] == 0) {
1942                         memset (cdb, 0, sizeof (cdb));
1943                         memset (table, 0, table_len);
1944                         cdb[0] = 0xFD;
1945                         cdb[1] = 0x01;
1946                         cdb[8] = 0x06;
1947                         cdb[9] = 0x64;
1948                         out = dvd_xbox_sgio_exact (
1949                                 dvd,
1950                                 "3-read-host-table-fallback-fd",
1951                                 cdb,
1952                                 sizeof (cdb),
1953                                 table,
1954                                 0x664,
1955                                 DVD_DATA_IN,
1956                                 120000);
1957                 }
1958         }
1959 #endif
1960
1961         if (out < 0 || table[772] != 1)
1962                 return -1;
1963
1964         xbox_ref_log_fprintf (
1965                 stderr,
1966                 "[XBOX-SGIO] challenge-table marker=%u entries-byte=%u result=PASS\n",
1967                 (unsigned int) table[772],
1968                 (unsigned int) table[773]);
1969         return 0;
1970 }
1971
1972 int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1973         int i, k, l;
1974         int out;
1975         int chalpos[24];
1976         u_int8_t table[0x664];
1977         u_int8_t restable[261];
1978         u_int8_t hash[0x2C];
1979         SHA1_HASH digest;
1980         u_int8_t page[28];
1981         u_int8_t sticky[12];
1982         u_int32_t sectors = 0, sector_size = 0;
1983         xbox_rc4_ctx rc4;
1984
1985         if (!dvd || !dvd_is_xbox_unlock_drive (dvd))
1986                 return -1;
1987
1988         /* Step 1/2: read current capacity and the Xbox mode page. If the drive is
1989          * already unlocked, this is harmless; the final capacity check below becomes
1990          * the authority. */
1991 #ifdef WIN32
1992         dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL);
1993         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
1994 #else
1995         dvd_xbox_exact_read_capacity (
1996                 dvd, "1-initial-read-capacity", &sectors, &sector_size);
1997         dvd_xbox_exact_mode_sense_10 (
1998                 dvd, "2-mode-sense-3e", 0x3E, page, sizeof (page));
1999 #endif
2000
2001         /* Step 3: retrieve and decode the host challenge table. */
2002         if (dvd_xbox_read_host_challenge_table (dvd, table, sizeof (table)) < 0) {
2003                 error ("Cannot retrieve Xbox host challenge table");
2004                 return -1;
2005         }
2006
2007         for (i = 0; i < 0x2C; i++)
2008                 hash[i] = table[0x4A3 + i];
2009         Sha1Calculate (hash, 0x2C, &digest);
2010
2011         for (i = 0; i <= 260; i++)
2012                 restable[i] = table[774 + i];
2013         xbox_rc4_init (&rc4, digest.bytes, 7);
2014         xbox_rc4_crypt (&rc4, restable, restable, 0xFD);
2015
2016         k = 0;
2017         for (l = 0; l <= 23; l++) {
2018                 if (restable[l * 11] == 1) {
2019                         chalpos[k++] = l;
2020                         if (k == (int) (sizeof (chalpos) / sizeof (chalpos[0])))
2021                                 break;
2022                 }
2023         }
2024         if (k < 2) {
2025                 error ("Xbox challenge table does not contain enough usable entries");
2026                 return -1;
2027         }
2028
2029         /* Step 4: first host challenge. */
2030         memset (page, 0, sizeof (page));
2031         page[1] = 0x1A;
2032         page[8] = 0x3E;
2033         page[9] = 0x12;
2034         page[11] = 0x01;
2035         page[13] = 0xD1;
2036         page[14] = 0x01;
2037         memcpy (&page[15], &restable[1 + chalpos[k - 2] * 11], 5);
2038         /* Match the original dumper: send the challenge and continue even if
2039          * Windows reports a transport failure.  The later XDVDFS probe is the
2040          * authority for whether the drive actually entered the game view. */
2041 #ifdef WIN32
2042         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2043         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2044 #else
2045         dvd_xbox_exact_mode_select_10 (
2046                 dvd, "4-mode-select-challenge-1", page, sizeof (page));
2047         dvd_xbox_exact_mode_sense_10 (
2048                 dvd, "5-mode-sense-verify-1", 0x3E, page, sizeof (page));
2049 #endif
2050
2051         /* Step 6: second host challenge. */
2052         memset (page, 0, sizeof (page));
2053         page[1] = 0x1A;
2054         page[8] = 0x3E;
2055         page[9] = 0x12;
2056         page[12] = 0x01;
2057         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2058 #ifdef WIN32
2059         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2060         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
2061 #else
2062         dvd_xbox_exact_mode_select_10 (
2063                 dvd, "6-mode-select-challenge-2", page, sizeof (page));
2064         dvd_xbox_exact_mode_sense_10 (
2065                 dvd, "7-mode-sense-verify-2", 0x3E, page, sizeof (page));
2066 #endif
2067
2068         /* Step 8: unlock partition 1. */
2069         memset (page, 0, sizeof (page));
2070         page[1] = 0x1A;
2071         page[8] = 0x3E;
2072         page[9] = 0x12;
2073         page[10] = 0x01;
2074         page[11] = 0x01;
2075         page[12] = 0x01;
2076         page[13] = 0xD1;
2077         page[14] = 0x01;
2078         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
2079 #ifdef WIN32
2080         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
2081 #else
2082         dvd_xbox_exact_mode_select_10 (
2083                 dvd, "8-mode-select-partition-1-unlock", page, sizeof (page));
2084 #endif
2085
2086         /* Step 9: sticky descrambling, mode page 0x31. */
2087         memset (sticky, 0, sizeof (sticky));
2088         sticky[4] = 0x31;
2089         sticky[5] = 0x06;
2090         sticky[6] = 0x01;
2091 #ifdef WIN32
2092         dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
2093 #else
2094         dvd_xbox_exact_mode_select_6 (
2095                 dvd, "9-mode-select-sticky-descrambling", sticky, sizeof (sticky));
2096 #endif
2097
2098         /* Step 10: final capacity observation.  The original UnlockDrive() only
2099          * prints this verification and does not fail if the capacity has not changed
2100          * yet.  This matters for the first GDR-8050L handshake, whose purpose is to
2101          * prime the drive before the required media-change event. */
2102         sectors = 0;
2103         sector_size = 0;
2104 #ifdef WIN32
2105         out = dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL);
2106 #else
2107         out = dvd_xbox_exact_read_capacity (
2108                 dvd, "10-final-read-capacity", &sectors, &sector_size);
2109 #endif
2110         if (out == 0) {
2111                 if (unlocked_sectors)
2112                         *unlocked_sectors = sectors;
2113                 xbox_ref_log_fprintf (
2114                         stderr,
2115                         "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n",
2116                         sectors,
2117                         sector_size);
2118         } else {
2119                 xbox_ref_log_fprintf (
2120                         stderr,
2121                         "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
2122         }
2123
2124         return 0;
2125 }
2126
2127 bool dvd_is_xbox_challenge_drive (dvd_drive *dvd) {
2128         return dvd_is_hlds_drive (dvd) && dvd_prod_has (dvd, "GDR8050L");
2129 }
2130
2131 bool dvd_is_xbox_vendor_unlock_drive (dvd_drive *dvd) {
2132         return (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L"))) ||
2133                dvd_is_tsst_kreon_candidate (dvd);
2134 }
2135
2136 bool dvd_is_xbox_unlock_drive (dvd_drive *dvd) {
2137         return dvd_is_xbox_challenge_drive (dvd) || dvd_is_xbox_vendor_unlock_drive (dvd);
2138 }
2139
2140 bool dvd_is_xbox_drive (dvd_drive *dvd) {
2141         /* Autodetect only the two native Xbox profiles currently wired into the
2142          * Xbox dump planner. Other candidate drives keep FriiDump's normal GC/Wii/DVD
2143          * behavior unless the user explicitly forces Xbox mode with -T 4. */
2144         return dvd_is_xbox_challenge_drive (dvd) ||
2145                (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L")));
2146 }
2147
2148
2149
2150 const char *dvd_get_hlds_e7_profile_name (dvd_drive *dvd) {
2151         if (!dvd) return "none";
2152         if (dvd -> hlds_e7_profile_label) return dvd -> hlds_e7_profile_label;
2153         return dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2154 }
2155
2156 const char *dvd_get_hlds_e7_support_tier (dvd_drive *dvd) {
2157         if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2158         return dvd -> hlds_e7_support_tier ? dvd -> hlds_e7_support_tier : "legacy_detected";
2159 }
2160
2161 const char *dvd_get_hlds_e7_family (dvd_drive *dvd) {
2162         if (!dvd || dvd -> hlds_e7_type == 0) return "none";
2163         return dvd -> hlds_e7_family ? dvd -> hlds_e7_family : dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
2164 }
2165
2166 const char *dvd_get_hlds_e7_tokens (dvd_drive *dvd) {
2167         if (!dvd || dvd -> hlds_e7_type == 0) return "";
2168         return dvd -> hlds_e7_tokens ? dvd -> hlds_e7_tokens : "";
2169 }
2170
2171 const char *dvd_get_hlds_e7_record_id (dvd_drive *dvd) {
2172         if (!dvd || dvd -> hlds_e7_type == 0) return "";
2173         return dvd -> hlds_e7_record_id ? dvd -> hlds_e7_record_id : "";
2174 }
2175
2176 const char *dvd_get_hlds_e7_notes (dvd_drive *dvd) {
2177         if (!dvd || dvd -> hlds_e7_type == 0) return "";
2178         return dvd -> hlds_e7_notes ? dvd -> hlds_e7_notes : "";
2179 }
2180
2181 u_int32_t dvd_get_hlds_e7_static_cdb_base (dvd_drive *dvd) {
2182         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_cdb_base : 0;
2183 }
2184
2185 u_int32_t dvd_get_hlds_e7_static_gate (dvd_drive *dvd) {
2186         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_gate : 0;
2187 }
2188
2189 int dvd_get_hlds_e7_preferred_method (dvd_drive *dvd) {
2190         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_preferred_method : -1;
2191 }
2192
2193 u_int32_t dvd_get_hlds_e7_type (dvd_drive *dvd) {
2194         return dvd ? dvd -> hlds_e7_type : 0;
2195 }
2196
2197 u_int32_t dvd_get_hlds_e7_cache_base (dvd_drive *dvd) {
2198         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_cache_base : 0;
2199 }
2200
2201 u_int32_t dvd_get_hlds_e7_mem_blocks (dvd_drive *dvd) {
2202         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_mem_blocks : 0;
2203 }
2204
2205 char *dvd_get_vendor (dvd_drive *dvd) {
2206         return (dvd -> vendor);
2207 }
2208
2209
2210 char *dvd_get_product_id (dvd_drive *dvd) {
2211         return (dvd -> prod_id);
2212 }
2213
2214
2215 char *dvd_get_product_revision (dvd_drive *dvd) {
2216         return (dvd -> prod_rev);
2217 }
2218
2219
2220 char *dvd_get_model_string (dvd_drive *dvd) {
2221         return (dvd -> model_string);
2222 }
2223
2224
2225 char *dvd_get_device (dvd_drive *dvd) {
2226         return (dvd -> device);
2227 }
2228
2229 void *dvd_get_native_handle (dvd_drive *dvd) {
2230         if (!dvd) return NULL;
2231 #ifdef WIN32
2232         return (void *) dvd -> fd;
2233 #else
2234         return NULL;
2235 #endif
2236 }
2237
2238
2239 bool dvd_get_support_status (dvd_drive *dvd) {
2240         return (dvd -> supported);
2241 }
2242
2243 u_int32_t dvd_get_def_method (dvd_drive *dvd){
2244         return (dvd -> def_method);
2245 }
2246
2247 u_int32_t dvd_get_command (dvd_drive *dvd){
2248         return (dvd -> command);
2249 }