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