diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a70e4b4..4250172b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,9 +143,9 @@ source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES}) add_executable(Alber ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${PICA_SOURCE_FILES} ${RENDERER_GL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES}) -target_link_libraries(Alber PRIVATE dynarmic SDL2-static glad) +target_link_libraries(Alber PRIVATE dynarmic SDL2-static glad cryptopp) if(GPU_DEBUG_INFO) target_compile_definitions(Alber PRIVATE GPU_DEBUG_INFO=1) -endif() \ No newline at end of file +endif() diff --git a/include/loader/ncch.hpp b/include/loader/ncch.hpp index 2d05602d..95856e8c 100644 --- a/include/loader/ncch.hpp +++ b/include/loader/ncch.hpp @@ -1,14 +1,22 @@ #pragma once #include +#include #include #include "io_file.hpp" #include "helpers.hpp" +#include "crypto/aes_engine.hpp" struct NCCH { + struct EncryptionInfo { + Crypto::AESKey normalKey; + Crypto::AESKey initialCounter; + }; + struct FSInfo { // Info on the ExeFS/RomFS u64 offset = 0; u64 size = 0; u64 hashRegionSize = 0; + std::optional encryptionInfo; }; // Descriptions for .text, .data and .rodata sections @@ -34,6 +42,8 @@ struct NCCH { bool mountRomFS = false; bool encrypted = false; bool fixedCryptoKey = false; + bool seedCrypto = false; + u8 secondaryKeySlot = 0; static constexpr u64 mediaUnit = 0x200; u64 size = 0; // Size of NCCH converted to bytes @@ -41,6 +51,7 @@ struct NCCH { u32 bssSize = 0; u32 exheaderSize = 0; + FSInfo exheaderInfo; FSInfo exeFS; FSInfo romFS; CodeSetInfo text, data, rodata; @@ -50,10 +61,9 @@ struct NCCH { // Contains of the cart's save data std::vector saveData; - // Header: 0x200 + 0x800 byte NCCH header + exheadr // Returns true on success, false on failure // Partition index/offset/size must have been set before this - bool loadFromHeader(u8* header, IOFile& file); + bool loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info); bool hasExtendedHeader() { return exheaderSize != 0; } bool hasExeFS() { return exeFS.size != 0; } @@ -61,7 +71,8 @@ struct NCCH { bool hasCode() { return codeFile.size() != 0; } bool hasSaveData() { return saveData.size() != 0; } -private: - std::array primaryKey = {}; // For exheader, ExeFS header and icons - std::array secondaryKey = {}; // For RomFS and some files in ExeFS + std::pair getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY); + std::pair getSecondaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY); + + std::pair readFromFile(IOFile& file, const FSInfo &info, u8 *dst, std::size_t offset, std::size_t size); }; \ No newline at end of file diff --git a/include/memory.hpp b/include/memory.hpp index 33b18ca5..3d68866e 100644 --- a/include/memory.hpp +++ b/include/memory.hpp @@ -5,6 +5,7 @@ #include #include #include +#include "crypto/aes_engine.hpp" #include "helpers.hpp" #include "handles.hpp" #include "loader/ncsd.hpp" @@ -146,7 +147,7 @@ public: void* getReadPointer(u32 address); void* getWritePointer(u32 address); std::optional loadELF(std::ifstream& file); - std::optional loadNCSD(const std::filesystem::path& path); + std::optional loadNCSD(Crypto::AESEngine &aesEngine, const std::filesystem::path& path); u8 read8(u32 vaddr); u16 read16(u32 vaddr); diff --git a/src/core/fs/archive_ncch.cpp b/src/core/fs/archive_ncch.cpp index 0c635c21..3a3330b8 100644 --- a/src/core/fs/archive_ncch.cpp +++ b/src/core/fs/archive_ncch.cpp @@ -133,6 +133,8 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size auto cxi = mem.getCXI(); IOFile& ioFile = mem.CXIFile; + NCCH::FSInfo fsInfo; + // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc switch (type) { case PathType::RomFS: { @@ -142,9 +144,8 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size Helpers::panic("Tried to read from NCCH with too big of an offset"); } - if (!ioFile.seek(cxi->fileOffset + romFSOffset + offset + 0x1000)) { - Helpers::panic("Failed to seek while reading from RomFS"); - } + fsInfo = cxi->romFS; + offset += 0x1000; break; } @@ -153,7 +154,7 @@ std::optional NCCHArchive::readFile(FileSession* file, u64 offset, u32 size } std::unique_ptr data(new u8[size]); - auto [success, bytesRead] = ioFile.readBytes(&data[0], size); + auto [success, bytesRead] = cxi->readFromFile(ioFile, fsInfo, &data[0], offset, size); if (!success) { Helpers::panic("Failed to read from NCCH archive"); diff --git a/src/core/fs/archive_self_ncch.cpp b/src/core/fs/archive_self_ncch.cpp index 2146c578..fa141b03 100644 --- a/src/core/fs/archive_self_ncch.cpp +++ b/src/core/fs/archive_self_ncch.cpp @@ -71,6 +71,8 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 auto cxi = mem.getCXI(); IOFile& ioFile = mem.CXIFile; + NCCH::FSInfo fsInfo; + // Seek to file offset depending on if we're reading from RomFS, ExeFS, etc switch (type) { case PathType::RomFS: { @@ -80,9 +82,8 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); } - if (!ioFile.seek(cxi->fileOffset + romFSOffset + offset + 0x1000)) { - Helpers::panic("Failed to seek while reading from RomFS"); - } + fsInfo = cxi->romFS; + offset += 0x1000; break; } @@ -93,9 +94,7 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 Helpers::panic("Tried to read from SelfNCCH with too big of an offset"); } - if (!ioFile.seek(cxi->fileOffset + exeFSOffset + offset)) { // TODO: Not sure if this needs the + 0x1000 - Helpers::panic("Failed to seek while reading from ExeFS"); - } + fsInfo = cxi->exeFS; break; } @@ -104,7 +103,7 @@ std::optional SelfNCCHArchive::readFile(FileSession* file, u64 offset, u32 } std::unique_ptr data(new u8[size]); - auto [success, bytesRead] = ioFile.readBytes(&data[0], size); + auto [success, bytesRead] = cxi->readFromFile(ioFile, fsInfo, &data[0], offset, size); if (!success) { Helpers::panic("Failed to read from SelfNCCH archive"); diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c3dd8d2e..196a29d4 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -1,142 +1,303 @@ +#include +#include #include #include #include "loader/lz77.hpp" #include "loader/ncch.hpp" #include "memory.hpp" -bool NCCH::loadFromHeader(u8* header, IOFile& file) { - if (header[0x100] != 'N' || header[0x101] != 'C' || header[0x102] != 'C' || header[0x103] != 'H') { - printf("Invalid header on NCCH\n"); +#include + +bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info) { + // 0x200 bytes for the NCCH header + constexpr u64 headerSize = 0x200; + u8 header[headerSize]; + + auto [success, bytes] = readFromFile(file, info, header, 0, headerSize); + if (!success || bytes != headerSize) { + printf("Failed to read NCCH header\n"); return false; } + + if (header[0x100] != 'N' || header[0x101] != 'C' || header[0x102] != 'C' || header[0x103] != 'H') { + printf("Invalid header on NCCH\n"); + return false; + } - codeFile.clear(); - saveData.clear(); + codeFile.clear(); + saveData.clear(); - size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break - exheaderSize = *(u32*)&header[0x180]; + size = u64(*(u32*)&header[0x104]) * mediaUnit; // TODO: Maybe don't type pun because big endian will break + exheaderSize = *(u32*)&header[0x180]; - const u64 programID = *(u64*)&header[0x118]; + const u64 programID = *(u64*)&header[0x118]; - // Read NCCH flags - isNew3DS = header[0x188 + 4] == 2; - fixedCryptoKey = (header[0x188 + 7] & 0x1) == 0x1; - mountRomFS = (header[0x188 + 7] & 0x2) != 0x2; - encrypted = (header[0x188 + 7] & 0x4) != 0x4; - - // Read ExeFS and RomFS info - exeFS.offset = u64(*(u32*)&header[0x1A0]) * mediaUnit; - exeFS.size = u64(*(u32*)&header[0x1A4]) * mediaUnit; - exeFS.hashRegionSize = u64(*(u32*)&header[0x1A8]) * mediaUnit; + // Read NCCH flags + secondaryKeySlot = header[0x188 + 3]; + isNew3DS = header[0x188 + 4] == 2; + fixedCryptoKey = (header[0x188 + 7] & 0x1) == 0x1; + mountRomFS = (header[0x188 + 7] & 0x2) != 0x2; + encrypted = (header[0x188 + 7] & 0x4) != 0x4; + seedCrypto = (header[0x188 + 7] & 0x20) == 0x20; - romFS.offset = u64(*(u32*)&header[0x1B0]) * mediaUnit; - romFS.size = u64(*(u32*)&header[0x1B4]) * mediaUnit; - romFS.hashRegionSize = u64(*(u32*)&header[0x1B8]) * mediaUnit; + // Read exheader, ExeFS and RomFS info + exheaderInfo.offset = info.offset + 0x200; + exheaderInfo.size = exheaderSize; + exheaderInfo.hashRegionSize = 0; - if (fixedCryptoKey) { - Helpers::panic("Fixed crypto keys for NCCH"); - } + exeFS.offset = info.offset + u64(*(u32*)&header[0x1A0]) * mediaUnit; + exeFS.size = u64(*(u32*)&header[0x1A4]) * mediaUnit; + exeFS.hashRegionSize = u64(*(u32*)&header[0x1A8]) * mediaUnit; - if (exheaderSize != 0) { - const u8* exheader = &header[0x200]; // Extended NCCH header - const u64 jumpID = *(u64*)&exheader[0x1C0 + 0x8]; + romFS.offset = info.offset + u64(*(u32*)&header[0x1B0]) * mediaUnit; + romFS.size = u64(*(u32*)&header[0x1B4]) * mediaUnit; + romFS.hashRegionSize = u64(*(u32*)&header[0x1B8]) * mediaUnit; - // It seems like some decryption tools will decrypt the file, without actually setting the NoCrypto flag in the NCCH header - // This is a nice and easy hack to see if a file is pretending to be encrypted, taken from 3DMoo and Citra - if (u32(programID) == u32(jumpID) && encrypted) { - printf("NCSD is supposedly ecrypted but not actually encrypted\n"); - encrypted = false; - } else if (encrypted) { - Helpers::panic("Encrypted NCSD file"); - } + if (encrypted) { + Crypto::AESKey primaryKeyY; + Crypto::AESKey secondaryKeyY; + std::memcpy(primaryKeyY.data(), header, primaryKeyY.size()); - const u64 saveDataSize = *(u64*)&exheader[0x1C0 + 0x0]; // Size of save data in bytes - saveData.resize(saveDataSize, 0xff); + if (!seedCrypto) { + secondaryKeyY = primaryKeyY; + } else { + Helpers::panic("Seed crypto is not supported"); + return false; + } - compressCode = (exheader[0xD] & 1) != 0; - stackSize = *(u32*)&exheader[0x1C]; - bssSize = *(u32*)&exheader[0x3C]; + auto primaryResult = getPrimaryKey(aesEngine, primaryKeyY); - text.extract(&exheader[0x10]); - rodata.extract(&exheader[0x20]); - data.extract(&exheader[0x30]); - } + if (!primaryResult.first) { + Helpers::panic("getPrimaryKey failed!"); + return false; + } - printf("Stack size: %08X\nBSS size: %08X\n", stackSize, bssSize); + Crypto::AESKey primaryKey = primaryResult.second; - // Read ExeFS - if (hasExeFS()) { - u64 exeFSOffset = fileOffset + exeFS.offset; // Offset of ExeFS in the file = exeFS offset + ncch offset - printf("ExeFS offset: %08llX, size: %08llX (Offset in file = %08llX)\n", exeFS.offset, exeFS.size, exeFSOffset); - constexpr size_t exeFSHeaderSize = 0x200; + auto secondaryResult = getSecondaryKey(aesEngine, secondaryKeyY); - u8 exeFSHeader[exeFSHeaderSize]; + if (!secondaryResult.first) { + Helpers::panic("getSecondaryKey failed!"); + return false; + } - file.seek(exeFSOffset); - auto [success, bytes] = file.readBytes(exeFSHeader, exeFSHeaderSize); - if (!success || bytes != exeFSHeaderSize) { - printf("Failed to parse ExeFS header\n"); - return false; - } + Crypto::AESKey secondaryKey = secondaryResult.second; - // ExeFS format allows up to 10 files - for (int i = 0; i < 10; i++) { - u8* fileInfo = &exeFSHeader[i * 16]; + EncryptionInfo encryptionInfoTmp; + encryptionInfoTmp.normalKey = primaryKey; + encryptionInfoTmp.initialCounter.fill(0); - char name[9]; - std::memcpy(name, fileInfo, 8); // Get file name as a string - name[8] = '\0'; // Add null terminator to it just in case there's none + for (std::size_t i = 1; i <= sizeof(std::uint64_t) - 1; i++) { + encryptionInfoTmp.initialCounter[i] = header[0x108 + sizeof(std::uint64_t) - 1 - i]; + } + encryptionInfoTmp.initialCounter[8] = 1; + exheaderInfo.encryptionInfo = encryptionInfoTmp; - u32 fileOffset = *(u32*)&fileInfo[0x8]; - u32 fileSize = *(u32*)&fileInfo[0xC]; + encryptionInfoTmp.initialCounter[8] = 2; + exeFS.encryptionInfo = encryptionInfoTmp; - if (fileSize != 0) { - printf("File %d. Name: %s, Size: %08X, Offset: %08X\n", i, name, fileSize, fileOffset); - } + encryptionInfoTmp.normalKey = secondaryKey; + encryptionInfoTmp.initialCounter[8] = 3; + romFS.encryptionInfo = encryptionInfoTmp; + } - if (std::strcmp(name, ".code") == 0) { - if (hasCode()) { - Helpers::panic("Second code file in a single NCCH partition. What should this do?\n"); - } + if (exheaderSize != 0) { + u8 exheader[exheaderSize]; - if (compressCode) { - std::vector tmp; - tmp.resize(fileSize); + auto [success, bytes] = readFromFile(file, info, exheader, 0x200, exheaderSize); + if (!success || bytes != exheaderSize) { + printf("Failed to read Extended NCCH header\n"); + return false; + } - // A file offset of 0 means our file is located right after the ExeFS header - // So in the ROM, files are located at (file offset + exeFS offset + exeFS header size) - file.seek(exeFSOffset + exeFSHeaderSize + fileOffset); - file.readBytes(tmp.data(), fileSize); - - // Decompress .code file from the tmp vector to the "code" vector - if (!CartLZ77::decompress(codeFile, tmp)) { - printf("Failed to decompress .code file\n"); - return false; - } - } else { - codeFile.resize(fileSize); - file.seek(exeFSOffset + exeFSHeaderSize + fileOffset); - file.readBytes(codeFile.data(), fileSize); - } - } - } - } + const u64 jumpID = *(u64*)&exheader[0x1C0 + 0x8]; - if (hasRomFS()) { - printf("RomFS offset: %08llX, size: %08llX\n", romFS.offset, romFS.size); - } + // It seems like some decryption tools will decrypt the file, without actually setting the NoCrypto flag in the NCCH header + // This is a nice and easy hack to see if a file is pretending to be encrypted, taken from 3DMoo and Citra + if (u32(programID) == u32(jumpID) && encrypted) { + printf("NCSD is supposedly ecrypted but not actually encrypted\n"); + encrypted = false; + } + // If it's truely encrypted, we need to read section again. + if (encrypted) { + auto [success, bytes] = readFromFile(file, exheaderInfo, exheader, 0, exheaderSize); + if (!success || bytes != exheaderSize) { + printf("Failed to read Extended NCCH header\n"); + return false; + } + } - if (stackSize != 0 && stackSize != VirtualAddrs::DefaultStackSize) { - Helpers::warn("Requested stack size is %08X bytes. Temporarily emulated as 0x4000 until adjustable sizes are added\n", stackSize); - } + const u64 saveDataSize = *(u64*)&exheader[0x1C0 + 0x0]; // Size of save data in bytes + saveData.resize(saveDataSize, 0xff); - if (encrypted) { - if (hasExeFS()) - Helpers::panic("Encrypted NCCH partition with ExeFS"); - else - printf("Encrypted NCCH partition. Hopefully not required because it doesn't have an ExeFS. Skipped\n"); - } + compressCode = (exheader[0xD] & 1) != 0; + stackSize = *(u32*)&exheader[0x1C]; + bssSize = *(u32*)&exheader[0x3C]; - initialized = true; - return true; + text.extract(&exheader[0x10]); + rodata.extract(&exheader[0x20]); + data.extract(&exheader[0x30]); + } + + printf("Stack size: %08X\nBSS size: %08X\n", stackSize, bssSize); + + // Read ExeFS + if (hasExeFS()) { + u64 exeFSOffset = fileOffset + exeFS.offset; // Offset of ExeFS in the file = exeFS offset + ncch offset + printf("ExeFS offset: %08llX, size: %08llX (Offset in file = %08llX)\n", exeFS.offset, exeFS.size, exeFSOffset); + constexpr size_t exeFSHeaderSize = 0x200; + + u8 exeFSHeader[exeFSHeaderSize]; + + auto [success, bytes] = readFromFile(file, exeFS, exeFSHeader, 0, exeFSHeaderSize); + if (!success || bytes != exeFSHeaderSize) { + printf("Failed to parse ExeFS header\n"); + return false; + } + + // ExeFS format allows up to 10 files + for (int i = 0; i < 10; i++) { + u8* fileInfo = &exeFSHeader[i * 16]; + + char name[9]; + std::memcpy(name, fileInfo, 8); // Get file name as a string + name[8] = '\0'; // Add null terminator to it just in case there's none + + u32 fileOffset = *(u32*)&fileInfo[0x8]; + u32 fileSize = *(u32*)&fileInfo[0xC]; + + if (fileSize != 0) { + printf("File %d. Name: %s, Size: %08X, Offset: %08X\n", i, name, fileSize, fileOffset); + } + + if (std::strcmp(name, ".code") == 0) { + if (hasCode()) { + Helpers::panic("Second code file in a single NCCH partition. What should this do?\n"); + } + + if (compressCode) { + std::vector tmp; + tmp.resize(fileSize); + + // A file offset of 0 means our file is located right after the ExeFS header + // So in the ROM, files are located at (file offset + exeFS offset + exeFS header size) + readFromFile(file, exeFS, tmp.data(), fileOffset + exeFSHeaderSize, fileSize); + + // Decompress .code file from the tmp vector to the "code" vector + if (!CartLZ77::decompress(codeFile, tmp)) { + printf("Failed to decompress .code file\n"); + return false; + } + } else { + codeFile.resize(fileSize); + readFromFile(file, exeFS, codeFile.data(), fileOffset + exeFSHeaderSize, fileSize); + } + } + } + } + + if (hasRomFS()) { + printf("RomFS offset: %08llX, size: %08llX\n", romFS.offset, romFS.size); + } + + if (stackSize != 0 && stackSize != VirtualAddrs::DefaultStackSize) { + Helpers::warn("Requested stack size is %08X bytes. Temporarily emulated as 0x4000 until adjustable sizes are added\n", stackSize); + } + + initialized = true; + return true; +} + +std::pair NCCH::getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY) { + Crypto::AESKey result; + + if (encrypted) { + if (fixedCryptoKey) { + return {true, result}; + } + + aesEngine.setKeyY(Crypto::KeySlotId::NCCHKey0, keyY); + + if (!aesEngine.hasNormalKey(Crypto::KeySlotId::NCCHKey0)) { + return {false, result}; + } + + result = aesEngine.getNormalKey(Crypto::KeySlotId::NCCHKey0); + } + + return {true, result}; +} + +std::pair NCCH::getSecondaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY) { + Crypto::AESKey result; + + if (encrypted) { + + if (fixedCryptoKey) { + return {true, result}; + } + + Crypto::KeySlotId keySlotId; + + switch (secondaryKeySlot) { + case 0: + keySlotId = Crypto::KeySlotId::NCCHKey0; + break; + case 1: + keySlotId = Crypto::KeySlotId::NCCHKey1; + break; + case 10: + keySlotId = Crypto::KeySlotId::NCCHKey2; + break; + case 11: + keySlotId = Crypto::KeySlotId::NCCHKey3; + break; + default: + return {false, result}; + } + + if (!aesEngine.hasKeyX(keySlotId)) { + return {false, result}; + } + + aesEngine.setKeyY(keySlotId, keyY); + + if (!aesEngine.hasNormalKey(keySlotId)) { + return {false, result}; + } + + result = aesEngine.getNormalKey(keySlotId); + } + + return {true, result}; +} + +std::pair NCCH::readFromFile(IOFile& file, const FSInfo &info, u8 *dst, std::size_t offset, std::size_t size) { + if (size == 0) { + return { true, 0 }; + } + + std::size_t readMaxSize = std::min(size, static_cast(info.size) - offset); + + file.seek(info.offset + offset); + auto [success, bytes] = file.readBytes(dst, readMaxSize); + + if (!success) { + return { success, bytes}; + } + + if (success && info.encryptionInfo.has_value()) { + auto& encryptionInfo = info.encryptionInfo.value(); + + CryptoPP::CTR_Mode::Decryption d(encryptionInfo.normalKey.data(), encryptionInfo.normalKey.size(), encryptionInfo.initialCounter.data()); + + if (offset > 0) { + d.Seek(offset); + } + + CryptoPP::byte* data = reinterpret_cast(dst); + d.ProcessData(data, data, bytes); + } + + return { success, bytes}; } \ No newline at end of file diff --git a/src/core/loader/ncsd.cpp b/src/core/loader/ncsd.cpp index bf93f490..1166b3fe 100644 --- a/src/core/loader/ncsd.cpp +++ b/src/core/loader/ncsd.cpp @@ -3,7 +3,7 @@ #include "loader/ncsd.hpp" #include "memory.hpp" -std::optional Memory::loadNCSD(const std::filesystem::path& path) { +std::optional Memory::loadNCSD(Crypto::AESEngine &aesEngine, const std::filesystem::path& path) { NCSD ncsd; if (!ncsd.file.open(path, "rb")) return std::nullopt; @@ -51,18 +51,12 @@ std::optional Memory::loadNCSD(const std::filesystem::path& path) { ncch.fileOffset = partition.offset; if (partition.length != 0) { // Initialize the NCCH of each partition - ncsd.file.seek(partition.offset); + NCCH::FSInfo ncchFsInfo; - // 0x200 bytes for the NCCH header and another 0x800 for the exheader - constexpr u64 headerSize = 0x200 + 0x800; - u8 ncchHeader[headerSize]; - std::tie(success, bytes) = ncsd.file.readBytes(ncchHeader, headerSize); - if (!success || bytes != headerSize) { - printf("Failed to read NCCH header\n"); - return std::nullopt; - } + ncchFsInfo.offset = partition.offset; + ncchFsInfo.size = partition.length; - if (!ncch.loadFromHeader(ncchHeader, ncsd.file)) { + if (!ncch.loadFromHeader(aesEngine, ncsd.file, ncchFsInfo)) { printf("Invalid NCCH partition\n"); return std::nullopt; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 92a9fb30..f37e8825 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -161,7 +161,7 @@ bool Emulator::loadROM(const std::filesystem::path& path) { bool Emulator::loadNCSD(const std::filesystem::path& path) { romType = ROMType::NCSD; - std::optional opt = memory.loadNCSD(path); + std::optional opt = memory.loadNCSD(aesEngine, path); if (!opt.has_value()) { return false;