diff --git a/support/n64/n64.cpp b/support/n64/n64.cpp index fb59738..f8635c1 100644 --- a/support/n64/n64.cpp +++ b/support/n64/n64.cpp @@ -13,15 +13,15 @@ static constexpr uint64_t FNV_PRIME = 0x100000001b3; static constexpr uint64_t FNV_OFFSET_BASIS = 0xcbf29ce484222325; +static constexpr uint8_t CARTID_LENGTH = 6; +static constexpr auto CARTID_PREFIX = "ID:"; -static constexpr uint64_t fnv_hash(const char *s, uint64_t h = FNV_OFFSET_BASIS) -{ +static constexpr uint64_t fnv_hash(const char *s, uint64_t h = FNV_OFFSET_BASIS) { if (s) while (uint8_t a = *(s++)) h = (h ^ a) * FNV_PRIME; return h; } -enum class MemoryType -{ +enum class MemoryType { NONE = 0, EEPROM_512, EEPROM_2k, @@ -30,9 +30,9 @@ enum class MemoryType FLASH_128k }; -enum class CIC -{ - CIC_NUS_6101 = 0, +enum class CIC { + UNKNOWN = -1, + CIC_NUS_6101, CIC_NUS_6102, CIC_NUS_7101, CIC_NUS_7102, @@ -48,90 +48,174 @@ enum class CIC CIC_NUS_DDUS }; -enum class SystemType -{ - NTSC = 0, +enum class SystemType { + UNKNOWN = -1, + NTSC, PAL }; -enum class RomFormat -{ +enum class RomFormat { UNKNOWN = 0, BIG_ENDIAN, BYTE_SWAPPED, LITTLE_ENDIAN, }; -enum class AutoDetect -{ +enum class AutoDetect { ON = 0, OFF = 1, }; -static RomFormat detectRomFormat(const uint8_t* data) -{ +static RomFormat detectRomFormat(const uint8_t* data) { // data should be aligned const uint32_t val = *(uint32_t*)data; // the following checks assume we're on a little-endian platform // for each check, the first value is for regular roms, the 2nd is for 64DD images - if (val == 0x40123780 || val == 0x40072780) return RomFormat::BIG_ENDIAN; - if (val == 0x12408037 || val == 0x07408027) return RomFormat::BYTE_SWAPPED; - if (val == 0x80371240 || val == 0x80270740) return RomFormat::LITTLE_ENDIAN; + // third is a malformed magic word used in homebrew (mostly pointless) + if (val == UINT32_C(0x40123780) || val == UINT32_C(0x40072780) || val == UINT32_C(0x41123780)) return RomFormat::BIG_ENDIAN; + if (val == UINT32_C(0x12408037) || val == UINT32_C(0x07408027) || val == UINT32_C(0x12418037)) return RomFormat::BYTE_SWAPPED; + if (val == UINT32_C(0x80371240) || val == UINT32_C(0x80270740) || val == UINT32_C(0x80371241)) return RomFormat::LITTLE_ENDIAN; return RomFormat::UNKNOWN; } -static void normalizeData(uint8_t* data, size_t size, RomFormat format) -{ - switch(format) - { +static void normalizeData(uint8_t* data, size_t size, RomFormat format) { + switch(format) { case RomFormat::BYTE_SWAPPED: - for (size_t i = 0; i < size; i += 2) - { - auto c0 = data[0]; - auto c1 = data[1]; - data[0] = c1; - data[1] = c0; - data += 2; - } - break; + for (size_t i = 0; i < size; i += 2) { + auto c0 = data[0]; + auto c1 = data[1]; + data[0] = c1; + data[1] = c0; + data += 2; + } + break; case RomFormat::LITTLE_ENDIAN: - for (size_t i = 0; i < size; i += 4) - { - auto c0 = data[0]; - auto c1 = data[1]; - auto c2 = data[2]; - auto c3 = data[3]; - data[0] = c3; - data[1] = c2; - data[2] = c1; - data[3] = c0; - data += 4; - } - break; + for (size_t i = 0; i < size; i += 4) { + auto c0 = data[0]; + auto c1 = data[1]; + auto c2 = data[2]; + auto c3 = data[3]; + data[0] = c3; + data[1] = c2; + data[2] = c1; + data[3] = c0; + data += 4; + } + break; default: - { // nothing to do - } + break; } } -static void normalizeString(char* s) -{ +static void normalizeString(char* s) { // change the string to lower-case while (*s) { *s = tolower(*s); ++s; } } -static bool detect_rom_settings_in_db(const char* lookup_hash, const char* db_file_name) -{ +// return true if cic and system region is detected +static bool parse_and_apply_db_tags(char* tags) { + MemoryType save_type = MemoryType::NONE; + SystemType system_type = SystemType::UNKNOWN; + CIC cic = CIC::UNKNOWN; + bool cpak = false; + bool rpak = false; + bool tpak = false; + bool rtc = false; + + const char separator[] = "|"; + + for (char* tag = strtok(tags, separator); tag; tag = strtok(nullptr, separator)) { + printf("Tag: %s\n", tag); + + normalizeString(tag); + switch (fnv_hash(tag)) { + case fnv_hash("eeprom512"): save_type = MemoryType::EEPROM_512; break; + case fnv_hash("eeprom2k"): save_type = MemoryType::EEPROM_2k; break; + case fnv_hash("sram32k"): save_type = MemoryType::SRAM_32k; break; + case fnv_hash("sram96k"): save_type = MemoryType::SRAM_96k; break; + case fnv_hash("flash128k"): save_type = MemoryType::FLASH_128k; break; + case fnv_hash("ntsc"): system_type = SystemType::NTSC; break; + case fnv_hash("pal"): system_type = SystemType::PAL; break; + case fnv_hash("cpak"): cpak = true; break; + case fnv_hash("rpak"): rpak = true; break; + case fnv_hash("tpak"): tpak = true; break; + case fnv_hash("rtc"): rtc = true; break; + case fnv_hash("cic6101"): cic = CIC::CIC_NUS_6101; break; + case fnv_hash("cic6102"): cic = CIC::CIC_NUS_6102; break; + case fnv_hash("cic6103"): cic = CIC::CIC_NUS_6103; break; + case fnv_hash("cic6105"): cic = CIC::CIC_NUS_6105; break; + case fnv_hash("cic6106"): cic = CIC::CIC_NUS_6106; break; + case fnv_hash("cic7101"): cic = CIC::CIC_NUS_7101; break; + case fnv_hash("cic7102"): cic = CIC::CIC_NUS_7102; break; + case fnv_hash("cic7103"): cic = CIC::CIC_NUS_7103; break; + case fnv_hash("cic7105"): cic = CIC::CIC_NUS_7105; break; + case fnv_hash("cic7106"): cic = CIC::CIC_NUS_7106; break; + case fnv_hash("cic8303"): cic = CIC::CIC_NUS_8303; break; + case fnv_hash("cic8401"): cic = CIC::CIC_NUS_8401; break; + case fnv_hash("cic5167"): cic = CIC::CIC_NUS_5167; break; + case fnv_hash("cicDDUS"): cic = CIC::CIC_NUS_DDUS; break; + default: printf("Unknown tag: %s\n", tag); break; + } + } + printf("System: %d, Save Type: %d, CIC: %d, CPak: %d, RPak: %d, TPak %d, RTC: %d\n", (int)system_type, (int)save_type, (int)cic, cpak, rpak, tpak, rtc); + + const auto auto_detect = (AutoDetect)user_io_status_get("[64]"); + + if (auto_detect == AutoDetect::ON) { + printf("Auto-detect is on, updating OSD settings\n"); + + if (system_type != SystemType::UNKNOWN) user_io_status_set("[80:79]", (uint32_t)system_type); + if (cic != CIC::UNKNOWN) user_io_status_set("[68:65]", (uint32_t)cic); + user_io_status_set("[71]", (uint32_t)cpak); + user_io_status_set("[72]", (uint32_t)rpak); + user_io_status_set("[73]", (uint32_t)tpak); + user_io_status_set("[74]", (uint32_t)rtc); + user_io_status_set("[77:75]", (uint32_t)save_type); + } + else { + printf("Auto-detect is off, not updating OSD settings\n"); + } + + return (auto_detect != AutoDetect::ON) || (system_type != SystemType::UNKNOWN && cic != CIC::UNKNOWN); +} + +bool md5_matches(const char* line, const char* md5) { + for (auto i = 0; i < 32; i++) { + if (line[i] == '\0' || md5[i] != tolower(line[i])) + return false; + } + + return true; +} + +bool cart_id_matches(const char* line, const char* cart_id) { + // A valid ID line should start with "ID:" + if (strncmp(line, CARTID_PREFIX, strlen(CARTID_PREFIX)) != 0) + return false; + + // Skip the line if it doesn't match our cart_id, '_' = don't care + auto lp = (char*)line + strlen(CARTID_PREFIX); + for (auto i = 0; i < CARTID_LENGTH; i++, lp++) { + if (*lp != '_' && *lp != cart_id[i]) + return false; // character didn't match + + if (*lp == ' ') // early termination + return true; + } + + return true; +} + +static uint8_t detect_rom_settings_in_db(const char* lookup_hash, const char* db_file_name) { fileTextReader reader = {}; char file_path[1024]; sprintf(file_path, "%s/%s", HomeDir(), db_file_name); - if (!FileOpenTextReader(&reader, file_path)) - { + if (!FileOpenTextReader(&reader, file_path)) { printf("Failed to open N64 data file %s\n", file_path); return false; } @@ -139,90 +223,56 @@ static bool detect_rom_settings_in_db(const char* lookup_hash, const char* db_fi char tags[128]; const char* line; - while ((line = FileReadLine(&reader))) - { - // skip the line if it doesn't start with our hash - if (strncmp(lookup_hash, line, 32) != 0) + while ((line = FileReadLine(&reader))) { + // Skip the line if it doesn't start with our hash + if (!md5_matches(line, lookup_hash)) continue; - if (sscanf(line, "%*s %s", tags) != 1) - { - printf("No tags found.\n"); - continue; - } - printf("Found ROM entry: %s\n", line); - MemoryType save_type = MemoryType::NONE; - SystemType system_type = SystemType::NTSC; - CIC cic = CIC::CIC_NUS_6102; - bool cpak = false; - bool rpak = false; - bool tpak = false; - bool rtc = false; - - const char separator[] = "|"; - - for (char* tag = strtok(tags, separator); tag; tag = strtok(nullptr, separator)) - { - printf("Tag: %s\n", tag); - - normalizeString(tag); - switch (fnv_hash(tag)) - { - case fnv_hash("eeprom512"): save_type = MemoryType::EEPROM_512; break; - case fnv_hash("eeprom2k"): save_type = MemoryType::EEPROM_2k; break; - case fnv_hash("sram32k"): save_type = MemoryType::SRAM_32k; break; - case fnv_hash("sram96k"): save_type = MemoryType::SRAM_96k; break; - case fnv_hash("flash128k"): save_type = MemoryType::FLASH_128k; break; - case fnv_hash("ntsc"): system_type = SystemType::NTSC; break; - case fnv_hash("pal"): system_type = SystemType::PAL; break; - case fnv_hash("cpak"): cpak = true; break; - case fnv_hash("rpak"): rpak = true; break; - case fnv_hash("tpak"): tpak = true; break; - case fnv_hash("rtc"): rtc = true; break; - case fnv_hash("cic6101"): cic = CIC::CIC_NUS_6101; break; - case fnv_hash("cic6102"): cic = CIC::CIC_NUS_6102; break; - case fnv_hash("cic6103"): cic = CIC::CIC_NUS_6103; break; - case fnv_hash("cic6105"): cic = CIC::CIC_NUS_6105; break; - case fnv_hash("cic6106"): cic = CIC::CIC_NUS_6106; break; - case fnv_hash("cic7101"): cic = CIC::CIC_NUS_7101; break; - case fnv_hash("cic7102"): cic = CIC::CIC_NUS_7102; break; - case fnv_hash("cic7103"): cic = CIC::CIC_NUS_7103; break; - case fnv_hash("cic7105"): cic = CIC::CIC_NUS_7105; break; - case fnv_hash("cic7106"): cic = CIC::CIC_NUS_7106; break; - case fnv_hash("cic8303"): cic = CIC::CIC_NUS_8303; break; - case fnv_hash("cic8401"): cic = CIC::CIC_NUS_8401; break; - case fnv_hash("cic5167"): cic = CIC::CIC_NUS_5167; break; - case fnv_hash("cicDDUS"): cic = CIC::CIC_NUS_DDUS; break; - default: printf("Unknown tag: %s\n", tag); - } - } - printf("System: %d, Save Type: %d, CIC: %d, CPak: %d, RPak: %d, TPak %d, RTC: %d\n", (int)system_type, (int)save_type, (int)cic, cpak, rpak, tpak, rtc); - - const auto auto_detect = (AutoDetect)user_io_status_get("[64]"); - - if (auto_detect == AutoDetect::ON) - { - printf("Auto-detect is on, updating OSD settings\n"); - - user_io_status_set("[80:79]", (uint32_t)system_type); - user_io_status_set("[68:65]", (uint32_t)cic); - user_io_status_set("[71]", (uint32_t)cpak); - user_io_status_set("[72]", (uint32_t)rpak); - user_io_status_set("[73]", (uint32_t)tpak); - user_io_status_set("[74]", (uint32_t)rtc); - user_io_status_set("[77:75]", (uint32_t)save_type); - } - else - { - printf("Auto-detect is off, not updating OSD settings\n"); + if (sscanf(line, "%*s %s", tags) != 1) { + printf("No tags found.\n"); + return 2; } - return true; + // 2 = System region and/or CIC wasn't in DB, will need detection + return parse_and_apply_db_tags(tags) ? 3 : 2; } - return false; + return 0; +} + +static uint8_t detect_rom_settings_in_db_with_cartid(const char* cart_id, const char* db_file_name) { + fileTextReader reader = {}; + + char file_path[1024]; + sprintf(file_path, "%s/%s", HomeDir(), db_file_name); + + if (!FileOpenTextReader(&reader, file_path)) { + printf("Failed to open N64 data file %s\n", file_path); + return false; + } + + char tags[128]; + + const char* line; + while ((line = FileReadLine(&reader))) { + // Skip the line if it doesn't start with our ID + if (!cart_id_matches(line, cart_id)) + continue; + + printf("Found ROM entry: %s\n", line); + + if (sscanf(line, "%*s %s", tags) != 1) { + printf("No tags found.\n"); + return 2; + } + + // 2 = System region and/or CIC wasn't in DB, will need detection + return parse_and_apply_db_tags(tags) ? 3 : 2; + } + + return 0; } static const char* DB_FILE_NAMES[] = @@ -231,77 +281,91 @@ static const char* DB_FILE_NAMES[] = "N64-database_user.txt", }; -static bool detect_rom_settings_in_dbs(const char* lookup_hash) -{ - for (const char* db_file_name: DB_FILE_NAMES) - { - if (detect_rom_settings_in_db(lookup_hash, db_file_name)) - return true; +static uint8_t detect_rom_settings_in_dbs_with_md5(const char* lookup) { + for (const char* db_file_name : DB_FILE_NAMES) { + const auto detected = detect_rom_settings_in_db(lookup, db_file_name); + if (detected != 0) return detected; } - return false; + + return 0; } -static bool detect_rom_settings_from_first_chunk(char region_code, uint64_t crc) -{ - SystemType system_type; +static uint8_t detect_rom_settings_in_dbs_with_cartid(const char* lookup) { + if (strlen(lookup) < CARTID_LENGTH) + return 0; + + for (auto i = 0; i < CARTID_LENGTH; i++) { + if ((lookup[i] >= '0' && lookup[i] <= '9') || (lookup[i] >= 'A' && lookup[i] <= 'Z')) + continue; + + return 0; + } + + for (const char* db_file_name : DB_FILE_NAMES) { + const auto detected = detect_rom_settings_in_db_with_cartid(lookup, db_file_name); + if (detected != 0) return detected; + } + + return 0; +} + +static bool detect_rom_settings_from_first_chunk(char region_code, uint64_t crc) { + SystemType system_type = SystemType::NTSC; CIC cic; + bool is_known_cic = true; - const auto auto_detect = (AutoDetect)user_io_status_get("[64]"); - - if (auto_detect != AutoDetect::ON) - { + if ((AutoDetect)user_io_status_get("[64]") != AutoDetect::ON) { printf("Auto-detect is off, not updating OSD settings\n"); return true; } - switch (region_code) - { - case 'D': //Germany - case 'F': //France - case 'H': //Netherlands (Dutch) - case 'I': //Italy - case 'L': //Gateway 64 - case 'P': //Europe - case 'S': //Spain - case 'U': //Australia - case 'W': //Scandinavia - case 'X': //Europe - case 'Y': //Europe + switch (region_code) { + case 'D': // Germany + case 'F': // France + case 'H': // Netherlands (Dutch) + case 'I': // Italy + case 'L': // Gateway 64 + case 'P': // Europe + case 'S': // Spain + case 'U': // Australia + case 'W': // Scandinavia + case 'X': // Europe + case 'Y': // Europe system_type = SystemType::PAL; break; - default: - system_type = SystemType::NTSC; break; } // the following checks assume we're on a little-endian platform - switch (crc) - { + switch (crc) { + default: + printf("Unknown CIC, uses default\n"); + is_known_cic = false; + // fall through case UINT64_C(0x000000a316adc55a): case UINT64_C(0x000000039c981107): // hcs64's CIC-6102 IPL3 replacement case UINT64_C(0x000000a30dacd530): // Unknown. Used in SM64 hacks case UINT64_C(0x000000d2828281b0): // Unknown. Used in some homebrew case UINT64_C(0x0000009acc31e644): // Unknown. Used in some betas and homebrew. Dev boot code? - cic = system_type == SystemType::NTSC + cic = system_type == SystemType::NTSC ? CIC::CIC_NUS_6102 : CIC::CIC_NUS_7101; break; - case UINT64_C(0x000000a405397b05): cic = CIC::CIC_NUS_7102; system_type = SystemType::PAL; break; - case UINT64_C(0x000000a0f26f62fe): cic = CIC::CIC_NUS_6101; system_type = SystemType::NTSC; break; + case UINT64_C(0x000000a405397b05): system_type = SystemType::PAL; cic = CIC::CIC_NUS_7102; break; + case UINT64_C(0x000000a0f26f62fe): system_type = SystemType::NTSC; cic = CIC::CIC_NUS_6101; break; case UINT64_C(0x000000a9229d7c45): - cic = system_type == SystemType::NTSC + cic = system_type == SystemType::NTSC ? CIC::CIC_NUS_6103 : CIC::CIC_NUS_7103; break; case UINT64_C(0x000000f8b860ed00): - cic = system_type == SystemType::NTSC + cic = system_type == SystemType::NTSC ? CIC::CIC_NUS_6105 : CIC::CIC_NUS_7105; break; case UINT64_C(0x000000ba5ba4b8cd): - cic = system_type == SystemType::NTSC + cic = system_type == SystemType::NTSC ? CIC::CIC_NUS_6106 : CIC::CIC_NUS_7106; break; case UINT64_C(0x0000012daafc8aab): cic = CIC::CIC_NUS_5167; break; case UINT64_C(0x000000a9df4b39e1): cic = CIC::CIC_NUS_8303; break; case UINT64_C(0x000000aa764e39e1): cic = CIC::CIC_NUS_8401; break; case UINT64_C(0x000000abb0b739e1): cic = CIC::CIC_NUS_DDUS; break; - default: return false; } printf("System: %d, CIC: %d\n", (int)system_type, (int)cic); @@ -310,22 +374,17 @@ static bool detect_rom_settings_from_first_chunk(char region_code, uint64_t crc) user_io_status_set("[80:79]", (uint32_t)system_type); user_io_status_set("[68:65]", (uint32_t)cic); - return true; + return is_known_cic; } -static void md5_to_hex(uint8_t* in, char* out) -{ - char *p = out; - for (int i = 0; i < 16; i++) - { - sprintf(p, "%02x", in[i]); - p += 2; +static void md5_to_hex(uint8_t* in, char* out) { + for (auto i = 0; i < 16; i++) { + sprintf(out, "%02x", in[i]); + out += 2; } - *p = '\0'; } -int n64_rom_tx(const char* name, unsigned char index) -{ +int n64_rom_tx(const char* name, unsigned char index) { static uint8_t buf[4096]; fileTYPE f = {}; @@ -349,16 +408,20 @@ int n64_rom_tx(const char* name, unsigned char index) process_ss(name); bool is_first_chunk = true; - bool rom_found_in_db = false; - bool cic_detected = false; - RomFormat rom_format = RomFormat::UNKNOWN; + + // 0 = Nothing detected + // 1 = System region and CIC detected + // 2 = Found some ROM info in DB (Save type etc.), but System region and CIC has not been determined + // 3 = Has detected everything, System type, CIC, Save type etc. + uint8_t rom_settings_detected = 0; + RomFormat rom_format; MD5Context ctx; MD5Init(&ctx); uint8_t md5[16]; char md5_hex[40]; - uint64_t ipl3_crc = 0; - char region_code = '\0'; + uint64_t bootcode_sum = 0; + char cart_id[8]; while (bytes2send) { @@ -367,14 +430,15 @@ int n64_rom_tx(const char* name, unsigned char index) FileReadAdv(&f, buf, chunk); // perform sanity checks and detect ROM format - if (is_first_chunk) - { - if (chunk < 4096) - { + if (is_first_chunk) { + if (chunk < 4096) { printf("Failed to load ROM: must be at least 4096 bytes\n"); return 0; } + rom_format = detectRomFormat(buf); + if (rom_format == RomFormat::UNKNOWN) + printf("Unknown ROM format\n"); } // normalize data to big-endian format @@ -382,9 +446,8 @@ int n64_rom_tx(const char* name, unsigned char index) MD5Update(&ctx, buf, chunk); - if (is_first_chunk) - { - // try to detect ROM settings based on header MD5 hash + if (is_first_chunk) { + // Try to detect ROM settings based on header MD5 hash // For calculating the MD5 hash of the header, we need to make a // copy of the context before calling MD5Final, otherwise the file @@ -395,13 +458,28 @@ int n64_rom_tx(const char* name, unsigned char index) md5_to_hex(md5, md5_hex); printf("Header MD5: %s\n", md5_hex); - rom_found_in_db = detect_rom_settings_in_dbs(md5_hex); - if (!rom_found_in_db) - { + rom_settings_detected = detect_rom_settings_in_dbs_with_md5(md5_hex); + if (rom_settings_detected == 0) printf("No ROM information found for header hash: %s\n", md5_hex); - for (size_t i = 0x40 / sizeof(uint32_t); i < 0x1000 / sizeof(uint32_t); i++) ipl3_crc += ((uint32_t*)buf)[i]; - region_code = buf[0x3e]; + + // Calculate boot ROM checksum + for (uint32_t i = 0x40 / sizeof(uint32_t); i < 0x1000 / sizeof(uint32_t); i++) { + bootcode_sum += ((uint32_t*)buf)[i]; } + + /* The first byte (starting at 0x3b) indicates the type of ROM + * 'N' = cart + * 'D' = 64DD disk + * 'C' = cartridge part of expandable game + * 'E' = 64DD expansion for cart + * 'Z' = Aleck64 cart + * The second and third byte form a 2-letter ID for the game + * The fourth byte indicates the region and language for the game + * The fifth byte indicates the revision of the game */ + + strncpy(cart_id, (char*)(buf + 0x3b), 4); + sprintf((char*)(cart_id + 4), "%02X", buf[0x3f]); + printf("Cartridge ID: %s\n", cart_id); } user_io_file_tx_data(buf, chunk); @@ -415,38 +493,44 @@ int n64_rom_tx(const char* name, unsigned char index) md5_to_hex(md5, md5_hex); printf("File MD5: %s\n", md5_hex); - // Try to detect ROM settings from file MD5 if they are not available yet - if (!rom_found_in_db) - { - rom_found_in_db = detect_rom_settings_in_dbs(md5_hex); - if (!rom_found_in_db) printf("No ROM information found for file hash: %s\n", md5_hex); - } + // Try to detect ROM settings from full file MD5 if they're are not detected yet + if (rom_settings_detected == 0) + rom_settings_detected = detect_rom_settings_in_dbs_with_md5(md5_hex); - // Try detect (partial) ROM settings by analyzing the ROM itself. (region, cic and save type) - // Fallback for missing db entries. - if (!rom_found_in_db) - { - cic_detected = detect_rom_settings_from_first_chunk(region_code, ipl3_crc); - if (!cic_detected) printf("Unknown CIC type: %016" PRIX64 "\n", ipl3_crc); + // Try to detect ROM settings by cart ID if they're are not detected yet + if (rom_settings_detected == 0) { + printf("No ROM information found for file hash: %s\n", md5_hex); + rom_settings_detected = detect_rom_settings_in_dbs_with_cartid(cart_id); + if (rom_settings_detected == 0) + printf("No ROM information found for cart ID: %s\n", cart_id); + // Try detect (partial) ROM settings by analyzing the ROM itself. (System region and CIC) + if ((rom_settings_detected == 0 || rom_settings_detected == 2) && + detect_rom_settings_from_first_chunk(cart_id[3], bootcode_sum)) { + rom_settings_detected |= 1; + } + } + // Complement info found in DB with System region and CIC + else if (rom_settings_detected == 2 && + detect_rom_settings_from_first_chunk(cart_id[3], bootcode_sum)) { + rom_settings_detected = 3; } printf("Done.\n"); FileClose(&f); - // mount save state + // Mount save state char file_path[1024]; FileGenerateSavePath(name, file_path); user_io_file_mount(file_path, 0, 1); - // signal end of transmission + // Signal end of transmission user_io_set_download(0); ProgressMessage(0, 0, 0, 0); - if (!rom_found_in_db) - { - if (!cic_detected) Info("auto-detect failed"); - else Info("use database if save is needed"); - } + if (rom_settings_detected == 0 || rom_settings_detected == 2) + Info("Auto-detect failed:\nUnknown CIC type."); + else if (rom_settings_detected == 1) + Info("Auto-detect failed:\nROM missing from database,\nyou might not be able to save.", 5000); return 1; }