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
This commit is contained in:
parent
b2fcff97c1
commit
d4e51f8060
|
@ -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
|
||||
|
|
100
src/DSi.cpp
100
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]);
|
||||
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]);
|
||||
|
||||
DSi_NAND::DSiSerialData hwinfoS {};
|
||||
DSi_NAND::DSiHardwareInfoN hwinfoN;
|
||||
DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN);
|
||||
DSi_NAND::DSiSerialData hwinfoS {};
|
||||
DSi_NAND::DSiHardwareInfoN hwinfoN;
|
||||
nand.ReadHardwareInfo(hwinfoS, hwinfoN);
|
||||
|
||||
for (u32 i = 0; i < 0x14; i+=4)
|
||||
ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]);
|
||||
for (u32 i = 0; i < 0x14; i+=4)
|
||||
ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]);
|
||||
|
||||
for (u32 i = 0; i < 0x18; i+=4)
|
||||
ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
238
src/DSi_NAND.cpp
238
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;
|
||||
return;
|
||||
|
||||
u64 nandlen = FileLength(nandfile);
|
||||
|
||||
ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
FileHandle* GetFile()
|
||||
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)
|
||||
{
|
||||
return CurFile;
|
||||
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;
|
||||
}
|
||||
|
||||
NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand)
|
||||
{
|
||||
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];
|
||||
|
||||
|
|
101
src/DSi_NAND.h
101
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();
|
||||
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;
|
||||
|
||||
Platform::FileHandle* GetFile();
|
||||
NANDImage(NANDImage&& other) noexcept;
|
||||
NANDImage& operator=(NANDImage&& other) noexcept;
|
||||
|
||||
void GetIDs(u8* emmc_cid, u64& consoleid);
|
||||
Platform::FileHandle* GetFile() { return CurFile; }
|
||||
|
||||
void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN);
|
||||
[[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; }
|
||||
[[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; }
|
||||
[[nodiscard]] u64 GetLength() const noexcept { return Length; }
|
||||
|
||||
void ReadUserData(DSiFirmwareSystemSettings& data);
|
||||
void PatchUserData();
|
||||
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 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);
|
||||
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;
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
src/DSi_SD.h
11
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];
|
||||
|
|
|
@ -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
|
||||
|
||||
#include "ff.h" /* Obtains integer types */
|
||||
#include "diskio.h" /* Declarations of disk functions */
|
||||
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/.
|
||||
*/
|
||||
|
||||
#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;
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -107,8 +107,6 @@ enum ConfigEntry
|
|||
|
||||
ExternalBIOSEnable,
|
||||
|
||||
DSi_NANDPath,
|
||||
|
||||
DLDI_Enable,
|
||||
DLDI_ImagePath,
|
||||
DLDI_ImageSize,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue