/*************************************************************************** * Copyright (C) 2007 by Arep * * Support is provided through the forums at * * http://wii.console-tribe.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ /*! \file * \brief Analyser and dumper for Nintendo GameCube/Wii discs. * * 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 * sectors, partitions, etc) and game-related (i.e.: Game Title, version, etc). This is the main object that should be used by applications. * * Most of the disc structure information used in this file comes from http://www.gc-linux.org/docs/yagcd.html and * http://www.wiili.org/index.php/GameCube_Optical_Disc . */ #include "misc.h" #include #include #include //#include #include "constants.h" #include "byteorder.h" #include "disc.h" #include "dvd_drive.h" #include "unscrambler.h" // #define cachedebug(...) debug (__VA_ARGS__); #define cachedebug(...) /* Cache always deals with 16-sector blocks. All numbers refer to the 16-sector blocks */ #define DISC_MINIMUM_CACHE_SIZE 5 #define DISC_DEFAULT_CACHE_SIZE 40 #define CACHE_ENTRY_INVALID ((u_int32_t) -1) #define DISC_GAMECUBE_SECTORS_NO 0x0AE0B0 /* 712880 */ #define DISC_WII_SECTORS_NO_SL 0x230480 /* 2294912 */ #define DISC_WII_SECTORS_NO_DL 0x3F69C0 /* 4155840 */ #define MAX_READ_RETRIES 5 #define DEFAULT_READ_METHOD 0 #define DEFAULT_READ_SECTOR disc_read_sector_0 typedef int (*disc_read_sector_func) (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata); u_int8_t buf[1024*1024*4]; u_int8_t buf_unscrambled[1024*1024*4]; //struct timeval tim; //double t1, t2; /*! \brief A structure that represents a Nintendo GameCube/Wii optical disc. */ struct disc_s { dvd_drive *dvd; //!< The structure for the DVD-drive the disc is inserted in. disc_type type; //!< The disc type. char system_id; //!< A letter identifying the target system. char game_id[2 + 1]; //!< Two letters identifying the game. disc_region region; //!< The disc region. char maker[3]; //!< Two letters identifying the maker of the game. u_int8_t version; //!< A number identifying the game version. char *version_string; //!< The same as version, in a more human-understandable format. char *title; //!< The game title. bool has_update; //!< True if the game contains a system update (Only possible for Wii discs). u_int32_t sectors_no; //!< The number of sectors of the disc. u_int32_t layerbreak; //!< For dual-layer DVDs. u_int32_t sec_disc; u_int32_t sec_mem; u_int32_t max_cnt; u_int32_t max_blk; /* Read function & stuff */ int command; //!< Buffer access command ID. int read_method; //!< The read method ID. // int def_read_method; //!< Default read method ID. disc_read_sector_func read_sector; //!< The actual function that will be used to perform read operations, corresponding to read_method. 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. unscrambler *u; //!< The unscrambler structure that will be used to perform the unscrambling. /* Read cache */ u_int32_t cache_size; //!< The number of blocks that will be cached when read. u_int8_t **raw_cache; //!< Memory area for raw sectors cache. u_int8_t **cache; //!< Memory area for unscrambled sectors cache. u_int32_t *cache_map; //!< Data structure used by the caching system to know which blocks are in memory. }; static void disc_cache_init (disc *d, u_int32_t size) { u_int32_t i; if (size < DISC_MINIMUM_CACHE_SIZE) { error ("Invalid cache size %u (must be >= %u)", size, DISC_MINIMUM_CACHE_SIZE); exit (3); } else { d -> cache_size = size; d -> cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size); d -> raw_cache = (u_int8_t **) malloc (sizeof (u_int8_t *) * size); for (i = 0; i < size; i++) { d -> cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * BLOCK_SIZE); d -> raw_cache[i] = (u_int8_t *) malloc (sizeof (u_int8_t) * RAW_BLOCK_SIZE); } d -> cache_map = (u_int32_t *) malloc (sizeof (u_int32_t) * size); for (i = 0; i < size; i++) d -> cache_map[i] = CACHE_ENTRY_INVALID; } return; } static void disc_cache_destroy (disc *d) { u_int32_t i; my_free (d -> cache_map); for (i = 0; i < d -> cache_size; i++) { my_free (d -> cache[i]); my_free (d -> raw_cache[i]); } my_free (d -> cache); my_free (d -> raw_cache); d -> cache_size = 0; return; } void disc_cache_add_block (disc *d, u_int32_t block, u_int8_t *data, u_int8_t *rawdata) { u_int32_t pos; u_int32_t cnt; pos = block % d -> cache_size; //uniform unscrambled output memcpy (d -> cache[pos], data, BLOCK_SIZE); if (d -> type == DISC_TYPE_DVD) { for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) { memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+12, data+(cnt*SECTOR_SIZE), SECTOR_SIZE); } } else { for (cnt = 0; cnt < SECTORS_PER_BLOCK; cnt++) { memcpy (rawdata+(cnt*RAW_SECTOR_SIZE)+6, data+(cnt*SECTOR_SIZE), SECTOR_SIZE); } } memcpy (d -> raw_cache[pos], rawdata, RAW_BLOCK_SIZE); d -> cache_map[pos] = block; cachedebug ("Cached block %u (sectors %u-%u) at position %u", block, block * SECTORS_PER_BLOCK, (block + 1) * SECTORS_PER_BLOCK - 1, pos); return; } static bool disc_cache_lookup_block (disc *d, u_int32_t block, u_int8_t **data, u_int8_t **rawdata) { u_int32_t pos; bool out; pos = block % d -> cache_size; if (d -> cache_map[pos] == block) { cachedebug ("Cache HIT for block %u", block); if (data) *data = d -> cache[pos]; if (rawdata) *rawdata = d -> raw_cache[pos]; out = true; } else { cachedebug ("Cache MISS for block %u", block); if (data) *data = NULL; if (rawdata) *rawdata = NULL; out = false; } return (out); } 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) { bool out; u_int32_t start_block; int ret, retry; u_int32_t step, cnt, max_cnt, max_blk; u_int32_t block_len, block_size, _block_size, last_block_size, block_cnt; //fprintf (stdout,"disc_read_sector_%d", method); start_block = sector_no / SECTORS_PER_BLOCK; out = false; step = d->sec_mem; max_cnt = d->max_cnt; max_blk = d->max_blk; block_size = step*2064; last_block_size = block_size; block_len = 1; if (block_size > 27 * 2064) { block_len = block_size / (27*2064); if (block_size % (27*2064) != 0) block_len += 1; block_size = 27*2064; last_block_size = (step*2064) - (27*2064*(block_len-1)); } _block_size=block_size; for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { /* Assume everything will turn out well */ out = true; //Streaming read if (retry < 3) { cnt=0; while (cnt <= max_cnt){ _block_size=block_size; if (method == 0 || method == 1 || method == 4) { if (sector_no+(cnt*step) +992 +16 <= d -> sectors_no) //smaller than last sector dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) +992, 16, NULL, NULL, 0); else if (sector_no+(cnt*step) -992 >= 0) //larger than first sector dvd_read_sector_dummy (d -> dvd, sector_no+(cnt*step) -992, 16, NULL, NULL, 0); else dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL); } if (method == 0 || method == 2 || method == 5) dvd_flush_cache_READ12 (d -> dvd, sector_no+(cnt*step), NULL); 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); 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); if (ret >= 0) { for (block_cnt=0; block_cnt dvd, block_cnt*27*2064, 1, _block_size, &buf[(cnt*(2064 * step))+(block_cnt*27*2064)]) < 0) { error ("Memdump failed"); //retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ //no it's not! out = false; break; } if (block_cnt==block_len-1) _block_size = last_block_size; } if (!out) break; //do this check only on 1st layer 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))) { out = false; break; } else cnt += 1; } else { error ("dvd_read_streaming() failed with %d", ret); out = false; break; } } if (cnt < max_cnt) out = false; else { #ifdef DEBUG if (d -> unscrambling) { #endif /* Try to unscramble all data to see if EDC fails */ //for(cnt=0; cnt <= 4; cnt++) { for(cnt=max_blk; cnt--;) { if (!unscrambler_unscramble_16sectors (d -> u, sector_no+(cnt*16), &buf[cnt*(2064*16)], &buf_unscrambled[cnt*(2048*16)])) out = false; } #ifdef DEBUG } #endif } if (out) { /* If data were unscrambled correctly, add them to the cache */ //for(cnt = 0; cnt <= 4; cnt++) { for(cnt=max_blk; cnt--;) { disc_cache_add_block (d, start_block+cnt, &buf_unscrambled[cnt*(2048*16)], &buf[cnt*(2064*16)]); } } } //if (retry < 3) //Simple read on 4rth try else { if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); else if (sector_no -992 >= 0) //larger than first sector dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); ret = dvd_read_sector_dummy (d -> dvd, sector_no, SECTORS_PER_BLOCK, NULL, NULL, 0); if (ret >= 0) { if (dvd_memdump (d -> dvd, 0, 1, RAW_BLOCK_SIZE, buf) < 0) { error ("Memdump failed"); //retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ out = false; } else if ( ((*(buf) & 1) == 0) && ((*(buf+1)<<16)+(*(buf+2)<<8)+(*(buf+3)) != 0x30000+sector_no) ) out = false; else { #ifdef DEBUG if (d -> unscrambling) { #endif /* Try to unscramble all data to see if EDC fails */ if (!unscrambler_unscramble_16sectors (d -> u, sector_no, buf, buf_unscrambled)) out = false; #ifdef DEBUG } #endif } if (out) { /* If data were unscrambled correctly, add them to the cache */ disc_cache_add_block (d, start_block, buf_unscrambled, buf); } } else { error ("dvd_read_sector_dummy() failed with %d", ret); out = false; } } //else } //for if (!out) error ("Too many retries, giving up"); return (out); } ///////////////////////////// General ///////////////////////////// static int disc_read_sector_0 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 0); } ////////////////////////// Non-Streaming ////////////////////////// static int disc_read_sector_1 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 1); } static int disc_read_sector_2 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 2); } static int disc_read_sector_3 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 3); } //////////////////////////// Streaming //////////////////////////// static int disc_read_sector_4 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 4); } static int disc_read_sector_5 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 5); } static int disc_read_sector_6 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { return disc_read_sector_generic (d, sector_no, data, rawdata, 6); } ///////////////////////////// Hitachi ///////////////////////////// static int disc_read_sector_7 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { bool out; u_int32_t start_block; int j, ret, retry; u_int8_t buf[5][16 * 2064]; u_int8_t buf_unscrambled[5][16 * 2048]; //fprintf (stdout,"disc_read_sector_7"); start_block = sector_no / SECTORS_PER_BLOCK; out = false; for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { /* Assume everything will turn out well */ out = true; if (retry > 0) { warning ("Read retry %d for sector %u", retry, sector_no); /* Try to reset in-memory data by seeking to a distant sector */ // if (sector_no > 1000) // dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); // else // dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); else if (sector_no -992 >= 0) //larger than first sector dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); } if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, NULL, 0)) >= 0) { for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { if (dvd_memdump (d -> dvd, 0 + (j * 16 * 2064), 1, 16 * 2064, buf[j]) < 0) { /* Dumping in a single block is faster */ error ("Memdump failed"); out = false; retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ } else { #ifdef DEBUG if (d -> unscrambling) { #endif /* Try to unscramble all data to see if EDC fails */ if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) out = false; #ifdef DEBUG } #endif } } if (out) { /* It seems all data was unscrambled correctly, so cache them out */ for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no; j++) disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); } } else { error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } } if (!out) error ("Too many retries, giving up"); return (out); } static int disc_read_sector_8 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { bool out; u_int32_t ram_offset; int j, k, ret, retry; u_int8_t *sect, buf[5][RAW_BLOCK_SIZE]; u_int8_t readbuf[BLOCK_SIZE]; u_int8_t buf_unscrambled[5][BLOCK_SIZE]; u_int32_t start_block; //fprintf (stdout,"disc_read_sector_8"); start_block = sector_no / SECTORS_PER_BLOCK; out = false; for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { /* Assume everything will turn out well */ out = true; if (retry > 0) { warning ("Read retry %d for sector %u", retry, sector_no); /* Try to reset in-memory data by seeking to a distant sector */ // if (sector_no > 1000) // dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); // else // dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); else if (sector_no -992 >= 0) //larger than first sector dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); } /* First READ command, this will cache 5 16-sector blocks. Immediately dump relevant data */ if (sector_no > d -> sectors_no - 1000) dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0); else dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0); if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, sizeof (readbuf))) >= 0) { for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { /* Reconstruct raw sectors */ for (k = 0; k < 16; k++) { sect = &buf[j][k * RAW_SECTOR_SIZE]; ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE; /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */ if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { error ("Memdump (1) failed"); out = false; retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ } else if (dvd_memdump (d -> dvd, ram_offset + 2060, 1, 4, sect + 2060) < 0) { /* Dumping in a single block is faster */ error ("Memdump (2) failed"); out = false; } } } /* Now the same for remaining 4 16-sector blocks */ for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, sizeof (readbuf))) >= 0) { /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */ for (k = 0; k < 16; k++) { sect = &buf[j][k * RAW_SECTOR_SIZE]; memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); } #ifdef DEBUG if (d -> unscrambling) { #endif /* Try to unscramble all data to see if EDC fails */ if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) out = false; #ifdef DEBUG } #endif } else { error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } } if (out) { /* It seems all data were unscrambled correctly, so cache them out */ for (j = 0; j < 5 && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++) disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); } } else { error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } } if (!out) error ("Too many retries, giving up"); return (out); } static int disc_read_sector_9 (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { bool out; u_int32_t ram_offset; int j, k, ret, retry; u_int8_t *sect, buf[5][RAW_BLOCK_SIZE]; u_int8_t readbuf[BLOCK_SIZE], tmp[16]; u_int8_t buf_unscrambled[5][BLOCK_SIZE]; u_int32_t start_block; //fprintf (stdout,"disc_read_sector_9"); start_block = sector_no / SECTORS_PER_BLOCK; out = false; for (retry = 0; !out && retry < MAX_READ_RETRIES; retry++) { /* Assume everything will turn out well */ out = true; if (retry > 0) { warning ("Read retry %d for sector %u", retry, sector_no); /* Try to reset in-memory data by seeking to a distant sector */ // if (sector_no > 1000) // dvd_read_sector_streaming (d -> dvd, 0, NULL, NULL, 0); // else // dvd_read_sector_streaming (d -> dvd, 1500, NULL, NULL, 0); if (sector_no +992 +16 <= d -> sectors_no) //smaller than last sector dvd_read_sector_dummy (d -> dvd, sector_no +992, 16, NULL, NULL, 0); else if (sector_no -992 >= 0) //larger than first sector dvd_read_sector_dummy (d -> dvd, sector_no -992, 16, NULL, NULL, 0); else dvd_flush_cache_READ12 (d -> dvd, sector_no, NULL); } /* First READ command, this will cache 5 16-sector blocks. Immediately dump relevant data */ if (sector_no > d -> sectors_no - 1000) dvd_read_sector_streaming (d -> dvd, sector_no - 16 * 5 * 2, NULL, NULL, 0); else dvd_read_sector_streaming (d -> dvd, sector_no + 16 * 5, NULL, NULL, 0); if ((ret = dvd_read_sector_streaming (d -> dvd, sector_no, NULL, readbuf, BLOCK_SIZE)) >= 0) { for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { /* Reconstruct raw sectors */ for (k = 0; k < 16; k++) { sect = &buf[j][k * RAW_SECTOR_SIZE]; ram_offset = (j * RAW_BLOCK_SIZE) + k * RAW_SECTOR_SIZE; /* Get first 12 bytes (ID. IED and CPR_MAI fields) and last 4 bytes (EDC field) with memdump */ if (j == 0 && k == 0) { if (dvd_memdump (d -> dvd, ram_offset, 1, 12, sect) < 0) { error ("Memdump (1) failed"); out = false; retry = MAX_READ_RETRIES; /* Well, if this fails going on is useless */ } } else { memcpy (sect, tmp + 4, 12); } if (out && dvd_memdump (d -> dvd, ram_offset + 2060, 1, 16, tmp) < 0) { /* Dumping in a single block is faster */ error ("Memdump (2) failed"); out = false; } else { memcpy (sect + 2060, tmp, 4); } } } /* Now the same for remaining 4 16-sector blocks */ for (j = 0; j < 5 && sector_no + j * 16 < d -> sectors_no && out; j++) { if (j == 0 || (ret = dvd_read_sector_streaming (d -> dvd, sector_no + j * 16, NULL, readbuf, BLOCK_SIZE)) >= 0) { /* Copy "user data" field which has been incorrectly unscrambled by the DVD drive firmware */ for (k = 0; k < 16; k++) { sect = &buf[j][k * RAW_SECTOR_SIZE]; memcpy (sect + 12, readbuf + k * SECTOR_SIZE, SECTOR_SIZE); } #ifdef DEBUG if (d -> unscrambling) { #endif /* Try to unscramble all data to see if EDC fails */ if (!unscrambler_unscramble_16sectors (d -> u, sector_no + (j * 16), buf[j], buf_unscrambled[j])) out = false; #ifdef DEBUG } #endif } else { error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } } if (out) { /* It seems all data were unscrambled correctly, so cache them out */ for (j = 0; j < 5 && sector_no + j * SECTORS_PER_BLOCK < d -> sectors_no; j++) disc_cache_add_block (d, start_block + j, buf_unscrambled[j], buf[j]); } } else { error ("dvd_read_sector_streaming() failed with %d", ret); out = false; } } if (!out) error ("Too many retries, giving up"); return (out); } /* We could also use the 'System ID' (first byte of the image) to tell the discs apart */ static disc_type disc_detect_type (disc *d, u_int32_t forced_type, u_int32_t sectors_no) { req_sense sense; if (forced_type==0) { d -> type = DISC_TYPE_GAMECUBE; d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; } else if (forced_type==1) { d -> type = DISC_TYPE_WII; d -> sectors_no = DISC_WII_SECTORS_NO_SL; } else if (forced_type==2) { d -> type = DISC_TYPE_WII_DL; d -> sectors_no = DISC_WII_SECTORS_NO_DL; //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); } else if (forced_type==3) { d -> type = DISC_TYPE_DVD; if (sectors_no == -1) dvd_get_size(d->dvd, &(d -> sectors_no), NULL); dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); } else { /* Try to read a sector beyond the end of GameCube discs */ 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) { d -> type = DISC_TYPE_GAMECUBE; d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; } else { 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) { d -> type = DISC_TYPE_WII; d -> sectors_no = DISC_WII_SECTORS_NO_SL; } else { d -> type = DISC_TYPE_WII_DL; d -> sectors_no = DISC_WII_SECTORS_NO_DL; //dvd_get_layerbreak(d->dvd, &(d -> layerbreak), NULL); } } } if (sectors_no != -1) d -> sectors_no = sectors_no; return (d -> type); } /** * Reads a sector from the disc (or from the cache), using the preset read method. * @param d The disc structure. * @param sector_no The requested sector number. * @param data A buffer to hold the unscrambled sector data (or NULL). * @param rawdata A buffer to hold the raw sector data (or NULL). * @return */ int disc_read_sector (disc *d, u_int32_t sector_no, u_int8_t **data, u_int8_t **rawdata) { u_int32_t block; u_int8_t *cdata, *crawdata; int out; /* Unscrambled data cannot be requested if unscrambling was disabled */ MY_ASSERT (!(data && !d -> unscrambling)); block = sector_no / SECTORS_PER_BLOCK; /* See if sector is in cache */ if (!(out = disc_cache_lookup_block (d, block, &cdata, &crawdata))) { /* Requested block is not in cache, try to read it from media */ out = d -> read_sector (d, sector_no, data, rawdata); /* Now requested sector is in cache, for sure ;) */ if (out) MY_ASSERT (disc_cache_lookup_block (d, block, &cdata, &crawdata)); } if (out) { if (data) *data = cdata + (sector_no % SECTORS_PER_BLOCK) * SECTOR_SIZE; if (rawdata) *rawdata = crawdata + (sector_no % SECTORS_PER_BLOCK) * RAW_SECTOR_SIZE; } else { if (data) *data = NULL; if (rawdata) *rawdata = NULL; } return (out); } static bool disc_analyze (disc *d) { u_int8_t *buf; char tmp[0x03E0 + 1]; bool unscramble_old, out; /* Force unscrambling for this read */ unscramble_old = d -> unscrambling; disc_set_unscrambling (d, true); if (disc_read_sector (d, 0, &buf, NULL)) { /* System ID */ d -> system_id = buf[0]; // if (d -> system_id == 'G') { // d -> type = DISC_TYPE_GAMECUBE; // d -> sectors_no = DISC_GAMECUBE_SECTORS_NO; // } else if (d -> system_id == 'R') { // d -> type = DISC_TYPE_WII; // d -> sectors_no = DISC_WII_SECTORS_NO; // } else { // error ("Unknown system ID: '%c'", d -> system_id); // MY_ASSERT (false); // } /* Game ID */ strncpy (d -> game_id, (char *) buf + 1, 2); d -> game_id[2] = '\0'; /* Region */ switch (buf[3]) { case 'P': d -> region = DISC_REGION_PAL; break; case 'E': d -> region = DISC_REGION_NTSC; break; case 'J': d -> region = DISC_REGION_JAPAN; break; case 'U': d -> region = DISC_REGION_AUSTRALIA; break; case 'F': d -> region = DISC_REGION_FRANCE; break; case 'D': d -> region = DISC_REGION_GERMANY; break; case 'I': d -> region = DISC_REGION_ITALY; break; case 'S': d -> region = DISC_REGION_SPAIN; break; case 'X': d -> region = DISC_REGION_PAL_X; break; case 'Y': d -> region = DISC_REGION_PAL_Y; break; default: d -> region = DISC_REGION_UNKNOWN; break; } /* Maker code */ strncpy (d -> maker, (char *) buf + 4, 2); d -> maker[2] = '\0'; /* Version */ d -> version = buf[7]; snprintf (tmp, sizeof (tmp), "1.%02u", d -> version); my_strdup (d -> version_string, tmp); /* Game title */ memcpy (tmp, buf + 0x0020, sizeof (tmp) - 1); tmp[sizeof (tmp) - 1] = '\0'; strtrimr (tmp); my_strdup (d -> title, tmp); out = true; } else { error ("Cannot analyze disc"); out = false; } disc_set_unscrambling (d, unscramble_old); return (out); } static char disc_type_strings[4][15] = { "GameCube", "Wii", "Wii_DL", "DVD" }; /** * Retrieves the disc type. * @param d The disc structure. * @param dt This will be set to the disc type. * @param dt_s This will point to a string describing the disc type. * @return A string describing the disc type. */ char *disc_get_type (disc *d, disc_type *dt, char **dt_s) { if (dt) *dt = d -> type; if (dt_s) { if (d -> type < DISC_TYPE_DVD) *dt_s = disc_type_strings[d -> type]; else *dt_s = disc_type_strings[DISC_TYPE_DVD]; } return (*dt_s); } /** * Retrieves the disc game ID. * @param d The disc structure. * @param gid_s This will point to a string containing the game ID. * @return A string containing the game ID. */ char *disc_get_gameid (disc *d, char **gid_s) { if (gid_s) *gid_s = d -> game_id; return (*gid_s); } static char disc_region_strings[11][15] = { "Europe/PAL", "USA/NTSC", "Japan/NTSC", "Australia/PAL", "France/PAL", "Germany/PAL", "Italy/PAL", "Spain/PAL", "Europe(X)/PAL", "Europe(Y)/PAL", "Unknown" }; /** * Retrieves the disc region. * @param d The disc structure. * @param dr This will be set to the disc region. * @param dr_s This will point to a string describing the disc region. * @return A string describing the disc region. */ char *disc_get_region (disc *d, disc_region *dr, char **dr_s) { if (dr) *dr = d -> region; if (dr_s) { if (d -> region < DISC_REGION_UNKNOWN) *dr_s = disc_region_strings[d -> region]; else *dr_s = disc_region_strings[DISC_REGION_UNKNOWN]; } return (*dr_s); } /* The following list has been derived from http://wiitdb.com/Company/HomePage */ static struct { char *code; char *name; } makers[] = { {"0A", "Jaleco"}, {"0B", "Coconuts Japan"}, {"0C", "Coconuts Japan / G.X.Media"}, {"0D", "Micronet"}, {"0E", "Technos"}, {"0F", "Mebio Software"}, {"0G", "Shouei System"}, {"0H", "Starfish"}, {"0J", "Mitsui Fudosan / Dentsu"}, {"0L", "Warashi Inc."}, {"0N", "Nowpro"}, {"0P", "Game Village"}, {"0Q", "IE Institute"}, {"01", "Nintendo"}, {"02", "Rocket Games / Ajinomoto"}, {"03", "Imagineer-Zoom"}, {"04", "Gray Matter"}, {"05", "Zamuse"}, {"06", "Falcom"}, {"07", "Enix"}, {"08", "Capcom"}, {"09", "Hot B Co."}, {"1A", "Yanoman"}, {"1C", "Tecmo Products"}, {"1D", "Japan Glary Business"}, {"1E", "Forum / OpenSystem"}, {"1F", "Virgin Games (Japan)"}, {"1G", "SMDE"}, {"1J", "Daikokudenki"}, {"1P", "Creatures Inc."}, {"1Q", "TDK Deep Impresion"}, {"2A", "Culture Brain"}, {"2C", "Palsoft"}, {"2D", "Visit Co.,Ltd."}, {"2E", "Intec"}, {"2F", "System Sacom"}, {"2G", "Poppo"}, {"2H", "Ubisoft Japan"}, {"2J", "Media Works"}, {"2K", "NEC InterChannel"}, {"2L", "Tam"}, {"2M", "Jordan"}, {"2N", "Smilesoft / Rocket"}, {"2Q", "Mediakite"}, {"3B", "Arcade Zone Ltd"}, {"3C", "Entertainment International / Empire Software"}, {"3D", "Loriciel"}, {"3E", "Gremlin Graphics"}, {"3F", "K.Amusement Leasing Co."}, {"4B", "Raya Systems"}, {"4C", "Renovation Products"}, {"4D", "Malibu Games"}, {"4F", "Eidos"}, {"4G", "Playmates Interactive"}, {"4J", "Fox Interactive"}, {"4K", "Time Warner Interactive"}, {"4Q", "Disney Interactive"}, {"4S", "Black Pearl"}, {"4U", "Advanced Productions"}, {"4X", "GT Interactive"}, {"4Y", "RARE"}, {"4Z", "Crave Entertainment"}, {"5A", "Mindscape / Red Orb Entertainment"}, {"5B", "Romstar"}, {"5C", "Taxan"}, {"5D", "Midway / Tradewest"}, {"5F", "American Softworks"}, {"5G", "Majesco Sales Inc"}, {"5H", "3DO"}, {"5K", "Hasbro"}, {"5L", "NewKidCo"}, {"5M", "Telegames"}, {"5N", "Metro3D"}, {"5P", "Vatical Entertainment"}, {"5Q", "LEGO Media"}, {"5S", "Xicat Interactive"}, {"5T", "Cryo Interactive"}, {"5W", "Red Storm Entertainment"}, {"5X", "Microids"}, {"5Z", "Data Design / Conspiracy / Swing"}, {"6B", "Laser Beam"}, {"6E", "Elite Systems"}, {"6F", "Electro Brain"}, {"6G", "The Learning Company"}, {"6H", "BBC"}, {"6J", "Software 2000"}, {"6K", "UFO Interactive Games"}, {"6L", "BAM! Entertainment"}, {"6M", "Studio 3"}, {"6Q", "Classified Games"}, {"6S", "TDK Mediactive"}, {"6U", "DreamCatcher"}, {"6V", "JoWood Produtions"}, {"6W", "Sega"}, {"6X", "Wannado Edition"}, {"6Y", "LSP (Light & Shadow Prod.)"}, {"6Z", "ITE Media"}, {"7A", "Triffix Entertainment"}, {"7C", "Microprose Software"}, {"7D", "Sierra / Universal Interactive"}, {"7F", "Kemco"}, {"7G", "Rage Software"}, {"7H", "Encore"}, {"7J", "Zoo"}, {"7K", "Kiddinx"}, {"7L", "Simon & Schuster Interactive"}, {"7M", "Asmik Ace Entertainment Inc."}, {"7N", "Empire Interactive"}, {"7Q", "Jester Interactive"}, {"7S", "Rockstar Games"}, {"7T", "Scholastic"}, {"7U", "Ignition Entertainment"}, {"7V", "Summitsoft"}, {"7W", "Stadlbauer"}, {"8B", "BulletProof Software (BPS)"}, {"8C", "Vic Tokai Inc."}, {"8E", "Character Soft"}, {"8F", "I'Max"}, {"8G", "Saurus"}, {"8J", "General Entertainment"}, {"8N", "Success"}, {"8P", "Sega Japan"}, {"9A", "Nichibutsu / Nihon Bussan"}, {"9B", "Tecmo"}, {"9C", "Imagineer"}, {"9F", "Nova"}, {"9G", "Take2 / Den'Z / Global Star"}, {"9H", "Bottom Up"}, {"9J", "TGL (Technical Group Laboratory)"}, {"9L", "Hasbro Japan"}, {"9N", "Marvelous Entertainment"}, {"9P", "Keynet Inc."}, {"9Q", "Hands-On Entertainment"}, {"12", "Infocom"}, {"13", "Electronic Arts Japan"}, {"15", "Cobra Team"}, {"16", "Human / Field"}, {"17", "KOEI"}, {"18", "Hudson Soft"}, {"19", "S.C.P."}, {"20", "Destination Software / Zoo Games / KSS"}, {"21", "Sunsoft / Tokai Engineering"}, {"22", "POW (Planning Office Wada) / VR1 Japan"}, {"23", "Micro World"}, {"25", "San-X"}, {"26", "Enix"}, {"27", "Loriciel / Electro Brain"}, {"28", "Kemco Japan"}, {"29", "Seta"}, {"30", "Viacom"}, {"31", "Carrozzeria"}, {"32", "Dynamic"}, {"34", "Magifact"}, {"35", "Hect"}, {"36", "Codemasters"}, {"37", "Taito / GAGA Communications"}, {"38", "Laguna"}, {"39", "Telstar / Event / Taito"}, {"40", "Seika Corp."}, {"41", "Ubi Soft Entertainment"}, {"42", "Sunsoft US"}, {"44", "Life Fitness"}, {"46", "System 3"}, {"47", "Spectrum Holobyte"}, {"49", "IREM"}, {"50", "Absolute Entertainment"}, {"51", "Acclaim"}, {"52", "Activision"}, {"53", "American Sammy"}, {"54", "Take 2 Interactive / GameTek"}, {"55", "Hi Tech"}, {"56", "LJN LTD."}, {"58", "Mattel"}, {"60", "Titus"}, {"61", "Virgin Interactive"}, {"62", "Maxis"}, {"64", "LucasArts Entertainment"}, {"67", "Ocean"}, {"68", "Bethesda Softworks"}, {"69", "Electronic Arts"}, {"70", "Atari (Infogrames)"}, {"71", "Interplay"}, {"72", "JVC (US)"}, {"73", "Parker Brothers"}, {"75", "Sales Curve (Storm / SCI)"}, {"78", "THQ"}, {"79", "Accolade"}, {"80", "Misawa"}, {"81", "Teichiku"}, {"82", "Namco Ltd."}, {"83", "LOZC"}, {"84", "KOEI"}, {"86", "Tokuma Shoten Intermedia"}, {"87", "Tsukuda Original"}, {"88", "DATAM-Polystar"}, {"90", "Takara Amusement"}, {"91", "Chun Soft"}, {"92", "Video System / Mc O' River"}, {"93", "BEC"}, {"95", "Varie"}, {"96", "Yonezawa / S'pal"}, {"97", "Kaneko"}, {"99", "Marvelous Entertainment"}, {"A0", "Telenet"}, {"A1", "Hori"}, {"A4", "Konami"}, {"A5", "K.Amusement Leasing Co."}, {"A6", "Kawada"}, {"A7", "Takara"}, {"A9", "Technos Japan Corp."}, {"AA", "JVC / Victor"}, {"AC", "Toei Animation"}, {"AD", "Toho"}, {"AF", "Namco"}, {"AG", "Media Rings Corporation"}, {"AH", "J-Wing"}, {"AJ", "Pioneer LDC"}, {"AK", "KID"}, {"AL", "Mediafactory"}, {"AP", "Infogrames / Hudson"}, {"AQ", "Kiratto. Ludic Inc"}, {"B0", "Acclaim Japan"}, {"B1", "ASCII"}, {"B2", "Bandai"}, {"B4", "Enix"}, {"B6", "HAL Laboratory"}, {"B7", "SNK"}, {"B9", "Pony Canyon"}, {"BA", "Culture Brain"}, {"BB", "Sunsoft"}, {"BC", "Toshiba EMI"}, {"BD", "Sony Imagesoft"}, {"BF", "Sammy"}, {"BG", "Magical"}, {"BH", "Visco"}, {"BJ", "Compile"}, {"BL", "MTO Inc."}, {"BN", "Sunrise Interactive"}, {"BP", "Global A Entertainment"}, {"BQ", "Fuuki"}, {"C0", "Taito"}, {"C2", "Kemco"}, {"C3", "Square"}, {"C4", "Tokuma Shoten"}, {"C5", "Data East"}, {"C6", "Tonkin House / Tokyo Shoseki"}, {"C8", "Koei"}, {"CA", "Konami / Ultra / Palcom"}, {"CB", "NTVIC / VAP"}, {"CC", "Use Co.,Ltd."}, {"CD", "Meldac"}, {"CE", "Pony Canyon / FCI"}, {"CF", "Angel / Sotsu Agency / Sunrise"}, {"CG", "Yumedia / Aroma Co., Ltd"}, {"CJ", "Boss"}, {"CK", "Axela / Crea-Tech"}, {"CL", "Sekaibunka-Sha / Sumire Kobo / Marigul Management Inc."}, {"CM", "Konami Computer Entertainment Osaka"}, {"CN", "NEC Interchannel"}, {"CP", "Enterbrain"}, {"CQ", "From Software"}, {"D0", "Taito / Disco"}, {"D1", "Sofel"}, {"D2", "Quest / Bothtec"}, {"D3", "Sigma"}, {"D4", "Ask Kodansha"}, {"D6", "Naxat"}, {"D7", "Copya System"}, {"D8", "Capcom Co., Ltd."}, {"D9", "Banpresto"}, {"DA", "Tomy"}, {"DB", "LJN Japan"}, {"DD", "NCS"}, {"DE", "Human Entertainment"}, {"DF", "Altron"}, {"DG", "Jaleco"}, {"DH", "Gaps Inc."}, {"DN", "Elf"}, {"DQ", "Compile Heart"}, {"E0", "Jaleco"}, {"E2", "Yutaka"}, {"E3", "Varie"}, {"E4", "T&ESoft"}, {"E5", "Epoch"}, {"E7", "Athena"}, {"E8", "Asmik"}, {"E9", "Natsume"}, {"EA", "King Records"}, {"EB", "Atlus"}, {"EC", "Epic / Sony Records"}, {"EE", "IGS (Information Global Service)"}, {"EG", "Chatnoir"}, {"EH", "Right Stuff"}, {"EL", "Spike"}, {"EM", "Konami Computer Entertainment Tokyo"}, {"EN", "Alphadream Corporation"}, {"EP", "Sting"}, {"ES", "Star-Fish"}, {"F0", "A Wave"}, {"F1", "Motown Software"}, {"F2", "Left Field Entertainment"}, {"F3", "Extreme Ent. Grp."}, {"F4", "TecMagik"}, {"F9", "Cybersoft"}, {"FB", "Psygnosis"}, {"FE", "Davidson / Western Tech."}, {"FK", "The Game Factory"}, {"FL", "Hip Games"}, {"FM", "Aspyr"}, {"FP", "Mastiff"}, {"FQ", "iQue"}, {"FR", "Digital Tainment Pool"}, {"FS", "XS Games / Jack Of All Games"}, {"FT", "Daiwon"}, {"G0", "Alpha Unit"}, {"G1", "PCCW Japan"}, {"G2", "Yuke's Media Creations"}, {"G4", "KiKi Co Ltd"}, {"G5", "Open Sesame Inc"}, {"G6", "Sims"}, {"G7", "Broccoli"}, {"G8", "Avex"}, {"G9", "D3 Publisher"}, {"GB", "Konami Computer Entertainment Japan"}, {"GD", "Square-Enix"}, {"GE", "KSG"}, {"GF", "Micott & Basara Inc."}, {"GH", "Orbital Media"}, {"GJ", "Detn8 Games"}, {"GL", "Gameloft / Ubi Soft"}, {"GM", "Gamecock Media Group"}, {"GN", "Oxygen Games"}, {"GT", "505 Games"}, {"GY", "The Game Factory"}, {"H1", "Treasure"}, {"H2", "Aruze"}, {"H3", "Ertain"}, {"H4", "SNK Playmore"}, {"HJ", "Genius Products"}, {"HY", "Reef Entertainment"}, {"HZ", "Nordcurrent"}, {"IH", "Yojigen"}, {"J9", "AQ Interactive"}, {"JF", "Arc System Works"}, {"JW", "Atari"}, {"K6", "Nihon System"}, {"KB", "NIS America"}, {"KM", "Deep Silver"}, {"LH", "Trend Verlag / East Entertainment"}, {"LT", "Legacy Interactive"}, {"MJ", "Mumbo Jumbo"}, {"MR", "Mindscape"}, {"MS", "Milestone / UFO Interactive"}, {"MT", "Blast !"}, {"N9", "Terabox"}, {"NK", "Neko Entertainment / Diffusion / Naps team"}, {"NP", "Nobilis"}, {"NR", "Data Design / Destineer Studios"}, {"PL", "Playlogic"}, {"RM", "Rondomedia"}, {"RS", "Warner Bros. Interactive Entertainment Inc."}, {"RT", "RTL Games"}, {"RW", "RealNetworks"}, {"S5", "Southpeak Interactive"}, {"SP", "Blade Interactive Studios"}, {"SV", "SevenGames"}, {"TK", "Tasuke / Works"}, {"UG", "Metro 3D / Data Design"}, {"VN", "Valcon Games"}, {"VP", "Virgin Play"}, {"WR", "Warner Bros. Interactive Entertainment Inc."}, {"XJ", "Xseed Games"}, {"XS", "Aksys Games"}, {NULL, NULL} }; /** * Retrieves the disk maker. * @param d The disc structure. * @param m This will point to a string containing the disc maker ID. * @param m_s This will point to a string describing the disc maker. * @return A string describing the disc maker. */ char *disc_get_maker (disc *d, char **m, char **m_s) { u_int32_t i; if (m) *m = d -> maker; if (m_s) { for (i = 0; makers[i].code; i++) { if (strcasecmp (d -> maker, makers[i].code) == 0) { *m_s = makers[i].name; break; } } if (!makers[i].code) { *m_s = "Unknown"; } } return (*m_s); } /** * Retrieves the disc version. * @param d The disc structure. * @param v This will contain the version ID. * @param v_s This will point to a string describing the disc version. * @return A string describing the disc version. */ char *disc_get_version (disc *d, u_int8_t *v, char **v_s) { if (v) *v = d -> version; if (v_s) *v_s = d -> version_string; return (*v_s); } /** * Retrieves the disc game title. * @param d The disc structure. * @param t_s This will point to a string describing the disc title. * @return A string describing the disc title. */ char *disc_get_title (disc *d, char **t_s) { if (t_s) *t_s = d -> title; return (*t_s); } /** * Retrieves if the disc has an update. * @param d The disc structure. * @return True if the disc contains an update, false otherwise. */ bool disc_get_update (disc *d) { return (d -> has_update); } /** * Retrieves the number of sectors of the disc. * @param d The disc structure. * @return The number of sectors. */ u_int32_t disc_get_sectors_no (disc *d) { return (d -> sectors_no); } u_int32_t disc_get_layerbreak (disc *d) { return (d -> layerbreak); } u_int32_t disc_get_command (disc *d) { return (d -> command); } u_int32_t disc_get_method (disc *d) { return (d -> read_method); } u_int32_t disc_get_def_method (disc *d) { return dvd_get_def_method(d -> dvd);//(d -> def_read_method); } u_int32_t disc_get_sec_disc (disc *d) { return (d -> sec_disc); } u_int32_t disc_get_sec_mem (disc *d) { return (d -> sec_mem); } /* wiidevel@stacktic.org */ static bool disc_check_update (disc *d) { u_int8_t *buf; u_int32_t x; bool unscramble_old; if (d -> type == DISC_TYPE_WII || d -> type == DISC_TYPE_WII_DL) { /* Force unscrambling for this read */ unscramble_old = d -> unscrambling; disc_set_unscrambling (d, true); /* We need to read offset 0x50004 of the disc. Sector 160 has offset 0x50000 */ if (disc_read_sector (d, 160, &buf, NULL)) { x = my_ntohl (*(u_int32_t *) (buf + 4)); if (x == 0xA5BED6AE) d -> has_update = false; else d -> has_update = true; } else { error ("disc_check_update() failed"); } disc_set_unscrambling (d, unscramble_old); } else { /* GameCube discs never have an update, as actually the GC firmware cannot be upgrade */ d -> has_update = false; } return (d -> has_update); } /** * Sets the disc read method. * @param d The disc structure. * @param method The requested method. * @return True if the method was set correctly, false otherwise (i. e.: method too small/big). */ bool disc_set_read_method (disc *d, int method) { bool out; u_int32_t deviation; u_int32_t counter; u_int32_t cnt1; d -> command = dvd_get_command(d -> dvd); // d -> def_read_method = dvd_get_def_method(d -> dvd); d -> read_method = method; out = true; switch (method) { case 0: d -> read_sector = disc_read_sector_0; break; case 1: d -> read_sector = disc_read_sector_1; break; case 2: d -> read_sector = disc_read_sector_2; break; case 3: d -> read_sector = disc_read_sector_3; break; case 4: d -> read_sector = disc_read_sector_4; break; case 5: d -> read_sector = disc_read_sector_5; break; case 6: d -> read_sector = disc_read_sector_6; break; case 7: d -> read_sector = disc_read_sector_7; break; case 8: d -> read_sector = disc_read_sector_8; break; case 9: d -> read_sector = disc_read_sector_9; break; default: switch (dvd_get_def_method(d -> dvd)) { case 0: d -> read_method = 0; d -> read_sector = disc_read_sector_0; break; case 1: d -> read_method = 1; d -> read_sector = disc_read_sector_1; break; case 2: d -> read_method = 2; d -> read_sector = disc_read_sector_2; break; case 3: d -> read_method = 3; d -> read_sector = disc_read_sector_3; break; case 4: d -> read_method = 4; d -> read_sector = disc_read_sector_4; break; case 5: d -> read_method = 5; d -> read_sector = disc_read_sector_5; break; case 6: d -> read_method = 6; d -> read_sector = disc_read_sector_6; break; case 7: d -> read_method = 7; d -> read_sector = disc_read_sector_7; break; case 8: d -> read_method = 8; d -> read_sector = disc_read_sector_8; break; case 9: d -> read_method = 9; d -> read_sector = disc_read_sector_9; break; default: d -> read_method = DEFAULT_READ_METHOD; d -> read_sector = DEFAULT_READ_SECTOR; break; } } if (d->sec_disc==-1) { if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) d->sec_disc=27; else d->sec_disc=16; } if (d->sec_mem==-1) { if ((d->read_method == 4) || (d->read_method == 5) || (d->read_method == 6)) d->sec_mem=27; else d->sec_mem=16; } deviation = d->sec_mem % SECTORS_PER_BLOCK; counter=0; if (deviation>3) { cnt1=deviation; while (1==1) { cnt1+=deviation; counter++; if (cnt1%SECTORS_PER_BLOCK<=1) break; } } d -> max_cnt = counter; d -> max_blk = ((d->sec_mem*(d->max_cnt+1))-((d->sec_mem*(d->max_cnt+1)) % SECTORS_PER_BLOCK)) / 16; if (out) { debug ("Read method set to %d", d -> read_method); } else { error ("Cannot set read method\n"); } return (out); } /** * Controls the unscrambling process. * @param d The disc structure. * @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. */ void disc_set_unscrambling (disc *d, bool unscramble) { d -> unscrambling = unscramble; debug ("Sectors unscrambling %s", unscramble ? "enabled" : "disabled"); return; } static void disc_crack_seeds (disc *d) { int i; /* As a Nintendo GameCube/Wii disc should not have too many keys, 20 should be enough */ debug ("Retrieving all DVD seeds"); for (i = 0; i < 20 * 16; i += 16) disc_read_sector (d, i, NULL, NULL); return; } /** * Creates a new structure representing a Nintendo GameCube/Wii optical disc. * @param dvd_device The CD/DVD-ROM device, in OS-dependent format (i.e.: /dev/something on Unix, x: on Windows). * @return The newly-created structure, to be used with the other commands. */ disc *disc_new (char *dvd_device, u_int32_t command) { dvd_drive *dvd; disc *d; if ((dvd = dvd_drive_new (dvd_device, command))) { d = (disc *) malloc (sizeof (disc)); memset (d, 0, sizeof (disc)); d -> dvd = dvd; d -> u = unscrambler_new (); disc_set_unscrambling (d, true); // Unscramble by default disc_set_read_method (d, DEFAULT_READ_METHOD); disc_cache_init (d, DISC_DEFAULT_CACHE_SIZE); } else { d = NULL; } return (d); } bool disc_init (disc *d, u_int32_t disctype, u_int32_t sectors_no) { bool out; d -> sectors_no = 1000; // TODO disc_detect_type (d, disctype, sectors_no); disc_crack_seeds (d); // unscrambler_set_bruteforce (d -> u, false); // Disabling bruteforcing will allow us to detect errors more quickly unscrambler_set_bruteforce (d -> u, true); if (d -> type==DISC_TYPE_DVD) { my_strdup (d -> title, "DVD"+'\0'); out = true; } else if (disc_analyze (d)) { disc_check_update (d); out = true; } else { out = false; } return (out); } /** * Frees resources used by a disc structure and destroys it. * @param d The disc structure. * @return NULL. */ void *disc_destroy (disc *d) { disc_cache_destroy (d); unscrambler_destroy (d -> u); my_free (d -> version_string); my_free (d -> title); dvd_drive_destroy (d -> dvd); my_free (d); return (NULL); } char *disc_get_drive_model_string (disc *d) { return (dvd_get_model_string (d -> dvd)); } bool disc_get_drive_support_status (disc *d) { return (dvd_get_support_status (d -> dvd)); } void disc_set_speed (disc *d, u_int32_t speed) { if (speed != -1) dvd_set_speed (d -> dvd, speed, NULL); } void disc_set_streaming_speed (disc *d, u_int32_t speed) { if (speed != -1) dvd_set_streaming (d -> dvd, speed, NULL); } bool disc_stop_unit (disc *d, bool start) { if (dvd_stop_unit (d -> dvd, start, NULL) == 0) return true; else return false; } void init_range (disc *d, u_int32_t sec_disc, u_int32_t sec_mem) { if ((sec_disc>=1)&&(sec_disc<=100)) d->sec_disc = sec_disc; else d->sec_disc = -1; if ((sec_mem>=16)&&(sec_mem<=100)) d->sec_mem = sec_mem; else d->sec_mem = -1; }