Added installer for cifinish.bin files

This commit is contained in:
d0k3 2021-09-01 16:13:11 +02:00
parent 07cb94d99a
commit 25bf8b3f93
8 changed files with 130 additions and 19 deletions

View File

@ -156,6 +156,9 @@ u64 IdentifyFileType(const char* path) {
return BIN_KEYDB; // key database
} else if ((sscanf(fname, "slot%02lXKey", &id) == 1) && (strncasecmp(ext, "bin", 4) == 0) && (fsize = 16) && (id < 0x40)) {
return BIN_LEGKEY; // legacy key file
} else if ((strncmp((char*) data, CIFINISH_MAGIC, strlen(CIFINISH_MAGIC)) == 0) &&
(fsize == CIFINISH_SIZE((void*) data)) && (fsize > sizeof(CifinishHeader))) {
return BIN_CIFNSH;
} else if (ValidateText((char*) data, (fsize > 0x200) ? 0x200 : fsize)) {
u64 type = 0;
if ((fsize < SCRIPT_MAX_SIZE) && (strncasecmp(ext, SCRIPT_EXT, strnlen(SCRIPT_EXT, 16) + 1) == 0))

View File

@ -27,17 +27,18 @@
#define SYS_DISA (1ULL<<22)
#define SYS_AGBSAVE (1ULL<<23)
#define SYS_TICKDB (1ULL<<24)
#define BIN_NCCHNFO (1ULL<<25)
#define BIN_TIKDB (1ULL<<26)
#define BIN_KEYDB (1ULL<<27)
#define BIN_LEGKEY (1ULL<<28)
#define TXT_SCRIPT (1ULL<<29)
#define TXT_GENERIC (1ULL<<30)
#define GFX_PNG (1ULL<<31)
#define FONT_PBM (1ULL<<32)
#define FONT_RIFF (1ULL<<33)
#define NOIMG_NAND (1ULL<<34)
#define HDR_NAND (1ULL<<35)
#define BIN_CIFNSH (1ULL<<25)
#define BIN_NCCHNFO (1ULL<<26)
#define BIN_TIKDB (1ULL<<27)
#define BIN_KEYDB (1ULL<<28)
#define BIN_LEGKEY (1ULL<<29)
#define TXT_SCRIPT (1ULL<<30)
#define TXT_GENERIC (1ULL<<31)
#define GFX_PNG (1ULL<<32)
#define FONT_PBM (1ULL<<33)
#define FONT_RIFF (1ULL<<34)
#define NOIMG_NAND (1ULL<<35)
#define HDR_NAND (1ULL<<36)
#define TYPE_BASE 0xFFFFFFFFFFULL // 40 bit reserved for base types
// #define FLAG_FIRM (1ULL<<58) // <--- for CXIs containing FIRMs
@ -55,6 +56,7 @@
#define FTYPE_CIABUILD_L(tp) (tp&(GAME_TMD|GAME_CDNTMD|GAME_TIE|GAME_TAD))
#define FTYPE_CIAINSTALL(tp) ((tp&(GAME_NCSD|GAME_NCCH|GAME_CIA|GAME_CDNTMD|GAME_TWLTMD)) || ((tp&GAME_NDS)&&(tp&(FLAG_DSIW))))
#define FTYPE_TIKINSTALL(tp) (tp&(GAME_TICKET))
#define FTYPE_CIFINSTALL(tp) (tp&(BIN_CIFNSH))
#define FTYPE_TIKDUMP(tp) (tp&(GAME_TIE))
#define FTYPE_CXIDUMP(tp) (tp&(GAME_TMD|GAME_TIE))
#define FTYPE_UNINSTALL(tp) (tp&(GAME_TIE))

View File

@ -0,0 +1,23 @@
#pragma once
#include "common.h"
#define CIFINISH_MAGIC "CIFINISH"
#define CIFINISH_TITLE_MAGIC "TITLE"
#define CIFINISH_SIZE(c) (sizeof(CifinishHeader) + ((((CifinishHeader*)(c))->n_entries) * sizeof(CifinishTitle)))
// see: https://github.com/ihaveamac/custom-install/blob/ac0be9d61d7ebef9356df23036dc53e8e862011a/custominstall.py#L163
typedef struct {
char magic[8];
u32 version;
u32 n_entries;
} __attribute__((packed, aligned(4))) CifinishHeader;
typedef struct {
char magic[5];
u8 padding0;
u8 has_seed; // 1 if it does, otherwise 0
u8 padding1;
u64 title_id;
u8 seed[16];
} __attribute__((packed, aligned(4))) CifinishTitle;

View File

@ -20,3 +20,4 @@
#include "bdri.h"
#include "ticketdb.h"
#include "ncchinfo.h"
#include "cifinish.h"

View File

@ -74,7 +74,7 @@ u32 BuildVariableFakeTicket(Ticket** ticket, u32* ticket_size, const u8* title_i
memset(_ticket->ecdsa, 0xFF, 0x3C);
_ticket->version = 0x01;
memset(_ticket->titlekey, 0xFF, 16);
memcpy(_ticket->title_id, title_id, 8);
if (title_id) memcpy(_ticket->title_id, title_id, 8);
_ticket->commonkey_idx = 0x00; // eshop
_ticket->audit = 0x01; // whatever

View File

@ -1107,6 +1107,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
!(drvtype & DRV_TWLNAND) && !(drvtype & DRV_ALIAS) && !(drvtype & DRV_IMAGE);
bool tik_installable = (FTYPE_TIKINSTALL(filetype)) && !(drvtype & DRV_IMAGE);
bool tik_dumpable = (FTYPE_TIKDUMP(filetype));
bool cif_installable = (FTYPE_CIFINSTALL(filetype)) && !(drvtype & DRV_IMAGE);
bool uninstallable = (FTYPE_UNINSTALL(filetype));
bool cxi_dumpable = (FTYPE_CXIDUMP(filetype));
bool tik_buildable = (FTYPE_TIKBUILD(filetype)) && !in_output_path;
@ -1149,7 +1150,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || trimable || transferable ||
hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable ||
keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable ||
agbimportable || cia_installable || tik_installable || tik_dumpable;
agbimportable || cia_installable || tik_installable || tik_dumpable || cif_installable;
char pathstr[UTF_BUFFER_BYTESIZE(32)];
TruncateString(pathstr, file_path, 32, 8);
@ -1211,6 +1212,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
(filetype & SYS_TICKDB) ? "Ticket.db options..." :
(filetype & SYS_DIFF) ? "Mount as DIFF image" :
(filetype & SYS_DISA) ? "Mount as DISA image" :
(filetype & BIN_CIFNSH) ? "Install cifinish.bin" :
(filetype & BIN_TIKDB) ? "Titlekey options..." :
(filetype & BIN_KEYDB) ? "AESkeydb options..." :
(filetype & BIN_LEGKEY) ? "Build " KEYDB_NAME :
@ -1359,6 +1361,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
int cia_install = (cia_installable) ? ++n_opt : -1;
int tik_install = (tik_installable) ? ++n_opt : -1;
int tik_dump = (tik_dumpable) ? ++n_opt : -1;
int cif_install = (cif_installable) ? ++n_opt : -1;
int uninstall = (uninstallable) ? ++n_opt : -1;
int tik_build_enc = (tik_buildable) ? ++n_opt : -1;
int tik_build_dec = (tik_buildable) ? ++n_opt : -1;
@ -1394,6 +1397,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
if (cia_install > 0) optionstr[cia_install-1] = "Install game image";
if (tik_install > 0) optionstr[tik_install-1] = "Install ticket";
if (tik_dump > 0) optionstr[tik_dump-1] = "Dump ticket file";
if (cif_install > 0) optionstr[cif_install-1] = "Install cifinish.bin";
if (uninstall > 0) optionstr[uninstall-1] = "Uninstall title";
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;
@ -1602,8 +1606,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
}
return 0;
}
else if ((user_select == cia_install) || (user_select == tik_install)) { // -> install game/ticket file
bool install_tik = (user_select == tik_install);
else if ((user_select == cia_install) || (user_select == tik_install) ||
(user_select == cif_install)) { // -> install game/ticket/cifinish file
u32 (*InstallFunction)(const char*, bool) =
(user_select == cia_install) ? &InstallGameFile :
(user_select == tik_install) ? &InstallTicketFile : &InstallCifinishFile;
bool to_emunand = false;
if (CheckVirtualDrive("E:")) {
optionstr[0] = "Install to SysNAND";
@ -1626,8 +1633,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
continue;
}
DrawDirContents(current_dir, (*cursor = i), scroll);
if ((!install_tik && (InstallGameFile(path, to_emunand) == 0)) ||
(install_tik && (InstallTicketFile(path, to_emunand) == 0)))
if ((*InstallFunction)(path, to_emunand) == 0)
n_success++;
else { // on failure: show error, continue
char lpathstr[UTF_BUFFER_BYTESIZE(32)];
@ -1642,8 +1648,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan
n_success, n_marked, n_other, n_marked);
} else ShowPrompt(false, "%lu/%lu files installed ok", n_success, n_marked);
} else {
u32 ret = install_tik ? InstallTicketFile(file_path, to_emunand) :
InstallGameFile(file_path, to_emunand);
u32 ret = (*InstallFunction)(file_path, to_emunand);
ShowPrompt(false, "%s\nInstall %s", pathstr, (ret == 0) ? "success" : "failed");
if ((ret != 0) && (filetype & (GAME_NCCH|GAME_NCSD)) &&
ShowPrompt(true, "%s\nfile failed install.\n \nVerify now?", pathstr)) {

View File

@ -2891,6 +2891,82 @@ u32 InstallGameFile(const char* path, bool to_emunand) {
return ret;
}
u32 InstallCifinishFile(const char* path, bool to_emunand) {
u8 ALIGN(4) seeddb_storage[sizeof(SeedInfo) + sizeof(SeedInfoEntry)];
SeedInfo* seeddb = (SeedInfo*) (void*) seeddb_storage;
TicketCommon ticket;
u32 ret = 0;
// sanity check / preparations
if (!(IdentifyFileType(path) & BIN_CIFNSH)) return 1;
if (BuildFakeTicket((Ticket*) &ticket, NULL) != 0) return 1;
seeddb->n_entries = 1;
// check ticket db
char path_ticketdb[32];
if ((GetInstallDbsPath(path_ticketdb, to_emunand ? "4:" : "1:", "ticket.db") != 0) || !fvx_qsize(path_ticketdb)) {
ShowPrompt(false, "Install error:\nThis system is missing the\nticket.db file.");
return 1;
}
// check permissions for SysNAND (this includes everything we need)
if (!CheckWritePermissions(to_emunand ? "4:" : "1:")) return 1;
// store mount path
char path_store[256] = { 0 };
char* path_bak = NULL;
strncpy(path_store, GetMountPath(), 256);
if (*path_store) path_bak = path_store;
// load the entire cifinish file into memory
CifinishHeader* cifinish = (CifinishHeader*) malloc(fvx_qsize(path));
CifinishTitle* cftitle = (CifinishTitle*) (cifinish+1);
if (!cifinish) return 1;
if (fvx_qread(path, cifinish, 0, fvx_qsize(path), NULL) != FR_OK) {
free(cifinish);
return 1;
}
// process tickets for the entire cifinish file
if (!ShowProgress(0, 0, path)) ret = 1;
if (!InitImgFS(path_ticketdb)) ret = 1;
for (u32 i = 0; !ret && (i < cifinish->n_entries); i++) {
// sanity
if (strncmp(cftitle[i].magic, CIFINISH_TITLE_MAGIC, strlen(CIFINISH_TITLE_MAGIC)) != 0) {
ret = 1;
continue;
}
// check for forbidden title id (the "too large dlc")
if ((TITLE_MAX_CONTENTS <= 1024) && (cftitle[i].title_id == 0x0004008C000CBD00)) {
ShowPrompt(false, "Skipped title:\nTitle with id 0004008C000CBD00\nneeds special compiler flags.");
ShowProgress(0, 0, path);
continue;
}
if (!ShowProgress(i, cifinish->n_entries, path)) ret = 1;
// insert ticket with correct title id
for (u32 t = 0; t < 8; t++)
ticket.title_id[7-t] = (cftitle[i].title_id >> (8*t)) & 0xFF;
AddTicketToDB(PART_PATH, ticket.title_id, (Ticket*) &ticket, false);
}
// process seeds for the entire cifinish file
if (!ShowProgress(0, 0, path)) ret = 1;
for (u32 i = 0; !ret && (i < cifinish->n_entries); i++) {
if (!ShowProgress(i, cifinish->n_entries, path)) ret = 1;
if ((TITLE_MAX_CONTENTS <= 1024) && (cftitle[i].title_id == 0x0004008C000CBD00)) continue;
if (!cftitle[i].has_seed) continue;
seeddb->entries[0].titleId = cftitle[i].title_id;
memcpy(&(seeddb->entries[0].seed), cftitle[i].seed, sizeof(Seed));
ret = InstallSeedDbToSystem(seeddb, to_emunand);
}
// cleanup
InitImgFS(path_bak);
free(cifinish);
return ret;
}
u32 InstallTicketFile(const char* path, bool to_emunand) {
// sanity check
if (!(IdentifyFileType(path) & GAME_TICKET))

View File

@ -7,6 +7,7 @@ u32 CheckEncryptedGameFile(const char* path);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 InstallGameFile(const char* path, bool to_emunand);
u32 InstallCifinishFile(const char* path, bool to_emunand);
u32 InstallTicketFile(const char* path, bool to_emunand);
u32 DumpTicketForGameFile(const char* path, bool force_legit);
u32 DumpCxiSrlFromGameFile(const char* path);