mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-27 00:00:07 -06:00
here have a pile of code
This commit is contained in:
394
src/DSi_NAND.cpp
394
src/DSi_NAND.cpp
@ -481,22 +481,22 @@ void PatchTSC()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void debug_listfiles(char* path)
|
void debug_listfiles(const char* path)
|
||||||
{
|
{
|
||||||
DIR dir;
|
DIR dir;
|
||||||
FILINFO info;
|
FILINFO info;
|
||||||
FRESULT res;
|
FRESULT res;
|
||||||
|
|
||||||
res = f_opendir(&dir, path);
|
res = f_opendir(&dir, path);
|
||||||
if (res) return;
|
if (res != FR_OK) return;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
res = f_readdir(&dir, &info);
|
res = f_readdir(&dir, &info);
|
||||||
if (res) return;
|
if (res != FR_OK) return;
|
||||||
if (!info.fname[0]) return;
|
if (!info.fname[0]) return;
|
||||||
|
|
||||||
char fullname[1024];
|
char fullname[512];
|
||||||
sprintf(fullname, "%s/%s", path, info.fname);
|
sprintf(fullname, "%s/%s", path, info.fname);
|
||||||
printf("[%c] %s\n", (info.fattrib&AM_DIR)?'D':'F', fullname);
|
printf("[%c] %s\n", (info.fattrib&AM_DIR)?'D':'F', fullname);
|
||||||
|
|
||||||
@ -505,21 +505,27 @@ void debug_listfiles(char* path)
|
|||||||
debug_listfiles(fullname);
|
debug_listfiles(fullname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
void debug_dumpfile(char* path, char* out)
|
void DumpFile(const char* path, const char* out)
|
||||||
{
|
{
|
||||||
FIL file;
|
FIL file;
|
||||||
FILE* fout;
|
FILE* fout;
|
||||||
FRESULT res;
|
FRESULT res;
|
||||||
|
|
||||||
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
|
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
|
||||||
if (res) return;
|
if (res != FR_OK) return;
|
||||||
|
|
||||||
u32 len = f_size(&file);
|
u32 len = f_size(&file);
|
||||||
printf("%s: len=%d\n", path, len);
|
|
||||||
|
|
||||||
fout = fopen(out, "wb");
|
fout = fopen(out, "wb");
|
||||||
|
if (!fout)
|
||||||
|
{
|
||||||
|
f_close(&file);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
u8 buf[0x200];
|
u8 buf[0x200];
|
||||||
for (u32 i = 0; i < len; i += 0x200)
|
for (u32 i = 0; i < len; i += 0x200)
|
||||||
@ -539,6 +545,71 @@ void debug_dumpfile(char* path, char* out)
|
|||||||
f_close(&file);
|
f_close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RemoveFile(const char* path)
|
||||||
|
{
|
||||||
|
FILINFO info;
|
||||||
|
FRESULT res = f_stat(path, &info);
|
||||||
|
if (res != FR_OK) return;
|
||||||
|
|
||||||
|
if (info.fattrib & AM_RDO)
|
||||||
|
f_chmod(path, 0, AM_RDO);
|
||||||
|
|
||||||
|
f_unlink(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveDir(const char* path)
|
||||||
|
{
|
||||||
|
DIR dir;
|
||||||
|
FILINFO info;
|
||||||
|
FRESULT res;
|
||||||
|
|
||||||
|
res = f_stat(path, &info);
|
||||||
|
if (res != FR_OK) return;
|
||||||
|
|
||||||
|
if (info.fattrib & AM_RDO)
|
||||||
|
f_chmod(path, 0, AM_RDO);
|
||||||
|
|
||||||
|
res = f_opendir(&dir, path);
|
||||||
|
if (res != FR_OK) return;
|
||||||
|
|
||||||
|
std::vector<std::string> dirlist;
|
||||||
|
std::vector<std::string> filelist;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
res = f_readdir(&dir, &info);
|
||||||
|
if (res != FR_OK) break;
|
||||||
|
if (!info.fname[0]) break;
|
||||||
|
|
||||||
|
char fullname[512];
|
||||||
|
sprintf(fullname, "%s/%s", path, info.fname);
|
||||||
|
|
||||||
|
if (info.fattrib & AM_RDO)
|
||||||
|
f_chmod(path, 0, AM_RDO);
|
||||||
|
|
||||||
|
if (info.fattrib & AM_DIR)
|
||||||
|
dirlist.push_back(fullname);
|
||||||
|
else
|
||||||
|
filelist.push_back(fullname);
|
||||||
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
|
||||||
|
for (std::vector<std::string>::iterator it = dirlist.begin(); it != dirlist.end(); it++)
|
||||||
|
{
|
||||||
|
const char* path = (*it).c_str();
|
||||||
|
RemoveDir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<std::string>::iterator it = filelist.begin(); it != filelist.end(); it++)
|
||||||
|
{
|
||||||
|
const char* path = (*it).c_str();
|
||||||
|
f_unlink(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
f_unlink(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
u32 GetTitleVersion(u32 category, u32 titleid)
|
u32 GetTitleVersion(u32 category, u32 titleid)
|
||||||
{
|
{
|
||||||
@ -609,6 +680,15 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
|
|||||||
f_closedir(&titledir);
|
f_closedir(&titledir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TitleExists(u32 category, u32 titleid)
|
||||||
|
{
|
||||||
|
char path[256];
|
||||||
|
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
|
||||||
|
|
||||||
|
FRESULT res = f_stat(path, nullptr);
|
||||||
|
return (res == FR_OK);
|
||||||
|
}
|
||||||
|
|
||||||
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner)
|
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner)
|
||||||
{
|
{
|
||||||
version = GetTitleVersion(category, titleid);
|
version = GetTitleVersion(category, titleid);
|
||||||
@ -639,17 +719,17 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banne
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CreateTicket(char* path, u32 titleid0, u32 titleid1, u8 version)
|
bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version)
|
||||||
{
|
{
|
||||||
FIL file;
|
FIL file;
|
||||||
FRESULT res;
|
FRESULT res;
|
||||||
u32 nwrite;
|
u32 nwrite;
|
||||||
|
|
||||||
res = f_open(&file, path, FA_CREATE_NEW | FA_WRITE);
|
res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
if (res != FR_OK)
|
if (res != FR_OK)
|
||||||
{
|
{
|
||||||
printf("CreateTicket: failed to create file (%d)\n", res);
|
printf("CreateTicket: failed to create file (%d)\n", res);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 ticket[0x2C4];
|
u8 ticket[0x2C4];
|
||||||
@ -670,15 +750,20 @@ void CreateTicket(char* path, u32 titleid0, u32 titleid1, u8 version)
|
|||||||
f_write(&file, ticket, 0x2C4, &nwrite);
|
f_write(&file, ticket, 0x2C4, &nwrite);
|
||||||
|
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateSaveFile(char* path, u32 len)
|
bool CreateSaveFile(const char* path, u32 len)
|
||||||
{
|
{
|
||||||
if (len < 0x200) return;
|
if (len == 0) return true;
|
||||||
if (len > 0x8000000) return;
|
if (len < 0x200) return false;
|
||||||
|
if (len > 0x8000000) return false;
|
||||||
|
|
||||||
u32 clustersize, maxfiles, totsec16, fatsz16;
|
u32 clustersize, maxfiles, totsec16, fatsz16;
|
||||||
|
|
||||||
|
// CHECKME!
|
||||||
|
// code inspired from https://github.com/JeffRuLz/TMFH/blob/master/arm9/src/sav.c
|
||||||
if (len < 573440)
|
if (len < 573440)
|
||||||
{
|
{
|
||||||
clustersize = 512;
|
clustersize = 512;
|
||||||
@ -706,11 +791,11 @@ void CreateSaveFile(char* path, u32 len)
|
|||||||
FRESULT res;
|
FRESULT res;
|
||||||
u32 nwrite;
|
u32 nwrite;
|
||||||
|
|
||||||
res = f_open(&file, path, FA_CREATE_NEW | FA_WRITE);
|
res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
if (res != FR_OK)
|
if (res != FR_OK)
|
||||||
{
|
{
|
||||||
printf("CreateSaveFile: failed to create file (%d)\n", res);
|
printf("CreateSaveFile: failed to create file (%d)\n", res);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* data = new u8[len];
|
u8* data = new u8[len];
|
||||||
@ -738,43 +823,16 @@ void CreateSaveFile(char* path, u32 len)
|
|||||||
|
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
delete[] data;
|
delete[] data;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportTest()
|
bool ImportTitle(const char* appfile, u8* tmd, bool readonly)
|
||||||
{
|
{
|
||||||
char* tmdfile = "treasure.tmd";
|
|
||||||
char* appfile = "treasure.nds";
|
|
||||||
|
|
||||||
FRESULT res;
|
|
||||||
|
|
||||||
/*debug_listfiles("0:");
|
|
||||||
|
|
||||||
debug_dumpfile("0:/sys/log/sysmenu.log", "sysmenu.log");
|
|
||||||
debug_dumpfile("0:/sys/log/product.log", "product.log");
|
|
||||||
|
|
||||||
f_unmount("0:");
|
|
||||||
ff_disk_close();
|
|
||||||
return;*/
|
|
||||||
|
|
||||||
//debug_dumpfile("0:/title/00030004/4b4e4448/content/00000002.app", "00000002.app");
|
|
||||||
//debug_dumpfile("0:/title/00030004/4b4e4448/content/title.tmd", "title.tmd");
|
|
||||||
//f_unlink("0:/title/00030004/4b475556/data/public.sav");
|
|
||||||
//f_unlink("0:/ticket/00030004/4b475556.tik");
|
|
||||||
//debug_dumpfile("0:/title/00030004/4b475556/content/title.tmd", "flipnote.tmd");
|
|
||||||
//f_unlink("0:/title/00030004/4b475556/content/title.tmd");
|
|
||||||
/*f_unmount("0:");
|
|
||||||
ff_disk_close();
|
|
||||||
return;*/
|
|
||||||
u8 tmd[0x208];
|
|
||||||
{
|
|
||||||
FILE* f = fopen(tmdfile, "rb");
|
|
||||||
fread(tmd, 0x208, 1, f);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 header[0x1000];
|
u8 header[0x1000];
|
||||||
{
|
{
|
||||||
FILE* f = fopen(appfile, "rb");
|
FILE* f = fopen(appfile, "rb");
|
||||||
|
if (!f) return false;
|
||||||
fread(header, 0x1000, 1, f);
|
fread(header, 0x1000, 1, f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
@ -786,157 +844,115 @@ return;*/
|
|||||||
u32 titleid1 = (tmd[0x190] << 24) | (tmd[0x191] << 16) | (tmd[0x192] << 8) | tmd[0x193];
|
u32 titleid1 = (tmd[0x190] << 24) | (tmd[0x191] << 16) | (tmd[0x192] << 8) | tmd[0x193];
|
||||||
printf("Title ID: %08x/%08x\n", titleid0, titleid1);
|
printf("Title ID: %08x/%08x\n", titleid0, titleid1);
|
||||||
|
|
||||||
|
FRESULT res;
|
||||||
|
DIR ticketdir;
|
||||||
|
FILINFO info;
|
||||||
|
|
||||||
|
char fname[128];
|
||||||
|
FIL file;
|
||||||
|
u32 nwrite;
|
||||||
|
|
||||||
|
// ticket
|
||||||
|
|
||||||
|
sprintf(fname, "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
|
||||||
|
if (!CreateTicket(fname, *(u32*)&tmd[0x18C], *(u32*)&tmd[0x190], header[0x1E]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (readonly) f_chmod(fname, AM_RDO, AM_RDO);
|
||||||
|
|
||||||
|
// folder
|
||||||
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x", titleid0, titleid1);
|
||||||
|
f_mkdir(fname);
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/content", titleid0, titleid1);
|
||||||
|
f_mkdir(fname);
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/data", titleid0, titleid1);
|
||||||
|
f_mkdir(fname);
|
||||||
|
|
||||||
|
// data
|
||||||
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
|
||||||
|
if (!CreateSaveFile(fname, *(u32*)&header[0x238]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
|
||||||
|
if (!CreateSaveFile(fname, *(u32*)&header[0x23C]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (header[0x1BF] & 0x04)
|
||||||
{
|
{
|
||||||
DIR ticketdir;
|
// custom banner file
|
||||||
FILINFO info;
|
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
|
||||||
|
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
char fname[128];
|
if (res != FR_OK)
|
||||||
FIL file;
|
|
||||||
FRESULT res;
|
|
||||||
u32 nwrite;
|
|
||||||
|
|
||||||
/*res = f_opendir(&ticketdir, "0:/ticket/00030004");
|
|
||||||
printf("dir res: %d\n", res);
|
|
||||||
|
|
||||||
res = f_readdir(&ticketdir, &info);
|
|
||||||
if (res)
|
|
||||||
{
|
{
|
||||||
printf("bad read res: %d\n", res);
|
printf("ImportTitle: failed to create banner.sav (%d)\n", res);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!info.fname[0])
|
u8 bannersav[0x4000];
|
||||||
{
|
memset(bannersav, 0, 0x4000);
|
||||||
printf("VERY BAD!! DIR IS EMPTY\n");
|
f_write(&file, bannersav, 0x4000, &nwrite);
|
||||||
// TODO ERROR MANAGEMENT!!
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("- %s\n", info.fname);
|
|
||||||
char fname[128];
|
|
||||||
sprintf(fname, "0:/ticket/00030004/%s", info.fname);
|
|
||||||
|
|
||||||
f_closedir(&ticketdir);
|
|
||||||
|
|
||||||
FIL file;
|
|
||||||
res = f_open(&file, fname, FA_OPEN_EXISTING | FA_READ);
|
|
||||||
//
|
|
||||||
|
|
||||||
u8 ticket[0x2C4];
|
|
||||||
u32 nread;
|
|
||||||
f_read(&file, ticket, 0x2C4, &nread);
|
|
||||||
//
|
|
||||||
|
|
||||||
f_close(&file);
|
|
||||||
|
|
||||||
ESDecrypt(ticket, 0x2A4);
|
|
||||||
|
|
||||||
// change title ID
|
|
||||||
*(u32*)&ticket[0x1DC] = *(u32*)&tmd[0x18C];
|
|
||||||
*(u32*)&ticket[0x1E0] = *(u32*)&tmd[0x190];
|
|
||||||
|
|
||||||
printf("ass console ID: %08X\n", *(u32*)&tmd[0x1D8]);
|
|
||||||
*(u32*)&tmd[0x1D8] = 0;
|
|
||||||
|
|
||||||
ESEncrypt(ticket, 0x2A4);*/
|
|
||||||
|
|
||||||
// insert shit!
|
|
||||||
|
|
||||||
// ticket
|
|
||||||
|
|
||||||
sprintf(fname, "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
|
|
||||||
printf("TICKET: %s\n", fname);
|
|
||||||
/*res = f_open(&file, fname, FA_CREATE_NEW | FA_WRITE);
|
|
||||||
printf("TICKET: %d\n", res);
|
|
||||||
|
|
||||||
f_write(&file, ticket, 0x2C4, &nread);
|
|
||||||
//
|
|
||||||
|
|
||||||
f_close(&file);*/
|
|
||||||
|
|
||||||
CreateTicket(fname, *(u32*)&tmd[0x18C], *(u32*)&tmd[0x190], header[0x1E]);
|
|
||||||
|
|
||||||
printf("----- POST TICKET:\n");
|
|
||||||
debug_listfiles("0:");
|
|
||||||
|
|
||||||
// folder
|
|
||||||
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x", titleid0, titleid1);
|
|
||||||
res = f_mkdir(fname);
|
|
||||||
printf("DIR0 RES=%d\n", res);
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/content", titleid0, titleid1);
|
|
||||||
res = f_mkdir(fname);
|
|
||||||
printf("DIR1 RES=%d\n", res);
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/data", titleid0, titleid1);
|
|
||||||
res = f_mkdir(fname);
|
|
||||||
printf("DIR2 RES=%d\n", res);
|
|
||||||
|
|
||||||
printf("----- POST DIRS:\n");
|
|
||||||
debug_listfiles("0:");
|
|
||||||
|
|
||||||
// data
|
|
||||||
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
|
|
||||||
CreateSaveFile(fname, *(u32*)&header[0x238]);
|
|
||||||
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
|
|
||||||
CreateSaveFile(fname, *(u32*)&header[0x23C]);
|
|
||||||
|
|
||||||
if (header[0x1BF] & 0x04)
|
|
||||||
{
|
|
||||||
// custom banner file
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
|
|
||||||
res = f_open(&file, fname, FA_CREATE_NEW | FA_WRITE);
|
|
||||||
|
|
||||||
u8 bannersav[0x4000];
|
|
||||||
memset(bannersav, 0, 0x4000);
|
|
||||||
f_write(&file, bannersav, 0x4000, &nwrite);
|
|
||||||
|
|
||||||
f_close(&file);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("----- POST SAVE:\n");
|
|
||||||
debug_listfiles("0:");
|
|
||||||
|
|
||||||
// TMD
|
|
||||||
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
|
|
||||||
printf("TMD: %s\n", fname);
|
|
||||||
res = f_open(&file, fname, FA_CREATE_NEW | FA_WRITE);
|
|
||||||
printf("TMD: %d\n", res);
|
|
||||||
|
|
||||||
f_write(&file, tmd, 0x208, &nwrite);
|
|
||||||
//
|
|
||||||
|
|
||||||
f_close(&file);
|
|
||||||
|
|
||||||
printf("----- POST TMD:\n");
|
|
||||||
debug_listfiles("0:");
|
|
||||||
|
|
||||||
// executable
|
|
||||||
|
|
||||||
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
|
||||||
printf("APP: %s\n", fname);
|
|
||||||
res = f_open(&file, fname, FA_CREATE_NEW | FA_WRITE);
|
|
||||||
printf("APP: %d\n", res);
|
|
||||||
|
|
||||||
FILE* app = fopen(appfile, "rb");
|
|
||||||
fseek(app, 0, SEEK_END);
|
|
||||||
u32 applen = (u32)ftell(app);
|
|
||||||
fseek(app, 0, SEEK_SET);
|
|
||||||
|
|
||||||
for (u32 i = 0; i < applen; i += 0x200)
|
|
||||||
{
|
|
||||||
u8 data[0x200];
|
|
||||||
|
|
||||||
u32 lenread = fread(data, 1, 0x200, app);
|
|
||||||
f_write(&file, data, lenread, &nwrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(app);
|
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("----- POST INSERTION:\n");
|
// TMD
|
||||||
debug_listfiles("0:");
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
|
||||||
|
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
if (res != FR_OK)
|
||||||
|
{
|
||||||
|
printf("ImportTitle: failed to create TMD (%d)\n", res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
f_write(&file, tmd, 0x208, &nwrite);
|
||||||
|
|
||||||
|
f_close(&file);
|
||||||
|
|
||||||
|
if (readonly) f_chmod(fname, AM_RDO, AM_RDO);
|
||||||
|
|
||||||
|
// executable
|
||||||
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
|
||||||
|
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
|
||||||
|
if (res != FR_OK)
|
||||||
|
{
|
||||||
|
printf("ImportTitle: failed to create executable (%d)\n", res);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* app = fopen(appfile, "rb");
|
||||||
|
fseek(app, 0, SEEK_END);
|
||||||
|
u32 applen = (u32)ftell(app);
|
||||||
|
fseek(app, 0, SEEK_SET);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < applen; i += 0x200)
|
||||||
|
{
|
||||||
|
u8 data[0x200];
|
||||||
|
|
||||||
|
u32 lenread = fread(data, 1, 0x200, app);
|
||||||
|
f_write(&file, data, lenread, &nwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(app);
|
||||||
|
f_close(&file);
|
||||||
|
|
||||||
|
if (readonly) f_chmod(fname, AM_RDO, AM_RDO);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteTitle(u32 category, u32 titleid)
|
||||||
|
{
|
||||||
|
char fname[128];
|
||||||
|
|
||||||
|
sprintf(fname, "0:/ticket/%08x/%08x.tik", category, titleid);
|
||||||
|
RemoveFile(fname);
|
||||||
|
|
||||||
|
sprintf(fname, "0:/title/%08x/%08x", category, titleid);
|
||||||
|
RemoveDir(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace DSi_NAND
|
namespace DSi_NAND
|
||||||
{
|
{
|
||||||
@ -33,8 +34,10 @@ void GetIDs(u8* emmc_cid, u64& consoleid);
|
|||||||
void PatchTSC();
|
void PatchTSC();
|
||||||
|
|
||||||
void ListTitles(u32 category, std::vector<u32>& titlelist);
|
void ListTitles(u32 category, std::vector<u32>& titlelist);
|
||||||
|
bool TitleExists(u32 category, u32 titleid);
|
||||||
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner);
|
void GetTitleInfo(u32 category, u32 titleid, u32& version, u8* header, u8* banner);
|
||||||
void ImportTest();
|
bool ImportTitle(const char* appfile, u8* tmd, bool readonly);
|
||||||
|
void DeleteTitle(u32 category, u32 titleid);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||||
|
|
||||||
|
|
||||||
#define FF_USE_MKFS 0
|
#define FF_USE_MKFS 1
|
||||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
@ -42,7 +42,7 @@
|
|||||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||||
|
|
||||||
|
|
||||||
#define FF_USE_CHMOD 0
|
#define FF_USE_CHMOD 1
|
||||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||||
|
|
||||||
|
@ -47,11 +47,12 @@ if (USE_QT6)
|
|||||||
set(Qt6Core_DIR ${QT6_STATIC_BASE}Core)
|
set(Qt6Core_DIR ${QT6_STATIC_BASE}Core)
|
||||||
set(Qt6Gui_DIR ${QT6_STATIC_BASE}Gui)
|
set(Qt6Gui_DIR ${QT6_STATIC_BASE}Gui)
|
||||||
set(Qt6Widgets_DIR ${QT6_STATIC_BASE}Widgets)
|
set(Qt6Widgets_DIR ${QT6_STATIC_BASE}Widgets)
|
||||||
|
set(Qt6Network_DIR ${QT6_STATIC_BASE}Network)
|
||||||
set(Qt6OpenGL_DIR ${QT6_STATIC_BASE}OpenGL)
|
set(Qt6OpenGL_DIR ${QT6_STATIC_BASE}OpenGL)
|
||||||
set(Qt6OpenGLWidgets_DIR ${QT6_STATIC_BASE}OpenGLWidgets)
|
set(Qt6OpenGLWidgets_DIR ${QT6_STATIC_BASE}OpenGLWidgets)
|
||||||
endif()
|
endif()
|
||||||
find_package(Qt6 COMPONENTS Core Gui Widgets OpenGL OpenGLWidgets REQUIRED)
|
find_package(Qt6 COMPONENTS Core Gui Widgets Network OpenGL OpenGLWidgets REQUIRED)
|
||||||
set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets)
|
set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::OpenGL Qt6::OpenGLWidgets)
|
||||||
else()
|
else()
|
||||||
if (BUILD_STATIC AND QT5_STATIC_DIR)
|
if (BUILD_STATIC AND QT5_STATIC_DIR)
|
||||||
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
|
set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
|
||||||
@ -59,9 +60,10 @@ else()
|
|||||||
set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
|
set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
|
||||||
set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
|
set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
|
||||||
set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
|
set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
|
||||||
|
set(Qt5Network_DIR ${QT5_STATIC_BASE}Network)
|
||||||
endif()
|
endif()
|
||||||
find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED)
|
find_package(Qt5 COMPONENTS Core Gui Widgets Network REQUIRED)
|
||||||
set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets)
|
set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
|
@ -51,39 +51,12 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne
|
|||||||
for (std::vector<u32>::iterator it = titlelist.begin(); it != titlelist.end(); it++)
|
for (std::vector<u32>::iterator it = titlelist.begin(); it != titlelist.end(); it++)
|
||||||
{
|
{
|
||||||
u32 titleid = *it;
|
u32 titleid = *it;
|
||||||
|
createTitleItem(category, titleid);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui->lstTitleList->sortItems();
|
||||||
|
|
||||||
|
ui->btnDeleteTitle->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleManagerDialog::~TitleManagerDialog()
|
TitleManagerDialog::~TitleManagerDialog()
|
||||||
@ -91,6 +64,42 @@ TitleManagerDialog::~TitleManagerDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TitleManagerDialog::createTitleItem(u32 category, u32 titleid)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
item->setData(Qt::UserRole, (((u64)category<<32) | (u64)titleid));
|
||||||
|
ui->lstTitleList->addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
bool TitleManagerDialog::openNAND()
|
bool TitleManagerDialog::openNAND()
|
||||||
{
|
{
|
||||||
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
|
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
|
||||||
@ -136,12 +145,77 @@ void TitleManagerDialog::done(int r)
|
|||||||
|
|
||||||
void TitleManagerDialog::on_btnImportTitle_clicked()
|
void TitleManagerDialog::on_btnImportTitle_clicked()
|
||||||
{
|
{
|
||||||
TitleImportDialog* importdlg = new TitleImportDialog(this);
|
TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, importTmdData, importReadOnly);
|
||||||
importdlg->open();
|
importdlg->open();
|
||||||
|
connect(importdlg, &TitleImportDialog::finished, this, &TitleManagerDialog::onImportTitleFinished);
|
||||||
|
|
||||||
|
importdlg->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleManagerDialog::onImportTitleFinished(int res)
|
||||||
|
{
|
||||||
|
if (res != QDialog::Accepted) return;
|
||||||
|
|
||||||
|
u32 titleid[2];
|
||||||
|
titleid[0] = (importTmdData[0x18C] << 24) | (importTmdData[0x18D] << 16) | (importTmdData[0x18E] << 8) | importTmdData[0x18F];
|
||||||
|
titleid[1] = (importTmdData[0x190] << 24) | (importTmdData[0x191] << 16) | (importTmdData[0x192] << 8) | importTmdData[0x193];
|
||||||
|
|
||||||
|
bool importres = DSi_NAND::ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly);
|
||||||
|
if (!importres)
|
||||||
|
{
|
||||||
|
// remove a potential half-completed install
|
||||||
|
DSi_NAND::DeleteTitle(titleid[0], titleid[1]);
|
||||||
|
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"An error occured while installing the title to the NAND.\nCheck that your NAND dump is valid.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// it worked, wee!
|
||||||
|
createTitleItem(titleid[0], titleid[1]);
|
||||||
|
ui->lstTitleList->sortItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleManagerDialog::on_btnExportTitle_clicked()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleManagerDialog::on_btnDeleteTitle_clicked()
|
||||||
|
{
|
||||||
|
QListWidgetItem* cur = ui->lstTitleList->currentItem();
|
||||||
|
if (!cur) return;
|
||||||
|
|
||||||
|
if (QMessageBox::question(this,
|
||||||
|
"Delete title - melonDS",
|
||||||
|
"The title and its associated data will be permanently deleted. Are you sure?",
|
||||||
|
QMessageBox::StandardButtons(QMessageBox::Yes|QMessageBox::No),
|
||||||
|
QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u64 titleid = cur->data(Qt::UserRole).toLongLong();
|
||||||
|
DSi_NAND::DeleteTitle((u32)(titleid >> 32), (u32)titleid);
|
||||||
|
|
||||||
|
delete cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleManagerDialog::on_lstTitleList_currentItemChanged(QListWidgetItem* cur, QListWidgetItem* prev)
|
||||||
|
{
|
||||||
|
if (!cur)
|
||||||
|
{
|
||||||
|
ui->btnDeleteTitle->setEnabled(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ui->btnDeleteTitle->setEnabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TitleImportDialog::TitleImportDialog(QWidget* parent) : QDialog(parent), ui(new Ui::TitleImportDialog)
|
TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, u8* tmd, bool& readonly)
|
||||||
|
: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
@ -149,6 +223,8 @@ TitleImportDialog::TitleImportDialog(QWidget* parent) : QDialog(parent), ui(new
|
|||||||
grpTmdSource = new QButtonGroup(this);
|
grpTmdSource = new QButtonGroup(this);
|
||||||
grpTmdSource->addButton(ui->rbTmdFromFile, 0);
|
grpTmdSource->addButton(ui->rbTmdFromFile, 0);
|
||||||
grpTmdSource->addButton(ui->rbTmdFromNUS, 1);
|
grpTmdSource->addButton(ui->rbTmdFromNUS, 1);
|
||||||
|
connect(grpTmdSource, SIGNAL(buttonClicked(int)), this, SLOT(onChangeTmdSource(int)));
|
||||||
|
grpTmdSource->button(0)->setChecked(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleImportDialog::~TitleImportDialog()
|
TitleImportDialog::~TitleImportDialog()
|
||||||
@ -156,6 +232,152 @@ TitleImportDialog::~TitleImportDialog()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::accept()
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
FILE* f;
|
||||||
|
|
||||||
|
bool tmdfromfile = (grpTmdSource->checkedId() == 0);
|
||||||
|
|
||||||
|
path = ui->txtAppFile->text();
|
||||||
|
f = fopen(path.toStdString().c_str(), "rb");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"Could not open executable file.\nCheck that the path is correct and that the file is accessible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(f, 0x230, SEEK_SET);
|
||||||
|
fread(titleid, 8, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
if (titleid[1] != 0x00030004)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"Executable file is not a DSiware title.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmdfromfile)
|
||||||
|
{
|
||||||
|
path = ui->txtTmdFile->text();
|
||||||
|
f = fopen(path.toStdString().c_str(), "rb");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"Could not open metadata file.\nCheck that the path is correct and that the file is accessible.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(tmdData, 0x208, 1, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
u32 tmdtitleid[2];
|
||||||
|
tmdtitleid[0] = (tmdData[0x18C] << 24) | (tmdData[0x18D] << 16) | (tmdData[0x18E] << 8) | tmdData[0x18F];
|
||||||
|
tmdtitleid[1] = (tmdData[0x190] << 24) | (tmdData[0x191] << 16) | (tmdData[0x192] << 8) | tmdData[0x193];
|
||||||
|
|
||||||
|
if (tmdtitleid[1] != titleid[0] || tmdtitleid[0] != titleid[1])
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"Title ID in metadata file does not match executable file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DSi_NAND::TitleExists(titleid[1], titleid[0]))
|
||||||
|
{
|
||||||
|
if (QMessageBox::question(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"The selected title is already installed. Overwrite it?",
|
||||||
|
QMessageBox::StandardButtons(QMessageBox::Yes|QMessageBox::No),
|
||||||
|
QMessageBox::No) != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DSi_NAND::DeleteTitle(titleid[1], titleid[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tmdfromfile)
|
||||||
|
{
|
||||||
|
network = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
|
char url[256];
|
||||||
|
sprintf(url, "http://nus.cdn.t.shop.nintendowifi.net/ccs/download/%08x%08x/tmd", titleid[1], titleid[0]);
|
||||||
|
|
||||||
|
QNetworkRequest req;
|
||||||
|
req.setUrl(QUrl(url));
|
||||||
|
|
||||||
|
netreply = network->get(req);
|
||||||
|
connect(netreply, &QNetworkReply::finished, this, &TitleImportDialog::tmdDownloaded);
|
||||||
|
|
||||||
|
setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::tmdDownloaded()
|
||||||
|
{
|
||||||
|
bool good = false;
|
||||||
|
|
||||||
|
if (netreply->error() != QNetworkReply::NoError)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
QString("An error occurred while trying to download the metadata file:\n\n") + netreply->errorString());
|
||||||
|
}
|
||||||
|
else if (netreply->bytesAvailable() < 2312)
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"NUS returned a malformed metadata file.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
netreply->read((char*)tmdData, 520);
|
||||||
|
|
||||||
|
u32 tmdtitleid[2];
|
||||||
|
tmdtitleid[0] = (tmdData[0x18C] << 24) | (tmdData[0x18D] << 16) | (tmdData[0x18E] << 8) | tmdData[0x18F];
|
||||||
|
tmdtitleid[1] = (tmdData[0x190] << 24) | (tmdData[0x191] << 16) | (tmdData[0x192] << 8) | tmdData[0x193];
|
||||||
|
|
||||||
|
if (tmdtitleid[1] != titleid[0] || tmdtitleid[0] != titleid[1])
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this,
|
||||||
|
"Import title - melonDS",
|
||||||
|
"NUS returned a malformed metadata file.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
netreply->deleteLater();
|
||||||
|
setEnabled(true);
|
||||||
|
|
||||||
|
if (good)
|
||||||
|
{
|
||||||
|
appPath = ui->txtAppFile->text();
|
||||||
|
readOnly = ui->cbReadOnly->isChecked();
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::on_TitleImportDialog_accepted()
|
||||||
|
{
|
||||||
|
setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::on_TitleImportDialog_rejected()
|
||||||
|
{
|
||||||
|
printf("rejected\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::on_TitleImportDialog_reject()
|
||||||
|
{
|
||||||
|
printf("reject\n");
|
||||||
|
}
|
||||||
|
|
||||||
void TitleImportDialog::on_btnAppBrowse_clicked()
|
void TitleImportDialog::on_btnAppBrowse_clicked()
|
||||||
{
|
{
|
||||||
QString file = QFileDialog::getOpenFileName(this,
|
QString file = QFileDialog::getOpenFileName(this,
|
||||||
@ -179,3 +401,11 @@ void TitleImportDialog::on_btnTmdBrowse_clicked()
|
|||||||
|
|
||||||
ui->txtTmdFile->setText(file);
|
ui->txtTmdFile->setText(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TitleImportDialog::onChangeTmdSource(int id)
|
||||||
|
{
|
||||||
|
bool pathenable = (id==0);
|
||||||
|
|
||||||
|
ui->txtTmdFile->setEnabled(pathenable);
|
||||||
|
ui->btnTmdBrowse->setEnabled(pathenable);
|
||||||
|
}
|
||||||
|
@ -21,8 +21,14 @@
|
|||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QListWidget>
|
||||||
#include <QButtonGroup>
|
#include <QButtonGroup>
|
||||||
|
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class TitleManagerDialog;
|
class TitleManagerDialog;
|
||||||
@ -74,9 +80,20 @@ private slots:
|
|||||||
void done(int r);
|
void done(int r);
|
||||||
|
|
||||||
void on_btnImportTitle_clicked();
|
void on_btnImportTitle_clicked();
|
||||||
|
void onImportTitleFinished(int res);
|
||||||
|
|
||||||
|
void on_btnExportTitle_clicked();
|
||||||
|
void on_btnDeleteTitle_clicked();
|
||||||
|
void on_lstTitleList_currentItemChanged(QListWidgetItem* cur, QListWidgetItem* prev);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::TitleManagerDialog* ui;
|
Ui::TitleManagerDialog* ui;
|
||||||
|
|
||||||
|
QString importAppPath;
|
||||||
|
u8 importTmdData[0x208];
|
||||||
|
bool importReadOnly;
|
||||||
|
|
||||||
|
void createTitleItem(u32 category, u32 titleid);
|
||||||
};
|
};
|
||||||
|
|
||||||
class TitleImportDialog : public QDialog
|
class TitleImportDialog : public QDialog
|
||||||
@ -84,17 +101,34 @@ class TitleImportDialog : public QDialog
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TitleImportDialog(QWidget* parent);
|
explicit TitleImportDialog(QWidget* parent, QString& apppath, u8* tmd, bool& readonly);
|
||||||
~TitleImportDialog();
|
~TitleImportDialog();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void accept() override;
|
||||||
|
void tmdDownloaded();
|
||||||
|
|
||||||
|
void on_TitleImportDialog_accepted();
|
||||||
|
void on_TitleImportDialog_rejected();
|
||||||
|
void on_TitleImportDialog_reject();
|
||||||
|
|
||||||
void on_btnAppBrowse_clicked();
|
void on_btnAppBrowse_clicked();
|
||||||
void on_btnTmdBrowse_clicked();
|
void on_btnTmdBrowse_clicked();
|
||||||
|
void onChangeTmdSource(int id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::TitleImportDialog* ui;
|
Ui::TitleImportDialog* ui;
|
||||||
|
|
||||||
QButtonGroup* grpTmdSource;
|
QButtonGroup* grpTmdSource;
|
||||||
|
|
||||||
|
QNetworkAccessManager* network;
|
||||||
|
QNetworkReply* netreply;
|
||||||
|
|
||||||
|
QString& appPath;
|
||||||
|
u8* tmdData;
|
||||||
|
bool& readOnly;
|
||||||
|
|
||||||
|
u32 titleid[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TITLEMANAGERDIALOG_H
|
#endif // TITLEMANAGERDIALOG_H
|
||||||
|
@ -31,7 +31,21 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QPushButton" name="btnImportTitle">
|
<widget class="QPushButton" name="btnImportTitle">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Import title</string>
|
<string>Import title...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnExportTitle">
|
||||||
|
<property name="text">
|
||||||
|
<string>Export title...</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btnDeleteTitle">
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete title</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
Reference in New Issue
Block a user