diff options
author | Jesse Talavera-Greenberg <jesse@jesse.tg> | 2023-10-11 11:20:05 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-11 17:20:05 +0200 |
commit | d4e51f80601f57399db49f1010c45427bd2bf3c4 (patch) | |
tree | 206985e7bedfc1755941eeba26d6605d4e03fa0a /src | |
parent | b2fcff97c186cc9db263089acd4810ea7d58517d (diff) |
Refactor DSi_NAND (#1844)
* Refactor diskio's contents
- Change ff_disk_read_cb/write_cb into a std::function instead of a raw pointer
- Add const specifiers as needed
* Refactor DSi_NAND to manage the file system's mounted lifetime with RAII
* Split NANDMount into NANDMount and NANDImage
- NANDImage is used for information about the NAND that doesn't require decryption or filesystem access
- NANDMount is used to actually access the file system
- Both classes manage their respective resources (the NAND file handle and the NAND's mount) with RAII
- Also split the file loading into another function that I will remove in a later PR
* Make NANDMount immovable
* Remove NAND-loading code that I had sectioned off into a function
- Incomplete copypasta
- I must have gotten distracted
* Tidy up NANDImage's initialization
- Don't unmount the disk image if the constructor fails (that's NANDMount's job now)
- Only assign CurFile if the constructor succeeds
* Add some const-correctness
* Move DSi NAND initialization to the frontend
- The NANDImage is now installed via a unique_ptr in DSi
* Remove Platform::DSi_NANDPath
- Not Config::DSiNANDPath; that can still be configured as usual
- The core no longer needs to care
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/DSi.cpp | 100 | ||||
-rw-r--r-- | src/DSi.h | 9 | ||||
-rw-r--r-- | src/DSi_AES.cpp | 14 | ||||
-rw-r--r-- | src/DSi_AES.h | 4 | ||||
-rw-r--r-- | src/DSi_NAND.cpp | 236 | ||||
-rw-r--r-- | src/DSi_NAND.h | 107 | ||||
-rw-r--r-- | src/DSi_SD.cpp | 39 | ||||
-rw-r--r-- | src/DSi_SD.h | 11 | ||||
-rw-r--r-- | src/FATIO.cpp (renamed from src/fatfs/diskio.c) | 37 | ||||
-rw-r--r-- | src/FATIO.h | 34 | ||||
-rw-r--r-- | src/FATStorage.cpp | 5 | ||||
-rw-r--r-- | src/FATStorage.h | 4 | ||||
-rw-r--r-- | src/Platform.h | 2 | ||||
-rw-r--r-- | src/fatfs/ff.h | 10 | ||||
-rw-r--r-- | src/frontend/qt_sdl/Platform.cpp | 2 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.cpp | 51 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.h | 2 | ||||
-rw-r--r-- | src/frontend/qt_sdl/TitleManagerDialog.cpp | 51 | ||||
-rw-r--r-- | src/frontend/qt_sdl/TitleManagerDialog.h | 15 |
20 files changed, 443 insertions, 292 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 850ec7c..0fa9684 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(core STATIC DSi_NWifi.cpp DSi_SD.cpp DSi_SPI_TSC.cpp + FATIO.cpp FATStorage.cpp FIFO.h GBACart.cpp @@ -51,7 +52,6 @@ add_library(core STATIC Wifi.cpp WifiAP.cpp - fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c diff --git a/src/DSi.cpp b/src/DSi.cpp index cdec819..17d79a6 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -75,12 +75,10 @@ u32 NWRAMMask[2][3]; u32 NDMACnt[2]; DSi_NDMA* NDMAs[8]; +std::unique_ptr<DSi_NAND::NANDImage> NANDImage; DSi_SDHost* SDMMC; DSi_SDHost* SDIO; -u64 ConsoleID; -u8 eMMC_CID[16]; - // FIXME: these currently have no effect (and aren't stored in a savestate) // ... not that they matter all that much u8 GPIO_Data; @@ -149,6 +147,10 @@ void DeInit() SDMMC = nullptr; delete SDIO; SDIO = nullptr; + + NANDImage = nullptr; + // The NANDImage is cleaned up (and its underlying file closed) + // as part of unique_ptr's destructor } void Reset() @@ -528,24 +530,25 @@ void SetupDirectBoot() ARM9Write32(0x02FFE000+i, tmp); } - if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) - { - DSi_NAND::DSiFirmwareSystemSettings userdata {}; - DSi_NAND::ReadUserData(userdata); - for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - - DSi_NAND::DSiSerialData hwinfoS {}; - DSi_NAND::DSiHardwareInfoN hwinfoN; - DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); + if (NANDImage && *NANDImage) + { // If a NAND image is installed, and it's valid... + if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage)) + { + DSi_NAND::DSiFirmwareSystemSettings userdata {}; + nand.ReadUserData(userdata); + for (u32 i = 0; i < 0x128; i+=4) + ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - for (u32 i = 0; i < 0x14; i+=4) - ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); + DSi_NAND::DSiSerialData hwinfoS {}; + DSi_NAND::DSiHardwareInfoN hwinfoN; + nand.ReadHardwareInfo(hwinfoS, hwinfoN); - for (u32 i = 0; i < 0x18; i+=4) - ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); + for (u32 i = 0; i < 0x14; i+=4) + ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); - DSi_NAND::DeInit(); + for (u32 i = 0; i < 0x18; i+=4) + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); + } } SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard; @@ -728,15 +731,21 @@ void SoftReset() bool LoadNAND() { + if (!NANDImage) + { + Log(LogLevel::Error, "No NAND image loaded\n"); + return false; + } Log(LogLevel::Info, "Loading DSi NAND\n"); - if (!DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) + DSi_NAND::NANDMount nandmount(*NANDImage); + if (!nandmount) { Log(LogLevel::Error, "Failed to load DSi NAND\n"); return false; } - FileHandle* nand = DSi_NAND::GetFile(); + FileHandle* nand = NANDImage->GetFile(); // Make sure NWRAM is accessible. // The Bits are set to the startup values in Reset() and we might @@ -892,13 +901,9 @@ bool LoadNAND() } } -#define printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } -#define printhex_rev(str, size) { for (int z = (size)-1; z >= 0; z--) printf("%02X", (str)[z]); printf("\n"); } - - DSi_NAND::GetIDs(eMMC_CID, ConsoleID); - - Log(LogLevel::Debug, "eMMC CID: "); printhex(eMMC_CID, 16); - Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", ConsoleID); + const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID(); + Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); + Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID()); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -909,10 +914,10 @@ bool LoadNAND() else { u32 eaddr = 0x03FFE6E4; - ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); - ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); - ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); - ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); + ARM7Write32(eaddr+0x00, *(const u32*)&emmccid[0]); + ARM7Write32(eaddr+0x04, *(const u32*)&emmccid[4]); + ARM7Write32(eaddr+0x08, *(const u32*)&emmccid[8]); + ARM7Write32(eaddr+0x0C, *(const u32*)&emmccid[12]); ARM7Write16(eaddr+0x2C, 0x0001); ARM7Write16(eaddr+0x2E, 0x0001); ARM7Write16(eaddr+0x3C, 0x0100); @@ -939,9 +944,7 @@ bool LoadNAND() NDS::ARM7->JumpTo(bootparams[6]); } - DSi_NAND::PatchUserData(); - - DSi_NAND::DeInit(); + nandmount.PatchUserData(); return true; } @@ -2690,6 +2693,7 @@ void ARM9IOWrite32(u32 addr, u32 val) u8 ARM7IORead8(u32 addr) { + switch (addr) { case 0x04004000: @@ -2710,14 +2714,14 @@ u8 ARM7IORead8(u32 addr) case 0x04004500: return DSi_I2C::ReadData(); case 0x04004501: return DSi_I2C::Cnt; - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFF; - case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 8) & 0xFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFF; - case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 24) & 0xFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFF; - case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 40) & 0xFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 48) & 0xFF; - case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 56; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; + case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF; + case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF; + case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF; + case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; case 0x04004D08: return 0; case 0x4004700: return DSi_DSP::SNDExCnt; @@ -2757,10 +2761,10 @@ u16 ARM7IORead16(u32 addr) CASE_READ16_32BIT(0x0400405C, MBK[1][7]) CASE_READ16_32BIT(0x04004060, MBK[1][8]) - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFFFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 48; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; case 0x04004D08: return 0; case 0x4004700: return DSi_DSP::SNDExCnt; @@ -2836,8 +2840,8 @@ u32 ARM7IORead32(u32 addr) case 0x04004400: return DSi_AES::ReadCnt(); case 0x0400440C: return DSi_AES::ReadOutputFIFO(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFFFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 32; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; case 0x04004D08: return 0; case 0x4004700: @@ -22,6 +22,11 @@ #include "NDS.h" #include "DSi_SD.h" +namespace DSi_NAND +{ + class NANDImage; +} + namespace DSi { @@ -33,9 +38,7 @@ extern u32 SCFG_EXT[2]; extern u8 ARM9iBIOS[0x10000]; extern u8 ARM7iBIOS[0x10000]; -extern u8 eMMC_CID[16]; -extern u64 ConsoleID; - +extern std::unique_ptr<DSi_NAND::NANDImage> NANDImage; extern DSi_SDHost* SDMMC; extern DSi_SDHost* SDIO; diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 5b1fc53..67b84ec 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -19,6 +19,7 @@ #include <stdio.h> #include <string.h> #include "DSi.h" +#include "DSi_NAND.h" #include "DSi_AES.h" #include "FIFO.h" #include "tiny-AES-c/aes.hpp" @@ -129,6 +130,7 @@ void Reset() OutputMACDue = false; // initialize keys + u64 consoleid = DSi::NANDImage->GetConsoleID(); // slot 0: modcrypt *(u32*)&KeyX[0][0] = 0x746E694E; @@ -137,14 +139,14 @@ void Reset() // slot 1: 'Tad'/dev.kp *(u32*)&KeyX[1][0] = 0x4E00004A; *(u32*)&KeyX[1][4] = 0x4A00004E; - *(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72; - *(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID; + *(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72; + *(u32*)&KeyX[1][12] = (u32)consoleid; // slot 3: console-unique eMMC crypto - *(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID; - *(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906; - *(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D; - *(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32); + *(u32*)&KeyX[3][0] = (u32)consoleid; + *(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906; + *(u32*)&KeyX[3][8] = (u32)(consoleid >> 32) ^ 0xE65B601D; + *(u32*)&KeyX[3][12] = (u32)(consoleid >> 32); *(u32*)&KeyY[3][0] = 0x0AB9DC76; *(u32*)&KeyY[3][4] = 0xBD4DC4D3; *(u32*)&KeyY[3][8] = 0x202DDD1D; diff --git a/src/DSi_AES.h b/src/DSi_AES.h index f3aa550..4ee9bdd 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -26,12 +26,12 @@ #pragma GCC diagnostic ignored "-Wattributes" #if defined(__GNUC__) && (__GNUC__ >= 11) // gcc 11.* // NOTE: Yes, the compiler does *not* recognize this code pattern, so it is indeed an optimization. -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { *(__int128*)Dst = __builtin_bswap128(*(__int128*)Src); } #else -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { for (int i = 0; i < 16; ++i) { diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 7c3e745..03bed9b 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -22,6 +22,7 @@ #include "DSi.h" #include "DSi_AES.h" #include "DSi_NAND.h" +#include "FATIO.h" #include "Platform.h" #include "sha1/sha1.hpp" @@ -34,78 +35,16 @@ using namespace Platform; namespace DSi_NAND { -FileHandle* CurFile; -FATFS CurFS; - -u8 eMMC_CID[16]; -u64 ConsoleID; - -u8 FATIV[16]; -u8 FATKey[16]; - -u8 ESKey[16]; - - -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num); - - -bool Init(u8* es_keyY) +NANDImage::NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept : NANDImage(nandfile, es_keyY.data()) { - CurFile = nullptr; - - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - if ((!nandfile) && (Platform::InstanceID() > 0)) - { - FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); - if (!orig) - { - Log(LogLevel::Error, "Failed to open DSi NAND\n"); - return false; - } - - long len = FileLength(orig); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWrite); - if (nandfile) - { - u8* tmpbuf = new u8[0x10000]; - for (long i = 0; i < len; i+=0x10000) - { - long blklen = 0x10000; - if ((i+blklen) > len) blklen = len-i; - - FileRead(tmpbuf, blklen, 1, orig); - FileWrite(tmpbuf, blklen, 1, nandfile); - } - delete[] tmpbuf; - } - - Platform::CloseFile(orig); - Platform::CloseFile(nandfile); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - } +} +NANDImage::NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept +{ if (!nandfile) - return false; - - u64 nandlen = FileLength(nandfile); - - ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9)); + return; - FRESULT res; - res = f_mount(&CurFS, "0:", 0); - if (res != FR_OK) - { - Log(LogLevel::Error, "NAND mounting failed: %d\n", res); - f_unmount("0:"); - ff_disk_close(); - return false; - } + Length = FileLength(nandfile); // read the nocash footer @@ -113,26 +52,24 @@ bool Init(u8* es_keyY) char nand_footer[16]; const char* nand_footer_ref = "DSi eMMC CID/CPU"; - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { // There is another copy of the footer at 000FF800h for the case // that by external tools the image was cut off // See https://problemkaputt.de/gbatek.htm#dsisdmmcimages FileSeek(nandfile, 0x000FF800, FileSeekOrigin::Start); - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { Log(LogLevel::Error, "ERROR: NAND missing nocash footer\n"); CloseFile(nandfile); - f_unmount("0:"); - ff_disk_close(); - return false; + return; } } - FileRead(eMMC_CID, 1, 16, nandfile); - FileRead(&ConsoleID, 1, 8, nandfile); + FileRead(eMMC_CID.data(), 1, sizeof(eMMC_CID), nandfile); + FileRead(&ConsoleID, 1, sizeof(ConsoleID), nandfile); // init NAND crypto @@ -141,10 +78,10 @@ bool Init(u8* es_keyY) u8 keyX[16], keyY[16]; SHA1Init(&sha); - SHA1Update(&sha, eMMC_CID, 16); + SHA1Update(&sha, eMMC_CID.data(), sizeof(eMMC_CID)); SHA1Final(tmp, &sha); - Bswap128(FATIV, tmp); + Bswap128(FATIV.data(), tmp); *(u32*)&keyX[0] = (u32)ConsoleID; *(u32*)&keyX[4] = (u32)ConsoleID ^ 0x24EE6906; @@ -157,7 +94,7 @@ bool Init(u8* es_keyY) *(u32*)&keyY[12] = 0xE1A00005; DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(FATKey, tmp); + Bswap128(FATKey.data(), tmp); *(u32*)&keyX[0] = 0x4E00004A; @@ -165,42 +102,87 @@ bool Init(u8* es_keyY) *(u32*)&keyX[8] = (u32)(ConsoleID >> 32) ^ 0xC80C4B72; *(u32*)&keyX[12] = (u32)ConsoleID; - memcpy(keyY, es_keyY, 16); + memcpy(keyY, es_keyY, sizeof(keyY)); DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(ESKey, tmp); + Bswap128(ESKey.data(), tmp); CurFile = nandfile; - return true; } -void DeInit() +NANDImage::~NANDImage() { - f_unmount("0:"); - ff_disk_close(); - if (CurFile) CloseFile(CurFile); CurFile = nullptr; } +NANDImage::NANDImage(NANDImage&& other) noexcept : + CurFile(other.CurFile), + eMMC_CID(other.eMMC_CID), + ConsoleID(other.ConsoleID), + FATIV(other.FATIV), + FATKey(other.FATKey), + ESKey(other.ESKey) +{ + other.CurFile = nullptr; +} + +NANDImage& NANDImage::operator=(NANDImage&& other) noexcept +{ + if (this != &other) + { + CurFile = other.CurFile; + eMMC_CID = other.eMMC_CID; + ConsoleID = other.ConsoleID; + FATIV = other.FATIV; + FATKey = other.FATKey; + ESKey = other.ESKey; + + other.CurFile = nullptr; + } + + return *this; +} -FileHandle* GetFile() +NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand) { - return CurFile; + if (!nand) + return; + + CurFS = std::make_unique<FATFS>(); + ff_disk_open( + [this](BYTE* buf, LBA_t sector, UINT num) { + return this->FF_ReadNAND(buf, sector, num); + }, + [this](const BYTE* buf, LBA_t sector, UINT num) { + return this->FF_WriteNAND(buf, sector, num); + }, + (LBA_t)(nand.GetLength()>>9) + ); + + FRESULT res; + res = f_mount(CurFS.get(), "0:", 0); + if (res != FR_OK) + { + Log(LogLevel::Error, "NAND mounting failed: %d\n", res); + f_unmount("0:"); + ff_disk_close(); + return; + } } -void GetIDs(u8* emmc_cid, u64& consoleid) +NANDMount::~NANDMount() { - memcpy(emmc_cid, eMMC_CID, 16); - consoleid = ConsoleID; + f_unmount("0:"); + ff_disk_close(); } -void SetupFATCrypto(AES_ctx* ctx, u32 ctr) +void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr) { u8 iv[16]; - memcpy(iv, FATIV, sizeof(iv)); + memcpy(iv, FATIV.data(), sizeof(iv)); u32 res; res = iv[15] + (ctr & 0xFF); @@ -218,10 +200,10 @@ void SetupFATCrypto(AES_ctx* ctx, u32 ctr) else break; } - AES_init_ctx_iv(ctx, FATKey, iv); + AES_init_ctx_iv(ctx, FATKey.data(), iv); } -u32 ReadFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::ReadFATBlock(u64 addr, u32 len, u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -243,7 +225,7 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf) return len; } -u32 WriteFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::WriteFATBlock(u64 addr, u32 len, const u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -272,30 +254,30 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf) } -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = ReadFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->ReadFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = WriteFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->WriteFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -bool ESEncrypt(u8* data, u32 len) +bool NANDImage::ESEncrypt(u8* data, u32 len) const { AES_ctx ctx; u8 iv[16]; @@ -307,7 +289,7 @@ bool ESEncrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -380,7 +362,7 @@ bool ESEncrypt(u8* data, u32 len) return true; } -bool ESDecrypt(u8* data, u32 len) +bool NANDImage::ESDecrypt(u8* data, u32 len) { AES_ctx ctx; u8 iv[16]; @@ -392,7 +374,7 @@ bool ESDecrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -486,7 +468,7 @@ bool ESDecrypt(u8* data, u32 len) } -void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) +void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) { FF_FIL file; FRESULT res; @@ -508,7 +490,7 @@ void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) } -void ReadUserData(DSiFirmwareSystemSettings& data) +void NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -557,7 +539,7 @@ void ReadUserData(DSiFirmwareSystemSettings& data) f_close(&file); } -void PatchUserData() +void NANDMount::PatchUserData() { FRESULT res; @@ -653,7 +635,7 @@ void debug_listfiles(const char* path) f_closedir(&dir); } -bool ImportFile(const char* path, const u8* data, size_t len) +bool NANDMount::ImportFile(const char* path, const u8* data, size_t len) { if (!data || !len || !path) return false; @@ -687,7 +669,7 @@ bool ImportFile(const char* path, const u8* data, size_t len) return true; } -bool ImportFile(const char* path, const char* in) +bool NANDMount::ImportFile(const char* path, const char* in) { FF_FIL file; FILE* fin; @@ -730,7 +712,7 @@ bool ImportFile(const char* path, const char* in) return true; } -bool ExportFile(const char* path, const char* out) +bool NANDMount::ExportFile(const char* path, const char* out) { FF_FIL file; FILE* fout; @@ -771,7 +753,7 @@ bool ExportFile(const char* path, const char* out) return true; } -void RemoveFile(const char* path) +void NANDMount::RemoveFile(const char* path) { FF_FILINFO info; FRESULT res = f_stat(path, &info); @@ -784,7 +766,7 @@ void RemoveFile(const char* path) Log(LogLevel::Debug, "Removed file at %s\n", path); } -void RemoveDir(const char* path) +void NANDMount::RemoveDir(const char* path) { FF_DIR dir; FF_FILINFO info; @@ -839,7 +821,7 @@ void RemoveDir(const char* path) } -u32 GetTitleVersion(u32 category, u32 titleid) +u32 NANDMount::GetTitleVersion(u32 category, u32 titleid) { FRESULT res; char path[256]; @@ -859,7 +841,7 @@ u32 GetTitleVersion(u32 category, u32 titleid) return version; } -void ListTitles(u32 category, std::vector<u32>& titlelist) +void NANDMount::ListTitles(u32 category, std::vector<u32>& titlelist) { FRESULT res; FF_DIR titledir; @@ -908,7 +890,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist) f_closedir(&titledir); } -bool TitleExists(u32 category, u32 titleid) +bool NANDMount::TitleExists(u32 category, u32 titleid) { char path[256]; snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid); @@ -917,7 +899,7 @@ bool TitleExists(u32 category, u32 titleid) return (res == FR_OK); } -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) +void NANDMount::GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) { version = GetTitleVersion(category, titleid); if (version == 0xFFFFFFFF) @@ -953,7 +935,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND } -bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) +bool NANDMount::CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) { FF_FIL file; FRESULT res; @@ -979,7 +961,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) memset(&ticket[0x222], 0xFF, 0x20); - ESEncrypt(ticket, 0x2A4); + Image->ESEncrypt(ticket, 0x2A4); f_write(&file, ticket, 0x2C4, &nwrite); @@ -988,7 +970,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) return true; } -bool CreateSaveFile(const char* path, u32 len) +bool NANDMount::CreateSaveFile(const char* path, u32 len) { if (len == 0) return true; if (len < 0x200) return false; @@ -1078,7 +1060,7 @@ bool CreateSaveFile(const char* path, u32 len) return true; } -bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) { u32 titleid0 = tmd.GetCategory(); u32 titleid1 = tmd.GetID(); @@ -1158,7 +1140,7 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat return true; } -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) { NDSHeader header {}; { @@ -1196,7 +1178,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re return true; } -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) { if (!app || appLength < sizeof(NDSHeader)) return false; @@ -1232,7 +1214,7 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& return true; } -void DeleteTitle(u32 category, u32 titleid) +void NANDMount::DeleteTitle(u32 category, u32 titleid) { char fname[128]; @@ -1243,7 +1225,7 @@ void DeleteTitle(u32 category, u32 titleid) RemoveDir(fname); } -u32 GetTitleDataMask(u32 category, u32 titleid) +u32 NANDMount::GetTitleDataMask(u32 category, u32 titleid) { u32 version; NDSHeader header; @@ -1260,7 +1242,7 @@ u32 GetTitleDataMask(u32 category, u32 titleid) return ret; } -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ImportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; @@ -1286,7 +1268,7 @@ bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) return ImportFile(fname, file); } -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ExportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 9bff8f2..777afe0 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -20,6 +20,7 @@ #define DSI_NAND_H #include "types.h" +#include "fatfs/ff.h" #include "NDS_Header.h" #include "DSi_TMD.h" #include "SPI_Firmware.h" @@ -27,6 +28,8 @@ #include <vector> #include <string> +struct AES_ctx; + namespace DSi_NAND { @@ -40,29 +43,95 @@ enum union DSiFirmwareSystemSettings; union DSiSerialData; using DSiHardwareInfoN = std::array<u8, 0x9C>; +using DSiKey = std::array<u8, 16>; -bool Init(u8* es_keyY); -void DeInit(); - -Platform::FileHandle* GetFile(); - -void GetIDs(u8* emmc_cid, u64& consoleid); - -void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); +class NANDImage +{ +public: + explicit NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept; + explicit NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept; + ~NANDImage(); + NANDImage(const NANDImage&) = delete; + NANDImage& operator=(const NANDImage&) = delete; + + NANDImage(NANDImage&& other) noexcept; + NANDImage& operator=(NANDImage&& other) noexcept; + + Platform::FileHandle* GetFile() { return CurFile; } + + [[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; } + [[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; } + [[nodiscard]] u64 GetLength() const noexcept { return Length; } + + explicit operator bool() const { return CurFile != nullptr; } +private: + friend class NANDMount; + void SetupFATCrypto(AES_ctx* ctx, u32 ctr); + u32 ReadFATBlock(u64 addr, u32 len, u8* buf); + u32 WriteFATBlock(u64 addr, u32 len, const u8* buf); + bool ESEncrypt(u8* data, u32 len) const; + bool ESDecrypt(u8* data, u32 len); + Platform::FileHandle* CurFile = nullptr; + DSiKey eMMC_CID; + u64 ConsoleID; + DSiKey FATIV; + DSiKey FATKey; + DSiKey ESKey; + u64 Length; +}; -void ReadUserData(DSiFirmwareSystemSettings& data); -void PatchUserData(); +class NANDMount +{ +public: + explicit NANDMount(NANDImage& nand) noexcept; + ~NANDMount(); + NANDMount(const NANDMount&) = delete; + NANDMount& operator=(const NANDMount&) = delete; + + // Move constructor deleted so that the closure passed to FATFS can't be invalidated + NANDMount(NANDMount&&) = delete; + NANDMount& operator=(NANDMount&&) = delete; + + void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); + + void ReadUserData(DSiFirmwareSystemSettings& data); + void PatchUserData(); + + void ListTitles(u32 category, std::vector<u32>& titlelist); + bool TitleExists(u32 category, u32 titleid); + void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); + bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); + bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); + void DeleteTitle(u32 category, u32 titleid); + + u32 GetTitleDataMask(u32 category, u32 titleid); + bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); + bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); + + bool ImportFile(const char* path, const u8* data, size_t len); + bool ImportFile(const char* path, const char* in); + bool ExportFile(const char* path, const char* out); + void RemoveFile(const char* path); + void RemoveDir(const char* path); + + explicit operator bool() const { return Image != nullptr && CurFS != nullptr; } +private: + u32 GetTitleVersion(u32 category, u32 titleid); + bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version); + bool CreateSaveFile(const char* path, u32 len); + bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly); + UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); + UINT FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num); + + NANDImage* Image; + + // We keep a pointer to CurFS because fatfs maintains a global pointer to it; + // therefore if we embed the FATFS directly in the object, + // we can't give it move semantics. + std::unique_ptr<FATFS> CurFS; -void ListTitles(u32 category, std::vector<u32>& titlelist); -bool TitleExists(u32 category, u32 titleid); -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); -void DeleteTitle(u32 category, u32 titleid); +}; -u32 GetTitleDataMask(u32 category, u32 titleid); -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); typedef std::array<u8, 20> SHA1Hash; typedef std::array<u8, 8> TitleID; diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 4f287b1..158ae1e 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -20,6 +20,7 @@ #include <string.h> #include "DSi.h" #include "DSi_SD.h" +#include "DSi_NAND.h" #include "DSi_NWifi.h" #include "Platform.h" @@ -137,11 +138,8 @@ void DSi_SDHost::Reset() else sd = nullptr; - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - mmc = new DSi_MMCStorage(this, true, instnand); - mmc->SetCID(DSi::eMMC_CID); + mmc = new DSi_MMCStorage(this, *DSi::NANDImage); + mmc->SetCID(DSi::NANDImage->GetEMMCID().data()); Ports[0] = sd; Ports[1] = mmc; @@ -768,14 +766,9 @@ void DSi_SDHost::CheckSwapFIFO() #define MMC_DESC (Internal?"NAND":"SDcard") -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename) - : DSi_SDDevice(host) +DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand) + : DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr) { - Internal = internal; - File = Platform::OpenLocalFile(filename, FileMode::ReadWriteExisting); - - SD = nullptr; - ReadOnly = false; } @@ -783,7 +776,7 @@ DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::strin : DSi_SDDevice(host) { Internal = internal; - File = nullptr; + NAND = nullptr; SD = new FATStorage(filename, size, readonly, sourcedir); SD->Open(); @@ -798,10 +791,8 @@ DSi_MMCStorage::~DSi_MMCStorage() SD->Close(); delete SD; } - if (File) - { - CloseFile(File); - } + + // Do not close the NANDImage, it's not owned by this object } void DSi_MMCStorage::Reset() @@ -925,7 +916,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 12: // stop operation SetState(0x04); - if (File) FileFlush(File); + if (NAND) FileFlush(NAND->GetFile()); RWCommand = 0; Host->SendResponse(CSR, true); return; @@ -1052,10 +1043,10 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr) { SD->ReadSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (NAND) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileRead(&data[addr & 0x1FF], 1, len, File); + FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); + FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile()); } return Host->DataRX(&data[addr & 0x1FF], len); @@ -1082,10 +1073,10 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) { SD->WriteSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (NAND) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileWrite(&data[addr & 0x1FF], 1, len, File); + FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); + FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile()); } } } diff --git a/src/DSi_SD.h b/src/DSi_SD.h index fe9e23a..8e53a77 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -24,6 +24,11 @@ #include "FATStorage.h" #include "Savestate.h" +namespace DSi_NAND +{ + class NANDImage; +} + class DSi_SDDevice; @@ -125,7 +130,7 @@ protected: class DSi_MMCStorage : public DSi_SDDevice { public: - DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename); + DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand); DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); ~DSi_MMCStorage(); @@ -133,7 +138,7 @@ public: void DoSavestate(Savestate* file); - void SetCID(u8* cid) { memcpy(CID, cid, 16); } + void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); } void SendCMD(u8 cmd, u32 param); void SendACMD(u8 cmd, u32 param); @@ -142,7 +147,7 @@ public: private: bool Internal; - Platform::FileHandle* File; + DSi_NAND::NANDImage* NAND; FATStorage* SD; u8 CID[16]; diff --git a/src/fatfs/diskio.c b/src/FATIO.cpp index 5b5c054..3e91fba 100644 --- a/src/fatfs/diskio.c +++ b/src/FATIO.cpp @@ -1,15 +1,24 @@ -/*-----------------------------------------------------------------------*/ -/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ -/*-----------------------------------------------------------------------*/ -/* If a working storage control module is available, it should be */ -/* attached to the FatFs via a glue function rather than modifying it. */ -/* This is an example of glue functions to attach various exsisting */ -/* storage control modules to the FatFs module with a defined API. */ -/*-----------------------------------------------------------------------*/ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS 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 3 of the License, or (at your option) + any later version. + + melonDS 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. -#include "ff.h" /* Obtains integer types */ -#include "diskio.h" /* Declarations of disk functions */ + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ +#include "FATIO.h" +#include "fatfs/ff.h" +#include "fatfs/diskio.h" static ff_disk_read_cb ReadCb; static ff_disk_write_cb WriteCb; @@ -17,7 +26,7 @@ static LBA_t SectorCount; static DSTATUS Status = STA_NOINIT | STA_NODISK; -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt) +void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt) { if (!readcb) return; @@ -30,10 +39,10 @@ void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt else Status &= ~STA_PROTECT; } -void ff_disk_close(void) +void ff_disk_close() { - ReadCb = (void*)0; - WriteCb = (void*)0; + ReadCb = nullptr; + WriteCb = nullptr; SectorCount = 0; Status &= ~STA_PROTECT; diff --git a/src/FATIO.h b/src/FATIO.h new file mode 100644 index 0000000..b3b63f3 --- /dev/null +++ b/src/FATIO.h @@ -0,0 +1,34 @@ +/* + Copyright 2016-2023 melonDS team + + This file is part of melonDS. + + melonDS 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 3 of the License, or (at your option) + any later version. + + melonDS 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 melonDS. If not, see http://www.gnu.org/licenses/. +*/ + + +#ifndef FATIO_H +#define FATIO_H + +#include <functional> +#include "fatfs/ff.h" + +// extra additions for interfacing with melonDS + +using ff_disk_read_cb = std::function<UINT(BYTE*, LBA_t, UINT)>; +using ff_disk_write_cb = std::function<UINT(const BYTE*, LBA_t, UINT)>; + +void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt); +void ff_disk_close(); + +#endif // FATIO_H diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index d882f8d..1d8b741 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -21,6 +21,7 @@ #include <inttypes.h> #include <vector> +#include "FATIO.h" #include "FATStorage.h" #include "Platform.h" @@ -122,7 +123,7 @@ UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num) return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf); } -UINT FATStorage::FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num) +UINT FATStorage::FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num) { return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf); } @@ -157,7 +158,7 @@ u32 FATStorage::ReadSectorsInternal(FileHandle* file, u64 filelen, u32 start, u3 return res; } -u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, u8* data) +u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data) { if (!file) return 0; diff --git a/src/FATStorage.h b/src/FATStorage.h index 6b9beb5..7edad13 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -55,10 +55,10 @@ private: static Platform::FileHandle* FF_File; static u64 FF_FileSize; static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num); - static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num); + static UINT FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num); static u32 ReadSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); - static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); + static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data); void LoadIndex(); void SaveIndex(); diff --git a/src/Platform.h b/src/Platform.h index 9752239..67e6b33 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -107,8 +107,6 @@ enum ConfigEntry ExternalBIOSEnable, - DSi_NANDPath, - DLDI_Enable, DLDI_ImagePath, DLDI_ImageSize, diff --git a/src/fatfs/ff.h b/src/fatfs/ff.h index a8c34aa..c2832be 100644 --- a/src/fatfs/ff.h +++ b/src/fatfs/ff.h @@ -414,16 +414,6 @@ int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ - -// extra additions for interfacing with melonDS - -typedef UINT (*ff_disk_read_cb)(BYTE* buff, LBA_t sector, UINT count); -typedef UINT (*ff_disk_write_cb)(BYTE* buff, LBA_t sector, UINT count); - -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt); -void ff_disk_close(void); - - #ifdef __cplusplus } #endif diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 0574d5d..7f6e1d5 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -250,8 +250,6 @@ std::string GetConfigString(ConfigEntry entry) { switch (entry) { - case DSi_NANDPath: return Config::DSiNANDPath; - case DLDI_ImagePath: return Config::DLDISDPath; case DLDI_FolderPath: return Config::DLDIFolderPath; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 49f4c45..206332b 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -595,6 +595,10 @@ void Reset() LoadBIOSFiles(); InstallFirmware(); + if (Config::ConsoleType == 1) + { + InstallNAND(&DSi::ARM7iBIOS[0x8308]); + } NDS::Reset(); SetBatteryLevels(); @@ -657,6 +661,9 @@ bool LoadBIOS() if (!InstallFirmware()) return false; + if (Config::ConsoleType == 1 && !InstallNAND(&DSi::ARM7iBIOS[0x8308])) + return false; + if (NDS::NeedsDirectBoot()) return false; @@ -948,6 +955,47 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) firmware.UpdateChecksums(); } +static Platform::FileHandle* OpenNANDFile() noexcept +{ + std::string nandpath = Config::DSiNANDPath; + std::string instnand = nandpath + Platform::InstanceFileSuffix(); + + FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); + if ((!nandfile) && (Platform::InstanceID() > 0)) + { + FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); + if (!orig) + { + Log(LogLevel::Error, "Failed to open DSi NAND\n"); + return nullptr; + } + + QFile::copy(QString::fromStdString(nandpath), QString::fromStdString(instnand)); + + nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); + } + + return nandfile; +} + +bool InstallNAND(const u8* es_keyY) +{ + Platform::FileHandle* nandfile = OpenNANDFile(); + if (!nandfile) + return false; + + if (auto nand = std::make_unique<DSi_NAND::NANDImage>(nandfile, es_keyY); *nand) + { + DSi::NANDImage = std::move(nand); + return true; + } + else + { + DSi::NANDImage = nullptr; + return false; + } +} + bool InstallFirmware() { using namespace SPI_Firmware; @@ -1089,6 +1137,9 @@ bool LoadROM(QStringList filepath, bool reset) NDS::SetConsoleType(Config::ConsoleType); NDS::EjectCart(); LoadBIOSFiles(); + if (Config::ConsoleType == 1) + InstallNAND(&DSi::ARM7iBIOS[0x8308]); + NDS::Reset(); SetBatteryLevels(); } diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 5faef1a..2eeed0a 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -22,6 +22,7 @@ #include "types.h" #include "SaveManager.h" #include "AREngine.h" +#include "DSi_NAND.h" #include <string> #include <memory> @@ -40,6 +41,7 @@ bool LoadBIOS(); void ClearBackupState(); bool InstallFirmware(); +bool InstallNAND(const u8* es_keyY); bool LoadROM(QStringList filepath, bool reset); void EjectCart(); bool CartInserted(); diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index d5147fc..84bd1ef 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -32,13 +32,13 @@ using namespace Platform; -bool TitleManagerDialog::NANDInited = false; +std::unique_ptr<DSi_NAND::NANDImage> TitleManagerDialog::nand = nullptr; TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr; extern std::string EmuDirectory; -TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::TitleManagerDialog) +TitleManagerDialog::TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image) : QDialog(parent), ui(new Ui::TitleManagerDialog), nandmount(image) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -47,7 +47,7 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne const u32 category = 0x00030004; std::vector<u32> titlelist; - DSi_NAND::ListTitles(category, titlelist); + nandmount.ListTitles(category, titlelist); for (std::vector<u32>::iterator it = titlelist.begin(); it != titlelist.end(); it++) { @@ -109,7 +109,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) NDSHeader header; NDSBanner banner; - DSi_NAND::GetTitleInfo(category, titleid, version, &header, &banner); + nandmount.GetTitleInfo(category, titleid, version, &header, &banner); u32 icondata[32*32]; ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata); @@ -137,7 +137,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) bool TitleManagerDialog::openNAND() { - NANDInited = false; + nand = nullptr; FileHandle* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read); if (!bios7i) @@ -148,22 +148,25 @@ bool TitleManagerDialog::openNAND() FileRead(es_keyY, 16, 1, bios7i); CloseFile(bios7i); - if (!DSi_NAND::Init(es_keyY)) - { + FileHandle* nandfile = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting); + if (!nandfile) + return false; + + nand = std::make_unique<DSi_NAND::NANDImage>(nandfile, es_keyY); + if (!*nand) + { // If loading and mounting the NAND image failed... + nand = nullptr; return false; + // NOTE: The NANDImage takes ownership of the FileHandle, + // so it will be closed even if the NANDImage constructor fails. } - NANDInited = true; return true; } void TitleManagerDialog::closeNAND() { - if (NANDInited) - { - DSi_NAND::DeInit(); - NANDInited = false; - } + nand = nullptr; } void TitleManagerDialog::done(int r) @@ -175,7 +178,7 @@ void TitleManagerDialog::done(int r) void TitleManagerDialog::on_btnImportTitle_clicked() { - TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly); + TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly, nandmount); importdlg->open(); connect(importdlg, &TitleImportDialog::finished, this, &TitleManagerDialog::onImportTitleFinished); @@ -190,14 +193,16 @@ void TitleManagerDialog::onImportTitleFinished(int res) titleid[0] = importTmdData.GetCategory(); titleid[1] = importTmdData.GetID(); + assert(nand != nullptr); + assert(*nand); // remove anything that might hinder the install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); - bool importres = DSi_NAND::ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); + bool importres = nandmount.ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); if (!importres) { // remove a potential half-completed install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); QMessageBox::critical(this, "Import title - melonDS", @@ -224,7 +229,7 @@ void TitleManagerDialog::on_btnDeleteTitle_clicked() return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - DSi_NAND::DeleteTitle((u32)(titleid >> 32), (u32)titleid); + nandmount.DeleteTitle((u32)(titleid >> 32), (u32)titleid); delete cur; } @@ -317,7 +322,7 @@ void TitleManagerDialog::onImportTitleData() } u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -370,7 +375,7 @@ void TitleManagerDialog::onExportTitleData() if (file.isEmpty()) return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -380,8 +385,8 @@ void TitleManagerDialog::onExportTitleData() } -TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly) -: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly) +TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nandmount) +: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly), nandmount(nandmount) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -455,7 +460,7 @@ void TitleImportDialog::accept() } } - if (DSi_NAND::TitleExists(titleid[1], titleid[0])) + if (nandmount.TitleExists(titleid[1], titleid[0])) { if (QMessageBox::question(this, "Import title - melonDS", diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index fc92fd8..5182e1d 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -19,6 +19,7 @@ #ifndef TITLEMANAGERDIALOG_H #define TITLEMANAGERDIALOG_H +#include <memory> #include <QDialog> #include <QMessageBox> #include <QListWidget> @@ -30,6 +31,7 @@ #include <QNetworkAccessManager> #include "DSi_TMD.h" +#include "DSi_NAND.h" namespace Ui { @@ -44,10 +46,10 @@ class TitleManagerDialog : public QDialog Q_OBJECT public: - explicit TitleManagerDialog(QWidget* parent); + explicit TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image); ~TitleManagerDialog(); - static bool NANDInited; + static std::unique_ptr<DSi_NAND::NANDImage> nand; static bool openNAND(); static void closeNAND(); @@ -68,7 +70,10 @@ public: return nullptr; } - currentDlg = new TitleManagerDialog(parent); + assert(nand != nullptr); + assert(*nand); + + currentDlg = new TitleManagerDialog(parent, *nand); currentDlg->open(); return currentDlg; } @@ -89,6 +94,7 @@ private slots: void onExportTitleData(); private: + DSi_NAND::NANDMount nandmount; Ui::TitleManagerDialog* ui; QString importAppPath; @@ -106,7 +112,7 @@ class TitleImportDialog : public QDialog Q_OBJECT public: - explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly); + explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nand); ~TitleImportDialog(); private slots: @@ -119,6 +125,7 @@ private slots: private: Ui::TitleImportDialog* ui; + DSi_NAND::NANDMount& nandmount; QButtonGroup* grpTmdSource; |