Implement 3DS region auto-detect

This commit is contained in:
wheremyfoodat 2023-08-13 18:08:22 +03:00
parent b896d9a4aa
commit 408dbe75a0
6 changed files with 84 additions and 0 deletions

View file

@ -209,10 +209,25 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
codeFile.resize(fileSize);
readFromFile(file, exeFS, codeFile.data(), fileOffset + exeFSHeaderSize, fileSize);
}
} else if (std::strcmp(name, "icon") == 0) {
// Parse icon file to extract region info and more in the future (logo, etc)
std::vector<u8> tmp;
tmp.resize(fileSize);
readFromFile(file, exeFS, tmp.data(), fileOffset + exeFSHeaderSize, fileSize);
if (!parseSMDH(tmp)) {
printf("Failed to parse SMDH!\n");
}
}
}
}
// If no region has been detected for CXI, set the region to USA by default
if (!region.has_value() && partitionIndex == 0) {
printf("No region detected for CXI, defaulting to USA\n");
region = Regions::USA;
}
if (hasRomFS()) {
printf("RomFS offset: %08llX, size: %08llX\n", romFS.offset, romFS.size);
}
@ -221,6 +236,51 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
return true;
}
bool NCCH::parseSMDH(const std::vector<u8>& smdh) {
if (smdh.size() < 0x36C0) {
printf("The cartridge .icon file is too small, considered invalid. Must be 0x36C0 bytes minimum\n");
return false;
}
if (char(smdh[0]) != 'S' || char(smdh[1]) != 'M' || char(smdh[2]) != 'D' || char(smdh[3]) != 'H') {
printf("Invalid SMDH magic!\n");
return false;
}
// Bitmask showing which regions are allowed.
// https://www.3dbrew.org/wiki/SMDH#Region_Lockout
const u32 regionMasks = *(u32*)&smdh[0x2018];
// Detect when games are region free (ie all regions are allowed) for future use
[[maybe_unused]] const bool isRegionFree = (regionMasks & 0x7f) == 0x7f;
// See which countries are allowed
const bool japan = (regionMasks & 0x1) != 0;
const bool northAmerica = (regionMasks & 0x2) != 0;
const bool europe = (regionMasks & 0x4) != 0;
const bool australia = (regionMasks & 0x8) != 0;
const bool china = (regionMasks & 0x10) != 0;
const bool korea = (regionMasks & 0x20) != 0;
const bool taiwan = (regionMasks & 0x40) != 0;
// Based on the allowed regions, set the autodetected 3DS region. We currently prefer English-speaking regions for practical purposes.
// But this should be configurable later.
if (northAmerica) {
region = Regions::USA;
} else if (europe) {
region = Regions::Europe;
} else if (australia) {
region = Regions::Australia;
} else if (japan) {
region = Regions::Japan;
} else if (korea) {
region = Regions::Korea;
} else if (china) {
region = Regions::China;
} else if (taiwan) {
region = Regions::Taiwan;
}
}
std::pair<bool, Crypto::AESKey> NCCH::getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY) {
Crypto::AESKey result;