]> FriiDump Source - friidump.git/blob - libfriidump/dvd_drive.c
036d38b861ae02684ffa0dad88af6828db9d54d1
[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
48 #ifdef WIN32
49 #include <windows.h>
50 #include <ntddscsi.h>
51 #else
52 #include <linux/cdrom.h>
53 #include <sys/ioctl.h>
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57 #endif
58
59
60 /*! \brief Timeout for MMC commands.
61  *
62  * This must be expressed in seconds (Windows uses seconds, right?).
63  */
64 #define MMC_CMD_TIMEOUT 10
65
66
67 /* Imported drive-specific functions */
68 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);
69 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);
70 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);
71 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);
72 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);
73 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);
74
75
76 /*! \brief A structure that represents a CD/DVD-ROM drive.
77  */
78 struct dvd_drive_s {
79         /* Device special file */
80         char *device;                   //!< The path to the drive (i.e.: /dev/something on Unix, x: on Windows).
81
82         /* Data about the drive */
83         char *vendor;                   //!< The drive vendor.
84         char *prod_id;                  //!< The drive product ID.
85         char *prod_rev;                 //!< The drive product revision (Usually firmware version).
86         char *model_string;             //!< The above three strings, joined in a single one.
87         u_int32_t def_method;
88         u_int32_t command;
89         u_int32_t hlds_e7_type;
90         u_int32_t hlds_e7_cache_base;
91         u_int32_t hlds_e7_mem_blocks;
92         u_int32_t hlds_e7_static_cdb_base;
93         u_int32_t hlds_e7_static_gate;
94         int hlds_e7_preferred_method;
95         const char *hlds_e7_profile_label;
96         const char *hlds_e7_support_tier;
97         const char *hlds_e7_family;
98         const char *hlds_e7_tokens;
99         const char *hlds_e7_record_id;
100         const char *hlds_e7_notes;
101
102         /* Device-dependent internal memory dump function */
103         /*! The intended area should start where sector data is stored upon a READ command. Here we assume that sectors are
104          *  stored one after the other, as heuristics showed it is the case for the Hitachi MN103-based drives, but this model
105          *  might be changed in the future, if we get support for other drives.
106          */
107         dvd_drive_memdump_func memdump; //!< A pointer to a function that is able to dump the drive's internal memory area.
108         bool supported;                 //!< True if the drive is a supported model, false otherwise.
109
110
111         /* File descriptor & stuff used to access drive */
112 #ifdef WIN32
113         HANDLE fd;                      //!< The HANDLE to interact with the drive on Windows.
114 #else
115         int fd;                         //!< The file descriptor to interact with the drive on Unix.
116 #endif
117 };
118
119
120 /** \brief Supported MMC commands.
121  */
122 enum mmc_commands_e {
123         SPC_TEST_UNIT_READY = 0x00,
124         SPC_INQUIRY = 0x12,
125         SPC_MODE_SELECT_6 = 0x15,
126         MMC_START_STOP_UNIT = 0x1B,
127         MMC_READ_CAPACITY_10 = 0x25,
128         MMC_READ_10 = 0x28,
129         SPC_MODE_SENSE_10 = 0x5A,
130         SPC_MODE_SELECT_10 = 0x55,
131         MMC_READ_12 = 0xA8,
132         MMC_READ_DVD_STRUCTURE = 0xAD,
133 };
134
135 typedef struct {
136         u_int8_t s[256];
137         u_int8_t i;
138         u_int8_t j;
139 } xbox_rc4_ctx;
140
141 static void xbox_rc4_init (xbox_rc4_ctx *ctx, const u_int8_t *key, size_t keylen) {
142         u_int32_t i;
143         u_int8_t j, tmp;
144
145         for (i = 0; i < 256; i++)
146                 ctx -> s[i] = (u_int8_t) i;
147         ctx -> i = 0;
148         ctx -> j = 0;
149
150         if (keylen == 0)
151                 return;
152
153         j = 0;
154         for (i = 0; i < 256; i++) {
155                 j = (u_int8_t) (j + ctx -> s[i] + key[i % keylen]);
156                 tmp = ctx -> s[i];
157                 ctx -> s[i] = ctx -> s[j];
158                 ctx -> s[j] = tmp;
159         }
160 }
161
162 static void xbox_rc4_crypt (xbox_rc4_ctx *ctx, const u_int8_t *in, u_int8_t *out, size_t len) {
163         size_t n;
164         u_int8_t tmp, k;
165
166         for (n = 0; n < len; n++) {
167                 ctx -> i = (u_int8_t) (ctx -> i + 1);
168                 ctx -> j = (u_int8_t) (ctx -> j + ctx -> s[ctx -> i]);
169                 tmp = ctx -> s[ctx -> i];
170                 ctx -> s[ctx -> i] = ctx -> s[ctx -> j];
171                 ctx -> s[ctx -> j] = tmp;
172                 k = ctx -> s[(u_int8_t) (ctx -> s[ctx -> i] + ctx -> s[ctx -> j])];
173                 out[n] = in[n] ^ k;
174         }
175 }
176
177 static bool dvd_prod_has (dvd_drive *dvd, const char *needle) {
178         return dvd && dvd -> prod_id && needle && strstr (dvd -> prod_id, needle) != NULL;
179 }
180
181 static bool dvd_vendor_is (dvd_drive *dvd, const char *vendor) {
182         return dvd && dvd -> vendor && vendor && strcmp (dvd -> vendor, vendor) == 0;
183 }
184
185 static bool dvd_is_hlds_drive (dvd_drive *dvd) {
186         return dvd_vendor_is (dvd, "HL-DT-ST");
187 }
188
189 static bool dvd_prod_has_any (dvd_drive *dvd, const char **needles, size_t count) {
190         size_t i;
191         for (i = 0; i < count; i++) {
192                 if (dvd_prod_has (dvd, needles[i]))
193                         return true;
194         }
195         return false;
196 }
197
198 static bool dvd_is_hlds_gcc4243_4244_drive (dvd_drive *dvd) {
199         static const char *names[] = {
200                 "GCC-4243N", "GCC4243N", "GCC4243",
201                 "GCC-4244N", "GCC4244N", "GCC4244"
202         };
203         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
204 }
205
206 static bool dvd_is_hlds_gdr8050l_drive (dvd_drive *dvd) {
207         static const char *names[] = {
208                 "GDR8050L", "GDR-8050L"
209         };
210         return dvd_is_hlds_drive (dvd) && dvd_prod_has_any (dvd, names, sizeof (names) / sizeof (names[0]));
211 }
212
213
214 typedef struct {
215         const char *model;
216         const char *firmware;
217         u_int32_t type;
218         u_int32_t cache_base;
219         u_int32_t mem_blocks;
220         int preferred_method;
221         const char *label;
222         const char *support_tier;
223         const char *family;
224         const char *tokens;
225         const char *record_id;
226         u_int32_t static_cdb_base;
227         u_int32_t static_gate;
228         const char *notes;
229 } hlds_e7_profile_desc;
230
231 static const hlds_e7_profile_desc hlds_e7_profiles[] = {
232         { "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" },
233         { "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" },
234         { "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" },
235         { "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" },
236         { "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" },
237         { "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" },
238         { "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" },
239         { "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" },
240         { "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" },
241         { "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" },
242         { "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" },
243         { "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" },
244         { "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" },
245         { "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" },
246         { "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" },
247         { "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" },
248         { "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" },
249         { "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" },
250         { "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" },
251         { "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" },
252         { "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" },
253         { "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" },
254         { NULL, NULL, 0, 0, 0, -1, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL }
255 };
256
257 static void hlds_normalize_model (const char *src, char *dst, size_t dst_size) {
258         size_t i, j;
259         if (!dst || dst_size == 0)
260                 return;
261         dst[0] = 0;
262         if (!src)
263                 return;
264         for (i = 0, j = 0; src[i] && j + 1 < dst_size; i++) {
265                 unsigned char c = (unsigned char) src[i];
266                 if (isalnum (c))
267                         dst[j++] = (char) toupper (c);
268         }
269         dst[j] = 0;
270 }
271
272 static bool hlds_model_matches (dvd_drive *dvd, const char *model) {
273         char prod_norm[64];
274         char model_norm[64];
275         if (!dvd || !dvd -> prod_id || !model)
276                 return false;
277         hlds_normalize_model (dvd -> prod_id, prod_norm, sizeof (prod_norm));
278         hlds_normalize_model (model, model_norm, sizeof (model_norm));
279         return prod_norm[0] && model_norm[0] && strstr (prod_norm, model_norm) != NULL;
280 }
281
282 static bool hlds_revision_matches (dvd_drive *dvd, const char *revision) {
283         const unsigned char *a;
284         const unsigned char *b;
285         if (!revision || !revision[0])
286                 return true;
287         if (!dvd || !dvd -> prod_rev)
288                 return false;
289         a = (const unsigned char *) dvd -> prod_rev;
290         b = (const unsigned char *) revision;
291         while (*a && *b) {
292                 if (toupper (*a) != toupper (*b))
293                         return false;
294                 a++;
295                 b++;
296         }
297         return *a == 0 && *b == 0;
298 }
299
300 static const hlds_e7_profile_desc *dvd_find_hlds_e7_profile (dvd_drive *dvd) {
301         const hlds_e7_profile_desc *p;
302         if (!dvd_is_hlds_drive (dvd))
303                 return NULL;
304         for (p = hlds_e7_profiles; p -> model; p++) {
305                 if (hlds_model_matches (dvd, p -> model) && hlds_revision_matches (dvd, p -> firmware))
306                         return p;
307         }
308         return NULL;
309 }
310
311 static u_int32_t dvd_hlds_e7_detect_type (dvd_drive *dvd) {
312         const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
313         static const char *type1[] = {
314                 "GCC-4160N", "GCC4160N", "GCC4160",
315                 "GCC-4240N", "GCC4240N", "GCC4240"
316         };
317         static const char *type2_1[] = {
318                 "GCC-4241N", "GCC4241N", "GCC4241"
319         };
320         static const char *type2_2[] = {
321                 "GCC-4242N", "GCC4242N", "GCC4242"
322         };
323         static const char *gdr8081n[] = {
324                 "GDR8081N", "GDR-8081N"
325         };
326         static const char *type3[] = {
327                 "GCC4244", "GCC4244N", "GCC-4244N",
328                 "GCC4247", "GCC4247N", "GCC-4247N",
329                 "GDR8083N", "GDR8084N",
330                 "GCC-4243N", "GCC4243N", "GCC4243",
331                 "GCC-4246N", "GCC4246N", "GCC4246"
332         };
333         static const char *type4[] = {
334                 "DU10N", "GDR8082N", "GDR8161B", "GDR8162B",
335                 "GDR8163B", "GDR8164B", "GDR-T10N",
336                 /* Local project keeps GDR-3120L in the same transport family for
337                  * classification, but Xbox dumping is still routed through the explicit
338                  * Xbox paths rather than the GC/Wii Method 8/9 readers. */
339                 "GDR3120L", "GDR-3120L"
340         };
341
342         if (!dvd_is_hlds_drive (dvd))
343                 return 0;
344         if (profile)
345                 return profile -> type;
346         if (dvd_prod_has_any (dvd, type1, sizeof (type1) / sizeof (type1[0])))
347                 return 1;
348         if (dvd_prod_has_any (dvd, type2_1, sizeof (type2_1) / sizeof (type2_1[0])))
349                 return 21;
350         if (dvd_prod_has_any (dvd, type2_2, sizeof (type2_2) / sizeof (type2_2[0])))
351                 return 22;
352         if (dvd_prod_has_any (dvd, gdr8081n, sizeof (gdr8081n) / sizeof (gdr8081n[0])))
353                 return 81;
354         if (dvd_prod_has_any (dvd, type3, sizeof (type3) / sizeof (type3[0])))
355                 return 3;
356         if (dvd_is_hlds_gdr8050l_drive (dvd))
357                 return 44;
358         if (dvd_prod_has_any (dvd, type4, sizeof (type4) / sizeof (type4[0])))
359                 return 4;
360         /* DIC labels GSA-4163B as an Xbox swap candidate, not a normal 0xE7
361          * Nintendo-disc cache dump profile, so keep it out of the profile layer. */
362         return 0;
363 }
364
365 static bool dvd_is_hlds_gc_wii_drive (dvd_drive *dvd) {
366         return dvd_hlds_e7_detect_type (dvd) != 0;
367 }
368
369 static const char *dvd_hlds_e7_profile_name_from_type (u_int32_t type) {
370         switch (type) {
371                 case 1: return "Type1";
372                 case 21: return "Type2_1 experimental";
373                 case 22: return "Type2_2 experimental";
374                 case 3: return "Type3";
375                 case 4: return "Type4";
376                 case 44: return "GDR-8050L modified 0xE7 single-window proven fallback";
377                 case 45: return "GDR-8050L modified 0xE7 speed-probe pending";
378                 case 442: return "GDR-8050L modified 0xE7 probe B 2-window";
379                 case 443: return "GDR-8050L modified 0xE7 probe A 3-window";
380                 case 445: return "GDR-8050L modified 0xE7 probe C 5-window guarded";
381                 case 81: return "GDR-8081N experimental 0xE7 probe";
382                 case 811: return "GDR-8081N probe A Type4-derived";
383                 case 812: return "GDR-8081N probe B single-window";
384                 case 813: return "GDR-8081N probe C Type1-base";
385                 case 814: return "GDR-8081N probe E exact-offset moving-cache candidate";
386                 case 815: return "GDR-8081N probe A scan-guided Type4-derived";
387                 default: return "none";
388         }
389 }
390
391 static void dvd_apply_hlds_e7_profile (dvd_drive *dvd) {
392         const hlds_e7_profile_desc *profile = dvd_find_hlds_e7_profile (dvd);
393         dvd -> hlds_e7_type = dvd_hlds_e7_detect_type (dvd);
394         dvd -> hlds_e7_cache_base = 0x80000000U;
395         dvd -> hlds_e7_mem_blocks = 5;
396         dvd -> hlds_e7_static_cdb_base = 0;
397         dvd -> hlds_e7_static_gate = 0;
398         dvd -> hlds_e7_preferred_method = -1;
399         dvd -> hlds_e7_profile_label = NULL;
400         dvd -> hlds_e7_support_tier = NULL;
401         dvd -> hlds_e7_family = NULL;
402         dvd -> hlds_e7_tokens = NULL;
403         dvd -> hlds_e7_record_id = NULL;
404         dvd -> hlds_e7_notes = NULL;
405
406         switch (dvd -> hlds_e7_type) {
407                 case 1:
408                         /* DIC Type1: GCC-4160N/GCC-4240N cache frames begin at 0x00a13000
409                          * and only one 16-sector cache window is consumed per READ. */
410                         dvd -> hlds_e7_cache_base = 0x00a13000U;
411                         dvd -> hlds_e7_mem_blocks = 1;
412                         break;
413                 case 21:
414                 case 22:
415                         /* DIC Type2 uses a moving 0x80000000-derived cache address.  This
416                          * branch logs/classifies it, but does not yet claim DIC parity. */
417                         dvd -> hlds_e7_cache_base = 0x80000000U;
418                         dvd -> hlds_e7_mem_blocks = 1;
419                         break;
420                 case 81:
421                         /* GDR-8081N is not in the confirmed DIC dump list, but local firmware
422                          * analysis suggests an 0xE7 command surface.  Start with a Type4-derived
423                          * candidate; disc.c probes and may switch to one of the 811..814 runtime
424                          * profiles before seed cracking continues. */
425                         dvd -> hlds_e7_cache_base = 0x80000000U;
426                         dvd -> hlds_e7_mem_blocks = 5;
427                         break;
428                 case 44:
429                         /* Stock GDR-8050L firmware does not expose the HIT 0xE7 memdump command.
430                          * The local test unit is GDR-8163B hardware cross-flashed with modified
431                          * GDR-8050L firmware where 0xE7 memdump was added.  Single-window has
432                          * completed and hash-matched Sonic, so it remains the proven fallback.
433                          * Start in an explicit speed-probe-pending profile so the initial drive
434                          * information does not look like the old static single-window build.
435                          * disc.c promotes to 2/3/5 windows only after guarded validation, or
436                          * settles back to the proven single-window profile. */
437                         dvd -> hlds_e7_type = 45;
438                         dvd -> hlds_e7_cache_base = 0x80000000U;
439                         dvd -> hlds_e7_mem_blocks = 1;
440                         break;
441                 case 3:
442                 case 4:
443                 default:
444                         dvd -> hlds_e7_cache_base = 0x80000000U;
445                         dvd -> hlds_e7_mem_blocks = 5;
446                         break;
447         }
448
449         if (profile) {
450                 dvd -> hlds_e7_cache_base = profile -> cache_base;
451                 dvd -> hlds_e7_mem_blocks = profile -> mem_blocks;
452                 dvd -> hlds_e7_static_cdb_base = profile -> static_cdb_base;
453                 dvd -> hlds_e7_static_gate = profile -> static_gate;
454                 dvd -> hlds_e7_preferred_method = profile -> preferred_method;
455                 dvd -> hlds_e7_profile_label = profile -> label;
456                 dvd -> hlds_e7_support_tier = profile -> support_tier;
457                 dvd -> hlds_e7_family = profile -> family;
458                 dvd -> hlds_e7_tokens = profile -> tokens;
459                 dvd -> hlds_e7_record_id = profile -> record_id;
460                 dvd -> hlds_e7_notes = profile -> notes;
461                 /* The modified GDR-8050L runtime profile still starts in the guarded
462                  * speed-probe-pending state, but keeps its static parser evidence fields. */
463                 if (profile -> type == 44) {
464                         dvd -> hlds_e7_type = 45;
465                         dvd -> hlds_e7_mem_blocks = 1;
466                 }
467         }
468 }
469
470 void dvd_set_hlds_e7_runtime_profile (dvd_drive *dvd, u_int32_t type, u_int32_t cache_base, u_int32_t mem_blocks) {
471         if (!dvd)
472                 return;
473         dvd -> hlds_e7_type = type;
474         dvd -> hlds_e7_cache_base = cache_base;
475         dvd -> hlds_e7_mem_blocks = mem_blocks;
476 }
477
478 static bool dvd_is_tsst_kreon_candidate (dvd_drive *dvd) {
479         if (!(dvd_vendor_is (dvd, "TSSTcorp") || dvd_vendor_is (dvd, "SAMSUNG")))
480                 return false;
481         return
482                 dvd_prod_has (dvd, "TS-H352C") ||
483                 dvd_prod_has (dvd, "TS-H353A") ||
484                 dvd_prod_has (dvd, "SH-D162C") ||
485                 dvd_prod_has (dvd, "SH-D162D") ||
486                 dvd_prod_has (dvd, "SH-D163A") ||
487                 dvd_prod_has (dvd, "SH-D163B");
488 }
489
490
491 /**
492  * Initializes a structure representing an MMC command.
493  * @param mmc A pointer to the MMC command structure.
494  * @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.
495  * @param len The length of the buffer (ignored in case buf is NULL).
496  * @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.
497  */
498 void dvd_init_command (mmc_command *mmc, u_int8_t *buf, int len, req_sense *sense) {
499         memset (mmc, 0, sizeof (mmc_command));
500         if (buf)
501                 memset (buf, 0, len);
502         mmc -> cmdlen = 12;
503         mmc -> direction = buf && len > 0 ? DVD_DATA_IN : DVD_DATA_NONE;
504         mmc -> buffer = buf;
505         mmc -> buflen = buf ? len : 0;
506         mmc -> sense = sense;
507         
508         return;
509 }
510
511
512 #ifdef WIN32
513
514 /* Doc is under the UNIX function */
515 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
516         SCSI_PASS_THROUGH_DIRECT *sptd;
517         unsigned char sptd_sense[sizeof (*sptd) + 18], *sense;
518         DWORD bytes;
519         int out;
520
521         sptd = (SCSI_PASS_THROUGH_DIRECT *) sptd_sense;
522         sense = &sptd_sense[sizeof (*sptd)];
523         
524         memset (sptd, 0, sizeof (sptd_sense));
525         memcpy (sptd -> Cdb, mmc -> cmd, sizeof (mmc -> cmd));
526         sptd -> Length = sizeof (SCSI_PASS_THROUGH_DIRECT);
527         sptd -> CdbLength = mmc -> cmdlen;
528         sptd -> SenseInfoLength = 18;
529         if (mmc -> direction == DVD_DATA_OUT)
530                 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
531         else if (mmc -> direction == DVD_DATA_NONE)
532                 sptd -> DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
533         else
534                 sptd -> DataIn = SCSI_IOCTL_DATA_IN;
535         sptd -> DataBuffer = mmc -> buffer;
536         // Quick hack: Windows hates sptd->DataTransferLength = 1, so we set it to 2 and ignore the second byte.
537         if (mmc -> buflen == 1)         // TODO
538                 sptd -> DataTransferLength = 2;
539         else
540                 sptd -> DataTransferLength = mmc -> buflen;
541         sptd -> TimeOutValue = MMC_CMD_TIMEOUT;
542         sptd -> SenseInfoOffset = sizeof (*sptd);
543
544         //fprintf (stdout,"mmc->cmd[00] = %d \n",mmc->cmd[00]);
545         //Set streaming hack
546         if (mmc->cmd[00]==0xB6) {
547                 sptd -> DataIn = SCSI_IOCTL_DATA_OUT;
548                 sptd -> DataTransferLength = 28;
549         }
550
551         {
552                 BOOL ioctl_ok = DeviceIoControl (dvd -> fd, IOCTL_SCSI_PASS_THROUGH_DIRECT,
553                         sptd, sizeof (*sptd) + 18, sptd, sizeof (*sptd) + 18, &bytes, NULL);
554                 /* DeviceIoControl may succeed while the drive returns CHECK CONDITION.
555                  * Treat any non-GOOD SCSI status as command failure.  ignore_errors only
556                  * suppresses diagnostics; it must not turn failed commands into success. */
557                 if (!ioctl_ok || sptd -> ScsiStatus != 0) {
558                         out = -1;       /* Failure */
559                         if (!ignore_errors) {
560                                 error ("Execution of MMC command failed: Win32=%lu SCSI=0x%02X",
561                                 (unsigned long) GetLastError (), sptd -> ScsiStatus);
562                                 debug ("Command was: ");
563                                 hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
564                                 debug ("Sense data: %02X/%02X/%02X\n", sense[2] & 0x0F, sense[12], sense[13]);
565                         }
566                 } else {
567                         out = 0;
568                 }
569         }
570         
571         if (mmc -> sense) {
572                 mmc -> sense -> sense_key = sense[2];
573                 mmc -> sense -> asc = sense[12];
574                 mmc -> sense -> ascq = sense[13];
575         }
576         
577         return (out);
578 }
579
580 #else
581
582 /**
583  * Executes an MMC command.
584  * @param dvd The DVD drive the command should be exectued on.
585  * @param mmc The command to be executed.
586  * @param ignore_errors If set to true, no error will be printed if the command fails.
587  * @return 0 if the command was executed successfully, < 0 otherwise.
588  */
589 int dvd_execute_cmd (dvd_drive *dvd, mmc_command *mmc, bool ignore_errors) {
590         int out;
591         struct cdrom_generic_command cgc;
592         struct request_sense sense;
593         
594 #if 0
595         debug ("Executing MMC command: ");
596         hex_and_ascii_print ("", mmc -> cmd, sizeof (mmc -> cmd));
597 #endif
598
599         /* Init Linux-format MMC command */
600         memset (&cgc, 0, sizeof (struct cdrom_generic_command));
601         memcpy (cgc.cmd, mmc -> cmd, sizeof (mmc -> cmd));
602         cgc.buffer = (unsigned char *) mmc -> buffer;
603         cgc.buflen = mmc -> buflen;
604         if (mmc -> direction == DVD_DATA_OUT)
605                 cgc.data_direction = CGC_DATA_WRITE;
606         else if (mmc -> direction == DVD_DATA_NONE)
607                 cgc.data_direction = CGC_DATA_NONE;
608         else
609                 cgc.data_direction = CGC_DATA_READ;
610         cgc.timeout = MMC_CMD_TIMEOUT * 1000;   /* Linux uses milliseconds */
611         cgc.sense = &sense;
612         if (ioctl (dvd -> fd, CDROM_SEND_PACKET, &cgc) < 0) {
613                 out = -1;       /* Failure */
614                 if (!ignore_errors) {
615                         error ("Execution of MMC command failed: %s", strerror (errno));
616                         debug ("Command was:");
617                         hex_and_ascii_print ("", cgc.cmd, sizeof (cgc.cmd));
618                         debug ("Sense data: %02X/%02X/%02X", sense.sense_key, sense.asc, sense.ascq);
619                 }
620         } else {
621                 out = 0;
622         }
623         
624         if (mmc -> sense) {
625                 mmc -> sense -> sense_key = sense.sense_key;
626                 mmc -> sense -> asc = sense.asc;
627                 mmc -> sense -> ascq = sense.ascq;
628         }
629         
630         return (out);
631 }
632 #endif
633
634
635 /**
636  * Sends an INQUIRY command to the drive to retrieve drive identification strings.
637  * @param dvd The DVD drive the command should be exectued on.
638  * @return 0 if the command was executed successfully, < 0 otherwise.
639  */
640 static int dvd_get_drive_info (dvd_drive *dvd) {
641         mmc_command mmc;
642         int out;
643         u_int8_t buf[36];
644         char tmp[36 * 4];
645         
646         dvd_init_command (&mmc, buf, sizeof (buf), NULL);
647         mmc.cmd[0] = SPC_INQUIRY;
648         mmc.cmd[4] = sizeof (buf);
649         if ((out = dvd_execute_cmd (dvd, &mmc, false)) >= 0) {
650                 my_strndup (dvd -> vendor, buf + 8, 8);
651                 strtrimr (dvd -> vendor);
652                 my_strndup (dvd -> prod_id, buf + 16, 16);
653                 strtrimr (dvd -> prod_id);
654                 my_strndup (dvd -> prod_rev, buf + 32, 4);
655                 strtrimr (dvd -> prod_rev);
656                 snprintf (tmp, sizeof (tmp), "%s/%s/%s", dvd -> vendor, dvd -> prod_id, dvd -> prod_rev);
657                 my_strdup (dvd -> model_string, tmp);
658                 
659                 debug ("DVD drive is \"%s\"", dvd -> model_string);
660         } else {
661                 error ("Cannot identify DVD drive\n");
662         }
663
664         return (out);
665 }
666
667
668 /**
669  * Assigns the proper memory dump functions to a dvd_drive object, according to vendor, model and other parameters. Actually this scheme probably needs to
670  * to be improved, but it is enough for the moment.
671  * @param dvd The DVD drive the command should be exectued on.
672  */
673 static void dvd_assign_functions (dvd_drive *dvd, u_int32_t command) {
674         dvd -> def_method = 0;
675         if (dvd_is_hlds_gc_wii_drive (dvd)) {
676                 dvd_apply_hlds_e7_profile (dvd);
677                 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",
678                         dvd_get_hlds_e7_profile_name (dvd),
679                         dvd_get_hlds_e7_support_tier (dvd),
680                         dvd_get_hlds_e7_family (dvd),
681                         dvd -> hlds_e7_cache_base, dvd -> hlds_e7_mem_blocks,
682                         dvd -> hlds_e7_static_cdb_base, dvd -> hlds_e7_static_gate);
683                 dvd -> memdump = &hitachi_dvd_dump_mem;
684                 dvd -> command = 2;
685                 dvd -> supported = true;
686                 /* DIC Type1/Type3/Type4 use READ12 + HIT 0xE7 cache extraction.
687                  * GCC-4244N is validated with Sonic Mega Collection and GCC-4243N is
688                  * actively under test, so both remain Method 8.  Type1 drives get the
689                  * DIC-derived 0x00a13000 / one-window cache profile and also default to
690                  * Method 8 so GCC-4160N/GCC-4240N can be tested without forcing a method.
691                  * GDR-8050L gets a proven single-window fallback plus guarded speed probes
692                  * for cross-flashed/modified firmware with 0xE7 memdump added; stock
693                  * GDR-8050L firmware does not expose this GC/Wii memdump path. GDR-8081N
694                  * is an experimental probe target and needs Method 8 so seed probing can run.
695                  * Type2 is still classified only; leave it on the older Method 9 path. */
696                 if (dvd -> hlds_e7_preferred_method >= 0)
697                         dvd -> def_method = (u_int32_t) dvd -> hlds_e7_preferred_method;
698                 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))
699                         dvd -> def_method = 8;
700                 else
701                         dvd -> def_method = 9;
702
703         } else if (strcmp (dvd -> vendor, "LITE-ON") == 0 && (
704                 strcmp (dvd ->prod_id, "DVDRW LH-18A1H") == 0 ||
705                 strcmp (dvd ->prod_id, "DVDRW LH-18A1P") == 0 ||
706                 strcmp (dvd ->prod_id, "DVDRW LH-20A1H") == 0 ||
707                 strcmp (dvd ->prod_id, "DVDRW LH-20A1P") == 0
708         )) {
709                 debug ("Lite-On DVD drive detected, using Lite-On memory dump command");
710                 dvd -> memdump = &liteon_dvd_dump_mem;
711                 dvd -> command = 3;
712                 dvd -> supported = true;
713                 dvd -> def_method = 5;
714
715         } else if (dvd_is_tsst_kreon_candidate (dvd) || (strcmp (dvd -> vendor, "TSSTcorp") == 0 && (
716                 strcmp (dvd ->prod_id, "DVD-ROM SH-D162A") == 0 ||
717                 strcmp (dvd ->prod_id, "DVD-ROM SH-D162B") == 0
718         ))) {
719                 debug ("Toshiba Samsung DVD drive detected, using vanilla 2384 memory dump command");
720                 dvd -> memdump = &vanilla_2384_dvd_dump_mem;
721                 dvd -> command = 1;
722                 dvd -> supported = true;
723                 dvd -> def_method = 0;
724
725         } else if (strcmp (dvd -> vendor, "PLEXTOR") == 0) {
726                 debug ("Plextor DVD drive detected, using vanilla 2064 memory dump command");
727                 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
728                 dvd -> command = 0;
729                 dvd -> supported = true;
730                 dvd -> def_method = 2;
731
732         } else {
733                 /* This is an unsupported drive (yet). */
734                 dvd -> memdump = &vanilla_2064_dvd_dump_mem;
735                 dvd -> command = 0;
736                 dvd -> supported = false;
737         }
738
739         if (command!=-1) {
740                 dvd -> command = command;
741                 if          (command == 0) dvd -> memdump = &vanilla_2064_dvd_dump_mem;
742                 else if (command == 1) dvd -> memdump = &vanilla_2384_dvd_dump_mem;
743                 else if (command == 2) dvd -> memdump = &hitachi_dvd_dump_mem;
744                 else if (command == 3) dvd -> memdump = &liteon_dvd_dump_mem;
745                 else if (command == 4) dvd -> memdump = &renesas_dvd_dump_mem;
746         }
747
748
749         //init Reed-Solomon for Lite-On
750         generate_gf();
751         gen_poly();
752
753         return;
754 }
755
756
757 /**
758  * Creates a new structure representing a CD/DVD-ROM drive.
759  * @param device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows).
760  * @return The newly-created structure, to be used with the other commands, or NULL if the drive could not be initialized.
761  */
762 dvd_drive *dvd_drive_new (char *device, u_int32_t command) {
763         dvd_drive *dvd;
764 #ifdef WIN32
765         HANDLE fd;
766         char dev[40];
767 #else
768         int fd;
769 #endif
770
771         /* Force the dropping of privileges: in our model, privileges are only used to execute memory dump commands, the user
772            must gain access to the device somehow else (i. e. get added to the "cdrom" group or similar things) */
773         drop_euid ();
774         
775         debug ("Trying to open DVD device %s", device);
776 #ifdef WIN32
777         sprintf (dev, "\\\\.\\%c:", device[0]);
778         if ((fd = CreateFile (dev, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) {
779                 error ("Cannot open drive: %d", GetLastError ());
780 #else
781         if ((fd = open (device, O_RDONLY | O_NONBLOCK)) < 0) {
782                 perror ("Cannot open drive");
783 #endif
784                 dvd = NULL;
785         } else {
786                 debug ("Opened successfully");
787                 drop_euid ();
788                 dvd = (dvd_drive *) malloc (sizeof (dvd_drive));
789                 if (!dvd) {
790                         fprintf (stderr, "malloc() failed\n");
791                         exit (100);
792                 }
793                 memset (dvd, 0, sizeof (dvd_drive));
794                 my_strdup (dvd -> device, device);
795                 dvd -> fd = fd;
796                 dvd_get_drive_info (dvd);
797                 dvd_assign_functions (dvd, command);
798         }
799
800         return (dvd);
801 }
802
803
804 /**
805  * Frees resources used by a DVD drive structure and destroys it.
806  * @param dvd The DVD drive structure to be destroyed.
807  * @return NULL.
808  */
809 void *dvd_drive_destroy (dvd_drive *dvd) {
810         if (dvd) {
811 #ifdef WIN32
812                 CloseHandle (dvd -> fd);
813 #else
814                 close (dvd -> fd);
815 #endif
816                 my_free (dvd -> device);
817                 my_free (dvd -> vendor);
818                 my_free (dvd -> prod_id);
819                 my_free (dvd -> prod_rev);
820                 my_free (dvd);
821         }
822
823         return (NULL);
824 }
825
826
827 /**
828  * Executes the drive-dependent function to dump the drive sector cache, and returns the dumped data.
829  * @param dvd The DVD drive the command should be exectued on.
830  * @param block_off The offset to start dumping, WRT the beginning of the sector cache.
831  * @param block_len The number of blocks to dump.
832  * @param block_size The block size to be used for dumping.
833  * @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.
834  * @return 0 if the command was executed successfully, < 0 otherwise.
835  */
836 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) {
837         int out;
838
839         /* Upgrade privileges and call actual dump functions */
840         upgrade_euid ();
841         out = dvd -> memdump (dvd, block_off, block_len, block_size, buf);
842         drop_euid ();
843
844         return (out);
845 }
846
847
848 /**
849  * 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
850  * at the beginning of the cache and can be dumped later.
851  * @param dvd The DVD drive the command should be exectued on.
852  * @param sector The sector to be read. What will be cached is the 16-sectors block to which the sector belongs.
853  * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
854  * @return 0 if the command was executed successfully, < 0 otherwise.
855  */
856 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) {
857         mmc_command mmc;
858         int out;
859         u_int8_t intbuf[64 * 1024], *buf;
860         size_t bufsize;
861
862         /* We need some buffer, be it provided externally or not */
863         if (extbuf) {
864                 buf = extbuf;
865                 bufsize = extbufsize;
866         } else {
867                 buf = intbuf;
868                 bufsize = sizeof (intbuf);
869         }
870
871         dvd_init_command (&mmc, buf, bufsize, sense);
872         mmc.cmd[0] = MMC_READ_12;
873         mmc.cmd[1] = 0x08;      /* FUA bit set */
874         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
875         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
876         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
877         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
878         mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
879         mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
880         mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
881         mmc.cmd[9] = (u_int8_t)  (sectors & 0x000000FF);
882         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
883
884         return (out);
885 }
886
887
888 /**
889  * Issues a READ(12) command using the STREAMING bit, which causes the requested 16-sector block to be read into memory,
890  * together with the following four. This way we will be able to dump 5 sector with a single READ request.
891  *
892  * Note the strange need for a big buffer even though we must only pass 0x10 as the transfer length, otherwise the drive will hang (!?).
893  * @param dvd The DVD drive the command should be exectued on.
894  * @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.
895  * @param sense A pointer to a structure which will hold the SENSE DATA got from the drive after the command has been executed.
896  * @param extbuf A buffer where to store the read data, or NULL.
897  * @param extbufsize The size of the buffer.
898  * @return 
899  */
900 int dvd_read_sector_streaming (dvd_drive *dvd, u_int32_t sector, req_sense *sense, u_int8_t *extbuf, size_t extbufsize) {
901         mmc_command mmc;
902         int out;
903         u_int8_t intbuf[2048 * 16], *buf;
904         size_t bufsize;
905
906         /* We need some buffer, be it provided externally or not */
907         if (extbuf) {
908                 buf = extbuf;
909                 bufsize = extbufsize;
910         } else {
911                 buf = intbuf;
912                 bufsize = sizeof (intbuf);
913         }
914         
915         dvd_init_command (&mmc, buf, bufsize, sense);
916         mmc.cmd[0] = MMC_READ_12;
917         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
918         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
919         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
920         mmc.cmd[5] = (u_int8_t) (sector & 0x000000FF);
921         mmc.cmd[6] = 0;
922         mmc.cmd[7] = 0;
923         mmc.cmd[8] = 0;
924         mmc.cmd[9] = 0x10;
925         mmc.cmd[10] = 0x80;     /* STREAMING bit set */
926         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
927         
928         return (out);
929 }
930
931
932 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) {
933         mmc_command mmc;
934         int out;
935         u_int8_t intbuf[64 * 1024], *buf;
936         size_t bufsize;
937
938         /* We need some buffer, be it provided externally or not */
939         if (extbuf) {
940                 buf = extbuf;
941                 bufsize = extbufsize;
942         } else {
943                 buf = intbuf;
944                 bufsize = sizeof (intbuf);
945         }
946         
947         dvd_init_command (&mmc, buf, bufsize, sense);
948         mmc.cmd[0] = MMC_READ_12;
949         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
950         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
951         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
952         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
953         mmc.cmd[6] = (u_int8_t) ((sectors & 0xFF000000) >> 24); /* Size from MSB to LSB */
954         mmc.cmd[7] = (u_int8_t) ((sectors & 0x00FF0000) >> 16);
955         mmc.cmd[8] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
956         mmc.cmd[9] = (u_int8_t)  (sectors & 0x000000FF);
957         mmc.cmd[10] = 0x80;     /* STREAMING bit set */
958         out = dvd_execute_cmd (dvd, &mmc, true);                /* Ignore errors! */
959         
960         return (out);
961 }
962
963
964 int dvd_flush_cache_READ12 (dvd_drive *dvd, u_int32_t sector, req_sense *sense) {
965         mmc_command mmc;
966         int out;
967         u_int8_t intbuf[64], *buf;
968         size_t bufsize;
969
970         buf = intbuf;
971         bufsize = 0;
972         
973         dvd_init_command (&mmc, buf, bufsize, sense);
974         mmc.cmd[0] = MMC_READ_12;
975         mmc.cmd[1] = 0x08;
976         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);  /* LBA from MSB to LSB */
977         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
978         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
979         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
980         out = dvd_execute_cmd (dvd, &mmc, true);
981         
982         return (out);
983 }
984
985 static void dvd_sleep_ms (unsigned int ms) {
986 #ifdef WIN32
987         Sleep (ms);
988 #else
989         usleep ((useconds_t) ms * 1000);
990 #endif
991 }
992
993 int dvd_start_stop_unit (dvd_drive *dvd, bool start, bool load_eject, req_sense *sense) {
994         mmc_command mmc;
995         int out;
996         u_int8_t intbuf[64], *buf;
997         size_t bufsize;
998
999         buf = intbuf;
1000         bufsize = 0;
1001         
1002         dvd_init_command (&mmc, buf, bufsize, sense);
1003         mmc.cmd[0] = 0x1B;
1004         /* START STOP UNIT byte 4: bit 1 = LoEj, bit 0 = Start. */
1005         mmc.cmd[4] = (load_eject ? 0x02 : 0x00) | (start ? 0x01 : 0x00);
1006         out = dvd_execute_cmd (dvd, &mmc, true);
1007         
1008         return (out);
1009 }
1010
1011 int dvd_stop_unit (dvd_drive *dvd, bool start, req_sense *sense) {
1012         return dvd_start_stop_unit (dvd, start, false, sense);
1013 }
1014
1015 int dvd_wait_ready (dvd_drive *dvd, unsigned int timeout_ms) {
1016         unsigned int waited = 0;
1017         if (!dvd)
1018                 return -1;
1019         while (waited <= timeout_ms) {
1020                 if (dvd_test_unit_ready (dvd, NULL) == 0)
1021                         return 0;
1022                 if (waited == 0)
1023                         dvd_stop_unit (dvd, true, NULL); /* START UNIT, like EnsureDriveReady() */
1024                 dvd_sleep_ms (500);
1025                 waited += 500;
1026         }
1027         return -1;
1028 }
1029
1030 int dvd_media_cycle (dvd_drive *dvd, req_sense *sense) {
1031         int i;
1032         int out;
1033
1034         /* Match the original GDR-8050L dumper's AutomateTrayCycle(): eject, wait
1035          * long enough for the tray to extend, close, poll readiness, then settle. */
1036         out = dvd_start_stop_unit (dvd, false, true, sense); /* LoEj=1, Start=0: eject */
1037         if (out < 0)
1038                 return out;
1039         dvd_sleep_ms (3000);
1040
1041         out = dvd_start_stop_unit (dvd, true, true, sense);  /* LoEj=1, Start=1: load */
1042         if (out < 0)
1043                 return out;
1044
1045         for (i = 0; i < 90; i++) {
1046                 dvd_sleep_ms (500);
1047                 if (dvd_test_unit_ready (dvd, NULL) == 0) {
1048                         dvd_sleep_ms (1500);
1049                         return 0;
1050                 }
1051         }
1052
1053         /* Original dumper falls back to a fixed 10s settle delay if TUR never
1054          * reports ready after tray close. */
1055         dvd_sleep_ms (10000);
1056         return -1;
1057 }
1058
1059 int dvd_set_speed (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1060         mmc_command mmc;
1061         int out;
1062         u_int8_t intbuf[64], *buf;
1063         size_t bufsize;
1064
1065         buf = intbuf;
1066         bufsize = 0;
1067         
1068         dvd_init_command (&mmc, buf, bufsize, sense);
1069         mmc.cmd[0] = 0xBB;
1070         mmc.cmd[2] = (u_int8_t) ((speed & 0x0000FF00) >> 8);
1071         mmc.cmd[3] = (u_int8_t)  (speed & 0x000000FF);
1072         out = dvd_execute_cmd (dvd, &mmc, true);
1073         
1074         return (out);
1075 }
1076
1077 int dvd_get_size (dvd_drive *dvd, u_int32_t *size, req_sense *sense) {
1078         mmc_command mmc;
1079         int out;
1080         u_int8_t intbuf[64], *buf;
1081         size_t bufsize;
1082
1083         buf = intbuf;
1084         bufsize = 0x22;
1085         
1086         dvd_init_command (&mmc, buf, bufsize, sense);
1087         mmc.cmd[0] = 0x52;
1088         mmc.cmd[1] = 0x01;
1089         mmc.cmd[5] = 0x01;
1090         mmc.cmd[8] = 0x22;
1091         out = dvd_execute_cmd (dvd, &mmc, true);
1092
1093         *(size)=*(size) << 8 | intbuf[0x18];
1094         *(size)=*(size) << 8 | intbuf[0x19];
1095         *(size)=*(size) << 8 | intbuf[0x1a];
1096         *(size)=*(size) << 8 | intbuf[0x1b];
1097
1098         return (out);
1099 }
1100
1101 int dvd_get_layerbreak (dvd_drive *dvd, u_int32_t *layerbreak, req_sense *sense) {
1102         mmc_command mmc;
1103         int out;
1104         u_int8_t intbuf[2052], *buf;
1105         size_t bufsize;
1106
1107         buf = intbuf;
1108         bufsize = 2052;
1109         
1110         dvd_init_command (&mmc, buf, bufsize, sense);
1111         mmc.cmd[0] = 0xad;
1112         mmc.cmd[8] = 0x08;
1113         mmc.cmd[9] = 0x04;
1114         out = dvd_execute_cmd (dvd, &mmc, true);
1115
1116         *(layerbreak)=*(layerbreak) << 8;
1117         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x11];
1118         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x12];
1119         *(layerbreak)=*(layerbreak) << 8 | intbuf[0x13];
1120         if (*(layerbreak) > 0) *(layerbreak)=*(layerbreak) - 0x30000 + 1;
1121
1122         return (out);
1123 }
1124
1125 int dvd_set_streaming (dvd_drive *dvd, u_int32_t speed, req_sense *sense) {
1126 /*
1127 DVD Decrypter->
1128 DeviceIoControl    : \Device\CdRom5
1129 Command            : IOCTL_SCSI_PASS_THROUGH_DIRECT
1130 Length             : 44 (0x002C)
1131 ScsiStatus         : 0
1132 PathId             : 0
1133 TargedId           : 0
1134 Lun                : 0
1135 CdbLength          : 12 (0x0C)
1136 SenseInfoLength    : 24 (0x18)
1137 DataTransferLength : 28 (0x0000001C)
1138 DataIn             : 0
1139 TimeOutValue       : 5000
1140
1141 CDB:
1142 00000000  B6 00 00 00 00 00 00 00 00 00 1C 00               ...........    
1143
1144 Data Sent:
1145 00000000  00 00 00 00 00 00 00 00 00 00 00 00 FF FF FF FF   ............____
1146 00000010  00 00 03 E8 FF FF FF FF 00 00 03 E8               ...____...    
1147 */
1148         mmc_command mmc;
1149         int out;
1150         u_int8_t inbuf[28], *buf;
1151         size_t bufsize;
1152
1153         buf = inbuf;
1154         bufsize = 28;
1155         
1156         dvd_init_command (&mmc, buf, bufsize, sense);
1157         mmc.cmd[00] = 0xB6;
1158         mmc.cmd[10] = 28;
1159
1160         *(buf+ 0)=0;//2
1161         *(buf+ 1)=0;
1162         *(buf+ 2)=0;
1163         *(buf+ 3)=0;
1164         *(buf+ 4)=0; //MSB
1165         *(buf+ 5)=0; //
1166         *(buf+ 6)=0; //
1167         *(buf+ 7)=0; //LSB
1168
1169         *(buf+ 8)=0xff; //MSB
1170         *(buf+ 9)=0xff; //
1171         *(buf+10)=0xff; //
1172         *(buf+11)=0xff; //LSB
1173
1174         *(buf+12)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1175         *(buf+13)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1176         *(buf+14)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1177         *(buf+15)=(u_int8_t)  (speed & 0x000000FF);
1178
1179         *(buf+16)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1180         *(buf+17)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1181         *(buf+18)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1182         *(buf+19)=(u_int8_t)  (1000 & 0x000000FF);
1183
1184         *(buf+20)=(u_int8_t) ((speed & 0xFF000000) >> 24);
1185         *(buf+21)=(u_int8_t) ((speed & 0x00FF0000) >> 16);
1186         *(buf+22)=(u_int8_t) ((speed & 0x0000FF00) >> 8);
1187         *(buf+23)=(u_int8_t)  (speed & 0x000000FF);
1188
1189         *(buf+24)=(u_int8_t) ((1000 & 0xFF000000) >> 24);
1190         *(buf+25)=(u_int8_t) ((1000 & 0x00FF0000) >> 16);
1191         *(buf+26)=(u_int8_t) ((1000 & 0x0000FF00) >> 8);
1192         *(buf+27)=(u_int8_t)  (1000 & 0x000000FF);
1193
1194         out = dvd_execute_cmd (dvd, &mmc, true);
1195
1196         return (out);
1197 }
1198
1199
1200 int dvd_test_unit_ready (dvd_drive *dvd, req_sense *sense) {
1201         mmc_command mmc;
1202         u_int8_t intbuf[1];
1203
1204         dvd_init_command (&mmc, intbuf, 0, sense);
1205         mmc.cmd[0] = SPC_TEST_UNIT_READY;
1206         mmc.cmdlen = 6;
1207         mmc.direction = DVD_DATA_NONE;
1208         return dvd_execute_cmd (dvd, &mmc, true);
1209 }
1210
1211 int dvd_read_capacity_10 (dvd_drive *dvd, u_int32_t *sectors, u_int32_t *sector_size, req_sense *sense) {
1212         mmc_command mmc;
1213         u_int8_t buf[8];
1214         int out;
1215         u_int32_t max_lba, block_len;
1216
1217         dvd_init_command (&mmc, buf, sizeof (buf), sense);
1218         mmc.cmd[0] = MMC_READ_CAPACITY_10;
1219         mmc.cmdlen = 10;
1220         out = dvd_execute_cmd (dvd, &mmc, false);
1221         if (out >= 0) {
1222                 max_lba = ((u_int32_t) buf[0] << 24) | ((u_int32_t) buf[1] << 16) | ((u_int32_t) buf[2] << 8) | buf[3];
1223                 block_len = ((u_int32_t) buf[4] << 24) | ((u_int32_t) buf[5] << 16) | ((u_int32_t) buf[6] << 8) | buf[7];
1224                 if (sectors)
1225                         *sectors = max_lba + 1;
1226                 if (sector_size)
1227                         *sector_size = block_len;
1228         }
1229
1230         return out;
1231 }
1232
1233 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) {
1234         mmc_command mmc;
1235         u_int8_t intbuf[64 * 1024], *buf;
1236         size_t need, bufsize;
1237
1238         need = (size_t) sectors * 2048;
1239         if (extbuf) {
1240                 buf = extbuf;
1241                 bufsize = extbufsize;
1242         } else {
1243                 buf = intbuf;
1244                 bufsize = sizeof (intbuf);
1245         }
1246
1247         if (need > bufsize) {
1248                 error ("dvd_read_10 buffer too small (%u sectors need %lu bytes)", sectors, (unsigned long) need);
1249                 return -1;
1250         }
1251
1252         dvd_init_command (&mmc, buf, (int) need, sense);
1253         mmc.cmd[0] = MMC_READ_10;
1254         mmc.cmdlen = 10;
1255         mmc.cmd[2] = (u_int8_t) ((sector & 0xFF000000) >> 24);
1256         mmc.cmd[3] = (u_int8_t) ((sector & 0x00FF0000) >> 16);
1257         mmc.cmd[4] = (u_int8_t) ((sector & 0x0000FF00) >> 8);
1258         mmc.cmd[5] = (u_int8_t)  (sector & 0x000000FF);
1259         mmc.cmd[7] = (u_int8_t) ((sectors & 0x0000FF00) >> 8);
1260         mmc.cmd[8] = (u_int8_t)  (sectors & 0x000000FF);
1261
1262         return dvd_execute_cmd (dvd, &mmc, true);
1263 }
1264
1265 int dvd_mode_sense_10 (dvd_drive *dvd, u_int8_t page, u_int8_t *extbuf, size_t extbufsize, req_sense *sense) {
1266         mmc_command mmc;
1267
1268         if (!extbuf || extbufsize > 0xFFFF)
1269                 return -1;
1270
1271         dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1272         mmc.cmd[0] = SPC_MODE_SENSE_10;
1273         mmc.cmd[2] = page;
1274         mmc.cmd[7] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1275         mmc.cmd[8] = (u_int8_t)  (extbufsize & 0x00FF);
1276         mmc.cmdlen = 10;
1277
1278         return dvd_execute_cmd (dvd, &mmc, false);
1279 }
1280
1281 int dvd_mode_select_10 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1282         mmc_command mmc;
1283         u_int8_t tmp[256];
1284
1285         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFFFF)
1286                 return -1;
1287         memset (tmp, 0, sizeof (tmp));
1288         memcpy (tmp, buf, bufsize);
1289
1290         dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1291         mmc.direction = DVD_DATA_OUT;
1292         mmc.cmd[0] = SPC_MODE_SELECT_10;
1293         mmc.cmd[7] = (u_int8_t) ((bufsize & 0xFF00) >> 8);
1294         mmc.cmd[8] = (u_int8_t)  (bufsize & 0x00FF);
1295         mmc.cmdlen = 10;
1296
1297         return dvd_execute_cmd (dvd, &mmc, false);
1298 }
1299
1300 int dvd_mode_select_6 (dvd_drive *dvd, const u_int8_t *buf, size_t bufsize, req_sense *sense) {
1301         mmc_command mmc;
1302         u_int8_t tmp[64];
1303
1304         if (!buf || bufsize > sizeof (tmp) || bufsize > 0xFF)
1305                 return -1;
1306         memset (tmp, 0, sizeof (tmp));
1307         memcpy (tmp, buf, bufsize);
1308
1309         dvd_init_command (&mmc, tmp, (int) bufsize, sense);
1310         mmc.direction = DVD_DATA_OUT;
1311         mmc.cmd[0] = SPC_MODE_SELECT_6;
1312         mmc.cmd[1] = 0x11;
1313         mmc.cmd[4] = (u_int8_t) (bufsize & 0xFF);
1314         mmc.cmdlen = 6;
1315
1316         return dvd_execute_cmd (dvd, &mmc, false);
1317 }
1318
1319 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) {
1320         mmc_command mmc;
1321
1322         if (!extbuf || extbufsize > 0xFFFF)
1323                 return -1;
1324
1325         dvd_init_command (&mmc, extbuf, (int) extbufsize, sense);
1326         mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1327         mmc.cmd[6] = layer;
1328         mmc.cmd[8] = (u_int8_t) ((extbufsize & 0xFF00) >> 8);
1329         mmc.cmd[9] = (u_int8_t)  (extbufsize & 0x00FF);
1330         mmc.cmd[11] = format;
1331         mmc.cmdlen = 12;
1332
1333         return dvd_execute_cmd (dvd, &mmc, false);
1334 }
1335
1336
1337 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) {
1338         mmc_command mmc;
1339
1340         dvd_init_command (&mmc, buf, (int) bufsize, NULL);
1341         mmc.cmd[0] = 0xFF;
1342         mmc.cmd[1] = 0x08;
1343         mmc.cmd[2] = 0x01;
1344         mmc.cmd[3] = subcommand;
1345         mmc.cmd[4] = value;
1346         mmc.cmdlen = 10;
1347         mmc.direction = direction;
1348         if (direction == DVD_DATA_NONE) {
1349                 mmc.buffer = NULL;
1350                 mmc.buflen = 0;
1351         }
1352         return dvd_execute_cmd (dvd, &mmc, false);
1353 }
1354
1355 static bool dvd_xbox_feature_list_has (const u_int16_t *features, size_t count, u_int16_t needle) {
1356         size_t i;
1357         if (!features)
1358                 return false;
1359         for (i = 0; i < count && features[i] != 0; i++) {
1360                 if (features[i] == needle)
1361                         return true;
1362         }
1363         return false;
1364 }
1365
1366 int dvd_xbox_vendor_get_feature_list (dvd_drive *dvd, u_int16_t *features, size_t max_features) {
1367         u_int8_t buf[26];
1368         size_t i, count;
1369
1370         if (!dvd || !features || max_features == 0)
1371                 return -1;
1372         memset (features, 0, max_features * sizeof (features[0]));
1373         memset (buf, 0, sizeof (buf));
1374         if (dvd_xbox_vendor_command (dvd, 0x10, 0, buf, sizeof (buf), DVD_DATA_IN) < 0)
1375                 return -1;
1376         if ((((u_int16_t) buf[0] << 8) | buf[1]) != 0xA55A ||
1377             (((u_int16_t) buf[2] << 8) | buf[3]) != 0x5AA5) {
1378                 error ("Xbox vendor feature-list signature is invalid");
1379                 return -1;
1380         }
1381         count = sizeof (buf) / 2;
1382         if (count > max_features)
1383                 count = max_features;
1384         for (i = 0; i < count; i++)
1385                 features[i] = ((u_int16_t) buf[i * 2] << 8) | buf[i * 2 + 1];
1386         return 0;
1387 }
1388
1389 int dvd_xbox_vendor_lock (dvd_drive *dvd) {
1390         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1391                 return -1;
1392         return dvd_xbox_vendor_command (dvd, 0x11, 0x00, NULL, 0, DVD_DATA_NONE);
1393 }
1394
1395 int dvd_xbox_vendor_set_error_skip (dvd_drive *dvd, bool enabled) {
1396         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1397                 return -1;
1398         return dvd_xbox_vendor_command (dvd, 0x15, enabled ? 0x01 : 0x00, NULL, 0, DVD_DATA_NONE);
1399 }
1400
1401 int dvd_xbox_vendor_unlock_wxripper (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1402         u_int16_t features[13];
1403         u_int32_t sectors = 0, sector_size = 0;
1404
1405         if (!dvd || !dvd_is_xbox_vendor_unlock_drive (dvd))
1406                 return -1;
1407
1408         if (dvd_xbox_vendor_get_feature_list (dvd, features, sizeof (features) / sizeof (features[0])) < 0)
1409                 return -1;
1410         if (!dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0201) &&
1411             !dvd_xbox_feature_list_has (features, sizeof (features) / sizeof (features[0]), 0x0221))
1412                 warning ("Xbox vendor feature list did not advertise Xbox unlock state 2/full challenge support; trying wxripper state anyway");
1413
1414         if (dvd_xbox_vendor_command (dvd, 0x11, 0x02, NULL, 0, DVD_DATA_NONE) < 0)
1415                 return -1;
1416
1417         /* DiscImageCreator disables error-skip before dumping; keep that behavior so
1418          * real read errors are visible to FriiDump unless a future option says otherwise. */
1419         dvd_xbox_vendor_set_error_skip (dvd, false);
1420
1421         if (dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL) < 0)
1422                 return -1;
1423         if (sector_size != 2048 || sectors < 1000000) {
1424                 error ("Xbox vendor unlock did not expose the expected 2048-byte view");
1425                 return -1;
1426         }
1427         if (unlocked_sectors)
1428                 *unlocked_sectors = sectors;
1429         return 0;
1430 }
1431
1432
1433 int dvd_xbox_recovery_kick (dvd_drive *dvd, bool auth_recovery) {
1434         mmc_command mmc;
1435         u_int8_t dummy[2048];
1436         u_int8_t auth_probe_buf[8];
1437         int i;
1438
1439         if (!dvd)
1440                 return -1;
1441
1442         /* Mirrors the proven dumper recovery cadence: after the GDR-8050L
1443          * authentication/view switch, re-trigger the Xbox media auth command, set
1444          * maximum speed, then issue a few harmless READ(10) probes.  All commands
1445          * deliberately ignore errors because the point is to wake/re-prime firmware
1446          * state, not to make any one probe authoritative.
1447          */
1448         if (auth_recovery) {
1449                 /* GDR-8050L / HLDS auth kick: READ DVD STRUCTURE C0 magic trigger. */
1450                 dvd_init_command (&mmc, auth_probe_buf, sizeof (auth_probe_buf), NULL);
1451                 mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1452                 mmc.cmd[2] = 0xFF;
1453                 mmc.cmd[3] = 0x02;
1454                 mmc.cmd[4] = 0xFD;
1455                 mmc.cmd[11] = 0xC0;
1456                 mmc.cmdlen = 12;
1457                 dvd_execute_cmd (dvd, &mmc, true);
1458
1459                 /* Hitachi/Kreon-style fallback auth kick: FF 08 01. */
1460                 dvd_init_command (&mmc, NULL, 0, NULL);
1461                 mmc.cmd[0] = 0xFF;
1462                 mmc.cmd[1] = 0x08;
1463                 mmc.cmd[2] = 0x01;
1464                 mmc.cmdlen = 10;
1465                 mmc.direction = DVD_DATA_NONE;
1466                 dvd_execute_cmd (dvd, &mmc, true);
1467         }
1468
1469         dvd_set_speed (dvd, 0xFFFF, NULL);
1470
1471         for (i = 0; i < 10; i++)
1472                 dvd_read_10 (dvd, 0, 1, NULL, dummy, sizeof (dummy));
1473
1474         return 0;
1475 }
1476
1477 int dvd_refresh_volume (dvd_drive *dvd) {
1478 #ifdef WIN32
1479         DWORD bytesReturned = 0;
1480         if (!dvd)
1481                 return -1;
1482         /* Match RefreshVolume() from the reference dumper: update properties only;
1483          * do not dismount here because that can reset drive state.  This is now
1484          * shared by Xbox and HLDS 0xE7 GC/Wii paths so Windows is less likely to
1485          * keep stale filesystem/probe state attached to odd discs. */
1486         DeviceIoControl (dvd -> fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &bytesReturned, NULL);
1487         dvd_sleep_ms (1000);
1488         return 0;
1489 #else
1490         (void) dvd;
1491         return 0;
1492 #endif
1493 }
1494
1495 int dvd_lock_volume (dvd_drive *dvd) {
1496 #ifdef WIN32
1497         DWORD bytesReturned = 0;
1498         if (!dvd)
1499                 return -1;
1500         return DeviceIoControl (dvd -> fd, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &bytesReturned, NULL) ? 0 : -1;
1501 #else
1502         (void) dvd;
1503         return 0;
1504 #endif
1505 }
1506
1507 int dvd_xbox_refresh_volume (dvd_drive *dvd) {
1508         return dvd_refresh_volume (dvd);
1509 }
1510
1511 int dvd_xbox_lock_volume (dvd_drive *dvd) {
1512         return dvd_lock_volume (dvd);
1513 }
1514
1515 static int dvd_xbox_read_host_challenge_table (dvd_drive *dvd, u_int8_t *table, size_t table_len) {
1516         mmc_command mmc;
1517         int out;
1518
1519         if (!table || table_len < 0x664)
1520                 return -1;
1521
1522         /* GDR-8050L / Xbox READ DVD STRUCTURE format C0. */
1523         dvd_init_command (&mmc, table, 0x664, NULL);
1524         mmc.cmd[0] = MMC_READ_DVD_STRUCTURE;
1525         mmc.cmd[2] = 0xFF;
1526         mmc.cmd[3] = 0x02;
1527         mmc.cmd[4] = 0xFD;
1528         mmc.cmd[5] = 0xFF;
1529         mmc.cmd[6] = 0xFE;
1530         mmc.cmd[8] = 0x06;
1531         mmc.cmd[9] = 0x64;
1532         mmc.cmd[11] = 0xC0;
1533         mmc.cmdlen = 12;
1534         out = dvd_execute_cmd (dvd, &mmc, true);
1535
1536         /* Some Hitachi-family drives expose the same table via vendor command 0xFD. */
1537         if (out < 0 || table[772] != 1 || table[773] == 0) {
1538                 dvd_init_command (&mmc, table, 0x664, NULL);
1539                 mmc.cmd[0] = 0xFD;
1540                 mmc.cmd[1] = 0x01;
1541                 mmc.cmd[8] = 0x06;
1542                 mmc.cmd[9] = 0x64;
1543                 mmc.cmdlen = 12;
1544                 out = dvd_execute_cmd (dvd, &mmc, true);
1545         }
1546
1547         if (out < 0 || table[772] != 1)
1548                 return -1;
1549         return 0;
1550 }
1551
1552 int dvd_xbox_gdr8050l_unlock (dvd_drive *dvd, u_int32_t *unlocked_sectors) {
1553         int i, k, l;
1554         int chalpos[24];
1555         u_int8_t table[0x664];
1556         u_int8_t restable[261];
1557         u_int8_t hash[0x2C];
1558         SHA1_HASH digest;
1559         u_int8_t page[28];
1560         u_int8_t sticky[12];
1561         u_int32_t sectors = 0, sector_size = 0;
1562         xbox_rc4_ctx rc4;
1563
1564         if (!dvd || !dvd_is_xbox_unlock_drive (dvd))
1565                 return -1;
1566
1567         /* Step 1/2: read current capacity and the Xbox mode page. If the drive is
1568          * already unlocked, this is harmless; the final capacity check below becomes
1569          * the authority. */
1570         dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL);
1571         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
1572
1573         /* Step 3: retrieve and decode the host challenge table. */
1574         if (dvd_xbox_read_host_challenge_table (dvd, table, sizeof (table)) < 0) {
1575                 error ("Cannot retrieve Xbox host challenge table");
1576                 return -1;
1577         }
1578
1579         for (i = 0; i < 0x2C; i++)
1580                 hash[i] = table[0x4A3 + i];
1581         Sha1Calculate (hash, 0x2C, &digest);
1582
1583         for (i = 0; i <= 260; i++)
1584                 restable[i] = table[774 + i];
1585         xbox_rc4_init (&rc4, digest.bytes, 7);
1586         xbox_rc4_crypt (&rc4, restable, restable, 0xFD);
1587
1588         k = 0;
1589         for (l = 0; l <= 23; l++) {
1590                 if (restable[l * 11] == 1) {
1591                         chalpos[k++] = l;
1592                         if (k == (int) (sizeof (chalpos) / sizeof (chalpos[0])))
1593                                 break;
1594                 }
1595         }
1596         if (k < 2) {
1597                 error ("Xbox challenge table does not contain enough usable entries");
1598                 return -1;
1599         }
1600
1601         /* Step 4: first host challenge. */
1602         memset (page, 0, sizeof (page));
1603         page[1] = 0x1A;
1604         page[8] = 0x3E;
1605         page[9] = 0x12;
1606         page[11] = 0x01;
1607         page[13] = 0xD1;
1608         page[14] = 0x01;
1609         memcpy (&page[15], &restable[1 + chalpos[k - 2] * 11], 5);
1610         /* Match the original dumper: send the challenge and continue even if
1611          * Windows reports a transport failure.  The later XDVDFS probe is the
1612          * authority for whether the drive actually entered the game view. */
1613         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
1614         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
1615
1616         /* Step 6: second host challenge. */
1617         memset (page, 0, sizeof (page));
1618         page[1] = 0x1A;
1619         page[8] = 0x3E;
1620         page[9] = 0x12;
1621         page[12] = 0x01;
1622         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
1623         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
1624         dvd_mode_sense_10 (dvd, 0x3E, page, sizeof (page), NULL);
1625
1626         /* Step 8: unlock partition 1. */
1627         memset (page, 0, sizeof (page));
1628         page[1] = 0x1A;
1629         page[8] = 0x3E;
1630         page[9] = 0x12;
1631         page[10] = 0x01;
1632         page[11] = 0x01;
1633         page[12] = 0x01;
1634         page[13] = 0xD1;
1635         page[14] = 0x01;
1636         memcpy (&page[15], &restable[1 + chalpos[k - 1] * 11], 5);
1637         dvd_mode_select_10 (dvd, page, sizeof (page), NULL);
1638
1639         /* Step 9: sticky descrambling, mode page 0x31. */
1640         memset (sticky, 0, sizeof (sticky));
1641         sticky[4] = 0x31;
1642         sticky[5] = 0x06;
1643         sticky[6] = 0x01;
1644         dvd_mode_select_6 (dvd, sticky, sizeof (sticky), NULL);
1645
1646         /* Step 10: final capacity observation.  The original UnlockDrive() only
1647          * prints this verification and does not fail if the capacity has not changed
1648          * yet.  This matters for the first GDR-8050L handshake, whose purpose is to
1649          * prime the drive before the required media-change event. */
1650         sectors = 0;
1651         sector_size = 0;
1652         if (dvd_read_capacity_10 (dvd, &sectors, &sector_size, NULL) == 0) {
1653                 if (unlocked_sectors)
1654                         *unlocked_sectors = sectors;
1655                 fprintf (stderr, "[XBOX] GDR-8050L handshake complete; READ CAPACITY reports %u sectors of %u bytes.\n", sectors, sector_size);
1656         } else {
1657                 fprintf (stderr, "[XBOX] GDR-8050L handshake sent; final READ CAPACITY verify failed, continuing like original dumper.\n");
1658         }
1659
1660         return 0;
1661 }
1662
1663 bool dvd_is_xbox_challenge_drive (dvd_drive *dvd) {
1664         return dvd_is_hlds_drive (dvd) && dvd_prod_has (dvd, "GDR8050L");
1665 }
1666
1667 bool dvd_is_xbox_vendor_unlock_drive (dvd_drive *dvd) {
1668         return (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L"))) ||
1669                dvd_is_tsst_kreon_candidate (dvd);
1670 }
1671
1672 bool dvd_is_xbox_unlock_drive (dvd_drive *dvd) {
1673         return dvd_is_xbox_challenge_drive (dvd) || dvd_is_xbox_vendor_unlock_drive (dvd);
1674 }
1675
1676 bool dvd_is_xbox_drive (dvd_drive *dvd) {
1677         /* Autodetect only the two native Xbox profiles currently wired into the
1678          * Xbox dump planner. Other candidate drives keep FriiDump's normal GC/Wii/DVD
1679          * behavior unless the user explicitly forces Xbox mode with -T 4. */
1680         return dvd_is_xbox_challenge_drive (dvd) ||
1681                (dvd_is_hlds_drive (dvd) && (dvd_prod_has (dvd, "GDR3120L") || dvd_prod_has (dvd, "GDR-3120L")));
1682 }
1683
1684
1685
1686 const char *dvd_get_hlds_e7_profile_name (dvd_drive *dvd) {
1687         if (!dvd) return "none";
1688         if (dvd -> hlds_e7_profile_label) return dvd -> hlds_e7_profile_label;
1689         return dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
1690 }
1691
1692 const char *dvd_get_hlds_e7_support_tier (dvd_drive *dvd) {
1693         if (!dvd || dvd -> hlds_e7_type == 0) return "none";
1694         return dvd -> hlds_e7_support_tier ? dvd -> hlds_e7_support_tier : "legacy_detected";
1695 }
1696
1697 const char *dvd_get_hlds_e7_family (dvd_drive *dvd) {
1698         if (!dvd || dvd -> hlds_e7_type == 0) return "none";
1699         return dvd -> hlds_e7_family ? dvd -> hlds_e7_family : dvd_hlds_e7_profile_name_from_type (dvd -> hlds_e7_type);
1700 }
1701
1702 const char *dvd_get_hlds_e7_tokens (dvd_drive *dvd) {
1703         if (!dvd || dvd -> hlds_e7_type == 0) return "";
1704         return dvd -> hlds_e7_tokens ? dvd -> hlds_e7_tokens : "";
1705 }
1706
1707 const char *dvd_get_hlds_e7_record_id (dvd_drive *dvd) {
1708         if (!dvd || dvd -> hlds_e7_type == 0) return "";
1709         return dvd -> hlds_e7_record_id ? dvd -> hlds_e7_record_id : "";
1710 }
1711
1712 const char *dvd_get_hlds_e7_notes (dvd_drive *dvd) {
1713         if (!dvd || dvd -> hlds_e7_type == 0) return "";
1714         return dvd -> hlds_e7_notes ? dvd -> hlds_e7_notes : "";
1715 }
1716
1717 u_int32_t dvd_get_hlds_e7_static_cdb_base (dvd_drive *dvd) {
1718         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_cdb_base : 0;
1719 }
1720
1721 u_int32_t dvd_get_hlds_e7_static_gate (dvd_drive *dvd) {
1722         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_static_gate : 0;
1723 }
1724
1725 int dvd_get_hlds_e7_preferred_method (dvd_drive *dvd) {
1726         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_preferred_method : -1;
1727 }
1728
1729 u_int32_t dvd_get_hlds_e7_type (dvd_drive *dvd) {
1730         return dvd ? dvd -> hlds_e7_type : 0;
1731 }
1732
1733 u_int32_t dvd_get_hlds_e7_cache_base (dvd_drive *dvd) {
1734         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_cache_base : 0;
1735 }
1736
1737 u_int32_t dvd_get_hlds_e7_mem_blocks (dvd_drive *dvd) {
1738         return (dvd && dvd -> hlds_e7_type != 0) ? dvd -> hlds_e7_mem_blocks : 0;
1739 }
1740
1741 char *dvd_get_vendor (dvd_drive *dvd) {
1742         return (dvd -> vendor);
1743 }
1744
1745
1746 char *dvd_get_product_id (dvd_drive *dvd) {
1747         return (dvd -> prod_id);
1748 }
1749
1750
1751 char *dvd_get_product_revision (dvd_drive *dvd) {
1752         return (dvd -> prod_rev);
1753 }
1754
1755
1756 char *dvd_get_model_string (dvd_drive *dvd) {
1757         return (dvd -> model_string);
1758 }
1759
1760
1761 char *dvd_get_device (dvd_drive *dvd) {
1762         return (dvd -> device);
1763 }
1764
1765 void *dvd_get_native_handle (dvd_drive *dvd) {
1766         if (!dvd) return NULL;
1767 #ifdef WIN32
1768         return (void *) dvd -> fd;
1769 #else
1770         return NULL;
1771 #endif
1772 }
1773
1774
1775 bool dvd_get_support_status (dvd_drive *dvd) {
1776         return (dvd -> supported);
1777 }
1778
1779 u_int32_t dvd_get_def_method (dvd_drive *dvd){
1780         return (dvd -> def_method);
1781 }
1782
1783 u_int32_t dvd_get_command (dvd_drive *dvd){
1784         return (dvd -> command);
1785 }