From 2773daf55be386a5745eb6de064a7f1975c605b4 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 21 Aug 2021 21:25:08 +0200 Subject: [PATCH] take it further --- src/DSi_NAND.cpp | 156 ++++++++++++++++----- src/DSi_NAND.h | 3 + src/frontend/qt_sdl/TitleManagerDialog.cpp | 92 +++++++++++- src/frontend/qt_sdl/TitleManagerDialog.h | 16 ++- 4 files changed, 230 insertions(+), 37 deletions(-) diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 629b9ca1..ca5cb502 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -32,6 +32,7 @@ namespace DSi_NAND { FILE* CurFile; +FATFS CurFS; u8 eMMC_CID[16]; u64 ConsoleID; @@ -42,11 +43,27 @@ 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(FILE* nandfile, u8* es_keyY) { if (!nandfile) return false; + ff_disk_open(FF_ReadNAND, FF_WriteNAND); + + FRESULT res; + res = f_mount(&CurFS, "0:", 0); + if (res != FR_OK) + { + printf("NAND mounting failed: %d\n", res); + f_unmount("0:"); + ff_disk_close(); + return false; + } + // read the nocash footer fseek(nandfile, -0x40, SEEK_END); @@ -113,6 +130,9 @@ bool Init(FILE* nandfile, u8* es_keyY) void DeInit() { + f_unmount("0:"); + ff_disk_close(); + CurFile = nullptr; } @@ -155,8 +175,8 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf) AES_ctx ctx; SetupFATCrypto(&ctx, ctr); - fseek(DSi::SDMMCFile, addr, SEEK_SET); - u32 res = fread(buf, len, 1, DSi::SDMMCFile); + fseek(CurFile, addr, SEEK_SET); + u32 res = fread(buf, len, 1, CurFile); if (!res) return 0; for (u32 i = 0; i < len; i += 16) @@ -177,7 +197,7 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf) AES_ctx ctx; SetupFATCrypto(&ctx, ctr); - fseek(DSi::SDMMCFile, addr, SEEK_SET); + fseek(CurFile, addr, SEEK_SET); for (u32 s = 0; s < len; s += 0x200) { @@ -191,7 +211,7 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf) DSi_AES::Swap16(&tempbuf[i], tmp); } - u32 res = fwrite(tempbuf, 0x200, 1, DSi::SDMMCFile); + u32 res = fwrite(tempbuf, 0x200, 1, CurFile); if (!res) return 0; } @@ -418,18 +438,7 @@ bool ESDecrypt(u8* data, u32 len) void PatchTSC() { - ff_disk_open(FF_ReadNAND, FF_WriteNAND); - FRESULT res; - FATFS fs; - res = f_mount(&fs, "0:", 0); - if (res != FR_OK) - { - printf("NAND mounting failed: %d\n", res); - f_unmount("0:"); - ff_disk_close(); - return; - } for (int i = 0; i < 2; i++) { @@ -469,9 +478,6 @@ void PatchTSC() f_close(&file); } - - f_unmount("0:"); - ff_disk_close(); } @@ -533,6 +539,106 @@ void debug_dumpfile(char* path, char* out) f_close(&file); } + +u32 GetTitleVersion(u32 category, u32 titleid) +{ + FRESULT res; + char path[256]; + sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid); + FIL file; + res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); + if (res != FR_OK) + return 0xFFFFFFFF; + + u32 version; + u32 nread; + f_lseek(&file, 0x1E4); + f_read(&file, &version, 4, &nread); + version = (version >> 24) | ((version & 0xFF0000) >> 8) | ((version & 0xFF00) << 8) | (version << 24); + + f_close(&file); + return version; +} + +void ListTitles(u32 category, std::vector& titlelist) +{ + FRESULT res; + DIR titledir; + char path[256]; + + sprintf(path, "0:/title/%08x", category); + res = f_opendir(&titledir, path); + if (res != FR_OK) + { + printf("NAND: !! no title dir (%s)\n", path); + return; + } + + for (;;) + { + FILINFO info; + f_readdir(&titledir, &info); + if (!info.fname[0]) + break; + + if (strlen(info.fname) != 8) + continue; + + u32 titleid; + if (sscanf(info.fname, "%08x", &titleid) < 1) + continue; + + u32 version = GetTitleVersion(category, titleid); + if (version == 0xFFFFFFFF) + continue; + + sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); + FILINFO appinfo; + res = f_stat(path, &appinfo); + if (res != FR_OK) + continue; + if (appinfo.fattrib & AM_DIR) + continue; + if (appinfo.fsize < 0x4000) + continue; + + // title is good, add it to the list + titlelist.push_back(titleid); + } + + f_closedir(&titledir); +} + +void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner) +{ + version = GetTitleVersion(category, titleid); + FRESULT res; + + char path[256]; + sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version); + FIL file; + res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); + if (res != FR_OK) + return; + + u32 nread; + f_read(&file, header, 0x1000, &nread); + + u32 banneraddr = *(u32*)&header[0x68]; + if (!banneraddr) + { + memset(banner, 0, 0x2400); + } + else + { + f_lseek(&file, banneraddr); + f_read(&file, banner, 0x2400, &nread); + } + + f_close(&file); +} + + void CreateTicket(char* path, u32 titleid0, u32 titleid1, u8 version) { FIL file; @@ -639,18 +745,7 @@ void ImportTest() char* tmdfile = "treasure.tmd"; char* appfile = "treasure.nds"; - ff_disk_open(FF_ReadNAND, FF_WriteNAND); - FRESULT res; - FATFS fs; - res = f_mount(&fs, "0:", 0); - if (res != FR_OK) - { - printf("NAND mounting failed: %d\n", res); - f_unmount("0:"); - ff_disk_close(); - return; - } /*debug_listfiles("0:"); @@ -842,9 +937,6 @@ return;*/ printf("----- POST INSERTION:\n"); debug_listfiles("0:"); - - f_unmount("0:"); - ff_disk_close(); } } diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 43061c8d..6f57a72d 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -20,6 +20,7 @@ #define DSI_NAND_H #include "types.h" +#include namespace DSi_NAND { @@ -31,6 +32,8 @@ void GetIDs(u8* emmc_cid, u64& consoleid); void PatchTSC(); +void ListTitles(u32 category, std::vector& titlelist); +void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner); void ImportTest(); } diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index 71ca4e38..ed576760 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -17,17 +17,19 @@ */ #include -#include #include "types.h" #include "Platform.h" #include "Config.h" #include "PlatformConfig.h" +#include "FrontendUtil.h" +#include "DSi_NAND.h" #include "TitleManagerDialog.h" #include "ui_TitleManagerDialog.h" +FILE* TitleManagerDialog::curNAND = nullptr; TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr; @@ -36,11 +38,50 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - //ui->lstTitleList->setViewMode(QListView::IconMode); - //ui->lstTitleList->setFlow(QListView::LeftToRight); ui->lstTitleList->setIconSize(QSize(32, 32)); + const u32 category = 0x00030004; + std::vector titlelist; + DSi_NAND::ListTitles(category, titlelist); + + for (std::vector::iterator it = titlelist.begin(); it != titlelist.end(); it++) { + u32 titleid = *it; + + u32 version; + u8 header[0x1000]; + u8 banner[0x2400]; + + DSi_NAND::GetTitleInfo(category, titleid, version, header, banner); + + u8 icongfx[512]; + u16 iconpal[16]; + memcpy(icongfx, &banner[0x20], 512); + memcpy(iconpal, &banner[0x220], 16*2); + u32 icondata[32*32]; + Frontend::ROMIcon(icongfx, iconpal, icondata); + QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_ARGB32); + QIcon icon(QPixmap::fromImage(iconimg.copy())); + + // TODO: make it possible to select other languages? + u16 titleraw[129]; + memcpy(titleraw, &banner[0x340], 128*sizeof(u16)); + titleraw[128] = '\0'; + QString title = QString::fromUtf16(titleraw); + title.replace("\n", " · "); + + char gamecode[5]; + *(u32*)&gamecode[0] = *(u32*)&header[0xC]; + gamecode[4] = '\0'; + char extra[128]; + sprintf(extra, "\n(title ID: %s · %08x/%08x · version %08x)", gamecode, category, titleid, version); + + QListWidgetItem* item = new QListWidgetItem(title + QString(extra)); + item->setIcon(icon); + ui->lstTitleList->addItem(item); + } + + /*{ QPixmap boobs(32, 32); boobs.fill(Qt::blue); QIcon piss(boobs); @@ -75,10 +116,53 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne QListWidgetItem* derp = new QListWidgetItem("trans\nrights"); derp->setIcon(piss); ui->lstTitleList->addItem(derp); - } + }*/ } TitleManagerDialog::~TitleManagerDialog() { delete ui; } + +bool TitleManagerDialog::openNAND() +{ + FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb"); + if (!bios7i) + return false; + + u8 es_keyY[16]; + fseek(bios7i, 0x8308, SEEK_SET); + fread(es_keyY, 16, 1, bios7i); + fclose(bios7i); + + curNAND = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b"); + if (!curNAND) + return false; + + if (!DSi_NAND::Init(curNAND, es_keyY)) + { + fclose(curNAND); + curNAND = nullptr; + return false; + } + + return true; +} + +void TitleManagerDialog::closeNAND() +{ + if (curNAND) + { + DSi_NAND::DeInit(); + + fclose(curNAND); + curNAND = nullptr; + } +} + +void TitleManagerDialog::done(int r) +{ + QDialog::done(r); + + closeDlg(); +} diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index 7cb88387..d2c8a9db 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -20,6 +20,7 @@ #define TITLEMANAGERDIALOG_H #include +#include namespace Ui { class TitleManagerDialog; } class TitleManagerDialog; @@ -32,6 +33,10 @@ public: explicit TitleManagerDialog(QWidget* parent); ~TitleManagerDialog(); + static FILE* curNAND; + static bool openNAND(); + static void closeNAND(); + static TitleManagerDialog* currentDlg; static TitleManagerDialog* openDlg(QWidget* parent) { @@ -41,6 +46,14 @@ public: return currentDlg; } + if (!openNAND()) + { + QMessageBox::critical(parent, + "DSi title manager - melonDS", + "Failed to mount the DSi NAND. Check that your NAND dump is valid."); + return nullptr; + } + currentDlg = new TitleManagerDialog(parent); currentDlg->open(); return currentDlg; @@ -48,10 +61,11 @@ public: static void closeDlg() { currentDlg = nullptr; + closeNAND(); } private slots: - // shit + void done(int r); private: Ui::TitleManagerDialog* ui;