take it further
This commit is contained in:
parent
9993dbdd55
commit
2773daf55b
156
src/DSi_NAND.cpp
156
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<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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define DSI_NAND_H
|
||||
|
||||
#include "types.h"
|
||||
#include <vector>
|
||||
|
||||
namespace DSi_NAND
|
||||
{
|
||||
|
@ -31,6 +32,8 @@ void GetIDs(u8* emmc_cid, u64& consoleid);
|
|||
|
||||
void PatchTSC();
|
||||
|
||||
void ListTitles(u32 category, std::vector<u32>& titlelist);
|
||||
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner);
|
||||
void ImportTest();
|
||||
|
||||
}
|
||||
|
|
|
@ -17,17 +17,19 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <QMessageBox>
|
||||
|
||||
#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<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);
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define TITLEMANAGERDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
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;
|
||||
|
|
Loading…
Reference in New Issue