]> FriiDump Source - friidump.git/blob - libfriidump/disc.c
FriiDump 0.5.3.16 candidate: add native reports and Linux Xbox support
[friidump.git] / libfriidump / disc.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 Analyser and dumper for Nintendo GameCube/Wii discs.
24  *
25  * The functions in this file can be used to retrieve information about a Nintendo GameCube/Wii optical disc. Information is both structural (i.e.: Number of
26  * sectors, partitions, etc) and game-related (i.e.: Game Title, version, etc). This is the main object that should be used by applications.
27  *
28  * Most of the disc structure information used in this file comes from http://www.gc-linux.org/docs/yagcd.html and
29  * http://www.wiili.org/index.php/GameCube_Optical_Disc .
30  */
31
32 #include "misc.h"
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <stdarg.h>
37 #ifdef WIN32
38 #include <windows.h>
39 #else
40 #include <unistd.h>
41 #endif
42 //#include <time.h>
43 #include "constants.h"
44 #include "byteorder.h"
45 #include "disc.h"
46 #include "dvd_drive.h"
47 #include "nintendo_disc_header.h"
48 #include "xbox_ref/xbox_ref_log.h"
49
50 static void hlds_e7_visible_probe_log (const char *fmt, ...) {
51         static bool started = false;
52         va_list ap;
53         if (!started) {
54                 fprintf (stderr, "\n");
55                 started = true;
56         }
57         va_start (ap, fmt);
58         vfprintf (stderr, fmt, ap);
59         va_end (ap);
60         fprintf (stderr, "\n");
61         fflush (stderr);
62 }
63 #include "unscrambler.h"
64
65 // #define cachedebug(...) debug (__VA_ARGS__);
66 #define cachedebug(...)
67
68
69 /* Cache always deals with 16-sector blocks. All numbers refer to the 16-sector blocks */
70 #define DISC_MINIMUM_CACHE_SIZE 5
71 #define DISC_DEFAULT_CACHE_SIZE 40
72 #define CACHE_ENTRY_INVALID ((u_int32_t) -1)
73
74
75 #define DISC_GAMECUBE_SECTORS_NO 0x0AE0B0       /* 712880 */
76 #define DISC_WII_SECTORS_NO_SL 0x230480         /* 2294912 */
77 #define DISC_WII_SECTORS_NO_DL 0x3F69C0         /* 4155840 */
78 #define DISC_XBOX_GDR8050L_UNLOCKED_SECTORS_NO 0x345B60 /* 3431264 */
79
80
81 #define MAX_READ_RETRIES 5
82
83 #define DEFAULT_READ_METHOD 0
84 #define DEFAULT_READ_SECTOR disc_read_sector_0
85
86
87 typedef int (*disc_read_sector_func) (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata);
88
89 u_int8_t buf[1024*1024*4];
90 u_int8_t buf_unscrambled[1024*1024*4];
91
92 //struct timeval tim;
93 //double t1, t2;
94
95 /*! \brief A structure that represents a Nintendo GameCube/Wii optical disc.
96  */
97 struct disc_s {
98         dvd_drive *dvd;                         //!< The structure for the DVD-drive the disc is inserted in.
99         disc_type type;                         //!< The disc type.
100         char system_id;                         //!< A letter identifying the target system.
101         char game_id[2 + 1];                    //!< Two letters identifying the game.
102         disc_region region;                     //!< The disc region.
103         char maker[3];                          //!< Two letters identifying the maker of the game.
104         u_int8_t version;                       //!< A number identifying the game version.
105         char *version_string;                   //!< The same as <code>version</code>, in a more human-understandable format.
106         char *title;                            //!< The game title.
107         bool has_update;                        //!< True if the game contains a system update (Only possible for Wii discs).
108         u_int32_t sectors_no;                   //!< The number of sectors of the disc.
109         u_int32_t layerbreak;                   //!< For dual-layer DVDs.
110
111         u_int32_t sec_disc;
112         u_int32_t sec_mem;
113         u_int32_t max_cnt;
114         u_int32_t max_blk;
115
116         /* Read function & stuff */
117         int command;                            //!< Buffer access command ID.
118         int read_method;                        //!< The read method ID.
119 //      int def_read_method;            //!< Default read method ID.
120         disc_read_sector_func read_sector;      //!< The actual function that will be used to perform read operations, corresponding to <code>read_method</code>.
121         bool unscrambling;                      //!< If true, raw data read from the disc will be unscrambled to assure it is error-free. Disabling this is only useful for raw performance tests.
122         unscrambler *u;                         //!< The unscrambler structure that will be used to perform the unscrambling.
123         
124         /* Read cache */
125         u_int32_t cache_size;                   //!< The number of blocks that will be cached when read.
126         bool hlds_e7_read_schedule_logged;      //!< True once the selected HLDS 0xE7 read schedule has been logged for this run.
127         bool seed_diagnostic_active;            //!< True while the initial 20 Nintendo seed blocks are being acquired.
128         unsigned int seed_diagnostic_block;     //!< Zero-based seed-block index currently being acquired.
129         disc_seed_diagnostic seed_diagnostic;   //!< Last actionable seed/cache failure evidence.
130         u_int8_t **raw_cache;                   //!< Memory area for raw sectors cache.
131         u_int8_t **cache;                       //!< Memory area for unscrambled sectors cache.
132         u_int32_t *cache_map;                   //!< Data structure used by the caching system to know which blocks are in memory.
133 };
134
135
136
137 static void disc_seed_diag_clear (disc *d) {
138         if (!d)
139                 return;
140         memset (&d -> seed_diagnostic, 0, sizeof (d -> seed_diagnostic));
141 }
142
143 static void disc_seed_diag_log (disc *d, const char *fmt, ...) {
144         va_list ap;
145         if (!d || !d -> seed_diagnostic_active || !fmt)
146                 return;
147         xbox_ref_log_fprintf (stderr, "[SEED-DIAG] ");
148         va_start (ap, fmt);
149         /* xbox_ref_log_fprintf is variadic rather than vfprintf-based, so format
150          * into a bounded buffer before sending the line to both console and log. */
151         {
152                 char line[768];
153                 vsnprintf (line, sizeof (line), fmt, ap);
154                 xbox_ref_log_fprintf (stderr, "%s\n", line);
155         }
156         va_end (ap);
157 }
158
159 static void disc_seed_diag_fail (
160         disc *d,
161         u_int32_t sector_start,
162         u_int32_t sector_end,
163         int retry,
164         const char *stage,
165         const char *detail,
166         int transport_result,
167         bool capture_command
168 ) {
169         dvd_command_diagnostic command;
170         char cdb_text[3 * 12 + 1];
171         int i;
172         int used;
173
174         if (!d || !d -> seed_diagnostic_active)
175                 return;
176         disc_seed_diag_clear (d);
177         d -> seed_diagnostic.valid = true;
178         d -> seed_diagnostic.seed_block_index = d -> seed_diagnostic_block;
179         d -> seed_diagnostic.sector_start = sector_start;
180         d -> seed_diagnostic.sector_end = sector_end;
181         d -> seed_diagnostic.retry = retry;
182         snprintf (d -> seed_diagnostic.stage, sizeof (d -> seed_diagnostic.stage), "%s", stage ? stage : "unknown");
183         snprintf (d -> seed_diagnostic.detail, sizeof (d -> seed_diagnostic.detail), "%s", detail ? detail : "");
184         d -> seed_diagnostic.transport_result = transport_result;
185
186         memset (&command, 0, sizeof (command));
187         if (capture_command && dvd_get_last_command_diagnostic (d -> dvd, &command)) {
188                 d -> seed_diagnostic.transport_result = command.transport_result;
189                 d -> seed_diagnostic.os_error = command.os_error;
190                 d -> seed_diagnostic.scsi_status = command.scsi_status;
191                 d -> seed_diagnostic.sense_key = command.sense_key;
192                 d -> seed_diagnostic.asc = command.asc;
193                 d -> seed_diagnostic.ascq = command.ascq;
194                 d -> seed_diagnostic.cdb_length = command.cdb_length;
195                 memcpy (d -> seed_diagnostic.cdb, command.cdb, sizeof (d -> seed_diagnostic.cdb));
196         }
197
198         cdb_text[0] = '\0';
199         used = 0;
200         for (i = 0; i < d -> seed_diagnostic.cdb_length && i < 12; i++) {
201                 int wrote = snprintf (cdb_text + used, sizeof (cdb_text) - (size_t) used,
202                         "%s%02X", i ? " " : "", d -> seed_diagnostic.cdb[i]);
203                 if (wrote < 0 || wrote >= (int) (sizeof (cdb_text) - (size_t) used))
204                         break;
205                 used += wrote;
206         }
207
208         disc_seed_diag_log (d,
209                 "block=%u/20 sectors=%u..%u retry=%d stage=%s result=FAIL rc=%d os=%d scsi=0x%02X sense=%02X/%02X/%02X cdb=[%s] detail=%s",
210                 d -> seed_diagnostic.seed_block_index + 1,
211                 sector_start, sector_end, retry,
212                 d -> seed_diagnostic.stage,
213                 d -> seed_diagnostic.transport_result,
214                 d -> seed_diagnostic.os_error,
215                 d -> seed_diagnostic.scsi_status & 0xff,
216                 d -> seed_diagnostic.sense_key & 0xff,
217                 d -> seed_diagnostic.asc & 0xff,
218                 d -> seed_diagnostic.ascq & 0xff,
219                 cdb_text[0] ? cdb_text : "none",
220                 d -> seed_diagnostic.detail[0] ? d -> seed_diagnostic.detail : "none");
221 }
222
223 bool disc_get_seed_diagnostic (disc *d, disc_seed_diagnostic *out) {
224         if (!d || !out || !d -> seed_diagnostic.valid)
225                 return false;
226         *out = d -> seed_diagnostic;
227         return true;
228 }
229
230 static void disc_cache_init (disc *d, u_int32_t size) {
231         u_int32_t i;
232
233         if (size < DISC_MINIMUM_CACHE_SIZE) {
234                 error ("Invalid cache size %u (must be >= %u)", size, DISC_MINIMUM_CACHE_SIZE);
235                 exit (3);
236         } else {
237                 d -> cache_size = size;
238                 d -> cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size);
239                 d -> raw_cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size);
240                 for (i = 0; i < size; i++) {
241                         d -> cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * BLOCK_SIZE);
242                         d -> raw_cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * RAW_BLOCK_SIZE);
243                 }
244
245                 d -> cache_map = (u_int32_t *) malloc (sizeof (u_int32_t) * size);
246                 for (i = 0; i < size; i++)
247                         d -> cache_map[i] = CACHE_ENTRY_INVALID;
248         }
249
250         return;
251 }
252
253
254 static void disc_cache_destroy (disc *d) {
255         u_int32_t i;
256         
257         my_free (d -> cache_map);
258         
259         for (i = 0; i < d -> cache_size; i++) {
260                         my_free (d -> cache[i]);
261                         my_free (d -> raw_cache[i]);
262         }
263         my_free (d -> cache);
264         my_free (d -> raw_cache);
265         d -> cache_size = 0;
266
267         return;
268 }
269
270 static void disc_cache_clear (disc *d) {
271         u_int32_t i;
272
273         if (!d || !d -> cache_map)
274                 return;
275         for (i = 0; i < d -> cache_size; i++)
276                 d -> cache_map[i] = CACHE_ENTRY_INVALID;
277 }
278
279
280 void disc_cache_add_block (disc *d, u_int32_t block, u_int8_t *data, u_int8_t *rawdata) {
281         u_int32_t pos;
282         u_int32_t cnt;
283         
284         pos = block % d -> cache_size;
285         //uniform unscrambled output
286         memcpy (d -> cache[pos], data, BLOCK_SIZE);
287         if (d -> type == DISC_TYPE_DVD || d -> type == DISC_TYPE_XBOX) {
288                 for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) {
289                         memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+12, data+(cnt*SECTOR_SIZE), SECTOR_SIZE);
290                 }
291         } else {
292                 for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) {
293                         memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+6, data+(cnt*SECTOR_SIZE), SECTOR_SIZE);
294                 }
295         }
296         memcpy (d -> raw_cache[pos], rawdata, RAW_BLOCK_SIZE);
297         d -> cache_map[pos] = block;
298
299         cachedebug ("Cached block %u (sectors %u-%u) at position %u", block, block * SECTORS_PER_BLOCK, (block + 1) * SECTORS_PER_BLOCK - 1, pos);
300
301         return;
302 }
303
304
305 static bool disc_cache_lookup_block (disc *d, u_int32_t block, u_int8_t **data, u_int8_t **rawdata) {
306         u_int32_t pos;
307         bool out;
308
309         pos = block % d -> cache_size;
310
311         if (d -> cache_map[pos] == block) {
312                 cachedebug ("Cache HIT for block %u", block);
313                 if (data)
314                         *data = d -> cache[pos];
315                 if (rawdata)
316                         *rawdata = d -> raw_cache[pos];
317                 out = true;
318         } else {
319                 cachedebug ("Cache MISS for block %u", block);
320                 if (data)
321                         *data = NULL;
322                 if (rawdata)
323                         *rawdata = NULL;
324                 out = false;
325         }
326
327         return (out);
328 }
329
330
331 static int disc_read_sector_generic (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata, u_int32_t method) {
332         bool out;
333         u_int32_t start_block;
334         int ret, retry;
335         u_int32_t step, cnt, max_cnt, max_blk;
336         u_int32_t block_len, block_size, _block_size, last_block_size, block_cnt;
337 //fprintf (stdout,"disc_read_sector_%d", method);
338         start_block = sector_no / SECTORS_PER_BLOCK;
339
340         out = false;
341         step = d->sec_mem;
342         max_cnt = d->max_cnt;
343         max_blk = d->max_blk;
344
345         block_size = step*2064;
346         last_block_size = block_size;
347         block_len = 1;
348         if (block_size > 27 * 2064) {
349                 block_len = block_size / (27*2064);
350                 if (block_size % (27*2064) != 0) block_len += 1;
351                 block_size = 27*2064;
352                 last_block_size = (step*2064) - (27*2064*(block_len-1));
353         }
354         _block_size=block_size;
355
356         for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) {
357                 /* Assume everything will turn out well */
358                 out = true;
359
360                 //Streaming read
361                 if (retry < 3) {
362                         cnt=0;
363                         while (cnt <= max_cnt){
364
365                                 _block_size=block_size;
366                                 if (method == 0 || method == 1 || method == 4) {
367                                         if (sector_no+(cnt*step) +992 +16 <= d -> sectors_no) //smaller than last sector
368                                                 dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) +992, 16, NULL, NULL, 0);
369                                         else if (sector_no+(cnt*step) -992 >= 0)             //larger than first sector
370                                                 dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) -992, 16, NULL, NULL, 0);
371                                         else dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL);
372                                 }
373
374                                 if (method == 0 || method == 2 || method == 5) dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL);
375                                 if (method == 0 || method == 1 || method == 2 || method == 3) ret = dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step), d->sec_disc, NULL, &buf_unscrambled[0], 2064*step);
376                                 if (method == 4 || method == 5 || method == 6) ret = dvd_read_streaming (d -> dvd, sector_no+(cnt*step), d->sec_disc, NULL, &buf_unscrambled[0], 2064*step);
377                                 if (ret >= 0) {
378                                         for (block_cnt=0; block_cnt<block_len; block_cnt++) {
379                                                 if (dvd_memdump (d -> dvd, block_cnt*27*2064, 1, _block_size, &buf[(cnt*(2064 * step))+(block_cnt*27*2064)]) < 0) {
380                                                         error ("Memdump failed");
381                                                         //retry = MAX_READ_RETRIES;             /* Well, if this fails going on is useless */ //no it's not!
382                                                         out = false;
383                                                         break;
384                                                 } 
385                                                 if (block_cnt==block_len-1) _block_size = last_block_size;
386                                         }
387                                         if (!out) break;
388                                         //do this check only on 1st layer
389                                         else if (((buf[cnt*(2064*step)] & 1) == 0) && ((buf[cnt*(2064*step)+1]<<16)+(buf[cnt*(2064*step)+2]<<8)+(buf[cnt*(2064*step)+3]) != 0x30000 + sector_no+(cnt*step))) {
390                                                 out = false;
391                                                 break;
392                                         }
393                                         else cnt += 1;
394                                 } else {
395                                         error ("dvd_read_streaming() failed with %d", ret);
396                                         out = false;
397                                         break;
398                                 }
399
400                         }
401                         
402                         if (cnt < max_cnt) out = false;
403                         else {
404 #ifdef DEBUG
405                                 if (d -> unscrambling) {
406 #endif
407                                         /* Try to unscramble all data to see if EDC fails */
408                                         //for(cnt=0; cnt <= 4; cnt++) {
409                                         for(cnt=max_blk; cnt--;) {
410                                                 if (!unscrambler_unscramble_16sectors (d -> u, sector_no+(cnt*16), &buf[cnt*(2064*16)], &buf_unscrambled[cnt*(2048*16)]))
411                                                         out = false;
412                                         }
413 #ifdef DEBUG
414                                 }
415 #endif
416                         }
417                         if (out) {
418                                 /* If data were unscrambled correctly, add them to the cache */
419                                 //for(cnt = 0; cnt <= 4; cnt++) {
420                                 for(cnt=max_blk; cnt--;) {
421                                         disc_cache_add_block (d, start_block+cnt, &buf_unscrambled[cnt*(2048*16)], &buf[cnt*(2064*16)]);
422                                 }
423                         }
424                 } //if (retry < 3)
425
426                 //Simple read on 4rth try
427                 else {
428                         if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector
429                                 dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0);
430                         else if (sector_no -992 >= 0)             //larger than first sector
431                                 dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0);
432                         else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL);
433
434                         dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL);
435                         ret = dvd_read_sector_dummy (d -> dvd, sector_no, SECTORS_PER_BLOCK, NULL, NULL, 0);
436                         if (ret >= 0) {
437                                 if (dvd_memdump (d -> dvd, 0, 1, RAW_BLOCK_SIZE, buf) < 0) {
438                                         error ("Memdump failed");
439                                         //retry = MAX_READ_RETRIES;             /* Well, if this fails going on is useless */
440                                         out = false;
441                                 } 
442                                 else if ( ((*(buf) & 1) == 0) && ((*(buf+1)<<16)+(*(buf+2)<<8)+(*(buf+3)) != 0x30000+sector_no) ) out = false;
443                                 else {
444 #ifdef DEBUG
445                                         if (d -> unscrambling) {
446 #endif
447                                                 /* Try to unscramble all data to see if EDC fails */
448                                                 if (!unscrambler_unscramble_16sectors (d -> u, sector_no, buf, buf_unscrambled))
449                                                         out = false;
450 #ifdef DEBUG
451                                         }
452 #endif
453                                 }
454                                 if (out) {
455                                         /* If data were unscrambled correctly, add them to the cache */
456                                         disc_cache_add_block (d, start_block, buf_unscrambled, buf);
457                                 }
458                         } else {
459                                 error ("dvd_read_sector_dummy() failed with %d", ret);
460                                 out = false;
461                         }
462                 } //else
463         } //for
464
465         if (!out)
466                 error ("Too many retries, giving up");
467
468         return (out);
469 }
470
471
472 static int disc_read_sector_xbox (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
473         bool out;
474         u_int32_t start_block, block_start, sectors_to_read;
475         u_int8_t readbuf[BLOCK_SIZE];
476         u_int8_t rawbuf[RAW_BLOCK_SIZE];
477
478         (void) data;
479         (void) rawdata;
480
481         start_block = sector_no / SECTORS_PER_BLOCK;
482         block_start = start_block * SECTORS_PER_BLOCK;
483         if (block_start >= d -> sectors_no)
484                 return false;
485
486         sectors_to_read = SECTORS_PER_BLOCK;
487         if (block_start + sectors_to_read > d -> sectors_no)
488                 sectors_to_read = d -> sectors_no - block_start;
489
490         memset (readbuf, 0, sizeof (readbuf));
491         memset (rawbuf, 0, sizeof (rawbuf));
492
493         out = dvd_read_10 (d -> dvd, block_start, sectors_to_read, NULL, readbuf, sizeof (readbuf)) >= 0;
494         if (out)
495                 disc_cache_add_block (d, start_block, readbuf, rawbuf);
496         else
497                 error ("Xbox READ(10) failed at sector %u", block_start);
498
499         return out;
500 }
501
502
503 ///////////////////////////// General /////////////////////////////
504 static int disc_read_sector_0 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
505         return disc_read_sector_generic (d, sector_no, data, rawdata, 0);
506 }
507
508
509
510 ////////////////////////// Non-Streaming //////////////////////////
511 static int disc_read_sector_1 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
512         return disc_read_sector_generic (d, sector_no, data, rawdata, 1);
513
514 }
515
516 static int disc_read_sector_2 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
517         return disc_read_sector_generic (d, sector_no, data, rawdata, 2);
518
519 }
520
521
522 static int disc_read_sector_3 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
523         return disc_read_sector_generic (d, sector_no, data, rawdata, 3);
524
525 }
526
527
528
529 //////////////////////////// Streaming ////////////////////////////
530 static int disc_read_sector_4 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
531         return disc_read_sector_generic (d, sector_no, data, rawdata, 4);
532 }
533
534
535 static int disc_read_sector_5 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
536         return disc_read_sector_generic (d, sector_no, data, rawdata, 5);
537 }
538
539
540 static int disc_read_sector_6 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
541         return disc_read_sector_generic (d, sector_no, data, rawdata, 6);
542 }
543
544
545
546 ///////////////////////////// Hitachi /////////////////////////////
547 static int disc_read_sector_7 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
548         bool out;
549         u_int32_t start_block;
550         int j, ret, retry;
551         u_int8_t buf[5][16 * 2064];
552         u_int8_t buf_unscrambled[5][16 * 2048];
553 //fprintf (stdout,"disc_read_sector_7");
554         start_block = sector_no / SECTORS_PER_BLOCK;
555
556         out = false;
557         for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) {
558                 /* Assume everything will turn out well */
559                 out = true;
560
561                 if (retry > 0) {
562                         warning ("Read retry %d for sector %u", retry, sector_no);
563
564                         /* Try to reset in-memory data by seeking to a distant sector */
565 //                      if (sector_no > 1000)
566 //                              dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0);
567 //                      else
568 //                              dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0);
569                         if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector
570                                 dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0);
571                         else if (sector_no -992 >= 0)             //larger than first sector
572                                 dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0);
573                         else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL);
574                 }
575
576                 if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, NULL, 0)) >= 0) {
577                         for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) {
578                                 if (dvd_memdump (d -> dvd, 0 + (j * 16 * 2064), 1, 16 * 2064, buf[j]) < 0) {    /* Dumping in a single block is faster */
579                                         error ("Memdump failed");
580                                         out = false;
581                                         retry = MAX_READ_RETRIES;               /* Well, if this fails going on is useless */
582                                 } else {
583 #ifdef DEBUG
584                                         if (d -> unscrambling) {
585 #endif
586                                                 /* Try to unscramble all data to see if EDC fails */
587                                                 if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j]))
588                                                         out = false;
589 #ifdef DEBUG
590                                         }
591 #endif
592                                 }
593                         }
594
595                         if (out) {
596                                 /* It seems all data was unscrambled correctly, so cache them out */
597                                 for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no; j++)
598                                         disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]);
599
600                         }
601                 } else {
602                         error ("dvd_read_sector_streaming() failed with %d", ret);
603                         out = false;
604                 }
605         }
606
607         if (!out)
608                 error ("Too many retries, giving up");
609
610         return (out);
611 }
612
613
614
615 static bool disc_read_sector_8_split_recover_block (disc *d, u_int32_t block_sector) {
616         static const int chunk_sizes[] = { 8, 4, 2, 1 };
617         bool out;
618         int c, chunk_len, chunk_start, k, ret;
619         u_int32_t ram_offset, block_no;
620         u_int8_t *sect;
621         u_int8_t raw_block[RAW_BLOCK_SIZE];
622         u_int8_t iso_block[BLOCK_SIZE];
623         u_int8_t readbuf[BLOCK_SIZE];
624         char detail[160];
625         req_sense sense;
626
627         block_no = block_sector / SECTORS_PER_BLOCK;
628
629         for (c = 0; c < (int) (sizeof (chunk_sizes) / sizeof (chunk_sizes[0])); c++) {
630                 chunk_len = chunk_sizes[c];
631                 memset (raw_block, 0, sizeof (raw_block));
632                 memset (iso_block, 0, sizeof (iso_block));
633                 out = true;
634                 disc_seed_diag_clear (d);
635
636                 warning ("Method 8 split recovery: trying %d-sector chunks for sectors %u..%u", chunk_len, block_sector, block_sector + SECTORS_PER_BLOCK - 1);
637                 disc_seed_diag_log (d,
638                         "block=%u/20 sectors=%u..%u retry=%d stage=split-profile result=BEGIN detail=chunk-sectors-%d",
639                         d -> seed_diagnostic_block + 1, block_sector,
640                         block_sector + SECTORS_PER_BLOCK - 1, c, chunk_len);
641
642                 for (chunk_start = 0; chunk_start < SECTORS_PER_BLOCK && out; chunk_start += chunk_len) {
643                         u_int32_t chunk_sector = block_sector + (u_int32_t) chunk_start;
644                         memset (readbuf, 0, sizeof (readbuf));
645
646                         memset (&sense, 0, sizeof (sense));
647                         if (chunk_sector + 992 + 16 <= d -> sectors_no)
648                                 ret = dvd_read_sector_dummy (d -> dvd, chunk_sector + 992, 16, &sense, NULL, 0);
649                         else if (chunk_sector >= 992)
650                                 ret = dvd_read_sector_dummy (d -> dvd, chunk_sector - 992, 16, &sense, NULL, 0);
651                         else
652                                 ret = dvd_flush_cache_READ12 (d -> dvd, chunk_sector, &sense);
653                         if (ret < 0) {
654                                 disc_seed_diag_log (d,
655                                         "block=%u/20 sectors=%u..%u retry=%d stage=split-prefetch result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X detail=chunk-sectors-%d",
656                                         d -> seed_diagnostic_block + 1, chunk_sector,
657                                         chunk_sector + (u_int32_t) chunk_len - 1, c, ret,
658                                         sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff, chunk_len);
659                         }
660
661                         memset (&sense, 0, sizeof (sense));
662                         ret = dvd_read_streaming (d -> dvd, chunk_sector, (u_int32_t) chunk_len,
663                                 &sense, readbuf, (size_t) chunk_len * SECTOR_SIZE);
664                         if (ret < 0) {
665                                 snprintf (detail, sizeof (detail), "chunk-sectors=%d sense=%02X/%02X/%02X",
666                                         chunk_len, sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff);
667                                 disc_seed_diag_fail (d, chunk_sector,
668                                         chunk_sector + (u_int32_t) chunk_len - 1, c,
669                                         "split-streaming-read", detail, ret, true);
670                                 warning ("Method 8 split recovery: READ12 streaming failed for sectors %u..%u with %d", chunk_sector, chunk_sector + (u_int32_t) chunk_len - 1, ret);
671                                 out = false;
672                                 break;
673                         }
674
675                         for (k = 0; k < chunk_len; k++) {
676                                 sect = &raw_block[(chunk_start + k) * RAW_SECTOR_SIZE];
677                                 ram_offset = (u_int32_t) k * RAW_SECTOR_SIZE;
678
679                                 ret = dvd_memdump (d -> dvd, ram_offset, 1, 12, sect);
680                                 if (ret < 0) {
681                                         snprintf (detail, sizeof (detail), "chunk-sectors=%d raw-offset=%u sector-index=%d",
682                                                 chunk_len, ram_offset, k);
683                                         disc_seed_diag_fail (d, chunk_sector + (u_int32_t) k,
684                                                 chunk_sector + (u_int32_t) k, c,
685                                                 "split-header-memdump", detail, ret, true);
686                                         warning ("Method 8 split recovery: header memdump failed at sector %u", chunk_sector + (u_int32_t) k);
687                                         out = false;
688                                         break;
689                                 }
690                                 ret = dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060);
691                                 if (ret < 0) {
692                                         snprintf (detail, sizeof (detail), "chunk-sectors=%d raw-offset=%u sector-index=%d",
693                                                 chunk_len, ram_offset + 2060, k);
694                                         disc_seed_diag_fail (d, chunk_sector + (u_int32_t) k,
695                                                 chunk_sector + (u_int32_t) k, c,
696                                                 "split-edc-memdump", detail, ret, true);
697                                         warning ("Method 8 split recovery: EDC memdump failed at sector %u", chunk_sector + (u_int32_t) k);
698                                         out = false;
699                                         break;
700                                 }
701
702                                 memcpy (sect + 12, readbuf + ((size_t) k * SECTOR_SIZE), SECTOR_SIZE);
703                         }
704                 }
705
706                 if (out && !unscrambler_unscramble_16sectors (d -> u, block_sector, raw_block, iso_block)) {
707                         snprintf (detail, sizeof (detail), "chunk-sectors=%d", chunk_len);
708                         disc_seed_diag_fail (d, block_sector,
709                                 block_sector + SECTORS_PER_BLOCK - 1, c,
710                                 "split-edc-unscramble-validation", detail, -1, false);
711                         warning ("Method 8 split recovery: EDC/unscramble validation failed for %d-sector chunks at sectors %u..%u", chunk_len, block_sector, block_sector + SECTORS_PER_BLOCK - 1);
712                         out = false;
713                 }
714
715                 if (out) {
716                         disc_cache_add_block (d, block_no, iso_block, raw_block);
717                         warning ("Method 8 split recovery: recovered sectors %u..%u using %d-sector chunks", block_sector, block_sector + SECTORS_PER_BLOCK - 1, chunk_len);
718                         disc_seed_diag_log (d,
719                                 "block=%u/20 sectors=%u..%u retry=%d stage=split-profile result=PASS detail=chunk-sectors-%d",
720                                 d -> seed_diagnostic_block + 1, block_sector,
721                                 block_sector + SECTORS_PER_BLOCK - 1, c, chunk_len);
722                         return true;
723                 }
724         }
725
726         warning ("Method 8 split recovery: all chunk sizes failed for sectors %u..%u", block_sector, block_sector + SECTORS_PER_BLOCK - 1);
727         return false;
728 }
729
730 static bool disc_hlds_type_is_gdr8050l_accel (u_int32_t type) {
731         return type == 442 || type == 443 || type == 445;
732 }
733
734 static bool disc_hlds_type_is_gdr8050l_no_prefetch (u_int32_t type) {
735         return type == 44 || type == 45 || disc_hlds_type_is_gdr8050l_accel (type);
736 }
737
738
739 static bool disc_hlds_type_is_gdr8081n_search_guided (u_int32_t type) {
740         return type == 815;
741 }
742
743 static bool hlds_e7_find_exact_raw_header_offset (const u_int8_t *dumpbuf, size_t dump_len, u_int32_t sector_no, size_t *out_off) {
744         size_t off;
745         u_int32_t expected;
746         u_int32_t got;
747
748         if (out_off)
749                 *out_off = (size_t) -1;
750         if (!dumpbuf || dump_len < RAW_SECTOR_SIZE)
751                 return false;
752         expected = 0x30000U + sector_no;
753         for (off = 0; off + RAW_SECTOR_SIZE <= dump_len; off++) {
754                 got = ((u_int32_t) dumpbuf[off + 1] << 16) | ((u_int32_t) dumpbuf[off + 2] << 8) | (u_int32_t) dumpbuf[off + 3];
755                 if (got != expected)
756                         continue;
757                 /* Avoid all-zero/all-ff false positives.  Raw headers observed on HLDS
758                  * families can have different first-byte control bits, so do not require
759                  * exact parity here; the final unscrambler/EDC pass is the authority. */
760                 if (((dumpbuf[off] | dumpbuf[off + 1] | dumpbuf[off + 2] | dumpbuf[off + 3]) == 0x00) ||
761                     ((dumpbuf[off] & dumpbuf[off + 1] & dumpbuf[off + 2] & dumpbuf[off + 3]) == 0xFF))
762                         continue;
763                 if (out_off)
764                         *out_off = off;
765                 return true;
766         }
767         return false;
768 }
769
770 static int hlds_e7_count_exact_raw_headers_for_block (const u_int8_t *dumpbuf, size_t dump_len, u_int32_t block_sector, size_t offsets[SECTORS_PER_BLOCK]) {
771         int k;
772         int count;
773         size_t off;
774         count = 0;
775         if (offsets) {
776                 for (k = 0; k < SECTORS_PER_BLOCK; k++)
777                         offsets[k] = (size_t) -1;
778         }
779         for (k = 0; k < SECTORS_PER_BLOCK; k++) {
780                 if (hlds_e7_find_exact_raw_header_offset (dumpbuf, dump_len, block_sector + (u_int32_t) k, &off)) {
781                         if (offsets)
782                                 offsets[k] = off;
783                         count++;
784                 }
785         }
786         return count;
787 }
788
789 static bool hlds_e7_raw_header_offsets_are_sector_shaped (const size_t offsets[SECTORS_PER_BLOCK]) {
790         int k;
791         int stride_matches;
792         size_t expected;
793         if (!offsets)
794                 return false;
795         for (k = 0; k < SECTORS_PER_BLOCK; k++) {
796                 if (offsets[k] == (size_t) -1)
797                         return false;
798         }
799         /* A real raw cache block has one 2064-byte raw sector per logical sector.
800          * False positives observed on GDR-8081N v4 looked like SRAM/tables with
801          * sector-number patterns only 4 bytes apart, so require a sane 2064-byte
802          * sector stride before treating matches as real cache sectors. */
803         stride_matches = 0;
804         for (k = 1; k < SECTORS_PER_BLOCK; k++) {
805                 expected = offsets[0] + ((size_t) k * RAW_SECTOR_SIZE);
806                 if (offsets[k] == expected)
807                         stride_matches++;
808         }
809         return stride_matches >= 12;
810 }
811
812 static size_t hlds_e7_find_command_echo_offset (const u_int8_t *buf, size_t len) {
813         static const u_int8_t sig[] = {0xE7, 0x48, 0x49, 0x54, 0x01};
814         size_t off;
815         if (!buf || len < sizeof (sig))
816                 return (size_t) -1;
817         for (off = 0; off + sizeof (sig) <= len; off++) {
818                 if (memcmp (buf + off, sig, sizeof (sig)) == 0)
819                         return off;
820         }
821         return (size_t) -1;
822 }
823
824 static int disc_read_sector_8_gdr8081n_search_guided (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
825         static bool logged = false;
826         bool out;
827         u_int32_t block_sector;
828         u_int32_t start_block;
829         u_int32_t profile_blocks;
830         u_int32_t scan_len;
831         int k;
832         int retry;
833         int ret;
834         int found_count;
835         int first_missing;
836         bool sector_shaped;
837         size_t offsets[SECTORS_PER_BLOCK];
838         u_int8_t *scanbuf;
839         u_int8_t *sect;
840         u_int8_t raw_block[RAW_BLOCK_SIZE];
841         u_int8_t iso_block[BLOCK_SIZE];
842         u_int8_t readbuf[BLOCK_SIZE];
843
844         start_block = sector_no / SECTORS_PER_BLOCK;
845         block_sector = start_block * SECTORS_PER_BLOCK;
846         profile_blocks = dvd_get_hlds_e7_mem_blocks (d -> dvd);
847         if (profile_blocks < 1 || profile_blocks > 5)
848                 profile_blocks = 5;
849         scan_len = profile_blocks * RAW_BLOCK_SIZE;
850         scanbuf = (u_int8_t *) malloc (scan_len);
851         if (!scanbuf) {
852                 error ("GDR-8081N scan-guided Method 8: unable to allocate %u-byte scan buffer", scan_len);
853                 return false;
854         }
855         if (!logged) {
856                 hlds_e7_visible_probe_log ("GDR-8081N 0xE7: using scan-guided single-block Method 8 profile at 0x%08x, scan windows=%u", dvd_get_hlds_e7_cache_base (d -> dvd), profile_blocks);
857                 logged = true;
858         }
859
860         out = false;
861         for (retry = 0; !out && retry < 1; retry++) {
862                 out = true;
863                 if (retry > 0)
864                         warning ("GDR-8081N scan-guided Method 8 retry %d for sectors %u..%u", retry, block_sector, block_sector + SECTORS_PER_BLOCK - 1);
865
866                 /* Keep this conservative: the v2 scanner showed sector-cache material inside
867                  * the 0x80000000 five-window range, but not necessarily in the exact Type4
868                  * j/k slot map.  For now, reconstruct only the requested 16-sector block
869                  * from a full-window search.  Do not require all five cached windows to map;
870                  * that made v3 reject useful data before seed cracking could start. */
871                 if (block_sector > d -> sectors_no - 1000)
872                         dvd_read_sector_streaming (d -> dvd, block_sector - 16 * 5 * 2, NULL, NULL, 0);
873                 else
874                         dvd_read_sector_streaming (d -> dvd, block_sector + 16 * 5, NULL, NULL, 0);
875
876                 if ((ret = dvd_read_sector_streaming (d -> dvd, block_sector, NULL, readbuf, sizeof (readbuf))) < 0) {
877                         error ("GDR-8081N scan-guided Method 8: dvd_read_sector_streaming(%u) failed with %d", block_sector, ret);
878                         out = false;
879                         continue;
880                 }
881
882                 memset (scanbuf, 0, scan_len);
883                 if (dvd_memdump (d -> dvd, 0, profile_blocks, RAW_BLOCK_SIZE, scanbuf) < 0) {
884                         error ("GDR-8081N scan-guided Method 8: full-window memdump failed");
885                         out = false;
886                         continue;
887                 }
888
889                 found_count = hlds_e7_count_exact_raw_headers_for_block (scanbuf, scan_len, block_sector, offsets);
890                 sector_shaped = hlds_e7_raw_header_offsets_are_sector_shaped (offsets);
891                 if (found_count == SECTORS_PER_BLOCK && !sector_shaped) {
892                         warning ("GDR-8081N scan-guided Method 8: found 16/16 sector-number patterns for sectors %u..%u, but offsets are not 2064-byte sector-shaped; treating as SRAM/table false positive",
893                                 block_sector, block_sector + SECTORS_PER_BLOCK - 1);
894                         out = false;
895                         break;
896                 }
897                 if (found_count != SECTORS_PER_BLOCK) {
898                         first_missing = -1;
899                         for (k = 0; k < SECTORS_PER_BLOCK; k++) {
900                                 if (offsets[k] == (size_t) -1) {
901                                         first_missing = k;
902                                         break;
903                                 }
904                         }
905                         warning ("GDR-8081N scan-guided Method 8: found %d/16 exact raw headers for sectors %u..%u; first missing sector %u",
906                                 found_count, block_sector, block_sector + SECTORS_PER_BLOCK - 1,
907                                 first_missing >= 0 ? block_sector + (u_int32_t) first_missing : block_sector);
908                         out = false;
909                         continue;
910                 }
911
912                 for (k = 0; k < SECTORS_PER_BLOCK; k++) {
913                         sect = &raw_block[k * RAW_SECTOR_SIZE];
914                         memcpy (sect, scanbuf + offsets[k], 12);
915                         memcpy (sect + 12, readbuf + ((size_t) k * SECTOR_SIZE), SECTOR_SIZE);
916                         memcpy (sect + 2060, scanbuf + offsets[k] + 2060, 4);
917                 }
918
919                 if (!unscrambler_unscramble_16sectors (d -> u, block_sector, raw_block, iso_block)) {
920                         warning ("GDR-8081N scan-guided Method 8: EDC/unscramble validation failed for sectors %u..%u after finding all 16 headers", block_sector, block_sector + SECTORS_PER_BLOCK - 1);
921                         out = false;
922                         continue;
923                 }
924
925                 disc_cache_add_block (d, start_block, iso_block, raw_block);
926         }
927
928         free (scanbuf);
929         if (!out)
930                 error ("GDR-8081N scan-guided Method 8: strict sector-layout validation failed");
931         return out;
932 }
933
934 static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
935         bool out;
936         u_int32_t ram_offset;
937         u_int32_t start_block;
938         u_int32_t profile_blocks;
939         u_int32_t prefetch_sector;
940         int j, k, ret, retry;
941         int prefetch_ret;
942         u_int8_t *sect, buf[5][RAW_BLOCK_SIZE];
943         u_int8_t readbuf[BLOCK_SIZE];
944         u_int8_t buf_unscrambled[5][BLOCK_SIZE];
945         req_sense sense;
946         char detail[160];
947
948         if (disc_hlds_type_is_gdr8081n_search_guided (dvd_get_hlds_e7_type (d -> dvd)))
949                 return disc_read_sector_8_gdr8081n_search_guided (d, sector_no, data, rawdata);
950
951         start_block = sector_no / SECTORS_PER_BLOCK;
952         profile_blocks = dvd_get_hlds_e7_mem_blocks (d -> dvd);
953         if (profile_blocks < 1 || profile_blocks > 5)
954                 profile_blocks = 5;
955         
956         out = false;
957         for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) {
958                 out = true;
959                 disc_seed_diag_clear (d);
960                 disc_seed_diag_log (d,
961                         "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=BEGIN detail=windows-%u",
962                         d -> seed_diagnostic_block + 1, sector_no,
963                         sector_no + SECTORS_PER_BLOCK - 1, retry, profile_blocks);
964
965                 if (retry > 0) {
966                         warning ("Read retry %d for sector %u", retry, sector_no);
967                         memset (&sense, 0, sizeof (sense));
968                         if (sector_no + 992 + 16 <= d -> sectors_no)
969                                 ret = dvd_read_sector_dummy (d -> dvd, sector_no + 992, 16, &sense, NULL, 0);
970                         else if (sector_no >= 992)
971                                 ret = dvd_read_sector_dummy (d -> dvd, sector_no - 992, 16, &sense, NULL, 0);
972                         else
973                                 ret = dvd_flush_cache_READ12 (d -> dvd, sector_no, &sense);
974                         if (ret < 0) {
975                                 disc_seed_diag_log (d,
976                                         "block=%u/20 sectors=%u..%u retry=%d stage=retry-cache-reset result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X",
977                                         d -> seed_diagnostic_block + 1, sector_no,
978                                         sector_no + SECTORS_PER_BLOCK - 1, retry, ret,
979                                         sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff);
980                         }
981                 }
982
983                 /* Prime the volatile cache, then immediately consume it. */
984                 memset (&sense, 0, sizeof (sense));
985                 if (disc_hlds_type_is_gdr8050l_no_prefetch (dvd_get_hlds_e7_type (d -> dvd))) {
986                         prefetch_sector = sector_no;
987                         prefetch_ret = dvd_flush_cache_READ12 (d -> dvd, sector_no, &sense);
988                 } else {
989                         if (sector_no > d -> sectors_no - 1000)
990                                 prefetch_sector = sector_no - 16 * 5 * 2;
991                         else
992                                 prefetch_sector = sector_no + 16 * 5;
993                         prefetch_ret = dvd_read_sector_streaming (d -> dvd, prefetch_sector, &sense, NULL, 0);
994                 }
995                 if (prefetch_ret < 0) {
996                         disc_seed_diag_log (d,
997                                 "block=%u/20 sectors=%u..%u retry=%d stage=prefetch result=NONFATAL-FAIL rc=%d sense=%02X/%02X/%02X detail=prefetch-lba-%u",
998                                 d -> seed_diagnostic_block + 1, sector_no,
999                                 sector_no + SECTORS_PER_BLOCK - 1, retry, prefetch_ret,
1000                                 sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff,
1001                                 prefetch_sector);
1002                 }
1003
1004                 memset (&sense, 0, sizeof (sense));
1005                 ret = dvd_read_sector_streaming (d -> dvd, sector_no, &sense, readbuf, sizeof (readbuf));
1006                 if (ret >= 0) {
1007                         for (j = 0; j < (int) profile_blocks && sector_no + j * 16 < d -> sectors_no && out; j++) {
1008                                 for (k = 0; k < 16; k++) {
1009                                         sect = &buf[j][k * RAW_SECTOR_SIZE];
1010                                         ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE;
1011                                         ret = dvd_memdump (d -> dvd, ram_offset, 1, 12, sect);
1012                                         if (ret < 0) {
1013                                                 snprintf (detail, sizeof (detail),
1014                                                         "window=%d sector-index=%d raw-offset=%u", j, k, ram_offset);
1015                                                 disc_seed_diag_fail (d, sector_no + (u_int32_t) (j * 16 + k),
1016                                                         sector_no + (u_int32_t) (j * 16 + k), retry,
1017                                                         "header-memdump", detail, ret, true);
1018                                                 error ("Memdump (1) failed");
1019                                                 out = false;
1020                                                 retry = MAX_READ_RETRIES;
1021                                         } else {
1022                                                 ret = dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060);
1023                                                 if (ret < 0) {
1024                                                         snprintf (detail, sizeof (detail),
1025                                                                 "window=%d sector-index=%d raw-offset=%u", j, k, ram_offset + 2060);
1026                                                         disc_seed_diag_fail (d, sector_no + (u_int32_t) (j * 16 + k),
1027                                                                 sector_no + (u_int32_t) (j * 16 + k), retry,
1028                                                                 "edc-memdump", detail, ret, true);
1029                                                         error ("Memdump (2) failed");
1030                                                         out = false;
1031                                                 }
1032                                         }
1033                                 }
1034                         }
1035
1036                         for (j = 0; j < (int) profile_blocks && sector_no + j * 16 < d -> sectors_no && out; j++) {
1037                                 if (j != 0) {
1038                                         memset (&sense, 0, sizeof (sense));
1039                                         ret = dvd_read_sector_streaming (d -> dvd, sector_no + (u_int32_t) j * 16,
1040                                                 &sense, readbuf, sizeof (readbuf));
1041                                         if (ret < 0) {
1042                                                 snprintf (detail, sizeof (detail),
1043                                                         "window=%d sense=%02X/%02X/%02X", j,
1044                                                         sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff);
1045                                                 disc_seed_diag_fail (d, sector_no + (u_int32_t) j * 16,
1046                                                         sector_no + (u_int32_t) j * 16 + SECTORS_PER_BLOCK - 1,
1047                                                         retry, "cached-window-streaming-read", detail, ret, true);
1048                                                 error ("dvd_read_sector_streaming() failed with %d", ret);
1049                                                 out = false;
1050                                                 break;
1051                                         }
1052                                 }
1053
1054                                 for (k = 0; k < 16; k++) {
1055                                         sect = &buf[j][k * RAW_SECTOR_SIZE];
1056                                         memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE);
1057                                 }
1058 #ifdef DEBUG
1059                                 if (d -> unscrambling) {
1060 #endif
1061                                         if (!unscrambler_unscramble_16sectors (d -> u,
1062                                                 sector_no + (u_int32_t) (j * 16), buf[j], buf_unscrambled[j])) {
1063                                                 snprintf (detail, sizeof (detail), "window=%d", j);
1064                                                 disc_seed_diag_fail (d,
1065                                                         sector_no + (u_int32_t) j * 16,
1066                                                         sector_no + (u_int32_t) j * 16 + SECTORS_PER_BLOCK - 1,
1067                                                         retry, "edc-unscramble-validation", detail, -1, false);
1068                                                 out = false;
1069                                         }
1070 #ifdef DEBUG
1071                                 }
1072 #endif
1073                         }
1074
1075                         if (out) {
1076                                 for (j = 0; j < (int) profile_blocks && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++)
1077                                         disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]);
1078                                 disc_seed_diag_clear (d);
1079                                 disc_seed_diag_log (d,
1080                                         "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=PASS detail=windows-%u",
1081                                         d -> seed_diagnostic_block + 1, sector_no,
1082                                         sector_no + SECTORS_PER_BLOCK - 1, retry, profile_blocks);
1083                         }
1084                 } else {
1085                         snprintf (detail, sizeof (detail), "sense=%02X/%02X/%02X",
1086                                 sense.sense_key & 0xff, sense.asc & 0xff, sense.ascq & 0xff);
1087                         disc_seed_diag_fail (d, sector_no,
1088                                 sector_no + SECTORS_PER_BLOCK - 1, retry,
1089                                 "streaming-read", detail, ret, true);
1090                         error ("dvd_read_sector_streaming() failed with %d", ret);
1091                         out = false;
1092                 }
1093         }
1094
1095         if (!out && disc_hlds_type_is_gdr8050l_accel (dvd_get_hlds_e7_type (d -> dvd))) {
1096                 u_int32_t failed_type = dvd_get_hlds_e7_type (d -> dvd);
1097                 warning ("GDR-8050L modified 0xE7: accelerated profile %s failed at sector %u; falling back to proven single-window profile for this run",
1098                         dvd_get_hlds_e7_profile_name (d -> dvd), sector_no);
1099                 dvd_set_hlds_e7_runtime_profile (d -> dvd, 44, 0x80000000U, 1);
1100                 d -> hlds_e7_read_schedule_logged = false;
1101                 disc_cache_clear (d);
1102                 out = disc_read_sector_8 (d, sector_no, data, rawdata);
1103                 if (!out)
1104                         warning ("GDR-8050L modified 0xE7: fallback from accelerated profile %u also failed", failed_type);
1105         }
1106
1107         if (!out) {
1108                 u_int32_t block_sector = start_block * SECTORS_PER_BLOCK;
1109                 warning ("Method 8 normal profile read failed at sectors %u..%u; entering split recovery", block_sector, block_sector + SECTORS_PER_BLOCK - 1);
1110                 disc_seed_diag_log (d,
1111                         "block=%u/20 sectors=%u..%u retry=%d stage=normal-profile result=EXHAUSTED detail=entering-split-recovery",
1112                         d -> seed_diagnostic_block + 1, block_sector,
1113                         block_sector + SECTORS_PER_BLOCK - 1, MAX_READ_RETRIES - 1);
1114                 out = disc_read_sector_8_split_recover_block (d, block_sector);
1115         }
1116
1117         if (!out)
1118                 error ("Too many retries, giving up");
1119
1120         return out;
1121 }
1122
1123
1124 static int disc_read_sector_9 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
1125         bool out;
1126         u_int32_t ram_offset;
1127         int j, k, ret, retry;
1128         u_int8_t *sect, buf[5][RAW_BLOCK_SIZE];
1129         u_int8_t readbuf[BLOCK_SIZE], tmp[16];
1130         u_int8_t buf_unscrambled[5][BLOCK_SIZE];
1131         u_int32_t start_block;
1132 //fprintf (stdout,"disc_read_sector_9");
1133         start_block = sector_no / SECTORS_PER_BLOCK;
1134
1135         out = false;
1136         for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) {
1137                 /* Assume everything will turn out well */
1138                 out = true;
1139
1140                 if (retry > 0) {
1141                         warning ("Read retry %d for sector %u", retry, sector_no);
1142
1143                         /* Try to reset in-memory data by seeking to a distant sector */
1144 //                      if (sector_no > 1000)
1145 //                              dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0);
1146 //                      else
1147 //                              dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0);
1148                         if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector
1149                                 dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0);
1150                         else if (sector_no -992 >= 0)             //larger than first sector
1151                                 dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0);
1152                         else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL);
1153                 }
1154
1155                 /* First READ command, this will cache 5 16-sector blocks. Immediately dump relevant data */
1156                 if (sector_no > d -> sectors_no - 1000)
1157                         dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0);
1158                 else
1159                         dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0);
1160                 if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, BLOCK_SIZE)) >= 0) {
1161                         for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) {
1162                                 /* Reconstruct raw sectors */
1163                                 for (k = 0; k < 16; k++) {
1164                                         sect = &buf[j][k * RAW_SECTOR_SIZE];
1165                                         ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE;
1166                                         /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */
1167                                         if (j == 0 && k == 0) {
1168                                                 if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) {
1169                                                         error ("Memdump (1) failed");
1170                                                         out = false;
1171                                                         retry = MAX_READ_RETRIES;               /* Well, if this fails going on is useless */
1172                                                 }
1173                                         } else {
1174                                                 memcpy (sect, tmp + 4, 12);
1175                                         }
1176                                         
1177                                         if (out && dvd_memdump (d -> dvd, ram_offset + 2060, 1, 16, tmp) < 0) { /* Dumping in a single block is faster */
1178                                                 error ("Memdump (2) failed");
1179                                                 out = false;
1180                                         } else {
1181                                                 memcpy (sect + 2060, tmp, 4);
1182                                         }
1183                                 }
1184                         }
1185
1186                         /* Now the same for remaining 4 16-sector blocks */
1187                         for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) {
1188                                 if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, BLOCK_SIZE)) >= 0) {
1189                                         /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */
1190                                         for (k = 0; k < 16; k++) {
1191                                                 sect = &buf[j][k * RAW_SECTOR_SIZE];
1192                                                 memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE);
1193                                         }
1194 #ifdef DEBUG
1195                                         if (d -> unscrambling) {
1196 #endif
1197                                                 /* Try to unscramble all data to see if EDC fails */
1198                                                 if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j]))
1199                                                         out = false;
1200 #ifdef DEBUG
1201                                         }
1202 #endif
1203                                 } else {
1204                                         error ("dvd_read_sector_streaming() failed with %d", ret);
1205                                         out = false;
1206                                 }
1207                         }
1208
1209                         if (out) {
1210                                 /* It seems all data were unscrambled correctly, so cache them out */
1211                                 for (j = 0; j < 5 && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++)
1212                                         disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]);
1213                         }
1214                 } else {
1215                         error ("dvd_read_sector_streaming() failed with %d", ret);
1216                         out = false;
1217                 }
1218         }
1219
1220         if (!out)
1221                 error ("Too many retries, giving up");
1222
1223         return (out);
1224 }
1225
1226
1227 /* We could also use the 'System ID' (first byte of the image) to tell the discs apart */
1228 static disc_type disc_detect_type (disc *d, u_int32_t forced_type, u_int32_t sectors_no) {
1229         req_sense sense;
1230
1231         if (forced_type==0) {
1232                 d -> type = DISC_TYPE_GAMECUBE;
1233                 d -> sectors_no = DISC_GAMECUBE_SECTORS_NO;
1234         } else if (forced_type==1) {
1235                 d -> type = DISC_TYPE_WII;
1236                 d -> sectors_no = DISC_WII_SECTORS_NO_SL;
1237         } else if (forced_type==2) {
1238                 d -> type = DISC_TYPE_WII_DL;
1239                 d -> sectors_no = DISC_WII_SECTORS_NO_DL;
1240                 //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL);
1241         } else if (forced_type==3) {
1242                 d -> type = DISC_TYPE_DVD;
1243                 if (sectors_no == -1) dvd_get_size(d->dvd, &(d -> sectors_no), NULL);
1244                 dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL);
1245         } else if (forced_type==4) {
1246                 d -> type = DISC_TYPE_XBOX;
1247                 d -> read_sector = disc_read_sector_xbox;
1248                 d -> read_method = 10;
1249                 if (sectors_no == -1) {
1250                         u_int32_t sector_size = 0;
1251                         /* Do not run the GDR-8050L handshake during type detection.
1252                          * Redump-style Xbox output must capture the visible DVD-video view
1253                          * before switching the drive into the unlocked game view. */
1254                         if (dvd_read_capacity_10(d->dvd, &(d -> sectors_no), &sector_size, NULL) < 0 || sector_size != SECTOR_SIZE)
1255                                 d -> sectors_no = DISC_XBOX_GDR8050L_UNLOCKED_SECTORS_NO;
1256                 }
1257         } else {
1258
1259         if (dvd_is_xbox_drive(d->dvd)) {
1260                 d -> type = DISC_TYPE_XBOX;
1261                 d -> read_sector = disc_read_sector_xbox;
1262                 d -> read_method = 10;
1263                 {
1264                         u_int32_t sector_size = 0;
1265                         /* Keep the drive in its current/locked view for dump planning.
1266                          * The Xbox dumper explicitly unlocks only when it needs the
1267                          * game/XDVDFS view. */
1268                         if (dvd_read_capacity_10(d->dvd, &(d -> sectors_no), &sector_size, NULL) < 0 || sector_size != SECTOR_SIZE)
1269                                 d -> sectors_no = DISC_XBOX_GDR8050L_UNLOCKED_SECTORS_NO;
1270                 }
1271                 if (sectors_no != -1) d -> sectors_no = sectors_no;
1272                 return (d -> type);
1273         }
1274
1275         /* Try to read a sector beyond the end of GameCube discs */
1276         if (!dvd_read_sector_dummy (d -> dvd, DISC_GAMECUBE_SECTORS_NO + 100, SECTORS_PER_BLOCK, &sense, NULL, 0) && sense.sense_key == 0x05 && sense.asc == 0x21) {
1277                 d -> type = DISC_TYPE_GAMECUBE;
1278                 d -> sectors_no = DISC_GAMECUBE_SECTORS_NO;
1279         } else {
1280                 if (!dvd_read_sector_dummy (d -> dvd, DISC_WII_SECTORS_NO_SL + 100, SECTORS_PER_BLOCK, &sense, NULL, 0) && sense.sense_key == 0x05 && sense.asc == 0x21) {
1281                         d -> type = DISC_TYPE_WII;
1282                         d -> sectors_no = DISC_WII_SECTORS_NO_SL;
1283                 } else {
1284                         d -> type = DISC_TYPE_WII_DL;
1285                         d -> sectors_no = DISC_WII_SECTORS_NO_DL;
1286                         //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL);
1287                 }
1288         }
1289
1290         }
1291         if (sectors_no != -1) d -> sectors_no = sectors_no;
1292
1293         return (d -> type);
1294 }
1295
1296
1297 /**
1298  * Reads a sector from the disc (or from the cache), using the preset read method.
1299  * @param d The disc structure.
1300  * @param sector_no The requested sector number.
1301  * @param data A buffer to hold the unscrambled sector data (or NULL).
1302  * @param rawdata A buffer to hold the raw sector data (or NULL).
1303  * @return 
1304  */
1305 int disc_read_sector (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) {
1306         u_int32_t block;
1307         u_int8_t *cdata, *crawdata;
1308         int out;
1309
1310         /* Unscrambled data cannot be requested if unscrambling was disabled */
1311         MY_ASSERT (!(data && !d -> unscrambling && d -> type != DISC_TYPE_XBOX));
1312         
1313         block = sector_no / SECTORS_PER_BLOCK;
1314         
1315         /* See if sector is in cache */
1316         if (!(out = disc_cache_lookup_block (d, block, &cdata, &crawdata))) {
1317                 /* Requested block is not in cache, try to read it from media */
1318                 out = d -> read_sector (d, sector_no, data, rawdata);
1319                 
1320                 /* Now requested sector is in cache, for sure ;) */
1321                 if (out)
1322                         MY_ASSERT (disc_cache_lookup_block (d, block, &cdata, &crawdata));
1323         }
1324
1325         if (out) {
1326                 if (data)
1327                         *data = cdata + (sector_no % SECTORS_PER_BLOCK) * SECTOR_SIZE;
1328                 if (rawdata)
1329                         *rawdata = crawdata + (sector_no % SECTORS_PER_BLOCK) * RAW_SECTOR_SIZE;
1330         } else {
1331                 if (data)
1332                         *data = NULL;
1333                 if (rawdata)
1334                         *rawdata = NULL;
1335         }
1336                 
1337         return (out);
1338 }
1339
1340
1341 static const char *disc_detected_type_name (disc_type type) {
1342         switch (type) {
1343                 case DISC_TYPE_GAMECUBE:
1344                         return "GameCube";
1345                 case DISC_TYPE_WII:
1346                         return "Wii";
1347                 case DISC_TYPE_WII_DL:
1348                         return "Wii_DL";
1349                 case DISC_TYPE_DVD:
1350                         return "DVD";
1351                 case DISC_TYPE_XBOX:
1352                         return "Xbox";
1353                 default:
1354                         return "Unknown";
1355         }
1356 }
1357
1358
1359 static bool disc_analyze (disc *d, bool allow_type_correction, bool allow_size_correction) {
1360         u_int8_t *buf;
1361         char tmp[0x03E0 + 1];
1362         bool unscramble_old, out;
1363         nintendo_disc_header_type header_type;
1364         disc_type previous_type;
1365
1366         /* Force unscrambling for this read */
1367         unscramble_old = d -> unscrambling;
1368         disc_set_unscrambling (d, true);
1369         
1370         if (disc_read_sector (d, 0, &buf, NULL)) {
1371                 /* System ID */
1372                 d -> system_id = buf[0];
1373
1374                 /*
1375                  * The legacy capacity probe can misclassify GameCube media on some
1376                  * HLDS drives because an out-of-range dummy read does not always
1377                  * return the expected ILLEGAL REQUEST / LBA OUT OF RANGE sense data.
1378                  * Once sector 0 has been unscrambled, its platform magic is the
1379                  * authoritative discriminator.
1380                  *
1381                  * Explicit -T selections remain authoritative and are not changed.
1382                  * Explicit -S geometry also remains authoritative.
1383                  */
1384                 header_type = nintendo_disc_header_detect (buf, SECTOR_SIZE);
1385                 previous_type = d -> type;
1386
1387                 if (allow_type_correction &&
1388                     header_type == NINTENDO_DISC_HEADER_GAMECUBE) {
1389                         d -> type = DISC_TYPE_GAMECUBE;
1390                         if (allow_size_correction)
1391                                 d -> sectors_no = DISC_GAMECUBE_SECTORS_NO;
1392                 } else if (allow_type_correction &&
1393                            header_type == NINTENDO_DISC_HEADER_WII &&
1394                            d -> type != DISC_TYPE_WII &&
1395                            d -> type != DISC_TYPE_WII_DL) {
1396                         d -> type = DISC_TYPE_WII;
1397                         if (allow_size_correction)
1398                                 d -> sectors_no = DISC_WII_SECTORS_NO_SL;
1399                 }
1400
1401                 if (allow_type_correction &&
1402                     header_type != NINTENDO_DISC_HEADER_UNKNOWN &&
1403                     previous_type != d -> type) {
1404                         warning ("Disc type corrected from %s to %s using sector-0 platform magic",
1405                                  disc_detected_type_name (previous_type),
1406                                  disc_detected_type_name (d -> type));
1407                 }
1408
1409                 /* Game ID */
1410                 strncpy (d -> game_id, (char *) buf + 1, 2);
1411                 d -> game_id[2] = '\0';
1412
1413                 /* Region */
1414                 switch (buf[3]) {
1415                         case 'P':
1416                                 d -> region = DISC_REGION_PAL;
1417                                 break;
1418                         case 'E':
1419                                 d -> region = DISC_REGION_NTSC;
1420                                 break;
1421                         case 'J':
1422                                 d -> region = DISC_REGION_JAPAN;
1423                                 break;
1424                         case 'U':
1425                                 d -> region = DISC_REGION_AUSTRALIA;
1426                                 break;
1427                         case 'F':
1428                                 d -> region = DISC_REGION_FRANCE;
1429                                 break;
1430                         case 'D':
1431                                 d -> region = DISC_REGION_GERMANY;
1432                                 break;
1433                         case 'I':
1434                                 d -> region = DISC_REGION_ITALY;
1435                                 break;
1436                         case 'S':
1437                                 d -> region = DISC_REGION_SPAIN;
1438                                 break;
1439                         case 'X':
1440                                 d -> region = DISC_REGION_PAL_X;
1441                                 break;
1442                         case 'Y':
1443                                 d -> region = DISC_REGION_PAL_Y;
1444                                 break;
1445                         default:
1446                                 d -> region = DISC_REGION_UNKNOWN;
1447                                 break;
1448                 }
1449
1450                 /* Maker code */
1451                 strncpy (d -> maker, (char *) buf + 4, 2);
1452                 d -> maker[2] = '\0';
1453
1454                 /* Version */
1455                 d -> version = buf[7];
1456                 snprintf (tmp, sizeof (tmp), "1.%02u", d -> version);
1457                 my_strdup (d -> version_string, tmp);
1458
1459                 /* Game title */
1460                 memcpy (tmp, buf + 0x0020, sizeof (tmp) - 1);
1461                 tmp[sizeof (tmp) - 1] = '\0';
1462                 strtrimr (tmp);
1463                 my_strdup (d -> title, tmp);
1464
1465                 out = true;
1466         } else {
1467                 error ("Cannot analyze disc");
1468                 out = false;
1469         }
1470
1471         disc_set_unscrambling (d, unscramble_old);
1472
1473         return (out);
1474 }
1475
1476
1477 static char disc_type_strings[5][15] = {
1478         "GameCube",
1479         "Wii",
1480         "Wii_DL",
1481         "DVD",
1482         "Xbox"
1483 };
1484
1485 /**
1486  * Retrieves the disc type.
1487  * @param d The disc structure.
1488  * @param dt This will be set to the disc type.
1489  * @param dt_s This will point to a string describing the disc type.
1490  * @return A string describing the disc type.
1491  */
1492 char *disc_get_type (disc *d, disc_type *dt, char **dt_s) {
1493         if (dt)
1494                 *dt = d -> type;
1495
1496         if (dt_s) {
1497                 if (d -> type <= DISC_TYPE_XBOX)
1498                         *dt_s = disc_type_strings[d -> type];
1499                 else
1500                         *dt_s = disc_type_strings[DISC_TYPE_DVD];
1501         }
1502
1503         return (*dt_s);
1504 }
1505
1506
1507 /**
1508  * Retrieves the disc game ID.
1509  * @param d The disc structure.
1510  * @param gid_s This will point to a string containing the game ID.
1511  * @return A string containing the game ID.
1512  */
1513 char *disc_get_gameid (disc *d, char **gid_s) {
1514         if (gid_s)
1515                 *gid_s = d -> game_id;
1516
1517         return (*gid_s);
1518 }
1519
1520
1521 static char disc_region_strings[11][15] = {
1522         "Europe/PAL",
1523         "USA/NTSC",
1524         "Japan/NTSC",
1525         "Australia/PAL",
1526         "France/PAL",
1527         "Germany/PAL",
1528         "Italy/PAL",
1529         "Spain/PAL",
1530         "Europe(X)/PAL",
1531         "Europe(Y)/PAL",
1532         "Unknown"
1533 };
1534
1535 /**
1536  * Retrieves the disc region.
1537  * @param d The disc structure.
1538  * @param dr This will be set to the disc region.
1539  * @param dr_s This will point to a string describing the disc region.
1540  * @return A string describing the disc region.
1541  */
1542 char *disc_get_region (disc *d, disc_region *dr, char **dr_s) {
1543         if (dr)
1544                 *dr = d -> region;
1545         
1546         if (dr_s) {
1547                 if (d -> region < DISC_REGION_UNKNOWN)
1548                         *dr_s = disc_region_strings[d -> region];
1549                 else
1550                         *dr_s = disc_region_strings[DISC_REGION_UNKNOWN];
1551         }
1552
1553         return (*dr_s);
1554 }
1555
1556
1557 /* The following list has been derived from http://wiitdb.com/Company/HomePage */
1558 static struct {
1559         char *code;
1560         char *name;
1561 } makers[] = {
1562         {"0A", "Jaleco"},
1563         {"0B", "Coconuts Japan"},
1564         {"0C", "Coconuts Japan / G.X.Media"},
1565         {"0D", "Micronet"},
1566         {"0E", "Technos"},
1567         {"0F", "Mebio Software"},
1568         {"0G", "Shouei System"},
1569         {"0H", "Starfish"},
1570         {"0J", "Mitsui Fudosan / Dentsu"},
1571         {"0L", "Warashi Inc."},
1572         {"0N", "Nowpro"},
1573         {"0P", "Game Village"},
1574         {"0Q", "IE Institute"},
1575         {"01", "Nintendo"},
1576         {"02", "Rocket Games / Ajinomoto"},
1577         {"03", "Imagineer-Zoom"},
1578         {"04", "Gray Matter"},
1579         {"05", "Zamuse"},
1580         {"06", "Falcom"},
1581         {"07", "Enix"},
1582         {"08", "Capcom"},
1583         {"09", "Hot B Co."},
1584         {"1A", "Yanoman"},
1585         {"1C", "Tecmo Products"},
1586         {"1D", "Japan Glary Business"},
1587         {"1E", "Forum / OpenSystem"},
1588         {"1F", "Virgin Games (Japan)"},
1589         {"1G", "SMDE"},
1590         {"1J", "Daikokudenki"},
1591         {"1P", "Creatures Inc."},
1592         {"1Q", "TDK Deep Impresion"},
1593         {"2A", "Culture Brain"},
1594         {"2C", "Palsoft"},
1595         {"2D", "Visit Co.,Ltd."},
1596         {"2E", "Intec"},
1597         {"2F", "System Sacom"},
1598         {"2G", "Poppo"},
1599         {"2H", "Ubisoft Japan"},
1600         {"2J", "Media Works"},
1601         {"2K", "NEC InterChannel"},
1602         {"2L", "Tam"},
1603         {"2M", "Jordan"},
1604         {"2N", "Smilesoft / Rocket"},
1605         {"2Q", "Mediakite"},
1606         {"3B", "Arcade Zone Ltd"},
1607         {"3C", "Entertainment International / Empire Software"},
1608         {"3D", "Loriciel"},
1609         {"3E", "Gremlin Graphics"},
1610         {"3F", "K.Amusement Leasing Co."},
1611         {"4B", "Raya Systems"},
1612         {"4C", "Renovation Products"},
1613         {"4D", "Malibu Games"},
1614         {"4F", "Eidos"},
1615         {"4G", "Playmates Interactive"},
1616         {"4J", "Fox Interactive"},
1617         {"4K", "Time Warner Interactive"},
1618         {"4Q", "Disney Interactive"},
1619         {"4S", "Black Pearl"},
1620         {"4U", "Advanced Productions"},
1621         {"4X", "GT Interactive"},
1622         {"4Y", "RARE"},
1623         {"4Z", "Crave Entertainment"},
1624         {"5A", "Mindscape / Red Orb Entertainment"},
1625         {"5B", "Romstar"},
1626         {"5C", "Taxan"},
1627         {"5D", "Midway / Tradewest"},
1628         {"5F", "American Softworks"},
1629         {"5G", "Majesco Sales Inc"},
1630         {"5H", "3DO"},
1631         {"5K", "Hasbro"},
1632         {"5L", "NewKidCo"},
1633         {"5M", "Telegames"},
1634         {"5N", "Metro3D"},
1635         {"5P", "Vatical Entertainment"},
1636         {"5Q", "LEGO Media"},
1637         {"5S", "Xicat Interactive"},
1638         {"5T", "Cryo Interactive"},
1639         {"5W", "Red Storm Entertainment"},
1640         {"5X", "Microids"},
1641         {"5Z", "Data Design / Conspiracy / Swing"},
1642         {"6B", "Laser Beam"},
1643         {"6E", "Elite Systems"},
1644         {"6F", "Electro Brain"},
1645         {"6G", "The Learning Company"},
1646         {"6H", "BBC"},
1647         {"6J", "Software 2000"},
1648         {"6K", "UFO Interactive Games"},
1649         {"6L", "BAM! Entertainment"},
1650         {"6M", "Studio 3"},
1651         {"6Q", "Classified Games"},
1652         {"6S", "TDK Mediactive"},
1653         {"6U", "DreamCatcher"},
1654         {"6V", "JoWood Produtions"},
1655         {"6W", "Sega"},
1656         {"6X", "Wannado Edition"},
1657         {"6Y", "LSP (Light & Shadow Prod.)"},
1658         {"6Z", "ITE Media"},
1659         {"7A", "Triffix Entertainment"},
1660         {"7C", "Microprose Software"},
1661         {"7D", "Sierra / Universal Interactive"},
1662         {"7F", "Kemco"},
1663         {"7G", "Rage Software"},
1664         {"7H", "Encore"},
1665         {"7J", "Zoo"},
1666         {"7K", "Kiddinx"},
1667         {"7L", "Simon & Schuster Interactive"},
1668         {"7M", "Asmik Ace Entertainment Inc."},
1669         {"7N", "Empire Interactive"},
1670         {"7Q", "Jester Interactive"},
1671         {"7S", "Rockstar Games"},
1672         {"7T", "Scholastic"},
1673         {"7U", "Ignition Entertainment"},
1674         {"7V", "Summitsoft"},
1675         {"7W", "Stadlbauer"},
1676         {"8B", "BulletProof Software (BPS)"},
1677         {"8C", "Vic Tokai Inc."},
1678         {"8E", "Character Soft"},
1679         {"8F", "I'Max"},
1680         {"8G", "Saurus"},
1681         {"8J", "General Entertainment"},
1682         {"8N", "Success"},
1683         {"8P", "Sega Japan"},
1684         {"9A", "Nichibutsu / Nihon Bussan"},
1685         {"9B", "Tecmo"},
1686         {"9C", "Imagineer"},
1687         {"9F", "Nova"},
1688         {"9G", "Take2 / Den'Z / Global Star"},
1689         {"9H", "Bottom Up"},
1690         {"9J", "TGL (Technical Group Laboratory)"},
1691         {"9L", "Hasbro Japan"},
1692         {"9N", "Marvelous Entertainment"},
1693         {"9P", "Keynet Inc."},
1694         {"9Q", "Hands-On Entertainment"},
1695         {"12", "Infocom"},
1696         {"13", "Electronic Arts Japan"},
1697         {"15", "Cobra Team"},
1698         {"16", "Human / Field"},
1699         {"17", "KOEI"},
1700         {"18", "Hudson Soft"},
1701         {"19", "S.C.P."},
1702         {"20", "Destination Software / Zoo Games / KSS"},
1703         {"21", "Sunsoft / Tokai Engineering"},
1704         {"22", "POW (Planning Office Wada) / VR1 Japan"},
1705         {"23", "Micro World"},
1706         {"25", "San-X"},
1707         {"26", "Enix"},
1708         {"27", "Loriciel / Electro Brain"},
1709         {"28", "Kemco Japan"},
1710         {"29", "Seta"},
1711         {"30", "Viacom"},
1712         {"31", "Carrozzeria"},
1713         {"32", "Dynamic"},
1714         {"34", "Magifact"},
1715         {"35", "Hect"},
1716         {"36", "Codemasters"},
1717         {"37", "Taito / GAGA Communications"},
1718         {"38", "Laguna"},
1719         {"39", "Telstar / Event / Taito"},
1720         {"40", "Seika Corp."},
1721         {"41", "Ubi Soft Entertainment"},
1722         {"42", "Sunsoft US"},
1723         {"44", "Life Fitness"},
1724         {"46", "System 3"},
1725         {"47", "Spectrum Holobyte"},
1726         {"49", "IREM"},
1727         {"50", "Absolute Entertainment"},
1728         {"51", "Acclaim"},
1729         {"52", "Activision"},
1730         {"53", "American Sammy"},
1731         {"54", "Take 2 Interactive / GameTek"},
1732         {"55", "Hi Tech"},
1733         {"56", "LJN LTD."},
1734         {"58", "Mattel"},
1735         {"60", "Titus"},
1736         {"61", "Virgin Interactive"},
1737         {"62", "Maxis"},
1738         {"64", "LucasArts Entertainment"},
1739         {"67", "Ocean"},
1740         {"68", "Bethesda Softworks"},
1741         {"69", "Electronic Arts"},
1742         {"70", "Atari (Infogrames)"},
1743         {"71", "Interplay"},
1744         {"72", "JVC (US)"},
1745         {"73", "Parker Brothers"},
1746         {"75", "Sales Curve (Storm / SCI)"},
1747         {"78", "THQ"},
1748         {"79", "Accolade"},
1749         {"80", "Misawa"},
1750         {"81", "Teichiku"},
1751         {"82", "Namco Ltd."},
1752         {"83", "LOZC"},
1753         {"84", "KOEI"},
1754         {"86", "Tokuma Shoten Intermedia"},
1755         {"87", "Tsukuda Original"},
1756         {"88", "DATAM-Polystar"},
1757         {"90", "Takara Amusement"},
1758         {"91", "Chun Soft"},
1759         {"92", "Video System / Mc O' River"},
1760         {"93", "BEC"},
1761         {"95", "Varie"},
1762         {"96", "Yonezawa / S'pal"},
1763         {"97", "Kaneko"},
1764         {"99", "Marvelous Entertainment"},
1765         {"A0", "Telenet"},
1766         {"A1", "Hori"},
1767         {"A4", "Konami"},
1768         {"A5", "K.Amusement Leasing Co."},
1769         {"A6", "Kawada"},
1770         {"A7", "Takara"},
1771         {"A9", "Technos Japan Corp."},
1772         {"AA", "JVC / Victor"},
1773         {"AC", "Toei Animation"},
1774         {"AD", "Toho"},
1775         {"AF", "Namco"},
1776         {"AG", "Media Rings Corporation"},
1777         {"AH", "J-Wing"},
1778         {"AJ", "Pioneer LDC"},
1779         {"AK", "KID"},
1780         {"AL", "Mediafactory"},
1781         {"AP", "Infogrames / Hudson"},
1782         {"AQ", "Kiratto. Ludic Inc"},
1783         {"B0", "Acclaim Japan"},
1784         {"B1", "ASCII"},
1785         {"B2", "Bandai"},
1786         {"B4", "Enix"},
1787         {"B6", "HAL Laboratory"},
1788         {"B7", "SNK"},
1789         {"B9", "Pony Canyon"},
1790         {"BA", "Culture Brain"},
1791         {"BB", "Sunsoft"},
1792         {"BC", "Toshiba EMI"},
1793         {"BD", "Sony Imagesoft"},
1794         {"BF", "Sammy"},
1795         {"BG", "Magical"},
1796         {"BH", "Visco"},
1797         {"BJ", "Compile"},
1798         {"BL", "MTO Inc."},
1799         {"BN", "Sunrise Interactive"},
1800         {"BP", "Global A Entertainment"},
1801         {"BQ", "Fuuki"},
1802         {"C0", "Taito"},
1803         {"C2", "Kemco"},
1804         {"C3", "Square"},
1805         {"C4", "Tokuma Shoten"},
1806         {"C5", "Data East"},
1807         {"C6", "Tonkin House / Tokyo Shoseki"},
1808         {"C8", "Koei"},
1809         {"CA", "Konami / Ultra / Palcom"},
1810         {"CB", "NTVIC / VAP"},
1811         {"CC", "Use Co.,Ltd."},
1812         {"CD", "Meldac"},
1813         {"CE", "Pony Canyon / FCI"},
1814         {"CF", "Angel / Sotsu Agency / Sunrise"},
1815         {"CG", "Yumedia / Aroma Co., Ltd"},
1816         {"CJ", "Boss"},
1817         {"CK", "Axela / Crea-Tech"},
1818         {"CL", "Sekaibunka-Sha / Sumire Kobo / Marigul Management Inc."},
1819         {"CM", "Konami Computer Entertainment Osaka"},
1820         {"CN", "NEC Interchannel"},
1821         {"CP", "Enterbrain"},
1822         {"CQ", "From Software"},
1823         {"D0", "Taito / Disco"},
1824         {"D1", "Sofel"},
1825         {"D2", "Quest / Bothtec"},
1826         {"D3", "Sigma"},
1827         {"D4", "Ask Kodansha"},
1828         {"D6", "Naxat"},
1829         {"D7", "Copya System"},
1830         {"D8", "Capcom Co., Ltd."},
1831         {"D9", "Banpresto"},
1832         {"DA", "Tomy"},
1833         {"DB", "LJN Japan"},
1834         {"DD", "NCS"},
1835         {"DE", "Human Entertainment"},
1836         {"DF", "Altron"},
1837         {"DG", "Jaleco"},
1838         {"DH", "Gaps Inc."},
1839         {"DN", "Elf"},
1840         {"DQ", "Compile Heart"},
1841         {"E0", "Jaleco"},
1842         {"E2", "Yutaka"},
1843         {"E3", "Varie"},
1844         {"E4", "T&ESoft"},
1845         {"E5", "Epoch"},
1846         {"E7", "Athena"},
1847         {"E8", "Asmik"},
1848         {"E9", "Natsume"},
1849         {"EA", "King Records"},
1850         {"EB", "Atlus"},
1851         {"EC", "Epic / Sony Records"},
1852         {"EE", "IGS (Information Global Service)"},
1853         {"EG", "Chatnoir"},
1854         {"EH", "Right Stuff"},
1855         {"EL", "Spike"},
1856         {"EM", "Konami Computer Entertainment Tokyo"},
1857         {"EN", "Alphadream Corporation"},
1858         {"EP", "Sting"},
1859         {"ES", "Star-Fish"},
1860         {"F0", "A Wave"},
1861         {"F1", "Motown Software"},
1862         {"F2", "Left Field Entertainment"},
1863         {"F3", "Extreme Ent. Grp."},
1864         {"F4", "TecMagik"},
1865         {"F9", "Cybersoft"},
1866         {"FB", "Psygnosis"},
1867         {"FE", "Davidson / Western Tech."},
1868         {"FK", "The Game Factory"},
1869         {"FL", "Hip Games"},
1870         {"FM", "Aspyr"},
1871         {"FP", "Mastiff"},
1872         {"FQ", "iQue"},
1873         {"FR", "Digital Tainment Pool"},
1874         {"FS", "XS Games / Jack Of All Games"},
1875         {"FT", "Daiwon"},
1876         {"G0", "Alpha Unit"},
1877         {"G1", "PCCW Japan"},
1878         {"G2", "Yuke's Media Creations"},
1879         {"G4", "KiKi Co Ltd"},
1880         {"G5", "Open Sesame Inc"},
1881         {"G6", "Sims"},
1882         {"G7", "Broccoli"},
1883         {"G8", "Avex"},
1884         {"G9", "D3 Publisher"},
1885         {"GB", "Konami Computer Entertainment Japan"},
1886         {"GD", "Square-Enix"},
1887         {"GE", "KSG"},
1888         {"GF", "Micott & Basara Inc."},
1889         {"GH", "Orbital Media"},
1890         {"GJ", "Detn8 Games"},
1891         {"GL", "Gameloft / Ubi Soft"},
1892         {"GM", "Gamecock Media Group"},
1893         {"GN", "Oxygen Games"},
1894         {"GT", "505 Games"},
1895         {"GY", "The Game Factory"},
1896         {"H1", "Treasure"},
1897         {"H2", "Aruze"},
1898         {"H3", "Ertain"},
1899         {"H4", "SNK Playmore"},
1900         {"HJ", "Genius Products"},
1901         {"HY", "Reef Entertainment"},
1902         {"HZ", "Nordcurrent"},
1903         {"IH", "Yojigen"},
1904         {"J9", "AQ Interactive"},
1905         {"JF", "Arc System Works"},
1906         {"JW", "Atari"},
1907         {"K6", "Nihon System"},
1908         {"KB", "NIS America"},
1909         {"KM", "Deep Silver"},
1910         {"LH", "Trend Verlag / East Entertainment"},
1911         {"LT", "Legacy Interactive"},
1912         {"MJ", "Mumbo Jumbo"},
1913         {"MR", "Mindscape"},
1914         {"MS", "Milestone / UFO Interactive"},
1915         {"MT", "Blast !"},
1916         {"N9", "Terabox"},
1917         {"NK", "Neko Entertainment / Diffusion / Naps team"},
1918         {"NP", "Nobilis"},
1919         {"NR", "Data Design / Destineer Studios"},
1920         {"PL", "Playlogic"},
1921         {"RM", "Rondomedia"},
1922         {"RS", "Warner Bros. Interactive Entertainment Inc."},
1923         {"RT", "RTL Games"},
1924         {"RW", "RealNetworks"},
1925         {"S5", "Southpeak Interactive"},
1926         {"SP", "Blade Interactive Studios"},
1927         {"SV", "SevenGames"},
1928         {"TK", "Tasuke / Works"},
1929         {"UG", "Metro 3D / Data Design"},
1930         {"VN", "Valcon Games"},
1931         {"VP", "Virgin Play"},
1932         {"WR", "Warner Bros. Interactive Entertainment Inc."},
1933         {"XJ", "Xseed Games"},
1934         {"XS", "Aksys Games"},
1935         {NULL, NULL}
1936 };
1937
1938 /**
1939  * Retrieves the disk maker.
1940  * @param d The disc structure.
1941  * @param m This will point to a string containing the disc maker ID.
1942  * @param m_s This will point to a string describing the disc maker.
1943  * @return A string describing the disc maker.
1944  */
1945 char *disc_get_maker (disc *d, char **m, char **m_s) {
1946         u_int32_t i;
1947         
1948         if (m)
1949                 *m = d -> maker;
1950
1951         if (m_s) {
1952                 for (i = 0; makers[i].code; i++) {
1953                         if (strcasecmp (d -> maker, makers[i].code) == 0) {
1954                                 *m_s = makers[i].name;
1955                                 break;
1956                         }
1957                 }
1958                 if (!makers[i].code) {
1959                         *m_s = "Unknown";
1960                 }
1961         }
1962
1963         return (*m_s);
1964 }
1965
1966
1967 /**
1968  * Retrieves the disc version.
1969  * @param d The disc structure.
1970  * @param v This will contain the version ID.
1971  * @param v_s This will point to a string describing the disc version.
1972  * @return A string describing the disc version.
1973  */
1974 char *disc_get_version (disc *d, u_int8_t *v, char **v_s) {
1975         if (v)
1976                 *v = d -> version;
1977         
1978         if (v_s)
1979                 *v_s = d -> version_string;
1980
1981         return (*v_s);
1982 }
1983
1984
1985 /**
1986  * Retrieves the disc game title.
1987  * @param d The disc structure.
1988  * @param t_s This will point to a string describing the disc title.
1989  * @return A string describing the disc title.
1990  */
1991 char *disc_get_title (disc *d, char **t_s) {
1992         if (t_s)
1993                 *t_s = d -> title;
1994
1995         return (*t_s);
1996 }
1997
1998
1999 /**
2000  * Retrieves if the disc has an update.
2001  * @param d The disc structure.
2002  * @return True if the disc contains an update, false otherwise.
2003  */
2004 bool disc_get_update (disc *d) {
2005         return (d -> has_update);
2006 }
2007
2008
2009 /**
2010  * Retrieves the number of sectors of the disc.
2011  * @param d The disc structure.
2012  * @return The number of sectors.
2013  */
2014 u_int32_t disc_get_sectors_no (disc *d) {
2015         return (d -> sectors_no);
2016 }
2017
2018 u_int32_t disc_get_layerbreak (disc *d) {
2019         return (d -> layerbreak);
2020 }
2021
2022 u_int32_t disc_get_command (disc *d) {
2023         return (d -> command);
2024 }
2025
2026 u_int32_t disc_get_method (disc *d) {
2027         return (d -> read_method);
2028 }
2029
2030 u_int32_t disc_get_def_method (disc *d) {
2031         return dvd_get_def_method(d -> dvd);//(d -> def_read_method);
2032 }
2033
2034 u_int32_t disc_get_sec_disc (disc *d) {
2035         return (d -> sec_disc);
2036 }
2037
2038 u_int32_t disc_get_sec_mem (disc *d) {
2039         return (d -> sec_mem);
2040 }
2041
2042 /* wiidevel@stacktic.org */
2043 static bool disc_check_update (disc *d) {
2044         u_int8_t *buf;
2045         u_int32_t x;
2046         bool unscramble_old;
2047
2048         if (d -> type == DISC_TYPE_WII || d -> type == DISC_TYPE_WII_DL) {
2049                 /* Force unscrambling for this read */
2050                 unscramble_old = d -> unscrambling;
2051                 disc_set_unscrambling (d, true);
2052
2053                 /* We need to read offset 0x50004 of the disc. Sector 160 has offset 0x50000 */
2054                 if (disc_read_sector (d, 160, &buf, NULL)) {
2055                         x = my_ntohl (*(u_int32_t *) (buf + 4));
2056                         if (x == 0xA5BED6AE)
2057                                 d -> has_update = false;
2058                         else
2059                                 d -> has_update = true;
2060                 } else {
2061                         error ("disc_check_update() failed");
2062                 }
2063
2064                 disc_set_unscrambling (d, unscramble_old);
2065         } else {
2066                 /* GameCube discs never have an update, as actually the GC firmware cannot be upgrade */
2067                 d -> has_update = false;
2068         }
2069
2070         return (d -> has_update);
2071 }
2072
2073
2074 /**
2075  * Sets the disc read method.
2076  * @param d The disc structure.
2077  * @param method The requested method.
2078  * @return True if the method was set correctly, false otherwise (i. e.: method too small/big).
2079  */
2080 bool disc_set_read_method (disc *d, int method) {
2081         bool out;
2082         u_int32_t deviation;
2083         u_int32_t counter;
2084         u_int32_t cnt1;
2085
2086         d -> command = dvd_get_command(d -> dvd);
2087 //      d -> def_read_method = dvd_get_def_method(d -> dvd);
2088         d -> read_method = method;
2089
2090         out = true;
2091         switch (method) {
2092                 case 0:
2093                         d -> read_sector = disc_read_sector_0;
2094                         break;
2095                 case 1:
2096                         d -> read_sector = disc_read_sector_1;
2097                         break;
2098                 case 2:
2099                         d -> read_sector = disc_read_sector_2;
2100                         break;
2101                 case 3:
2102                         d -> read_sector = disc_read_sector_3;
2103                         break;
2104                 case 4:
2105                         d -> read_sector = disc_read_sector_4;
2106                         break;
2107                 case 5:
2108                         d -> read_sector = disc_read_sector_5;
2109                         break;
2110                 case 6:
2111                         d -> read_sector = disc_read_sector_6;
2112                         break;
2113                 case 7:
2114                         d -> read_sector = disc_read_sector_7;
2115                         break;
2116                 case 8:
2117                         d -> read_sector = disc_read_sector_8;
2118                         break;
2119                 case 9:
2120                         d -> read_sector = disc_read_sector_9;
2121                         break;
2122                 case 10:
2123                         d -> read_sector = disc_read_sector_xbox;
2124                         break;
2125                 default:
2126                         switch (dvd_get_def_method(d -> dvd)) {
2127                         case 0: 
2128                                 d -> read_method = 0;
2129                                 d -> read_sector = disc_read_sector_0;
2130                                 break;
2131                         case 1: 
2132                                 d -> read_method = 1;
2133                                 d -> read_sector = disc_read_sector_1;
2134                                 break;
2135                         case 2: 
2136                                 d -> read_method = 2;
2137                                 d -> read_sector = disc_read_sector_2;
2138                                 break;
2139                         case 3: 
2140                                 d -> read_method = 3;
2141                                 d -> read_sector = disc_read_sector_3;
2142                                 break;
2143                         case 4: 
2144                                 d -> read_method = 4;
2145                                 d -> read_sector = disc_read_sector_4;
2146                                 break;
2147                         case 5: 
2148                                 d -> read_method = 5;
2149                                 d -> read_sector = disc_read_sector_5;
2150                                 break;
2151                         case 6: 
2152                                 d -> read_method = 6;
2153                                 d -> read_sector = disc_read_sector_6;
2154                                 break;
2155                         case 7: 
2156                                 d -> read_method = 7;
2157                                 d -> read_sector = disc_read_sector_7;
2158                                 break;
2159                         case 8: 
2160                                 d -> read_method = 8;
2161                                 d -> read_sector = disc_read_sector_8;
2162                                 break;
2163                         case 9: 
2164                                 d -> read_method = 9;
2165                                 d -> read_sector = disc_read_sector_9;
2166                                 break;
2167                         case 10: 
2168                                 d -> read_method = 10;
2169                                 d -> read_sector = disc_read_sector_xbox;
2170                                 break;
2171                         default:
2172                                 d -> read_method = DEFAULT_READ_METHOD;
2173                                 d -> read_sector = DEFAULT_READ_SECTOR;
2174                                 break;
2175                         }
2176         }
2177
2178         if (d->sec_disc==-1) {
2179                 if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) 
2180                         d->sec_disc=27;
2181                 else 
2182                         d->sec_disc=16;
2183         }
2184         if (d->sec_mem==-1) {
2185                 if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) 
2186                         d->sec_mem=27;
2187                 else
2188                         d->sec_mem=16;
2189         }
2190
2191         deviation = d->sec_mem % SECTORS_PER_BLOCK;
2192         counter=0;
2193
2194         if (deviation>3) {
2195                 cnt1=deviation;
2196                 while (1==1) {
2197                         cnt1+=deviation;
2198                         counter++;
2199                         if (cnt1%SECTORS_PER_BLOCK<=1) break;
2200                 }
2201         }
2202         d -> max_cnt = counter;
2203         d -> max_blk = ((d->sec_mem*(d->max_cnt+1))-((d->sec_mem*(d->max_cnt+1)) % SECTORS_PER_BLOCK)) / 16;
2204
2205         if (out) {
2206                 debug ("Read method set to %d", d -> read_method);
2207         } else {
2208                 error ("Cannot set read method\n");
2209         }
2210
2211         return (out);
2212 }
2213
2214
2215 /**
2216  * Controls the unscrambling process.
2217  * @param d The disc structure.
2218  * @param unscramble If true, every raw sectors read will be unscrambled to check if they are error-free, otherwise read data will be returned as-is.
2219  */
2220 void disc_set_unscrambling (disc *d, bool unscramble) {
2221         d -> unscrambling = unscramble;
2222         debug ("Sectors unscrambling %s", unscramble ? "enabled" : "disabled");
2223
2224         return;
2225 }
2226
2227
2228
2229 static unsigned int hlds_e7_sector_header_value (const u_int8_t *hdr) {
2230         if (!hdr)
2231                 return 0xFFFFFFFFU;
2232         return ((unsigned int) hdr[1] << 16) | ((unsigned int) hdr[2] << 8) | (unsigned int) hdr[3];
2233 }
2234
2235 static int hlds_e7_score_sector_header (const u_int8_t *hdr, u_int32_t sector_no) {
2236         unsigned int got;
2237         unsigned int expected;
2238         int score;
2239
2240         if (!hdr)
2241                 return 0;
2242         got = hlds_e7_sector_header_value (hdr);
2243         expected = 0x30000U + sector_no;
2244         score = 0;
2245         if ((hdr[0] & 1) == 0)
2246                 score += 5;
2247         if (got == expected)
2248                 score += 100;
2249         if ((hdr[0] | hdr[1] | hdr[2] | hdr[3]) == 0x00)
2250                 score -= 10;
2251         if ((hdr[0] & hdr[1] & hdr[2] & hdr[3]) == 0xFF)
2252                 score -= 10;
2253         return score;
2254 }
2255
2256 static void hlds_e7_json_escape (FILE *f, const char *s) {
2257         const unsigned char *p;
2258         if (!f)
2259                 return;
2260         if (!s)
2261                 s = "";
2262         for (p = (const unsigned char *) s; *p; p++) {
2263                 if (*p == '"' || *p == '\\')
2264                         fprintf (f, "\\%c", *p);
2265                 else if (*p == '\n')
2266                         fprintf (f, "\\n");
2267                 else if (*p == '\r')
2268                         fprintf (f, "\\r");
2269                 else if (*p == '\t')
2270                         fprintf (f, "\\t");
2271                 else if (*p < 32)
2272                         fprintf (f, "\\u%04x", (unsigned int) *p);
2273                 else
2274                         fputc (*p, f);
2275         }
2276 }
2277
2278 static void hlds_e7_json_bytes (FILE *f, const u_int8_t *b, size_t n) {
2279         size_t i;
2280         fprintf (f, "\"");
2281         if (b) {
2282                 for (i = 0; i < n; i++) {
2283                         if (i)
2284                                 fprintf (f, " ");
2285                         fprintf (f, "%02x", (unsigned int) b[i]);
2286                 }
2287         }
2288         fprintf (f, "\"");
2289 }
2290
2291 static u_int32_t hlds_e7_fnv1a32 (const u_int8_t *buf, size_t len) {
2292         size_t i;
2293         u_int32_t h;
2294         h = 2166136261U;
2295         if (!buf)
2296                 return 0;
2297         for (i = 0; i < len; i++) {
2298                 h ^= (u_int32_t) buf[i];
2299                 h *= 16777619U;
2300         }
2301         return h;
2302 }
2303
2304 static size_t hlds_e7_count_byte_diffs (const u_int8_t *a, const u_int8_t *b, size_t len) {
2305         size_t i;
2306         size_t out;
2307         out = 0;
2308         if (!a || !b)
2309                 return 0;
2310         for (i = 0; i < len; i++) {
2311                 if (a[i] != b[i])
2312                         out++;
2313         }
2314         return out;
2315 }
2316
2317 static bool hlds_e7_probe_bytes_useful (const u_int8_t *p, size_t len) {
2318         size_t i;
2319         unsigned int orv;
2320         unsigned int andv;
2321         if (!p || len == 0)
2322                 return false;
2323         orv = 0;
2324         andv = 0xFF;
2325         for (i = 0; i < len; i++) {
2326                 orv |= p[i];
2327                 andv &= p[i];
2328         }
2329         return !(orv == 0x00 || andv == 0xFF);
2330 }
2331
2332 static int hlds_e7_find_raw_header_match (const u_int8_t *buf, size_t len, u_int32_t block_sector, size_t *match_offset, u_int32_t *match_sector) {
2333         size_t off;
2334         int k;
2335         u_int32_t expected;
2336         int best_score;
2337         int score;
2338         best_score = 0;
2339         if (match_offset)
2340                 *match_offset = (size_t) -1;
2341         if (match_sector)
2342                 *match_sector = 0xFFFFFFFFU;
2343         if (!buf || len < 4)
2344                 return 0;
2345         for (off = 0; off + 4 <= len; off++) {
2346                 for (k = 0; k < SECTORS_PER_BLOCK; k++) {
2347                         expected = 0x30000U + block_sector + (u_int32_t) k;
2348                         if (((u_int32_t) buf[off + 1] << 16 | (u_int32_t) buf[off + 2] << 8 | (u_int32_t) buf[off + 3]) == expected) {
2349                                 score = 90;
2350                                 if ((buf[off] & 1) == 0)
2351                                         score += 10;
2352                                 if ((off % RAW_SECTOR_SIZE) == (size_t) (k * RAW_SECTOR_SIZE))
2353                                         score += 25;
2354                                 else if ((off % RAW_SECTOR_SIZE) == 0)
2355                                         score += 10;
2356                                 if (score > best_score) {
2357                                         best_score = score;
2358                                         if (match_offset)
2359                                                 *match_offset = off;
2360                                         if (match_sector)
2361                                                 *match_sector = block_sector + (u_int32_t) k;
2362                                 }
2363                         }
2364                 }
2365         }
2366         return best_score;
2367 }
2368
2369 static int hlds_e7_find_user_data_match (const u_int8_t *dumpbuf, size_t dump_len, const u_int8_t *readbuf, size_t read_len, size_t *match_offset, u_int32_t *match_sector, size_t *read_offset) {
2370         static const size_t probe_offsets[] = {0x00, 0x20, 0x80, 0x100, 0x400, 0x700};
2371         const size_t probe_len = 32;
2372         size_t k;
2373         size_t po;
2374         size_t off;
2375         const u_int8_t *needle;
2376         if (match_offset)
2377                 *match_offset = (size_t) -1;
2378         if (match_sector)
2379                 *match_sector = 0xFFFFFFFFU;
2380         if (read_offset)
2381                 *read_offset = (size_t) -1;
2382         if (!dumpbuf || !readbuf || dump_len < probe_len || read_len < SECTOR_SIZE)
2383                 return 0;
2384         for (k = 0; k < SECTORS_PER_BLOCK && ((k * SECTOR_SIZE) + SECTOR_SIZE) <= read_len; k++) {
2385                 for (po = 0; po < sizeof (probe_offsets) / sizeof (probe_offsets[0]); po++) {
2386                         if (probe_offsets[po] + probe_len > SECTOR_SIZE)
2387                                 continue;
2388                         needle = readbuf + k * SECTOR_SIZE + probe_offsets[po];
2389                         if (!hlds_e7_probe_bytes_useful (needle, probe_len))
2390                                 continue;
2391                         for (off = 0; off + probe_len <= dump_len; off++) {
2392                                 if (memcmp (dumpbuf + off, needle, probe_len) == 0) {
2393                                         if (match_offset)
2394                                                 *match_offset = off;
2395                                         if (match_sector)
2396                                                 *match_sector = (u_int32_t) k;
2397                                         if (read_offset)
2398                                                 *read_offset = probe_offsets[po];
2399                                         return 70;
2400                                 }
2401                         }
2402                 }
2403         }
2404         return 0;
2405 }
2406
2407 typedef struct {
2408         u_int32_t type;
2409         u_int32_t base;
2410         u_int32_t windows;
2411         const char *label;
2412 } hlds_e7_probe_candidate;
2413
2414 bool disc_hlds_e7_scan (disc *d, const char *json_path, const char *dump_prefix) {
2415         typedef struct {
2416                 const char *label;
2417                 u_int32_t base;
2418                 u_int32_t windows;
2419                 const char *origin;
2420         } scan_candidate;
2421         static const scan_candidate candidates[] = {
2422                 {"type4_base_5win",        0x80000000U, 5, "known Type3/Type4 family base"},
2423                 {"type4_base_1win",        0x80000000U, 1, "known Type3/Type4 base, conservative window"},
2424                 {"type4_plus_0x8000",      0x80008000U, 1, "nearby +0x8000 alias candidate"},
2425                 {"type4_plus_0x10000",     0x80010000U, 1, "nearby +0x10000 alias candidate"},
2426                 {"type4_plus_0x20000",     0x80020000U, 1, "nearby +0x20000 alias candidate"},
2427                 {"type4_plus_0x30000",     0x80030000U, 1, "nearby +0x30000 alias candidate"},
2428                 {"type4_minus_0x8000",     0x7FFF8000U, 1, "moving/boundary candidate"},
2429                 {"type4_minus_0x10000",    0x7FFF0000U, 1, "moving/boundary candidate"},
2430                 {"type4_minus_0x18000",    0x7FFE8000U, 1, "moving/boundary candidate"},
2431                 {"type1_a00000",           0x00A00000U, 1, "Type1 neighborhood"},
2432                 {"type1_a13000",           0x00A13000U, 1, "GCC-4160N Type1 known base"},
2433                 {"firmware_table_00380000",0x00380000U, 1, "observed 0x00380030 neighborhood, aligned down"},
2434                 {"firmware_table_00380030",0x00380030U, 1, "observed stale/profile-garbage value; test only"},
2435                 {"low_sram_00000000",      0x00000000U, 1, "low SRAM alias"},
2436                 {"low_sram_00008000",      0x00008000U, 1, "low SRAM alias +0x8000"},
2437                 {"low_sram_00010000",      0x00010000U, 1, "low SRAM alias +0x10000"},
2438                 {"low_sram_00020000",      0x00020000U, 1, "low SRAM alias +0x20000"},
2439                 {"firmware_sram_00001800", 0x00001800U, 1, "GDR-8081N plaintext firmware references 0x18xx SRAM/MMIO neighborhood"},
2440                 {"firmware_sram_000018a8", 0x000018A8U, 1, "GDR-8081N plaintext firmware references 0x18a8"},
2441                 {"firmware_sram_00009300", 0x00009300U, 1, "GDR-8081N plaintext firmware references 0x93xx"},
2442                 {"firmware_sram_00009b00", 0x00009B00U, 1, "GDR-8081N plaintext firmware references 0x9bxx"},
2443                 {"firmware_sram_0000a800", 0x0000A800U, 1, "GDR-8081N plaintext firmware references 0xa800"},
2444                 {"firmware_alias_40000000",0x40000000U, 1, "firmware mapping base as alias sanity check"}
2445         };
2446         static const u_int32_t probe_sectors[] = {0U, 320U};
2447         FILE *f;
2448         u_int8_t sample[16];
2449         u_int8_t readbuf[BLOCK_SIZE];
2450         u_int8_t *dumpbuf;
2451         u_int8_t *first_dump;
2452         u_int32_t old_type;
2453         u_int32_t old_base;
2454         u_int32_t old_windows;
2455         size_t i;
2456         size_t j;
2457         int k;
2458         size_t scan_len;
2459         size_t max_scan_len;
2460         size_t raw_offset;
2461         size_t exact_offsets[SECTORS_PER_BLOCK];
2462         int exact_count;
2463         bool sector_shaped;
2464         size_t command_echo_offset;
2465         size_t user_offset;
2466         size_t read_offset;
2467         size_t window_diff;
2468         u_int32_t raw_sector;
2469         u_int32_t user_sector;
2470         u_int32_t hash;
2471         int read_ret;
2472         int dump_ret;
2473         int raw_score;
2474         int user_score;
2475         int sector_score;
2476         char dump_path[512];
2477         FILE *df;
2478         int total_score;
2479         int best_score;
2480         u_int32_t best_base;
2481         u_int32_t best_windows;
2482         const char *path;
2483         bool any_readable;
2484
2485         if (!d || !d -> dvd)
2486                 return false;
2487         path = (json_path && json_path[0]) ? json_path : "hlds_e7_scan.json";
2488         f = fopen (path, "wb");
2489         if (!f) {
2490                 warning ("HLDS 0xE7 scan: could not open %s for writing", path);
2491                 return false;
2492         }
2493
2494         max_scan_len = 5U * RAW_BLOCK_SIZE;
2495         dumpbuf = (u_int8_t *) malloc (max_scan_len);
2496         first_dump = (u_int8_t *) malloc (max_scan_len);
2497         if (!dumpbuf || !first_dump) {
2498                 if (dumpbuf)
2499                         free (dumpbuf);
2500                 if (first_dump)
2501                         free (first_dump);
2502                 fclose (f);
2503                 warning ("HLDS 0xE7 scan: out of memory");
2504                 return false;
2505         }
2506
2507         old_type = dvd_get_hlds_e7_type (d -> dvd);
2508         old_base = dvd_get_hlds_e7_cache_base (d -> dvd);
2509         old_windows = dvd_get_hlds_e7_mem_blocks (d -> dvd);
2510         best_score = -999999;
2511         best_base = 0;
2512         best_windows = 0;
2513         any_readable = false;
2514
2515         fprintf (stderr, "\nHLDS 0xE7 scan mode v5: strict cache/memdump validation without seed cracking\n");
2516         fprintf (stderr, "HLDS 0xE7 scan mode v5: requiring 2064-byte raw-sector stride or READ payload echo; table/command echoes are not promoted\n");
2517         fprintf (stderr, "HLDS 0xE7 scan mode v5: writing JSON report to %s\n", path);
2518
2519         fprintf (f, "{\n");
2520         fprintf (f, "  \"scan_version\": \"v5_strict_sector_shape_command_echo\",\n");
2521         fprintf (f, "  \"drive\": \"");
2522         hlds_e7_json_escape (f, disc_get_drive_model_string (d));
2523         fprintf (f, "\",\n");
2524         fprintf (f, "  \"initial_profile\": \"");
2525         hlds_e7_json_escape (f, dvd_get_hlds_e7_profile_name (d -> dvd));
2526         fprintf (f, "\",\n");
2527         fprintf (f, "  \"initial_cache_base\": \"0x%08x\",\n", old_base);
2528         fprintf (f, "  \"initial_windows\": %u,\n", old_windows);
2529         fprintf (f, "  \"probe_sectors\": [%u, %u],\n", probe_sectors[0], probe_sectors[1]);
2530         fprintf (f, "  \"notes\": \"v5 does not promote sector-number table matches. It requires exact raw-sector IDs to be laid out with a 2064-byte stride or a direct READ payload echo. It also records HIT command-echo offsets because those indicate SRAM/command buffers, not proven sector cache.\",\n");
2531         fprintf (f, "  \"candidates\": [\n");
2532
2533         for (i = 0; i < sizeof (candidates) / sizeof (candidates[0]); i++) {
2534                 scan_len = candidates[i].windows * RAW_BLOCK_SIZE;
2535                 if (scan_len == 0 || scan_len > max_scan_len)
2536                         scan_len = RAW_BLOCK_SIZE;
2537                 total_score = 0;
2538                 window_diff = 0;
2539                 dvd_set_hlds_e7_runtime_profile (d -> dvd, 9000U + (u_int32_t) i, candidates[i].base, candidates[i].windows);
2540                 fprintf (stderr, "  [%02u/%02u] %-25s base=0x%08x windows=%u scan=%lu... ",
2541                         (unsigned int) (i + 1), (unsigned int) (sizeof (candidates) / sizeof (candidates[0])),
2542                         candidates[i].label, candidates[i].base, candidates[i].windows, (unsigned long) scan_len);
2543                 fprintf (f, "    {\n");
2544                 fprintf (f, "      \"label\": \"");
2545                 hlds_e7_json_escape (f, candidates[i].label);
2546                 fprintf (f, "\",\n");
2547                 fprintf (f, "      \"base\": \"0x%08x\",\n", candidates[i].base);
2548                 fprintf (f, "      \"windows\": %u,\n", candidates[i].windows);
2549                 fprintf (f, "      \"scan_bytes\": %lu,\n", (unsigned long) scan_len);
2550                 fprintf (f, "      \"origin\": \"");
2551                 hlds_e7_json_escape (f, candidates[i].origin);
2552                 fprintf (f, "\",\n");
2553                 fprintf (f, "      \"sector_tests\": [\n");
2554                 for (j = 0; j < sizeof (probe_sectors) / sizeof (probe_sectors[0]); j++) {
2555                         memset (sample, 0, sizeof (sample));
2556                         memset (readbuf, 0, sizeof (readbuf));
2557                         memset (dumpbuf, 0, scan_len);
2558                         dvd_flush_cache_READ12 (d -> dvd, probe_sectors[j], NULL);
2559                         read_ret = dvd_read_sector_streaming (d -> dvd, probe_sectors[j], NULL, readbuf, sizeof (readbuf));
2560                         dump_ret = dvd_memdump (d -> dvd, 0, candidates[i].windows ? candidates[i].windows : 1, RAW_BLOCK_SIZE, dumpbuf);
2561                         if (dump_ret >= 0) {
2562                                 if (dump_prefix && dump_prefix[0]) {
2563                                         snprintf (dump_path, sizeof (dump_path), "%s_%02lu_%s_sector_%u.bin", dump_prefix, (unsigned long) (i + 1), candidates[i].label, probe_sectors[j]);
2564                                         df = fopen (dump_path, "wb");
2565                                         if (df) {
2566                                                 fwrite (dumpbuf, 1, scan_len, df);
2567                                                 fclose (df);
2568                                         }
2569                                 }
2570                                 any_readable = true;
2571                                 memcpy (sample, dumpbuf, sizeof (sample));
2572                                 hash = hlds_e7_fnv1a32 (dumpbuf, scan_len);
2573                                 (void) hlds_e7_find_raw_header_match (dumpbuf, scan_len, probe_sectors[j], &raw_offset, &raw_sector);
2574                                 exact_count = hlds_e7_count_exact_raw_headers_for_block (dumpbuf, scan_len, probe_sectors[j], exact_offsets);
2575                                 sector_shaped = hlds_e7_raw_header_offsets_are_sector_shaped (exact_offsets);
2576                                 command_echo_offset = hlds_e7_find_command_echo_offset (dumpbuf, scan_len);
2577                                 if (sector_shaped)
2578                                         raw_score = 220;
2579                                 else if (exact_count > 0)
2580                                         raw_score = exact_count;
2581                                 else
2582                                         raw_score = 0;
2583                                 user_score = hlds_e7_find_user_data_match (dumpbuf, scan_len, readbuf, sizeof (readbuf), &user_offset, &user_sector, &read_offset);
2584                                 sector_score = raw_score + user_score;
2585                                 if (command_echo_offset != (size_t) -1 && !sector_shaped && user_score <= 0)
2586                                         sector_score -= 10;
2587                                 if (j == 0)
2588                                         memcpy (first_dump, dumpbuf, scan_len);
2589                                 else {
2590                                         window_diff = hlds_e7_count_byte_diffs (first_dump, dumpbuf, scan_len);
2591                                         if (window_diff > 4096)
2592                                                 sector_score += 40;
2593                                         else if (window_diff > 512)
2594                                                 sector_score += 20;
2595                                         else if (window_diff < 16)
2596                                                 sector_score -= 20;
2597                                 }
2598                         } else {
2599                                 hash = 0;
2600                                 raw_score = -50;
2601                                 user_score = 0;
2602                                 sector_score = -50;
2603                                 raw_offset = (size_t) -1;
2604                                 exact_count = 0;
2605                                 for (k = 0; k < SECTORS_PER_BLOCK; k++)
2606                                         exact_offsets[k] = (size_t) -1;
2607                                 raw_sector = 0xFFFFFFFFU;
2608                                 sector_shaped = false;
2609                                 command_echo_offset = (size_t) -1;
2610                                 user_offset = (size_t) -1;
2611                                 user_sector = 0xFFFFFFFFU;
2612                                 read_offset = (size_t) -1;
2613                         }
2614                         if (read_ret < 0)
2615                                 sector_score -= 20;
2616                         total_score += sector_score;
2617                         fprintf (f, "        {\"sector\": %u, \"read_ret\": %d, \"window_memdump_ret\": %d, \"score\": %d, ",
2618                                 probe_sectors[j], read_ret, dump_ret, sector_score);
2619                         fprintf (f, "\"raw_header_score\": %d, \"raw_header_offset\": ", raw_score);
2620                         if (raw_offset == (size_t) -1)
2621                                 fprintf (f, "null, \"raw_header_sector\": null, ");
2622                         else
2623                                 fprintf (f, "%lu, \"raw_header_sector\": %u, ", (unsigned long) raw_offset, raw_sector);
2624                         fprintf (f, "\"raw_header_found_count\": %d, \"raw_header_offsets\": [", exact_count);
2625                         for (k = 0; k < SECTORS_PER_BLOCK; k++) {
2626                                 if (k)
2627                                         fprintf (f, ", ");
2628                                 if (exact_offsets[k] == (size_t) -1)
2629                                         fprintf (f, "null");
2630                                 else
2631                                         fprintf (f, "%lu", (unsigned long) exact_offsets[k]);
2632                         }
2633                         fprintf (f, "], ");
2634                         fprintf (f, "\"raw_header_sector_shaped\": %s, ", sector_shaped ? "true" : "false");
2635                         fprintf (f, "\"command_echo_offset\": ");
2636                         if (command_echo_offset == (size_t) -1)
2637                                 fprintf (f, "null, ");
2638                         else
2639                                 fprintf (f, "%lu, ", (unsigned long) command_echo_offset);
2640                         fprintf (f, "\"user_data_score\": %d, \"user_data_offset\": ", user_score);
2641                         if (user_offset == (size_t) -1)
2642                                 fprintf (f, "null, \"user_data_sector_index\": null, \"read_probe_offset\": null, ");
2643                         else
2644                                 fprintf (f, "%lu, \"user_data_sector_index\": %u, \"read_probe_offset\": %lu, ", (unsigned long) user_offset, user_sector, (unsigned long) read_offset);
2645                         fprintf (f, "\"window_hash_fnv1a32\": \"0x%08x\", \"sample\": ", hash);
2646                         hlds_e7_json_bytes (f, sample, sizeof (sample));
2647                         fprintf (f, "}%s\n", (j + 1 < sizeof (probe_sectors) / sizeof (probe_sectors[0])) ? "," : "");
2648                 }
2649                 if (window_diff > 4096)
2650                         total_score += 40;
2651                 else if (window_diff > 512)
2652                         total_score += 20;
2653                 else if (window_diff < 16)
2654                         total_score -= 20;
2655                 fprintf (f, "      ],\n");
2656                 fprintf (f, "      \"sector_window_diff_bytes\": %lu,\n", (unsigned long) window_diff);
2657                 fprintf (f, "      \"sector_window_diff_per_1000\": %lu,\n", scan_len ? (unsigned long) ((window_diff * 1000U) / scan_len) : 0UL);
2658                 fprintf (f, "      \"total_score\": %d,\n", total_score);
2659                 fprintf (f, "      \"classification\": \"%s\"\n", total_score >= 220 ? "strict_cache_candidate" : (total_score >= 80 ? "needs_more_candidates" : (total_score > 0 ? "sram_or_table_match" : "no_match")));
2660                 fprintf (f, "    }%s\n", (i + 1 < sizeof (candidates) / sizeof (candidates[0])) ? "," : "");
2661                 fprintf (stderr, "score=%d diff=%lu%s\n", total_score, (unsigned long) window_diff, total_score >= 220 ? " STRICT" : (total_score >= 80 ? " REVIEW" : ""));
2662                 if (total_score > best_score) {
2663                         best_score = total_score;
2664                         best_base = candidates[i].base;
2665                         best_windows = candidates[i].windows;
2666                 }
2667         }
2668
2669         fprintf (f, "  ],\n");
2670         fprintf (f, "  \"best\": {\"base\": \"0x%08x\", \"windows\": %u, \"score\": %d, \"confidence\": \"%s\"},\n",
2671                 best_base, best_windows, best_score, best_score >= 220 ? "strict" : (best_score >= 80 ? "review" : (best_score > 0 ? "weak" : "none")));
2672         fprintf (f, "  \"sector_cache_candidate_found\": %s,\n", best_score >= 220 ? "true" : "false");
2673         fprintf (f, "  \"promotion_recommendation\": \"%s\",\n", best_score >= 220 ? "candidate may be promoted into an experimental dump profile" : "do not promote; scan found command/SRAM/table echoes but no strict 2064-byte sector cache");
2674         fprintf (f, "  \"next_recommended_action\": \"%s\",\n", best_score >= 220 ? "try the strict scan-guided normal probe" : "do not run normal seed retrieval yet; use firmware analysis or a wider address/subcommand sweep to find the real cache path");
2675         fprintf (f, "  \"e7_memdump_command\": \"%s\"\n", any_readable ? "accepted_by_at_least_one_candidate" : "no_successful_window_memdump");
2676         fprintf (f, "}\n");
2677         fclose (f);
2678
2679         dvd_set_hlds_e7_runtime_profile (d -> dvd, old_type, old_base, old_windows);
2680         fprintf (stderr, "HLDS 0xE7 scan mode v5 complete: best base=0x%08x windows=%u score=%d (%s; %s)\n",
2681                 best_base, best_windows, best_score,
2682                 best_score >= 220 ? "strict" : (best_score >= 80 ? "review" : (best_score > 0 ? "weak" : "no match")),
2683                 best_score >= 220 ? "promotion allowed" : "do not promote");
2684         free (dumpbuf);
2685         free (first_dump);
2686         return best_score >= 80;
2687 }
2688
2689
2690 static int hlds_e7_raw_hit_data_in (disc *d, u_int8_t subcmd, u_int32_t offset, u_int32_t length, u_int8_t *buf) {
2691         mmc_command mmc;
2692         if (!d || !d -> dvd || !buf || length == 0 || length > 65535U)
2693                 return -1;
2694         dvd_init_command (&mmc, buf, (int) length, NULL);
2695         mmc.cmd[0] = 0xE7;
2696         mmc.cmd[1] = 0x48; /* H */
2697         mmc.cmd[2] = 0x49; /* I */
2698         mmc.cmd[3] = 0x54; /* T */
2699         mmc.cmd[4] = subcmd;
2700         mmc.cmd[6] = (u_int8_t) ((offset >> 24) & 0xFF);
2701         mmc.cmd[7] = (u_int8_t) ((offset >> 16) & 0xFF);
2702         mmc.cmd[8] = (u_int8_t) ((offset >> 8) & 0xFF);
2703         mmc.cmd[9] = (u_int8_t) (offset & 0xFF);
2704         mmc.cmd[10] = (u_int8_t) ((length >> 8) & 0xFF);
2705         mmc.cmd[11] = (u_int8_t) (length & 0xFF);
2706         return dvd_execute_cmd (d -> dvd, &mmc, true);
2707 }
2708
2709 typedef struct {
2710         u_int8_t subcmd;
2711         const char *label;
2712 } hlds_e7_subcmd_probe;
2713
2714 typedef struct {
2715         u_int32_t address;
2716         const char *label;
2717 } hlds_e7_raw_addr_probe;
2718
2719 bool disc_hlds_e7_subcmd_sweep (disc *d, const char *json_path, const char *dump_prefix) {
2720         static const hlds_e7_subcmd_probe subcmds[] = {
2721                 {0x00, "subcmd_00"}, {0x01, "subcmd_01_known_memdump"},
2722                 {0x02, "subcmd_02"}, {0x03, "subcmd_03"},
2723                 {0x04, "subcmd_04"}, {0x05, "subcmd_05"},
2724                 {0x06, "subcmd_06"}, {0x07, "subcmd_07"},
2725                 {0x08, "subcmd_08"}, {0x09, "subcmd_09"},
2726                 {0x0A, "subcmd_0a"}, {0x0B, "subcmd_0b"},
2727                 {0x0C, "subcmd_0c"}, {0x0D, "subcmd_0d"},
2728                 {0x0E, "subcmd_0e"}, {0x0F, "subcmd_0f"}
2729         };
2730         static const hlds_e7_raw_addr_probe addrs[] = {
2731                 {0x80000000U, "type4_base"},
2732                 {0x80008000U, "type4_plus_8000"},
2733                 {0x80010000U, "type4_plus_10000"},
2734                 {0x00000000U, "low_sram_0"},
2735                 {0x00001800U, "firmware_sram_1800"},
2736                 {0x000018A8U, "firmware_sram_18a8"},
2737                 {0x00380000U, "table_00380000"},
2738                 {0x40000000U, "firmware_alias_40000000"}
2739         };
2740         static const u_int32_t probe_sectors[] = {0, 320};
2741         const char *path;
2742         FILE *f;
2743         FILE *df;
2744         char dump_path[512];
2745         u_int8_t *buf;
2746         u_int8_t readbuf[BLOCK_SIZE];
2747         u_int32_t len;
2748         size_t i, a, j;
2749         int ret;
2750         int read_ret;
2751         int exact_count;
2752         int raw_score;
2753         int user_score;
2754         int best_score;
2755         int total_promotable;
2756         size_t raw_offset;
2757         size_t user_offset;
2758         size_t read_offset;
2759         size_t command_echo_offset;
2760         size_t offsets[SECTORS_PER_BLOCK];
2761         u_int32_t raw_sector;
2762         u_int32_t user_sector;
2763         u_int32_t hash;
2764         bool sector_shaped;
2765         u_int8_t sample[16];
2766
2767         if (!d)
2768                 return false;
2769         path = (json_path && json_path[0]) ? json_path : "hlds_e7_subcmd_sweep.json";
2770         len = RAW_BLOCK_SIZE; /* one 16-sector raw-cache-sized window; enough to find strict stride without huge runtimes */
2771         buf = (u_int8_t *) malloc (len);
2772         if (!buf) {
2773                 warning ("HLDS 0xE7 subcmd sweep: out of memory");
2774                 return false;
2775         }
2776         f = fopen (path, "wb");
2777         if (!f) {
2778                 free (buf);
2779                 warning ("HLDS 0xE7 subcmd sweep: could not open %s for writing", path);
2780                 return false;
2781         }
2782
2783         fprintf (stderr, "\nHLDS 0xE7 subcommand sweep v2: probing HIT subcommands 0x00..0x0f without seed cracking\n");
2784         fprintf (stderr, "HLDS 0xE7 subcommand sweep v2: data-in only, %u-byte reads, no dump attempt\n", len);
2785         fprintf (stderr, "HLDS 0xE7 subcommand sweep v2: writing JSON report to %s\n", path);
2786
2787         best_score = -999999;
2788         total_promotable = 0;
2789
2790         fprintf (f, "{\n");
2791         fprintf (f, "  \"sweep_version\": \"v1_hlds_hit_subcmd_address_probe\",\n");
2792         fprintf (f, "  \"drive\": \"");
2793         hlds_e7_json_escape (f, disc_get_drive_model_string (d));
2794         fprintf (f, "\",\n");
2795         fprintf (f, "  \"notes\": \"This diagnostic sends HIT 0xE7 data-in commands with subcommands 0x00..0x0f over a small address set. It does not crack seeds or dump the disc. A promotable result requires sector-shaped 2064-byte raw headers or a direct READ-payload echo; command echoes alone are not promoted.\",\n");
2796         fprintf (f, "  \"read_length\": %u,\n", len);
2797         fprintf (f, "  \"probe_sectors\": [%u, %u],\n", probe_sectors[0], probe_sectors[1]);
2798         fprintf (f, "  \"results\": [\n");
2799
2800         for (i = 0; i < sizeof (subcmds) / sizeof (subcmds[0]); i++) {
2801                 for (a = 0; a < sizeof (addrs) / sizeof (addrs[0]); a++) {
2802                         int total_score = 0;
2803                         int promotable = 0;
2804                         fprintf (stderr, "  subcmd=0x%02x %-24s addr=0x%08x... ",
2805                                 (unsigned int) subcmds[i].subcmd, subcmds[i].label, addrs[a].address);
2806                         fprintf (f, "    {\n");
2807                         fprintf (f, "      \"subcmd\": \"0x%02x\",\n", (unsigned int) subcmds[i].subcmd);
2808                         fprintf (f, "      \"subcmd_label\": \"");
2809                         hlds_e7_json_escape (f, subcmds[i].label);
2810                         fprintf (f, "\",\n");
2811                         fprintf (f, "      \"address\": \"0x%08x\",\n", addrs[a].address);
2812                         fprintf (f, "      \"address_label\": \"");
2813                         hlds_e7_json_escape (f, addrs[a].label);
2814                         fprintf (f, "\",\n");
2815                         fprintf (f, "      \"sector_tests\": [\n");
2816                         for (j = 0; j < sizeof (probe_sectors) / sizeof (probe_sectors[0]); j++) {
2817                                 int score = 0;
2818                                 int kk;
2819                                 memset (buf, 0, len);
2820                                 memset (readbuf, 0, sizeof (readbuf));
2821                                 memset (sample, 0, sizeof (sample));
2822                                 read_ret = dvd_read_sector_streaming (d -> dvd, probe_sectors[j], NULL, readbuf, sizeof (readbuf));
2823                                 ret = hlds_e7_raw_hit_data_in (d, subcmds[i].subcmd, addrs[a].address, len, buf);
2824                                 raw_offset = (size_t) -1;
2825                                 user_offset = (size_t) -1;
2826                                 read_offset = (size_t) -1;
2827                                 command_echo_offset = (size_t) -1;
2828                                 raw_sector = 0xFFFFFFFFU;
2829                                 user_sector = 0xFFFFFFFFU;
2830                                 exact_count = 0;
2831                                 for (kk = 0; kk < SECTORS_PER_BLOCK; kk++)
2832                                         offsets[kk] = (size_t) -1;
2833                                 sector_shaped = false;
2834                                 raw_score = 0;
2835                                 user_score = 0;
2836                                 hash = 0;
2837                                 if (ret >= 0) {
2838                                         memcpy (sample, buf, sizeof (sample));
2839                                         hash = hlds_e7_fnv1a32 (buf, len);
2840                                         (void) hlds_e7_find_raw_header_match (buf, len, probe_sectors[j], &raw_offset, &raw_sector);
2841                                         exact_count = hlds_e7_count_exact_raw_headers_for_block (buf, len, probe_sectors[j], offsets);
2842                                         sector_shaped = hlds_e7_raw_header_offsets_are_sector_shaped (offsets);
2843                                         command_echo_offset = hlds_e7_find_command_echo_offset (buf, len);
2844                                         user_score = hlds_e7_find_user_data_match (buf, len, readbuf, sizeof (readbuf), &user_offset, &user_sector, &read_offset);
2845                                         raw_score = sector_shaped ? 220 : exact_count;
2846                                 }
2847                                 if (ret < 0)
2848                                         score -= 50;
2849                                 if (read_ret < 0)
2850                                         score -= 20;
2851                                 score += raw_score + user_score;
2852                                 if (command_echo_offset != (size_t) -1 && !sector_shaped && user_score <= 0)
2853                                         score -= 10;
2854                                 if (sector_shaped || user_score > 0)
2855                                         promotable++;
2856                                 total_score += score;
2857                                 fprintf (f, "        {\"sector\": %u, \"read_ret\": %d, \"e7_ret\": %d, \"score\": %d, ",
2858                                         probe_sectors[j], read_ret, ret, score);
2859                                 fprintf (f, "\"raw_header_found_count\": %d, \"raw_header_sector_shaped\": %s, ",
2860                                         exact_count, sector_shaped ? "true" : "false");
2861                                 fprintf (f, "\"raw_header_offset\": ");
2862                                 if (raw_offset == (size_t) -1)
2863                                         fprintf (f, "null, \"raw_header_sector\": null, ");
2864                                 else
2865                                         fprintf (f, "%lu, \"raw_header_sector\": %u, ", (unsigned long) raw_offset, raw_sector);
2866                                 fprintf (f, "\"command_echo_offset\": ");
2867                                 if (command_echo_offset == (size_t) -1)
2868                                         fprintf (f, "null, ");
2869                                 else
2870                                         fprintf (f, "%lu, ", (unsigned long) command_echo_offset);
2871                                 fprintf (f, "\"user_data_score\": %d, \"user_data_offset\": ", user_score);
2872                                 if (user_offset == (size_t) -1)
2873                                         fprintf (f, "null, \"user_data_sector_index\": null, \"read_probe_offset\": null, ");
2874                                 else
2875                                         fprintf (f, "%lu, \"user_data_sector_index\": %u, \"read_probe_offset\": %lu, ",
2876                                                 (unsigned long) user_offset, user_sector, (unsigned long) read_offset);
2877                                 fprintf (f, "\"window_hash_fnv1a32\": \"0x%08x\", \"sample\": ", hash);
2878                                 hlds_e7_json_bytes (f, sample, sizeof (sample));
2879                                 fprintf (f, "}%s\n", (j + 1 < sizeof (probe_sectors) / sizeof (probe_sectors[0])) ? "," : "");
2880
2881                                 /*
2882                                  * If the caller requested raw sweep dumps, write every successful
2883                                  * HIT 0xE7 data-in response, not only promotable sector-cache hits.
2884                                  *
2885                                  * v7 only dumped promotable windows. That meant a useful negative
2886                                  * sweep produced no gdr8081n_subcmd_*.bin files at all, even though
2887                                  * non-promotable command/SRAM echoes were exactly what we needed to
2888                                  * inspect next.
2889                                  */
2890                                 if (dump_prefix && dump_prefix[0] && ret >= 0) {
2891                                         snprintf (dump_path, sizeof (dump_path), "%s_sub%02x_%s_sector_%u.bin",
2892                                                 dump_prefix, (unsigned int) subcmds[i].subcmd, addrs[a].label, probe_sectors[j]);
2893                                         df = fopen (dump_path, "wb");
2894                                         if (df) {
2895                                                 fwrite (buf, 1, len, df);
2896                                                 fclose (df);
2897                                         }
2898                                 }
2899                         }
2900                         fprintf (f, "      ],\n");
2901                         fprintf (f, "      \"total_score\": %d,\n", total_score);
2902                         fprintf (f, "      \"promotable_sector_tests\": %d,\n", promotable);
2903                         fprintf (f, "      \"classification\": \"%s\"\n", promotable > 0 ? "promotable_candidate" : (total_score > 0 ? "responds_nonpromotable" : "no_useful_response"));
2904                         fprintf (f, "    }%s\n",
2905                                 (i + 1 == sizeof (subcmds) / sizeof (subcmds[0]) && a + 1 == sizeof (addrs) / sizeof (addrs[0])) ? "" : ",");
2906                         fprintf (stderr, "score=%d%s\n", total_score, promotable > 0 ? " PROMOTABLE" : "");
2907                         if (total_score > best_score)
2908                                 best_score = total_score;
2909                         total_promotable += promotable;
2910                 }
2911         }
2912
2913         fprintf (f, "  ],\n");
2914         fprintf (f, "  \"promotable_candidate_found\": %s,\n", total_promotable > 0 ? "true" : "false");
2915         fprintf (f, "  \"promotion_recommendation\": \"%s\",\n", total_promotable > 0 ? "review promotable candidates and try a targeted profile" : "do not promote; no subcommand/address pair exposed sector-shaped cache or READ-payload echo");
2916         fprintf (f, "  \"next_recommended_action\": \"%s\"\n", total_promotable > 0 ? "send the JSON and any dumped promotable windows" : "continue firmware handler analysis; avoid normal seed retrieval on GDR-8081N until a promotable candidate appears");
2917         fprintf (f, "}\n");
2918         fclose (f);
2919         free (buf);
2920
2921         fprintf (stderr, "HLDS 0xE7 subcommand sweep v2 complete: promotable candidates=%d (%s)\n",
2922                 total_promotable, total_promotable > 0 ? "review JSON" : "none found");
2923         return true;
2924 }
2925
2926
2927 typedef struct {
2928         u_int32_t start;
2929         u_int32_t end;
2930         u_int32_t step;
2931         const char *label;
2932 } hlds_e7_range_probe;
2933
2934 bool disc_hlds_e7_memrange_sweep (disc *d, const char *json_path, const char *dump_prefix) {
2935         static const hlds_e7_range_probe ranges[] = {
2936                 {0x7FFE0000U, 0x80080000U, 0x00000800U, "type4_dense_neighborhood"},
2937                 {0x00000000U, 0x00040000U, 0x00000800U, "low_sram_dense"},
2938                 {0x00370000U, 0x00390000U, 0x00000800U, "table_0038_dense"},
2939                 {0x00A00000U, 0x00A40000U, 0x00000800U, "type1_dense_neighborhood"},
2940                 {0x40000000U, 0x40010000U, 0x00000800U, "firmware_alias_dense"}
2941         };
2942         static const u_int32_t probe_sectors[] = {0, 320};
2943         const char *path;
2944         FILE *f;
2945         FILE *df;
2946         char dump_path[512];
2947         u_int8_t *buf;
2948         u_int8_t readbuf[BLOCK_SIZE];
2949         u_int32_t len;
2950         size_t r, j;
2951         u_int32_t addr;
2952         unsigned long tested;
2953         unsigned long nonzero_windows;
2954         unsigned long command_echo_windows;
2955         unsigned long raw_table_windows;
2956         unsigned long promotable_windows;
2957         int best_score;
2958         u_int32_t best_addr;
2959         const char *best_range;
2960
2961         if (!d)
2962                 return false;
2963         path = (json_path && json_path[0]) ? json_path : "hlds_e7_memrange_sweep.json";
2964         len = RAW_BLOCK_SIZE;
2965         buf = (u_int8_t *) malloc (len);
2966         if (!buf) {
2967                 warning ("HLDS 0xE7 memrange sweep: out of memory");
2968                 return false;
2969         }
2970         f = fopen (path, "wb");
2971         if (!f) {
2972                 free (buf);
2973                 warning ("HLDS 0xE7 memrange sweep: could not open %s for writing", path);
2974                 return false;
2975         }
2976
2977         fprintf (stderr, "\nHLDS 0xE7 memory-range sweep v1: using known memdump subcmd 0x01 only\n");
2978         fprintf (stderr, "HLDS 0xE7 memory-range sweep v1: dense address stride, no seed cracking, no dump attempt\n");
2979         fprintf (stderr, "HLDS 0xE7 memory-range sweep v1: writing JSON report to %s\n", path);
2980
2981         tested = 0;
2982         nonzero_windows = 0;
2983         command_echo_windows = 0;
2984         raw_table_windows = 0;
2985         promotable_windows = 0;
2986         best_score = -999999;
2987         best_addr = 0;
2988         best_range = "none";
2989
2990         fprintf (f, "{\n");
2991         fprintf (f, "  \"sweep_version\": \"v1_known_memdump_dense_address_range\",\n");
2992         fprintf (f, "  \"drive\": \"");
2993         hlds_e7_json_escape (f, disc_get_drive_model_string (d));
2994         fprintf (f, "\",\n");
2995         fprintf (f, "  \"notes\": \"This diagnostic uses only HIT 0xE7 subcmd 0x01, because the subcommand sweep showed only that subcommand returns nonzero data. It densely sweeps address ranges and promotes only sector-shaped 2064-byte raw headers or direct READ-payload echoes. Command echoes and sector-number table matches are recorded but not promoted.\",\n");
2996         fprintf (f, "  \"read_length\": %u,\n", len);
2997         fprintf (f, "  \"probe_sectors\": [%u, %u],\n", probe_sectors[0], probe_sectors[1]);
2998         fprintf (f, "  \"ranges\": [\n");
2999         for (r = 0; r < sizeof (ranges) / sizeof (ranges[0]); r++) {
3000                 fprintf (f, "    {\"label\": \"");
3001                 hlds_e7_json_escape (f, ranges[r].label);
3002                 fprintf (f, "\", \"start\": \"0x%08x\", \"end\": \"0x%08x\", \"step\": \"0x%08x\"}%s\n",
3003                         ranges[r].start, ranges[r].end, ranges[r].step,
3004                         (r + 1 < sizeof (ranges) / sizeof (ranges[0])) ? "," : "");
3005         }
3006         fprintf (f, "  ],\n");
3007         fprintf (f, "  \"results\": [\n");
3008
3009         for (r = 0; r < sizeof (ranges) / sizeof (ranges[0]); r++) {
3010                 fprintf (stderr, "  range %-28s 0x%08x..0x%08x step=0x%04x\n",
3011                         ranges[r].label, ranges[r].start, ranges[r].end, ranges[r].step);
3012                 for (addr = ranges[r].start; addr < ranges[r].end; addr += ranges[r].step) {
3013                         int addr_score = 0;
3014                         int addr_promotable = 0;
3015                         int addr_nonzero = 0;
3016                         int addr_command_echo = 0;
3017                         int addr_raw_table = 0;
3018                         bool first_result = (tested == 0);
3019
3020                         tested++;
3021                         if (!first_result)
3022                                 fprintf (f, ",\n");
3023                         fprintf (f, "    {\n");
3024                         fprintf (f, "      \"range\": \"");
3025                         hlds_e7_json_escape (f, ranges[r].label);
3026                         fprintf (f, "\",\n");
3027                         fprintf (f, "      \"address\": \"0x%08x\",\n", addr);
3028                         fprintf (f, "      \"sector_tests\": [\n");
3029
3030                         for (j = 0; j < sizeof (probe_sectors) / sizeof (probe_sectors[0]); j++) {
3031                                 int ret;
3032                                 int read_ret;
3033                                 int score = 0;
3034                                 int exact_count = 0;
3035                                 int raw_score = 0;
3036                                 int user_score = 0;
3037                                 int kk;
3038                                 size_t raw_offset = (size_t) -1;
3039                                 size_t user_offset = (size_t) -1;
3040                                 size_t read_offset = (size_t) -1;
3041                                 size_t command_echo_offset = (size_t) -1;
3042                                 size_t offsets[SECTORS_PER_BLOCK];
3043                                 u_int32_t raw_sector = 0xFFFFFFFFU;
3044                                 u_int32_t user_sector = 0xFFFFFFFFU;
3045                                 u_int32_t hash = 0;
3046                                 bool sector_shaped = false;
3047                                 bool is_zero = true;
3048                                 u_int8_t sample[16];
3049
3050                                 memset (buf, 0, len);
3051                                 memset (readbuf, 0, sizeof (readbuf));
3052                                 memset (sample, 0, sizeof (sample));
3053                                 for (kk = 0; kk < SECTORS_PER_BLOCK; kk++)
3054                                         offsets[kk] = (size_t) -1;
3055                                 read_ret = dvd_read_sector_streaming (d -> dvd, probe_sectors[j], NULL, readbuf, sizeof (readbuf));
3056                                 ret = hlds_e7_raw_hit_data_in (d, 0x01, addr, len, buf);
3057                                 if (ret >= 0) {
3058                                         size_t zi;
3059                                         memcpy (sample, buf, sizeof (sample));
3060                                         hash = hlds_e7_fnv1a32 (buf, len);
3061                                         for (zi = 0; zi < len; zi++) {
3062                                                 if (buf[zi] != 0) {
3063                                                         is_zero = false;
3064                                                         break;
3065                                                 }
3066                                         }
3067                                         (void) hlds_e7_find_raw_header_match (buf, len, probe_sectors[j], &raw_offset, &raw_sector);
3068                                         exact_count = hlds_e7_count_exact_raw_headers_for_block (buf, len, probe_sectors[j], offsets);
3069                                         sector_shaped = hlds_e7_raw_header_offsets_are_sector_shaped (offsets);
3070                                         command_echo_offset = hlds_e7_find_command_echo_offset (buf, len);
3071                                         user_score = hlds_e7_find_user_data_match (buf, len, readbuf, sizeof (readbuf), &user_offset, &user_sector, &read_offset);
3072                                         raw_score = sector_shaped ? 220 : exact_count;
3073                                         score += raw_score + user_score;
3074                                         if (command_echo_offset != (size_t) -1 && !sector_shaped && user_score <= 0)
3075                                                 score -= 10;
3076                                         if (!is_zero)
3077                                                 addr_nonzero++;
3078                                         if (command_echo_offset != (size_t) -1)
3079                                                 addr_command_echo++;
3080                                         if (exact_count > 0 && !sector_shaped)
3081                                                 addr_raw_table++;
3082                                         if (sector_shaped || user_score > 0)
3083                                                 addr_promotable++;
3084                                         if (dump_prefix && dump_prefix[0] && !is_zero) {
3085                                                 snprintf (dump_path, sizeof (dump_path), "%s_%s_0x%08x_sector_%u.bin",
3086                                                         dump_prefix, ranges[r].label, addr, probe_sectors[j]);
3087                                                 df = fopen (dump_path, "wb");
3088                                                 if (df) {
3089                                                         fwrite (buf, 1, len, df);
3090                                                         fclose (df);
3091                                                 }
3092                                         }
3093                                 } else {
3094                                         score -= 50;
3095                                 }
3096                                 if (read_ret < 0)
3097                                         score -= 20;
3098                                 addr_score += score;
3099
3100                                 fprintf (f, "        {\"sector\": %u, \"read_ret\": %d, \"e7_ret\": %d, \"score\": %d, ",
3101                                         probe_sectors[j], read_ret, ret, score);
3102                                 fprintf (f, "\"nonzero\": %s, \"raw_header_found_count\": %d, \"raw_header_sector_shaped\": %s, ",
3103                                         is_zero ? "false" : "true", exact_count, sector_shaped ? "true" : "false");
3104                                 fprintf (f, "\"raw_header_offset\": ");
3105                                 if (raw_offset == (size_t) -1)
3106                                         fprintf (f, "null, \"raw_header_sector\": null, ");
3107                                 else
3108                                         fprintf (f, "%lu, \"raw_header_sector\": %u, ", (unsigned long) raw_offset, raw_sector);
3109                                 fprintf (f, "\"command_echo_offset\": ");
3110                                 if (command_echo_offset == (size_t) -1)
3111                                         fprintf (f, "null, ");
3112                                 else
3113                                         fprintf (f, "%lu, ", (unsigned long) command_echo_offset);
3114                                 fprintf (f, "\"user_data_score\": %d, \"user_data_offset\": ", user_score);
3115                                 if (user_offset == (size_t) -1)
3116                                         fprintf (f, "null, \"user_data_sector_index\": null, \"read_probe_offset\": null, ");
3117                                 else
3118                                         fprintf (f, "%lu, \"user_data_sector_index\": %u, \"read_probe_offset\": %lu, ",
3119                                                 (unsigned long) user_offset, user_sector, (unsigned long) read_offset);
3120                                 fprintf (f, "\"window_hash_fnv1a32\": \"0x%08x\", \"sample\": ", hash);
3121                                 hlds_e7_json_bytes (f, sample, sizeof (sample));
3122                                 fprintf (f, "}%s\n", (j + 1 < sizeof (probe_sectors) / sizeof (probe_sectors[0])) ? "," : "");
3123                         }
3124
3125                         if (addr_nonzero)
3126                                 nonzero_windows += addr_nonzero;
3127                         if (addr_command_echo)
3128                                 command_echo_windows += addr_command_echo;
3129                         if (addr_raw_table)
3130                                 raw_table_windows += addr_raw_table;
3131                         if (addr_promotable)
3132                                 promotable_windows += addr_promotable;
3133                         if (addr_score > best_score) {
3134                                 best_score = addr_score;
3135                                 best_addr = addr;
3136                                 best_range = ranges[r].label;
3137                         }
3138
3139                         fprintf (f, "      ],\n");
3140                         fprintf (f, "      \"total_score\": %d,\n", addr_score);
3141                         fprintf (f, "      \"nonzero_sector_tests\": %d,\n", addr_nonzero);
3142                         fprintf (f, "      \"command_echo_sector_tests\": %d,\n", addr_command_echo);
3143                         fprintf (f, "      \"raw_table_like_sector_tests\": %d,\n", addr_raw_table);
3144                         fprintf (f, "      \"promotable_sector_tests\": %d,\n", addr_promotable);
3145                         fprintf (f, "      \"classification\": \"%s\"\n",
3146                                 addr_promotable > 0 ? "promotable_candidate" : (addr_raw_table || addr_command_echo ? "sram_or_table_match" : (addr_nonzero ? "nonzero_no_cache" : "zero_or_no_response")));
3147                         fprintf (f, "    }");
3148                 }
3149         }
3150
3151         fprintf (f, "\n  ],\n");
3152         fprintf (f, "  \"addresses_tested\": %lu,\n", tested);
3153         fprintf (f, "  \"nonzero_windows\": %lu,\n", nonzero_windows);
3154         fprintf (f, "  \"command_echo_windows\": %lu,\n", command_echo_windows);
3155         fprintf (f, "  \"raw_table_like_windows\": %lu,\n", raw_table_windows);
3156         fprintf (f, "  \"promotable_windows\": %lu,\n", promotable_windows);
3157         fprintf (f, "  \"best\": {\"range\": \"");
3158         hlds_e7_json_escape (f, best_range);
3159         fprintf (f, "\", \"address\": \"0x%08x\", \"score\": %d},\n", best_addr, best_score);
3160         fprintf (f, "  \"promotable_candidate_found\": %s,\n", promotable_windows ? "true" : "false");
3161         fprintf (f, "  \"promotion_recommendation\": \"%s\",\n",
3162                 promotable_windows ? "review promotable address and try a targeted profile" : "do not promote; dense subcmd 0x01 address sweep found no sector-shaped cache or READ-payload echo");
3163         fprintf (f, "  \"next_recommended_action\": \"%s\"\n",
3164                 promotable_windows ? "send JSON and matching dumped windows" : "focus on firmware handler/control-flow analysis or different pre-read/cache-fill sequences before another seed attempt");
3165         fprintf (f, "}\n");
3166         fclose (f);
3167         free (buf);
3168
3169         fprintf (stderr, "HLDS 0xE7 memory-range sweep v1 complete: addresses=%lu promotable_windows=%lu nonzero_windows=%lu command_echo_windows=%lu\n",
3170                 tested, promotable_windows, nonzero_windows, command_echo_windows);
3171         return true;
3172 }
3173
3174
3175 static bool disc_probe_gdr8050l_e7_speed_profile (disc *d) {
3176         static const hlds_e7_probe_candidate candidates[] = {
3177                 {443, 0x80000000U, 3, "Probe A 3-window no-prefetch: base 0x80000000, 3 windows"},
3178                 {442, 0x80000000U, 2, "Probe B 2-window no-prefetch: base 0x80000000, 2 windows"},
3179                 {445, 0x80000000U, 5, "Probe C 5-window guarded no-prefetch: base 0x80000000, 5 windows"},
3180                 {44,  0x80000000U, 1, "Probe D proven fallback: base 0x80000000, 1 window"}
3181         };
3182         static const u_int32_t probe_sectors[] = {0, 320};
3183         size_t i, j;
3184         bool ok;
3185
3186         if (!d || (dvd_get_hlds_e7_type (d -> dvd) != 44 && dvd_get_hlds_e7_type (d -> dvd) != 45))
3187                 return true;
3188
3189         hlds_e7_visible_probe_log ("GDR-8050L modified 0xE7 speed probe: single-window is proven; trying guarded no-prefetch multi-window profiles before seed cracking");
3190         for (i = 0; i < sizeof (candidates) / sizeof (candidates[0]); i++) {
3191                 hlds_e7_visible_probe_log ("GDR-8050L modified 0xE7 speed probe: %s", candidates[i].label);
3192                 dvd_set_hlds_e7_runtime_profile (d -> dvd, candidates[i].type, candidates[i].base, candidates[i].windows);
3193                 ok = true;
3194                 for (j = 0; j < sizeof (probe_sectors) / sizeof (probe_sectors[0]); j++) {
3195                         disc_cache_clear (d);
3196                         if (!disc_read_sector (d, probe_sectors[j], NULL, NULL) || dvd_get_hlds_e7_type (d -> dvd) != candidates[i].type) {
3197                                 ok = false;
3198                                 break;
3199                         }
3200                 }
3201                 disc_cache_clear (d);
3202                 if (ok) {
3203                         hlds_e7_visible_probe_log ("GDR-8050L modified 0xE7 speed probe: selected %s", candidates[i].label);
3204                         return true;
3205                 }
3206                 hlds_e7_visible_probe_log ("GDR-8050L modified 0xE7 speed probe: failed %s", candidates[i].label);
3207         }
3208
3209         dvd_set_hlds_e7_runtime_profile (d -> dvd, 44, 0x80000000U, 1);
3210         disc_cache_clear (d);
3211         hlds_e7_visible_probe_log ("GDR-8050L modified 0xE7 speed probe: all accelerated profiles failed; using proven single-window fallback");
3212         return true;
3213 }
3214
3215 static bool disc_probe_gdr8081n_e7_profile (disc *d) {
3216         static const hlds_e7_probe_candidate candidates[] = {
3217                 {815, 0x80000000U, 5, "Probe A strict scan-guided Type4-derived: base 0x80000000, 5 windows"}
3218         };
3219         size_t i;
3220
3221         if (!d || dvd_get_hlds_e7_type (d -> dvd) != 81)
3222                 return true;
3223
3224         hlds_e7_visible_probe_log ("GDR-8081N 0xE7 profile probe: drive is experimental; trying strict scan-guided profile only; exact-offset fallbacks were removed to avoid 10-minute false-negative loops");
3225         for (i = 0; i < sizeof (candidates) / sizeof (candidates[0]); i++) {
3226                 hlds_e7_visible_probe_log ("GDR-8081N 0xE7 profile probe: %s", candidates[i].label);
3227                 dvd_set_hlds_e7_runtime_profile (d -> dvd, candidates[i].type, candidates[i].base, candidates[i].windows);
3228                 disc_cache_clear (d);
3229                 if (disc_read_sector (d, 0, NULL, NULL)) {
3230                         hlds_e7_visible_probe_log ("GDR-8081N 0xE7 profile probe: selected %s", candidates[i].label);
3231                         return true;
3232                 }
3233                 hlds_e7_visible_probe_log ("GDR-8081N 0xE7 profile probe: failed %s", candidates[i].label);
3234         }
3235
3236         dvd_set_hlds_e7_runtime_profile (d -> dvd, 81, 0x80000000U, 5);
3237         disc_cache_clear (d);
3238         hlds_e7_visible_probe_log ("GDR-8081N 0xE7 profile probe: strict scan-guided candidate failed; run --hlds-e7-scan with --scan-dump-prefix and inspect strict/user-data fields before another seed attempt");
3239         return false;
3240 }
3241
3242 static bool disc_crack_seeds (disc *d) {
3243         int i;
3244
3245         /* As a Nintendo GameCube/Wii disc should not have too many keys, 20 should be enough. */
3246         debug ("Retrieving all DVD seeds");
3247         disc_seed_diag_clear (d);
3248         d -> seed_diagnostic_active = true;
3249         d -> seed_diagnostic_block = 0;
3250         xbox_ref_log_fprintf (stderr, "\n");
3251         disc_seed_diag_log (d,
3252                 "campaign=begin blocks=20 method=%d profile=%s cache-base=0x%08X windows=%u",
3253                 d -> read_method,
3254                 dvd_get_hlds_e7_profile_name (d -> dvd),
3255                 dvd_get_hlds_e7_cache_base (d -> dvd),
3256                 dvd_get_hlds_e7_mem_blocks (d -> dvd));
3257
3258         if (!disc_probe_gdr8050l_e7_speed_profile (d)) {
3259                 disc_seed_diag_fail (d, 0, 15, 0, "profile-probe",
3260                         "GDR-8050L runtime profile probe failed", -1, false);
3261                 d -> seed_diagnostic_active = false;
3262                 return false;
3263         }
3264         if (!disc_probe_gdr8081n_e7_profile (d)) {
3265                 disc_seed_diag_fail (d, 0, 15, 0, "profile-probe",
3266                         "GDR-8081N runtime profile probe failed", -1, false);
3267                 d -> seed_diagnostic_active = false;
3268                 return false;
3269         }
3270
3271         for (i = 0; i < 20 * 16; i += 16) {
3272                 d -> seed_diagnostic_block = (unsigned int) (i / 16);
3273                 disc_seed_diag_clear (d);
3274                 disc_seed_diag_log (d,
3275                         "block=%u/20 sectors=%d..%d stage=seed-block result=BEGIN",
3276                         d -> seed_diagnostic_block + 1, i, i + 15);
3277                 if (!disc_read_sector (d, (u_int32_t) i, NULL, NULL)) {
3278                         if (!d -> seed_diagnostic.valid)
3279                                 disc_seed_diag_fail (d, (u_int32_t) i, (u_int32_t) i + 15,
3280                                         0, "seed-block-read", "disc_read_sector returned failure without a classified sub-stage", -1, false);
3281                         disc_seed_diag_log (d,
3282                                 "campaign=fail block=%u/20 sectors=%d..%d",
3283                                 d -> seed_diagnostic_block + 1, i, i + 15);
3284                         d -> seed_diagnostic_active = false;
3285                         return false;
3286                 }
3287                 disc_seed_diag_log (d,
3288                         "block=%u/20 sectors=%d..%d stage=seed-block result=PASS",
3289                         d -> seed_diagnostic_block + 1, i, i + 15);
3290         }
3291
3292         disc_seed_diag_clear (d);
3293         disc_seed_diag_log (d, "campaign=pass blocks=20");
3294         d -> seed_diagnostic_active = false;
3295         return true;
3296 }
3297
3298
3299 /**
3300  * Creates a new structure representing a Nintendo GameCube/Wii optical disc.
3301  * @param dvd_device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows).
3302  * @return The newly-created structure, to be used with the other commands.
3303  */
3304 disc *disc_new (char *dvd_device, u_int32_t command) {
3305         dvd_drive *dvd;
3306         disc *d;
3307
3308         if ((dvd = dvd_drive_new (dvd_device, command))) {
3309                 d = (disc *) malloc (sizeof (disc));
3310                 memset (d, 0, sizeof (disc));
3311                 d -> dvd = dvd;
3312                 d -> u = unscrambler_new ();
3313                 disc_set_unscrambling (d, true);        // Unscramble by default
3314                 disc_set_read_method (d, DEFAULT_READ_METHOD);
3315                 disc_cache_init (d, DISC_DEFAULT_CACHE_SIZE);
3316         } else {
3317                 d = NULL;
3318         }
3319
3320         return (d);
3321 }
3322
3323
3324 int disc_media_preflight (disc *d, unsigned int timeout_ms, int *sense_key, int *asc, int *ascq) {
3325         req_sense sense;
3326         unsigned int elapsed = 0;
3327         const unsigned int interval_ms = 500;
3328         int rc;
3329
3330         if (sense_key) *sense_key = 0;
3331         if (asc) *asc = 0;
3332         if (ascq) *ascq = 0;
3333         if (!d || !d -> dvd)
3334                 return -1;
3335
3336         for (;;) {
3337                 u_int32_t sectors = 0, sector_size = 0;
3338
3339                 memset (&sense, 0, sizeof (sense));
3340                 rc = dvd_test_unit_ready (d -> dvd, &sense);
3341                 if (rc >= 0) {
3342                         /* Some optical drives and USB bridges report TEST UNIT READY=GOOD
3343                          * with an empty tray.  Require a second, media-dependent command
3344                          * before allowing vendor seed/cache reads. */
3345                         memset (&sense, 0, sizeof (sense));
3346                         rc = dvd_read_capacity_10 (d -> dvd, &sectors, &sector_size, &sense);
3347                         if (rc >= 0 && sectors > 1 && sector_size == SECTOR_SIZE)
3348                                 return 1;
3349                         /* A successful command with zero/invalid capacity is not proof of media. */
3350                         if (rc >= 0) {
3351                                 if (sense_key) *sense_key = 0;
3352                                 if (asc) *asc = 0;
3353                                 if (ascq) *ascq = 0;
3354                                 return 0;
3355                         }
3356                 }
3357
3358                 if (sense_key) *sense_key = sense.sense_key;
3359                 if (asc) *asc = sense.asc;
3360                 if (ascq) *ascq = sense.ascq;
3361
3362                 /* SPC/MMC: NOT READY / MEDIUM NOT PRESENT. */
3363                 if ((sense.sense_key & 0x0f) == 0x02 && sense.asc == 0x3a)
3364                         return 0;
3365
3366                 /* Retry transient becoming-ready / unit-attention states. */
3367                 if (!(((sense.sense_key & 0x0f) == 0x02 && sense.asc == 0x04) ||
3368                       ((sense.sense_key & 0x0f) == 0x06 && (sense.asc == 0x28 || sense.asc == 0x29))))
3369                         return -1;
3370                 if (elapsed >= timeout_ms)
3371                         return -1;
3372 #ifdef WIN32
3373                 Sleep (interval_ms);
3374 #else
3375                 usleep ((useconds_t) interval_ms * 1000);
3376 #endif
3377                 elapsed += interval_ms;
3378         }
3379 }
3380
3381 bool disc_init (disc *d, u_int32_t disctype, u_int32_t sectors_no) {
3382         bool out;
3383         
3384         d -> sectors_no = 1000;                 // TODO
3385         disc_detect_type (d, disctype, sectors_no);
3386         if (d -> type != DISC_TYPE_XBOX && !disc_crack_seeds (d))
3387                 return false;
3388 //      unscrambler_set_bruteforce (d -> u, false);             // Disabling bruteforcing will allow us to detect errors more quickly
3389         unscrambler_set_bruteforce (d -> u, true);
3390         if (d -> type==DISC_TYPE_DVD) {
3391                 my_strdup (d -> title, "DVD");
3392                 out = true;
3393         }
3394         else if (d -> type==DISC_TYPE_XBOX) {
3395                 my_strdup (d -> title, "Xbox DVD");
3396                 d -> system_id = 'X';
3397                 strncpy (d -> game_id, "XB", sizeof (d -> game_id));
3398                 strncpy (d -> maker, "MS", sizeof (d -> maker));
3399                 my_strdup (d -> version_string, "N/A");
3400                 d -> has_update = false;
3401                 disc_set_unscrambling (d, false);
3402                 out = true;
3403         }
3404         else if (disc_analyze (
3405                      d,
3406                      disctype > 4,
3407                      sectors_no == (u_int32_t) -1
3408                  )) {
3409                 disc_check_update (d);
3410                 out = true;
3411         } else {
3412                 out = false;
3413         }
3414
3415         return (out);
3416 }
3417
3418
3419 /**
3420  * Frees resources used by a disc structure and destroys it.
3421  * @param d The disc structure.
3422  * @return NULL.
3423  */
3424 void *disc_destroy (disc *d) {
3425         disc_cache_destroy (d);
3426         unscrambler_destroy (d -> u);
3427         my_free (d -> version_string);
3428         my_free (d -> title);
3429         dvd_drive_destroy (d -> dvd);
3430         my_free (d);
3431
3432         return (NULL);
3433 }
3434
3435
3436 bool disc_is_xbox_unlock_drive (disc *d) {
3437         return d && dvd_is_xbox_unlock_drive (d -> dvd);
3438 }
3439
3440 bool disc_is_xbox_challenge_drive (disc *d) {
3441         return d && dvd_is_xbox_challenge_drive (d -> dvd);
3442 }
3443
3444 bool disc_is_xbox_vendor_unlock_drive (disc *d) {
3445         return d && dvd_is_xbox_vendor_unlock_drive (d -> dvd);
3446 }
3447
3448 int disc_xbox_lock (disc *d) {
3449         u_int32_t sectors = 0;
3450         u_int32_t sector_size = 0;
3451
3452         if (!d || d -> type != DISC_TYPE_XBOX)
3453                 return -1;
3454
3455         if (dvd_is_xbox_vendor_unlock_drive (d -> dvd)) {
3456                 if (dvd_xbox_vendor_lock (d -> dvd) < 0)
3457                         return -1;
3458         }
3459
3460         if (dvd_read_capacity_10 (d -> dvd, &sectors, &sector_size, NULL) == 0 && sector_size == SECTOR_SIZE)
3461                 d -> sectors_no = sectors;
3462         return 0;
3463 }
3464
3465
3466 int disc_xbox_unlock (disc *d) {
3467         u_int32_t sectors = 0;
3468         u_int32_t sector_size = 0;
3469
3470         if (!d || d -> type != DISC_TYPE_XBOX)
3471                 return -1;
3472
3473         if (dvd_is_xbox_challenge_drive (d -> dvd)) {
3474                 if (dvd_xbox_gdr8050l_unlock (d -> dvd, &sectors) < 0)
3475                         return -1;
3476                 d -> sectors_no = sectors;
3477                 return 0;
3478         }
3479
3480         if (dvd_is_xbox_vendor_unlock_drive (d -> dvd)) {
3481                 if (dvd_xbox_vendor_unlock_wxripper (d -> dvd, &sectors) < 0)
3482                         return -1;
3483                 d -> sectors_no = sectors;
3484                 return 0;
3485         }
3486
3487         /* Forced Xbox mode on an unknown drive keeps FriiDump's direct READ(10)
3488          * experiment path, but no model-specific unlock is applied. */
3489         if (dvd_read_capacity_10 (d -> dvd, &sectors, &sector_size, NULL) == 0 && sector_size == SECTOR_SIZE)
3490                 d -> sectors_no = sectors;
3491
3492         return 0;
3493 }
3494
3495
3496 int disc_xbox_read_10 (disc *d, u_int32_t sector, u_int32_t sectors, u_int8_t *buf, size_t bufsize) {
3497         if (!d || d -> type != DISC_TYPE_XBOX || !buf)
3498                 return -1;
3499         return dvd_read_10 (d -> dvd, sector, sectors, NULL, buf, bufsize);
3500 }
3501
3502
3503 int disc_xbox_read_dvd_structure (disc *d, u_int8_t format, u_int8_t layer, u_int8_t *buf, size_t bufsize) {
3504         if (!d || d -> type != DISC_TYPE_XBOX || !buf)
3505                 return -1;
3506         return dvd_read_dvd_structure (d -> dvd, format, layer, buf, bufsize, NULL);
3507 }
3508
3509
3510 int disc_xbox_read_capacity_10 (disc *d, u_int32_t *sectors, u_int32_t *sector_size) {
3511         if (!d || d -> type != DISC_TYPE_XBOX)
3512                 return -1;
3513         return dvd_read_capacity_10 (d -> dvd, sectors, sector_size, NULL);
3514 }
3515
3516
3517
3518 int disc_xbox_prepare_game_view (disc *d, u_int32_t *sectors, u_int32_t *sector_size) {
3519         int out;
3520         if (!d || d -> type != DISC_TYPE_XBOX)
3521                 return -1;
3522         out = dvd_xbox_prepare_game_view (d -> dvd, sectors, sector_size);
3523         if (out == 0 && sectors)
3524                 d -> sectors_no = *sectors;
3525         return out;
3526 }
3527
3528 int disc_refresh_volume (disc *d) {
3529         if (!d)
3530                 return -1;
3531         return dvd_refresh_volume (d -> dvd);
3532 }
3533
3534 int disc_lock_volume (disc *d) {
3535         if (!d)
3536                 return -1;
3537         return dvd_lock_volume (d -> dvd);
3538 }
3539
3540 int disc_xbox_refresh_volume (disc *d) {
3541         if (!d || d -> type != DISC_TYPE_XBOX)
3542                 return -1;
3543         return dvd_xbox_refresh_volume (d -> dvd);
3544 }
3545
3546 int disc_xbox_lock_volume (disc *d) {
3547         if (!d || d -> type != DISC_TYPE_XBOX)
3548                 return -1;
3549         return dvd_xbox_lock_volume (d -> dvd);
3550 }
3551
3552 int disc_xbox_media_cycle (disc *d) {
3553         if (!d || d -> type != DISC_TYPE_XBOX)
3554                 return -1;
3555         return dvd_media_cycle (d -> dvd, NULL);
3556 }
3557
3558 int disc_xbox_wait_ready (disc *d, unsigned int timeout_ms) {
3559         if (!d || d -> type != DISC_TYPE_XBOX)
3560                 return -1;
3561         return dvd_wait_ready (d -> dvd, timeout_ms);
3562 }
3563
3564 char *disc_get_drive_vendor (disc *d) {
3565         return d ? dvd_get_vendor (d -> dvd) : NULL;
3566 }
3567
3568 char *disc_get_drive_product_id (disc *d) {
3569         return d ? dvd_get_product_id (d -> dvd) : NULL;
3570 }
3571
3572 char *disc_get_drive_firmware_revision (disc *d) {
3573         return d ? dvd_get_product_revision (d -> dvd) : NULL;
3574 }
3575
3576 char *disc_get_drive_model_string (disc *d) {
3577         return d ? dvd_get_model_string (d -> dvd) : NULL;
3578 }
3579
3580
3581 char *disc_get_device (disc *d) {
3582         return (dvd_get_device (d -> dvd));
3583 }
3584
3585 void *disc_get_native_handle (disc *d) {
3586         if (!d) return NULL;
3587         return dvd_get_native_handle (d -> dvd);
3588 }
3589
3590
3591 bool disc_get_drive_support_status (disc *d) {
3592         return (dvd_get_support_status (d -> dvd));
3593 }
3594
3595 const char *disc_get_hlds_e7_profile_name (disc *d) {
3596         return d ? dvd_get_hlds_e7_profile_name (d -> dvd) : "none";
3597 }
3598
3599 const char *disc_get_hlds_e7_support_tier (disc *d) {
3600         return d ? dvd_get_hlds_e7_support_tier (d -> dvd) : "none";
3601 }
3602
3603 const char *disc_get_hlds_e7_family (disc *d) {
3604         return d ? dvd_get_hlds_e7_family (d -> dvd) : "none";
3605 }
3606
3607 const char *disc_get_hlds_e7_tokens (disc *d) {
3608         return d ? dvd_get_hlds_e7_tokens (d -> dvd) : "";
3609 }
3610
3611 const char *disc_get_hlds_e7_record_id (disc *d) {
3612         return d ? dvd_get_hlds_e7_record_id (d -> dvd) : "";
3613 }
3614
3615 const char *disc_get_hlds_e7_notes (disc *d) {
3616         return d ? dvd_get_hlds_e7_notes (d -> dvd) : "";
3617 }
3618
3619 u_int32_t disc_get_hlds_e7_type (disc *d) {
3620         return d ? dvd_get_hlds_e7_type (d -> dvd) : 0;
3621 }
3622
3623 u_int32_t disc_get_hlds_e7_cache_base (disc *d) {
3624         return d ? dvd_get_hlds_e7_cache_base (d -> dvd) : 0;
3625 }
3626
3627 u_int32_t disc_get_hlds_e7_mem_blocks (disc *d) {
3628         return d ? dvd_get_hlds_e7_mem_blocks (d -> dvd) : 0;
3629 }
3630
3631 u_int32_t disc_get_hlds_e7_static_cdb_base (disc *d) {
3632         return d ? dvd_get_hlds_e7_static_cdb_base (d -> dvd) : 0;
3633 }
3634
3635 u_int32_t disc_get_hlds_e7_static_gate (disc *d) {
3636         return d ? dvd_get_hlds_e7_static_gate (d -> dvd) : 0;
3637 }
3638
3639 int disc_get_hlds_e7_preferred_method (disc *d) {
3640         return d ? dvd_get_hlds_e7_preferred_method (d -> dvd) : -1;
3641 }
3642
3643 void disc_set_speed (disc *d, u_int32_t speed) {
3644         if (speed != -1) dvd_set_speed (d -> dvd, speed, NULL);
3645 }
3646
3647 void disc_set_streaming_speed (disc *d, u_int32_t speed) {
3648         if (speed != -1) dvd_set_streaming (d -> dvd, speed, NULL);
3649 }
3650
3651 bool disc_stop_unit (disc *d, bool start) {
3652         if (dvd_stop_unit (d -> dvd, start, NULL) == 0) return true;
3653         else return false;
3654 }
3655
3656 void init_range (disc *d, u_int32_t sec_disc, u_int32_t sec_mem) {
3657         if ((sec_disc>=1)&&(sec_disc<=100)) d->sec_disc = sec_disc;
3658         else d->sec_disc = -1;
3659         if ((sec_mem>=16)&&(sec_mem<=100)) d->sec_mem = sec_mem;
3660         else d->sec_mem = -1;
3661 }