mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 05:32:47 +00:00
Enable building titlekey databases
This commit is contained in:
parent
f856211773
commit
dd377dbf8e
@ -53,7 +53,7 @@
|
||||
|
||||
|
||||
// GodMode9 version
|
||||
#define VERSION "1.0.8"
|
||||
#define VERSION "1.0.9"
|
||||
|
||||
// Maximum payload size (arbitrary value!)
|
||||
#define SELF_MAX_SIZE (320 * 1024) // 320kB
|
||||
|
@ -9,6 +9,7 @@ u32 IdentifyFileType(const char* path) {
|
||||
const u8 romfs_magic[] = { ROMFS_MAGIC };
|
||||
const u8 tickdb_magic[] = { TICKDB_MAGIC };
|
||||
const u8 smdh_magic[] = { SMDH_MAGIC };
|
||||
if (!path) return 0; // safety
|
||||
u8 header[0x200] __attribute__((aligned(32))); // minimum required size
|
||||
void* data = (void*) header;
|
||||
size_t fsize = FileGetSize(path);
|
||||
@ -90,9 +91,11 @@ u32 IdentifyFileType(const char* path) {
|
||||
strncpy(ext_cetk, "cetk", 5);
|
||||
if (FileGetSize(path_cetk) > 0)
|
||||
return GAME_NUSCDN; // NUS/CDN type 2
|
||||
} else if (strncasecmp(fname, TIKDB_NAME_ENC, sizeof(TIKDB_NAME_ENC)) == 0) {
|
||||
return BIN_TIKDB | FLAG_ENC; // titlekey database / encrypted
|
||||
} else if (strncasecmp(fname, TIKDB_NAME_DEC, sizeof(TIKDB_NAME_DEC)) == 0) {
|
||||
return BIN_TIKDB; // titlekey database / decrypted
|
||||
} else if ((strncasecmp(fname, "seeddb.bin", 11) == 0) ||
|
||||
(strncasecmp(fname, "encTitlekeys.bin", 17) == 0) ||
|
||||
(strncasecmp(fname, "decTitlekeys.bin", 17) == 0) ||
|
||||
(strncasecmp(fname, "aeskeydb.bin", 13) == 0) ||
|
||||
(strncasecmp(fname, "otp.bin", 8) == 0) ||
|
||||
(strncasecmp(fname, "secret_sector.bin", 18) == 0) ||
|
||||
|
@ -19,9 +19,11 @@
|
||||
#define SYS_TICKDB (1UL<<14)
|
||||
#define BIN_NCCHNFO (1UL<<15)
|
||||
#define BIN_LAUNCH (1UL<<16)
|
||||
#define BIN_SUPPORT (1UL<<17)
|
||||
#define BIN_TIKDB (1UL<<17)
|
||||
#define BIN_SUPPORT (1UL<<18)
|
||||
#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types
|
||||
|
||||
#define FLAG_ENC (1UL<<28)
|
||||
#define FLAG_CTR (1UL<<29)
|
||||
#define FLAG_NUSCDN (1UL<<30)
|
||||
#define FLAG_CXI (1UL<<31)
|
||||
@ -30,8 +32,9 @@
|
||||
#define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_BOSS|SYS_FIRM))
|
||||
#define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|GAME_NUSCDN|SYS_FIRM))
|
||||
#define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS))
|
||||
#define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
|
||||
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
|
||||
#define FTYPE_CIABUILD(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
|
||||
#define FTYPE_CIABUILD_L(tp) (FTYPE_CIABUILD(tp) && (tp&(GAME_TMD)))
|
||||
#define FTYPE_TIKBUILD(tp) (tp&(GAME_TICKET|SYS_TICKDB|BIN_TIKDB))
|
||||
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS))
|
||||
#define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR))
|
||||
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
|
||||
|
@ -1662,3 +1662,70 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump) {
|
||||
TitleKeysInfo* tik_info = (TitleKeysInfo*) MAIN_BUFFER;
|
||||
const char* path_out = (dec) ? OUTPUT_PATH "/" TIKDB_NAME_DEC : OUTPUT_PATH "/" TIKDB_NAME_ENC;
|
||||
const char* path_in = path;
|
||||
UINT br;
|
||||
|
||||
if (!path_in && !dump) { // no input path given - initialize
|
||||
memset(tik_info, 0, 16);
|
||||
if ((fvx_stat(path_out, NULL) == FR_OK) &&
|
||||
(ShowPrompt(true, "%s\nOutput file already exists.\nUpdate this?", path_out)))
|
||||
path_in = path_out;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
u32 filetype = path_in ? IdentifyFileType(path_in) : 0;
|
||||
if (filetype & GAME_TICKET) {
|
||||
Ticket* ticket = (Ticket*) TEMP_BUFFER;
|
||||
if ((fvx_qread(path_in, ticket, 0, TICKET_SIZE, &br) != FR_OK) || (br != TICKET_SIZE) ||
|
||||
(TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) || (AddTicketToInfo(tik_info, ticket, dec) != 0)) return 1;
|
||||
} else if (filetype & SYS_TICKDB) {
|
||||
const u32 area_offsets[] = { TICKDB_AREA_OFFSETS };
|
||||
FIL file;
|
||||
if (fvx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) return 1;
|
||||
// parse file, sector by sector
|
||||
for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) {
|
||||
fvx_lseek(&file, area_offsets[p]);
|
||||
fvx_sync(&file);
|
||||
for (u32 i = 0; i < TICKDB_AREA_SIZE; i += (TEMP_BUFFER_SIZE - 0x200)) {
|
||||
u32 read_bytes = min(TEMP_BUFFER_SIZE, TICKDB_AREA_SIZE - i);
|
||||
u8* data = (u8*) TEMP_BUFFER;
|
||||
if ((fvx_read(&file, data, read_bytes, &br) != FR_OK) || (br != read_bytes)) {
|
||||
fvx_close(&file);
|
||||
return 1;
|
||||
}
|
||||
for (; data + TICKET_SIZE < ((u8*) TEMP_BUFFER) + read_bytes; data += 0x200) {
|
||||
Ticket* ticket = TicketFromTickDbChunk(data, NULL, false);
|
||||
if (!ticket || (ticket->commonkey_idx >= 2) || !getbe64(ticket->ticket_id)) continue;
|
||||
if (TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) return 1;
|
||||
AddTicketToInfo(tik_info, ticket, dec); // ignore result
|
||||
}
|
||||
}
|
||||
}
|
||||
fvx_close(&file);
|
||||
} else if (filetype & BIN_TIKDB) {
|
||||
TitleKeysInfo* tik_info_merge = (TitleKeysInfo*) TEMP_BUFFER;
|
||||
if ((fvx_qread(path_in, tik_info_merge, 0, TEMP_BUFFER_SIZE, &br) != FR_OK) ||
|
||||
(TIKDB_SIZE(tik_info_merge) != br)) return 1;
|
||||
// merge and rebuild TitleKeyInfo
|
||||
u32 n_entries = tik_info_merge->n_entries;
|
||||
TitleKeyEntry* tik = tik_info_merge->entries;
|
||||
for (u32 i = 0; i < n_entries; i++, tik++) {
|
||||
if (TIKDB_SIZE(tik_info) + 32 > MAIN_BUFFER_SIZE) return 1;
|
||||
AddTitleKeyToInfo(tik_info, tik, !(filetype & FLAG_ENC), dec, false); // ignore result
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (dump) {
|
||||
u32 dump_size = TIKDB_SIZE(tik_info);
|
||||
f_unlink(path_out);
|
||||
if ((dump_size <= 16) || (fvx_qwrite(path_out, tik_info, 0, dump_size, &br) != FR_OK) || (br != dump_size))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -10,3 +10,4 @@ u32 ShowGameFileTitleInfo(const char* path);
|
||||
u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
|
||||
u32 CheckHealthAndSafetyInject(const char* hsdrv);
|
||||
u32 InjectHealthAndSafety(const char* path, const char* destdrv);
|
||||
u32 BuildTitleKeyInfo(const char* path, bool dec, bool dump);
|
||||
|
@ -4,19 +4,6 @@
|
||||
#include "sha.h"
|
||||
#include "ff.h"
|
||||
|
||||
typedef struct {
|
||||
u32 commonkey_idx;
|
||||
u8 reserved[4];
|
||||
u8 title_id[8];
|
||||
u8 titlekey[16];
|
||||
} __attribute__((packed)) TitleKeyEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 n_entries;
|
||||
u8 reserved[12];
|
||||
TitleKeyEntry entries[256]; // this number is only a placeholder
|
||||
} __attribute__((packed)) TitleKeysInfo;
|
||||
|
||||
u32 ValidateTicket(Ticket* ticket) {
|
||||
const u8 magic[] = { TICKET_SIG_TYPE };
|
||||
if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
|
||||
@ -148,20 +135,31 @@ u32 FindTitleKey(Ticket* ticket, u8* title_id) {
|
||||
return (found) ? 0 : 1;
|
||||
}
|
||||
|
||||
/*u32 BuildTitleKeyInfo(TitleKeysInfo* tik_info, TicketInfo* tick_info, bool decrypt) {
|
||||
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit) {
|
||||
if (!tik_entry) { // no titlekey entry -> reset database
|
||||
memset(tik_info, 0, 16);
|
||||
for (u32 i = 0; i < tick_info->n_entries; i++) {
|
||||
TicketEntry* tick_entry = tick_info->entries + i;
|
||||
TitleKeyEntry* tik_entry = tik_info->entries + tik_info->n_entries;
|
||||
if (!getbe64(tick_entry->ticket_id)) continue;
|
||||
tik_entry->commonkey_idx = tick_entry->commonkey_idx;
|
||||
memcpy(tik_entry->title_id, tick_entry->title_id, 8);
|
||||
memcpy(tik_entry->titlekey, tick_entry->titlekey, 16);
|
||||
if (decrypt) CryptTitleKey(tik_entry, false, false);
|
||||
tik_info->n_entries++;
|
||||
}
|
||||
return 0;
|
||||
}*/
|
||||
}
|
||||
// check if entry already in DB
|
||||
u32 n_entries = tik_info->n_entries;
|
||||
TitleKeyEntry* tik = tik_info->entries;
|
||||
for (u32 i = 0; i < n_entries; i++, tik++)
|
||||
if (memcmp(tik->title_id, tik_entry->title_id, 8) == 0) return 0;
|
||||
// actually a new titlekey
|
||||
memcpy(tik, tik_entry, sizeof(TitleKeyEntry));
|
||||
if ((decrypted_in != decrypted_out) && (CryptTitleKey(tik, !decrypted_out, devkit) != 0)) return 1;
|
||||
tik_info->n_entries++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt) { // TODO: check for legit tickets?
|
||||
if (!ticket) return AddTitleKeyToInfo(tik_info, NULL, false, false, false);
|
||||
TitleKeyEntry tik = { 0 };
|
||||
memcpy(tik.title_id, ticket->title_id, 8);
|
||||
memcpy(tik.titlekey, ticket->titlekey, 16);
|
||||
tik.commonkey_idx = ticket->commonkey_idx;
|
||||
return AddTitleKeyToInfo(tik_info, &tik, false, decrypt, TICKET_DEVKIT(ticket));
|
||||
}
|
||||
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
|
||||
const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#define TIKDB_NAME_ENC "encTitleKeys.bin"
|
||||
#define TIKDB_NAME_DEC "decTitleKeys.bin"
|
||||
#define TIKDB_SIZE(tdb) (16 + ((tdb)->n_entries * sizeof(TitleKeyEntry)))
|
||||
|
||||
#define TICKDB_PATH(emu) ((emu) ? "4:/dbs/ticket.db" : "1:/dbs/ticket.db") // EmuNAND / SysNAND
|
||||
#define TICKDB_AREA_OFFSETS 0x0137F000, 0x001C0C00 // second partition is more likely to be in use
|
||||
@ -56,10 +57,25 @@ typedef struct {
|
||||
u8 content_index[0xAC];
|
||||
} __attribute__((packed)) Ticket;
|
||||
|
||||
typedef struct {
|
||||
u32 commonkey_idx;
|
||||
u8 reserved[4];
|
||||
u8 title_id[8];
|
||||
u8 titlekey[16];
|
||||
} __attribute__((packed)) TitleKeyEntry;
|
||||
|
||||
typedef struct {
|
||||
u32 n_entries;
|
||||
u8 reserved[12];
|
||||
TitleKeyEntry entries[256]; // this number is only a placeholder
|
||||
} __attribute__((packed)) TitleKeysInfo;
|
||||
|
||||
u32 ValidateTicket(Ticket* ticket);
|
||||
u32 GetTitleKey(u8* titlekey, Ticket* ticket);
|
||||
Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls);
|
||||
u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand);
|
||||
u32 FindTitleKey(Ticket* ticket, u8* title_id);
|
||||
u32 AddTitleKeyToInfo(TitleKeysInfo* tik_info, TitleKeyEntry* tik_entry, bool decrypted_in, bool decrypted_out, bool devkit);
|
||||
u32 AddTicketToInfo(TitleKeysInfo* tik_info, Ticket* ticket, bool decrypt);
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||
u32 BuildTicketCert(u8* tickcert);
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nand.h"
|
||||
#include "virtual.h"
|
||||
#include "vcart.h"
|
||||
#include "game.h"
|
||||
#include "nandcmac.h"
|
||||
#include "ctrtransfer.h"
|
||||
#include "ncchinfo.h"
|
||||
@ -651,8 +652,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
bool decryptable = (FYTPE_DECRYPTABLE(filetype));
|
||||
bool encryptable = (FYTPE_ENCRYPTABLE(filetype));
|
||||
bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT));
|
||||
bool buildable = (FTYPE_BUILDABLE(filetype));
|
||||
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
|
||||
bool cia_buildable = (FTYPE_CIABUILD(filetype));
|
||||
bool cia_buildable_legit = (FTYPE_CIABUILD_L(filetype));
|
||||
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path;
|
||||
bool titleinfo = (FTYPE_TITLEINFO(filetype));
|
||||
bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT));
|
||||
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
|
||||
@ -660,8 +662,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
bool ebackupable = (FTYPE_EBACKUP(filetype));
|
||||
bool xorpadable = (FTYPE_XORPAD(filetype));
|
||||
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
|
||||
bool special_opt = mountable || verificable || decryptable || encryptable || buildable || buildable_legit ||
|
||||
titleinfo || hsinjectable || restorable || xorpadable || launchable || ebackupable;
|
||||
bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit ||
|
||||
tik_buildable || titleinfo || hsinjectable || restorable || xorpadable || launchable || ebackupable;
|
||||
|
||||
char pathstr[32+1];
|
||||
TruncateString(pathstr, curr_entry->path, 32, 8);
|
||||
@ -698,8 +700,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
(filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" :
|
||||
(filetype & GAME_SMDH) ? "Show SMDH title info" :
|
||||
(filetype & GAME_NDS) ? "Show NDS title info" :
|
||||
(filetype & GAME_TICKET)? "Ticket options..." :
|
||||
(filetype & SYS_FIRM ) ? "FIRM image options..." :
|
||||
(filetype & SYS_TICKDB) ? "Mount as ticket.db" :
|
||||
(filetype & SYS_TICKDB) ? (tik_buildable) ? "Ticket.db options..." : "Mount as ticket.db" :
|
||||
(filetype & BIN_TIKDB) ? "Titlekey options..." :
|
||||
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." :
|
||||
(filetype & BIN_LAUNCH) ? "Launch as arm9 payload" : "???";
|
||||
optionstr[hexviewer-1] = "Show in Hexeditor";
|
||||
@ -795,8 +799,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
int ebackup = (ebackupable) ? ++n_opt : -1;
|
||||
int decrypt = (decryptable) ? ++n_opt : -1;
|
||||
int encrypt = (encryptable) ? ++n_opt : -1;
|
||||
int build = (buildable) ? ++n_opt : -1;
|
||||
int build_legit = (buildable_legit) ? ++n_opt : -1;
|
||||
int cia_build = (cia_buildable) ? ++n_opt : -1;
|
||||
int cia_build_legit = (cia_buildable_legit) ? ++n_opt : -1;
|
||||
int tik_build_enc = (tik_buildable) ? ++n_opt : -1;
|
||||
int tik_build_dec = (tik_buildable) ? ++n_opt : -1;
|
||||
int verify = (verificable) ? ++n_opt : -1;
|
||||
int ctrtransfer = (transferable) ? ++n_opt : -1;
|
||||
int hsinject = (hsinjectable) ? ++n_opt : -1;
|
||||
@ -809,8 +815,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
if (show_info > 0) optionstr[show_info-1] = "Show title info";
|
||||
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
|
||||
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
|
||||
if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
|
||||
if (build_legit > 0) optionstr[build_legit-1] = "Build CIA (legit)";
|
||||
if (cia_build > 0) optionstr[cia_build-1] = (cia_build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
|
||||
if (cia_build_legit > 0) optionstr[cia_build_legit-1] = "Build CIA (legit)";
|
||||
if (tik_build_enc > 0) optionstr[tik_build_enc-1] = "Build " TIKDB_NAME_ENC;
|
||||
if (tik_build_dec > 0) optionstr[tik_build_dec-1] = "Build " TIKDB_NAME_DEC;
|
||||
if (verify > 0) optionstr[verify-1] = "Verify file";
|
||||
if (ctrtransfer > 0) optionstr[ctrtransfer-1] = "Transfer image to CTRNAND";
|
||||
if (hsinject > 0) optionstr[hsinject-1] = "Inject to H&S";
|
||||
@ -930,8 +938,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
else ShowPrompt(false, "%s\nEncrypted to %s", pathstr, OUTPUT_PATH);
|
||||
}
|
||||
return 0;
|
||||
} else if ((user_select == build) || (user_select == build_legit)) { // -> build CIA
|
||||
bool force_legit = (user_select == build_legit);
|
||||
} else if ((user_select == cia_build) || (user_select == cia_build_legit)) { // -> build CIA
|
||||
bool force_legit = (user_select == cia_build_legit);
|
||||
if ((n_marked > 1) && ShowPrompt(true, "Try to process all %lu selected files?", n_marked)) {
|
||||
u32 n_success = 0;
|
||||
u32 n_other = 0;
|
||||
@ -998,6 +1006,33 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
(VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed");
|
||||
}
|
||||
return 0;
|
||||
} else if ((user_select == tik_build_enc) || (user_select == tik_build_dec)) { // -> (Re)Build titlekey database
|
||||
bool dec = (user_select == tik_build_dec);
|
||||
const char* path_out = (dec) ? OUTPUT_PATH "/" TIKDB_NAME_DEC : OUTPUT_PATH "/" TIKDB_NAME_ENC;
|
||||
if (BuildTitleKeyInfo(NULL, dec, false) != 0) return 1; // init database
|
||||
ShowString("Building %s...", (dec) ? TIKDB_NAME_DEC : TIKDB_NAME_ENC);
|
||||
if (n_marked > 1) {
|
||||
u32 n_success = 0;
|
||||
u32 n_other = 0;
|
||||
for (u32 i = 0; i < current_dir->n_entries; i++) {
|
||||
const char* path = current_dir->entry[i].path;
|
||||
if (!current_dir->entry[i].marked)
|
||||
continue;
|
||||
if (!FTYPE_TIKBUILD(IdentifyFileType(path))) {
|
||||
n_other++;
|
||||
continue;
|
||||
}
|
||||
current_dir->entry[i].marked = false;
|
||||
if (BuildTitleKeyInfo(path, dec, false) == 0) n_success++; // ignore failures for now
|
||||
}
|
||||
if (BuildTitleKeyInfo(NULL, dec, true) == 0) {
|
||||
if (n_other) ShowPrompt(false, "%s\n%lu/%lu files processed\n%lu/%lu files ignored",
|
||||
path_out, n_success, n_marked, n_other, n_marked);
|
||||
else ShowPrompt(false, "%s\n%lu/%lu files processed", path_out, n_success, n_marked);
|
||||
} else ShowPrompt(false, "%s\nBuild database failed.");
|
||||
} else ShowPrompt(false, "%s\nBuild database %s.", path_out,
|
||||
(BuildTitleKeyInfo(curr_entry->path, dec, true) == 0) ? "success" : "failed");
|
||||
return 0;
|
||||
} else if (user_select == show_info) { // -> Show title info
|
||||
if (ShowGameFileTitleInfo(curr_entry->path) != 0)
|
||||
ShowPrompt(false, "Title info: not found");
|
||||
@ -1019,7 +1054,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
|
||||
(InjectHealthAndSafety(curr_entry->path, destdrv[user_select-1]) == 0) ? "success" : "failed");
|
||||
}
|
||||
return 0;
|
||||
} else if (user_select == ctrtransfer) { // -> adapt CTRNAND image to SysNAND
|
||||
} else if (user_select == ctrtransfer) { // -> transfer CTRNAND image to SysNAND
|
||||
char* destdrv[2] = { NULL };
|
||||
n_opt = 0;
|
||||
if (DriveType("1:")) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user