mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-27 00:00:07 -06:00
take it further
This commit is contained in:
156
src/DSi_NAND.cpp
156
src/DSi_NAND.cpp
@ -32,6 +32,7 @@ namespace DSi_NAND
|
|||||||
{
|
{
|
||||||
|
|
||||||
FILE* CurFile;
|
FILE* CurFile;
|
||||||
|
FATFS CurFS;
|
||||||
|
|
||||||
u8 eMMC_CID[16];
|
u8 eMMC_CID[16];
|
||||||
u64 ConsoleID;
|
u64 ConsoleID;
|
||||||
@ -42,11 +43,27 @@ u8 FATKey[16];
|
|||||||
u8 ESKey[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)
|
bool Init(FILE* nandfile, u8* es_keyY)
|
||||||
{
|
{
|
||||||
if (!nandfile)
|
if (!nandfile)
|
||||||
return false;
|
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
|
// read the nocash footer
|
||||||
|
|
||||||
fseek(nandfile, -0x40, SEEK_END);
|
fseek(nandfile, -0x40, SEEK_END);
|
||||||
@ -113,6 +130,9 @@ bool Init(FILE* nandfile, u8* es_keyY)
|
|||||||
|
|
||||||
void DeInit()
|
void DeInit()
|
||||||
{
|
{
|
||||||
|
f_unmount("0:");
|
||||||
|
ff_disk_close();
|
||||||
|
|
||||||
CurFile = nullptr;
|
CurFile = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,8 +175,8 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf)
|
|||||||
AES_ctx ctx;
|
AES_ctx ctx;
|
||||||
SetupFATCrypto(&ctx, ctr);
|
SetupFATCrypto(&ctx, ctr);
|
||||||
|
|
||||||
fseek(DSi::SDMMCFile, addr, SEEK_SET);
|
fseek(CurFile, addr, SEEK_SET);
|
||||||
u32 res = fread(buf, len, 1, DSi::SDMMCFile);
|
u32 res = fread(buf, len, 1, CurFile);
|
||||||
if (!res) return 0;
|
if (!res) return 0;
|
||||||
|
|
||||||
for (u32 i = 0; i < len; i += 16)
|
for (u32 i = 0; i < len; i += 16)
|
||||||
@ -177,7 +197,7 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf)
|
|||||||
AES_ctx ctx;
|
AES_ctx ctx;
|
||||||
SetupFATCrypto(&ctx, ctr);
|
SetupFATCrypto(&ctx, ctr);
|
||||||
|
|
||||||
fseek(DSi::SDMMCFile, addr, SEEK_SET);
|
fseek(CurFile, addr, SEEK_SET);
|
||||||
|
|
||||||
for (u32 s = 0; s < len; s += 0x200)
|
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);
|
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;
|
if (!res) return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,18 +438,7 @@ bool ESDecrypt(u8* data, u32 len)
|
|||||||
|
|
||||||
void PatchTSC()
|
void PatchTSC()
|
||||||
{
|
{
|
||||||
ff_disk_open(FF_ReadNAND, FF_WriteNAND);
|
|
||||||
|
|
||||||
FRESULT res;
|
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++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@ -469,9 +478,6 @@ void PatchTSC()
|
|||||||
|
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
f_unmount("0:");
|
|
||||||
ff_disk_close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -533,6 +539,106 @@ void debug_dumpfile(char* path, char* out)
|
|||||||
f_close(&file);
|
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<u32>& 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)
|
void CreateTicket(char* path, u32 titleid0, u32 titleid1, u8 version)
|
||||||
{
|
{
|
||||||
FIL file;
|
FIL file;
|
||||||
@ -639,18 +745,7 @@ void ImportTest()
|
|||||||
char* tmdfile = "treasure.tmd";
|
char* tmdfile = "treasure.tmd";
|
||||||
char* appfile = "treasure.nds";
|
char* appfile = "treasure.nds";
|
||||||
|
|
||||||
ff_disk_open(FF_ReadNAND, FF_WriteNAND);
|
|
||||||
|
|
||||||
FRESULT res;
|
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:");
|
/*debug_listfiles("0:");
|
||||||
|
|
||||||
@ -842,9 +937,6 @@ return;*/
|
|||||||
|
|
||||||
printf("----- POST INSERTION:\n");
|
printf("----- POST INSERTION:\n");
|
||||||
debug_listfiles("0:");
|
debug_listfiles("0:");
|
||||||
|
|
||||||
f_unmount("0:");
|
|
||||||
ff_disk_close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define DSI_NAND_H
|
#define DSI_NAND_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace DSi_NAND
|
namespace DSi_NAND
|
||||||
{
|
{
|
||||||
@ -31,6 +32,8 @@ void GetIDs(u8* emmc_cid, u64& consoleid);
|
|||||||
|
|
||||||
void PatchTSC();
|
void PatchTSC();
|
||||||
|
|
||||||
|
void ListTitles(u32 category, std::vector<u32>& titlelist);
|
||||||
|
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner);
|
||||||
void ImportTest();
|
void ImportTest();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <QMessageBox>
|
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "PlatformConfig.h"
|
#include "PlatformConfig.h"
|
||||||
|
#include "FrontendUtil.h"
|
||||||
|
#include "DSi_NAND.h"
|
||||||
|
|
||||||
#include "TitleManagerDialog.h"
|
#include "TitleManagerDialog.h"
|
||||||
#include "ui_TitleManagerDialog.h"
|
#include "ui_TitleManagerDialog.h"
|
||||||
|
|
||||||
|
|
||||||
|
FILE* TitleManagerDialog::curNAND = nullptr;
|
||||||
TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr;
|
TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr;
|
||||||
|
|
||||||
|
|
||||||
@ -36,11 +38,50 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne
|
|||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
//ui->lstTitleList->setViewMode(QListView::IconMode);
|
|
||||||
//ui->lstTitleList->setFlow(QListView::LeftToRight);
|
|
||||||
ui->lstTitleList->setIconSize(QSize(32, 32));
|
ui->lstTitleList->setIconSize(QSize(32, 32));
|
||||||
|
|
||||||
|
const u32 category = 0x00030004;
|
||||||
|
std::vector<u32> titlelist;
|
||||||
|
DSi_NAND::ListTitles(category, titlelist);
|
||||||
|
|
||||||
|
for (std::vector<u32>::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);
|
QPixmap boobs(32, 32);
|
||||||
boobs.fill(Qt::blue);
|
boobs.fill(Qt::blue);
|
||||||
QIcon piss(boobs);
|
QIcon piss(boobs);
|
||||||
@ -75,10 +116,53 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne
|
|||||||
QListWidgetItem* derp = new QListWidgetItem("trans\nrights");
|
QListWidgetItem* derp = new QListWidgetItem("trans\nrights");
|
||||||
derp->setIcon(piss);
|
derp->setIcon(piss);
|
||||||
ui->lstTitleList->addItem(derp);
|
ui->lstTitleList->addItem(derp);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleManagerDialog::~TitleManagerDialog()
|
TitleManagerDialog::~TitleManagerDialog()
|
||||||
{
|
{
|
||||||
delete ui;
|
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();
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#define TITLEMANAGERDIALOG_H
|
#define TITLEMANAGERDIALOG_H
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
namespace Ui { class TitleManagerDialog; }
|
namespace Ui { class TitleManagerDialog; }
|
||||||
class TitleManagerDialog;
|
class TitleManagerDialog;
|
||||||
@ -32,6 +33,10 @@ public:
|
|||||||
explicit TitleManagerDialog(QWidget* parent);
|
explicit TitleManagerDialog(QWidget* parent);
|
||||||
~TitleManagerDialog();
|
~TitleManagerDialog();
|
||||||
|
|
||||||
|
static FILE* curNAND;
|
||||||
|
static bool openNAND();
|
||||||
|
static void closeNAND();
|
||||||
|
|
||||||
static TitleManagerDialog* currentDlg;
|
static TitleManagerDialog* currentDlg;
|
||||||
static TitleManagerDialog* openDlg(QWidget* parent)
|
static TitleManagerDialog* openDlg(QWidget* parent)
|
||||||
{
|
{
|
||||||
@ -41,6 +46,14 @@ public:
|
|||||||
return currentDlg;
|
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 = new TitleManagerDialog(parent);
|
||||||
currentDlg->open();
|
currentDlg->open();
|
||||||
return currentDlg;
|
return currentDlg;
|
||||||
@ -48,10 +61,11 @@ public:
|
|||||||
static void closeDlg()
|
static void closeDlg()
|
||||||
{
|
{
|
||||||
currentDlg = nullptr;
|
currentDlg = nullptr;
|
||||||
|
closeNAND();
|
||||||
}
|
}
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// shit
|
void done(int r);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::TitleManagerDialog* ui;
|
Ui::TitleManagerDialog* ui;
|
||||||
|
Reference in New Issue
Block a user