Enable build CIA from NCCH/NCSD

This commit is contained in:
d0k3 2016-12-19 01:33:30 +01:00
parent fcd61794a8
commit 32c5cd2196
9 changed files with 235 additions and 67 deletions

View File

@ -38,7 +38,7 @@
(((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
// GodMode9 version // GodMode9 version
#define VERSION "0.8.8" #define VERSION "0.8.9"
// input / output paths // input / output paths
#define INPUT_PATHS "0:", "0:/files9", "0:/Decrypt9" #define INPUT_PATHS "0:", "0:/files9", "0:/Decrypt9"

View File

@ -15,7 +15,7 @@
#define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS) #define FTYPE_MOUNTABLE (IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS)
#define FYTPE_VERIFICABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD) #define FYTPE_VERIFICABLE (GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD)
#define FYTPE_DECRYPTABLE (GAME_CIA|GAME_NCSD|GAME_NCCH) #define FYTPE_DECRYPTABLE (GAME_CIA|GAME_NCSD|GAME_NCCH)
#define FTYPE_BUILDABLE (GAME_TMD) #define FTYPE_BUILDABLE (GAME_NCSD|GAME_NCCH|GAME_TMD)
#define FTYPE_BUILDABLE_L (FTYPE_BUILDABLE&(GAME_TMD)) #define FTYPE_BUILDABLE_L (FTYPE_BUILDABLE&(GAME_TMD))
u32 IdentifyFileType(const char* path); u32 IdentifyFileType(const char* path);

View File

@ -102,7 +102,7 @@ u32 BuildCiaCert(u8* ciacert) {
return 0; return 0;
} }
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents) { u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size) {
const u8 sig_type[4] = { TMD_SIG_TYPE }; const u8 sig_type[4] = { TMD_SIG_TYPE };
// safety check: number of contents // safety check: number of contents
if (n_contents > CIA_MAX_CONTENTS) return 1; // !!! if (n_contents > CIA_MAX_CONTENTS) return 1; // !!!
@ -115,7 +115,7 @@ u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents) {
tmd->version = 0x01; tmd->version = 0x01;
memcpy(tmd->title_id, title_id, 8); memcpy(tmd->title_id, title_id, 8);
tmd->title_type[3] = 0x40; // whatever tmd->title_type[3] = 0x40; // whatever
memset(tmd->save_size, 0x00, 4); // placeholder for (u32 i = 0; i < 4; i++) tmd->save_size[i] = (save_size >> (i*8)) & 0xFF; // little endian?
tmd->content_count[1] = (u8) n_contents; tmd->content_count[1] = (u8) n_contents;
memset(tmd->contentinfo_hash, 0xFF, 0x20); // placeholder (hash) memset(tmd->contentinfo_hash, 0xFF, 0x20); // placeholder (hash)
tmd->contentinfo[0].cmd_count[1] = (u8) n_contents; tmd->contentinfo[0].cmd_count[1] = (u8) n_contents;
@ -125,12 +125,12 @@ u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents) {
return 0; return 0;
} }
u32 BuildCiaMeta(CiaMeta* meta, u8* exthdr, u8* smdh) { u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh) {
// init metadata with all zeroes and core version // init metadata with all zeroes and core version
memset(meta, 0x00, sizeof(CiaMeta)); memset(meta, 0x00, sizeof(CiaMeta));
meta->core_version = 2; meta->core_version = 2;
// copy dependencies from extheader // copy dependencies from extheader
if (exthdr) memcpy(meta->dependencies, exthdr + 0x40, sizeof(meta->dependencies)); if (exthdr) memcpy(meta->dependencies, ((NcchExtHeader*) exthdr)->dependencies, sizeof(meta->dependencies));
// copy smdh (icon file in exefs) // copy smdh (icon file in exefs)
if (smdh) memcpy(meta->smdh, smdh, sizeof(meta->smdh)); if (smdh) memcpy(meta->smdh, smdh, sizeof(meta->smdh));
return 0; return 0;

View File

@ -118,8 +118,8 @@ u32 FixTmdHashes(TitleMetaData* tmd);
u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd); u32 FixCiaHeaderForTmd(CiaHeader* header, TitleMetaData* tmd);
u32 BuildCiaCert(u8* ciacert); u32 BuildCiaCert(u8* ciacert);
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents); u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size);
u32 BuildCiaMeta(CiaMeta* meta, u8* exthdr, u8* smdh); u32 BuildCiaMeta(CiaMeta* meta, void* exthdr, void* smdh);
u32 BuildCiaHeader(CiaHeader* header); u32 BuildCiaHeader(CiaHeader* header);
u32 DecryptCiaContentSequential(u8* data, u32 size, u8* ctr, const u8* titlekey); u32 DecryptCiaContentSequential(u8* data, u32 size, u8* ctr, const u8* titlekey);

View File

@ -35,7 +35,7 @@ u32 GetOutputPath(char* dest, const char* path, const char* ext) {
return 0; return 0;
} }
u32 GetNcchHeaders(NcchHeader* ncch, ExeFsHeader* exefs, FIL* file) { u32 GetNcchHeaders(NcchHeader* ncch, NcchExtHeader* exthdr, ExeFsHeader* exefs, FIL* file) {
u32 offset_ncch = f_tell(file); u32 offset_ncch = f_tell(file);
UINT btr; UINT btr;
@ -43,7 +43,16 @@ u32 GetNcchHeaders(NcchHeader* ncch, ExeFsHeader* exefs, FIL* file) {
(ValidateNcchHeader(ncch) != 0)) (ValidateNcchHeader(ncch) != 0))
return 1; return 1;
if (exefs && ncch->size_exefs) { if (exthdr) {
if(!ncch->size_exthdr) return 1;
f_lseek(file, offset_ncch + NCCH_EXTHDR_OFFSET);
if ((fx_read(file, exthdr, NCCH_EXTHDR_SIZE, &btr) != FR_OK) ||
(DecryptNcch((u8*) exthdr, NCCH_EXTHDR_OFFSET, NCCH_EXTHDR_SIZE, ncch, NULL) != 0))
return 1;
}
if (exefs) {
if (!ncch->size_exefs) return 1;
u32 offset_exefs = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT); u32 offset_exefs = offset_ncch + (ncch->offset_exefs * NCCH_MEDIA_UNIT);
f_lseek(file, offset_exefs); f_lseek(file, offset_exefs);
if ((fx_read(file, exefs, sizeof(ExeFsHeader), &btr) != FR_OK) || if ((fx_read(file, exefs, sizeof(ExeFsHeader), &btr) != FR_OK) ||
@ -72,6 +81,22 @@ u32 CheckNcchHash(u8* expected, FIL* file, u32 size_data, u32 offset_ncch, NcchH
return (memcmp(hash, expected, 32) == 0) ? 0 : 1; return (memcmp(hash, expected, 32) == 0) ? 0 : 1;
} }
u32 LoadNcchHeaders(NcchHeader* ncch, NcchExtHeader* exthdr, ExeFsHeader* exefs, const char* path, u32 offset) {
FIL file;
// open file, get NCCH header
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 1;
f_lseek(&file, offset);
if (GetNcchHeaders(ncch, exthdr, exefs, &file) != 0) {
fx_close(&file);
return 1;
}
fx_close(&file);
return 0;
}
u32 LoadNcsdHeader(NcsdHeader* ncsd, const char* path) { u32 LoadNcsdHeader(NcsdHeader* ncsd, const char* path) {
FIL file; FIL file;
UINT btr; UINT btr;
@ -118,67 +143,62 @@ u32 LoadCiaStub(CiaStub* stub, const char* path) {
return 0; return 0;
} }
u32 LoadNcchMeta(CiaMeta* meta, const char* path, u64 offset) { u32 LoadExeFsFile(void* data, const char* path, u32 offset, const char* name, u32 size_max) {
NcchHeader ncch; NcchHeader ncch;
ExeFsHeader exefs; ExeFsHeader exefs;
FIL file; FIL file;
UINT btr; UINT btr;
u32 ret = 0; u32 ret = 0;
// this uses the meta builder function only in part
if (BuildCiaMeta(meta, NULL, NULL) != 0) return 1;
// open file, get NCCH, ExeFS header // open file, get NCCH, ExeFS header
if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&file, path, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 1; return 1;
f_lseek(&file, offset); f_lseek(&file, offset);
if (GetNcchHeaders(&ncch, &exefs, &file) != 0) { if ((GetNcchHeaders(&ncch, NULL, &exefs, &file) != 0) ||
(!ncch.size_exefs)) {
fx_close(&file); fx_close(&file);
return 1; return 1;
} }
// dependencies // load file from exefs
if (ncch.size_exthdr > 0) { ExeFsFileHeader* exefile = NULL;
u8* dep = meta->dependencies;
u32 size_dep = sizeof(meta->dependencies);
f_lseek(&file, offset + NCCH_EXTHDR_OFFSET + 0x40); // offset to dependencies
if ((fx_read(&file, dep, size_dep, &btr) != FR_OK) ||
(DecryptNcch(dep, NCCH_EXTHDR_OFFSET + 0x40, size_dep, &ncch, NULL) != 0) ||
(btr != size_dep)) {
ret = 1;
}
} else ret = 1;
// smdh from exefs
if (ncch.size_exefs > 0) {
ExeFsFileHeader* icon = NULL;
u32 size_smdh = sizeof(meta->smdh);
for (u32 i = 0; i < 10; i++) { for (u32 i = 0; i < 10; i++) {
u32 size = exefs.files[i].size; u32 size = exefs.files[i].size;
if (!size || (size > size_smdh)) continue; if (!size || (size > size_max)) continue;
char* name = exefs.files[i].name; char* exename = exefs.files[i].name;
if (strncmp(name, "icon", 8) == 0) { if (strncmp(name, exename, 8) == 0) {
icon = exefs.files + i; exefile = exefs.files + i;
break; break;
} }
} }
if (icon) { if (exefile) {
u32 size_icon = icon->size; u32 size_exefile = exefile->size;
u32 offset_icon = (ncch.offset_exefs * NCCH_MEDIA_UNIT) + sizeof(ExeFsHeader) + icon->offset; u32 offset_exefile = (ncch.offset_exefs * NCCH_MEDIA_UNIT) + sizeof(ExeFsHeader) + exefile->offset;
u8* smdh = meta->smdh; f_lseek(&file, offset + offset_exefile); // offset to file
f_lseek(&file, offset + offset_icon); // offset to icon if ((fx_read(&file, data, size_exefile, &btr) != FR_OK) ||
if ((fx_read(&file, smdh, size_icon, &btr) != FR_OK) || (DecryptNcch(data, offset_exefile, size_exefile, &ncch, &exefs) != 0) ||
(DecryptNcch(smdh, offset_icon, size_icon, &ncch, &exefs) != 0) || (btr != size_exefile)) {
(btr != size_icon)) {
ret = 1; ret = 1;
} }
} else ret = 1; } else ret = 1;
} else ret = 1;
fx_close(&file); fx_close(&file);
return ret; return ret;
} }
u32 LoadNcchMeta(CiaMeta* meta, const char* path, u64 offset) {
NcchHeader ncch;
NcchExtHeader exthdr;
// get dependencies from exthdr, icon from exeFS
if ((LoadNcchHeaders(&ncch, &exthdr, NULL, path, offset) != 0) ||
(BuildCiaMeta(meta, &exthdr, NULL) != 0) ||
(LoadExeFsFile(meta->smdh, path, offset, "icon", sizeof(meta->smdh))))
return 1;
return 0;
}
u32 LoadTmdFile(TitleMetaData* tmd, const char* path) { u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
const u8 magic[] = { TMD_SIG_TYPE }; const u8 magic[] = { TMD_SIG_TYPE };
FIL file; FIL file;
@ -267,7 +287,7 @@ u32 VerifyNcchFile(const char* path, u32 offset, u32 size) {
return 1; return 1;
f_lseek(&file, offset); f_lseek(&file, offset);
if (GetNcchHeaders(&ncch, &exefs, &file) != 0) { if (GetNcchHeaders(&ncch, NULL, &exefs, &file) != 0) {
if (!offset) ShowPrompt(false, "%s\nError: Not a NCCH file", pathstr); if (!offset) ShowPrompt(false, "%s\nError: Not a NCCH file", pathstr);
fx_close(&file); fx_close(&file);
return 1; return 1;
@ -669,7 +689,7 @@ u32 DecryptGameFile(const char* path, bool inplace) {
} }
u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, u32 size, u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset, u32 size,
TmdContentChunk* chunk, const u8* titlekey, bool force_legit) { TmdContentChunk* chunk, const u8* titlekey, bool force_legit, bool cxi_fix) {
// crypto types // crypto types
bool ncch_crypto = (!force_legit && (CheckEncryptedNcchFile(path_content, offset) == 0)); bool ncch_crypto = (!force_legit && (CheckEncryptedNcchFile(path_content, offset) == 0));
bool cia_crypto = (force_legit && (getbe16(chunk->type) & 0x01)); bool cia_crypto = (force_legit && (getbe16(chunk->type) & 0x01));
@ -683,7 +703,9 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset,
if (fx_open(&ofile, path_content, FA_READ | FA_OPEN_EXISTING) != FR_OK) if (fx_open(&ofile, path_content, FA_READ | FA_OPEN_EXISTING) != FR_OK)
return 1; return 1;
f_lseek(&ofile, offset); f_lseek(&ofile, offset);
fsize = f_size(&ofile); // for progress bar fsize = f_size(&ofile);
if (offset > fsize) return 1;
if (!size) size = fsize - offset;
if (fx_open(&dfile, path_cia, FA_WRITE | FA_OPEN_APPEND) != FR_OK) { if (fx_open(&dfile, path_cia, FA_WRITE | FA_OPEN_APPEND) != FR_OK) {
fx_close(&ofile); fx_close(&ofile);
return 1; return 1;
@ -703,12 +725,13 @@ u32 InsertCiaContent(const char* path_cia, const char* path_content, u32 offset,
u8 ctr[16]; u8 ctr[16];
u32 ret = 0; u32 ret = 0;
GetTmdCtr(ctr, chunk); GetTmdCtr(ctr, chunk);
sha_init(SHA256_MODE);
if (!ShowProgress(0, 0, path_content)) ret = 1; if (!ShowProgress(0, 0, path_content)) ret = 1;
for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) {
u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i));
if (fx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; if (fx_read(&ofile, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1;
if (ncch_crypto && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1; if (ncch_crypto && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1;
if ((i == 0) && cxi_fix && (SetNcchSdFlag(MAIN_BUFFER) != 0)) ret = 1;
if (i == 0) sha_init(SHA256_MODE);
sha_update(MAIN_BUFFER, read_bytes); sha_update(MAIN_BUFFER, read_bytes);
if (cia_crypto && (EncryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1; if (cia_crypto && (EncryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1;
if (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; if (fx_write(&dfile, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1;
@ -800,7 +823,7 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l
for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) { for (u32 i = 0; (i < content_count) && (i < CIA_MAX_CONTENTS); i++) {
TmdContentChunk* chunk = &(content_list[i]); TmdContentChunk* chunk = &(content_list[i]);
snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(chunk->id)); snprintf(name_content, 256 - (name_content - path_content), "%08lx.app", getbe32(chunk->id));
if (InsertCiaContent(path_cia, path_content, 0, (u32) getbe64(chunk->size), chunk, titlekey, force_legit) != 0) { if (InsertCiaContent(path_cia, path_content, 0, (u32) getbe64(chunk->size), chunk, titlekey, force_legit, false) != 0) {
ShowPrompt(false, "ID %016llX.%08lX\nInsert content failed", getbe64(title_id), getbe32(chunk->id)); ShowPrompt(false, "ID %016llX.%08lX\nInsert content failed", getbe64(title_id), getbe32(chunk->id));
return 1; return 1;
} }
@ -820,6 +843,125 @@ u32 BuildCiaFromTmdFile(const char* path_tmd, const char* path_cia, bool force_l
return 0; return 0;
} }
u32 BuildCiaFromNcchFile(const char* path_ncch, const char* path_cia) {
CiaStub* cia = (CiaStub*) TEMP_BUFFER;
CiaMeta* meta = (CiaMeta*) (void*) (cia + 1);
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (meta + 1);
NcchHeader ncch;
u8 title_id[8];
u32 save_size = 0;
// Init progress bar
if (!ShowProgress(0, 0, path_ncch)) return 1;
// load NCCH header / extheader, get save size && title id
if (LoadNcchHeaders(&ncch, exthdr, NULL, path_ncch, 0) == 0) {
save_size = getle32(exthdr->sys_info);
} else {
exthdr = NULL;
if (LoadNcchHeaders(&ncch, NULL, NULL, path_ncch, 0) != 0) return 1;
}
for (u32 i = 0; i < 8; i++)
title_id[i] = (ncch.programId >> ((7-i)*8)) & 0xFF;
// build the CIA stub
memset(cia, 0, sizeof(CiaStub));
if ((BuildCiaHeader(&(cia->header)) != 0) ||
(BuildCiaCert(cia->cert) != 0) ||
(BuildFakeTicket(&(cia->ticket), title_id) != 0) ||
(BuildFakeTmd(&(cia->tmd), title_id, 1, save_size)) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0)) {
return 1;
}
// insert NCCH content
TmdContentChunk* chunk = cia->content_list;
memset(chunk, 0, sizeof(TmdContentChunk)); // nothing else to do
if (InsertCiaContent(path_cia, path_ncch, 0, 0, chunk, NULL, false, true) != 0)
return 1;
// optional stuff (proper titlekey / meta data)
SearchTitleKeysBin((&cia->ticket), title_id);
if (exthdr && (BuildCiaMeta(meta, exthdr, NULL) == 0) &&
(LoadExeFsFile(meta->smdh, path_ncch, 0, "icon", sizeof(meta->smdh)) == 0) &&
(InsertCiaMeta(path_cia, meta) == 0))
cia->header.size_meta = CIA_META_SIZE;
// write the CIA stub (take #2)
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0))
return 1;
return 0;
}
u32 BuildCiaFromNcsdFile(const char* path_ncsd, const char* path_cia) {
CiaStub* cia = (CiaStub*) TEMP_BUFFER;
CiaMeta* meta = (CiaMeta*) (void*) (cia + 1);
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (meta + 1);
NcsdHeader ncsd;
NcchHeader ncch;
u8 title_id[8];
u32 save_size = 0;
// Init progress bar
if (!ShowProgress(0, 0, path_ncsd)) return 1;
// load NCSD header, get content count, title id
u32 content_count = 0;
if (LoadNcsdHeader(&ncsd, path_ncsd) != 0) return 1;
for (u32 i = 0; i < 3; i++)
if (ncsd.partitions[i].size) content_count++;
for (u32 i = 0; i < 8; i++)
title_id[i] = (ncsd.mediaId >> ((7-i)*8)) & 0xFF;
// load first content NCCH / extheader
if (LoadNcchHeaders(&ncch, exthdr, NULL, path_ncsd, NCSD_CNT0_OFFSET) != 0)
return 1;
save_size = getle32(exthdr->sys_info);
// build the CIA stub
memset(cia, 0, sizeof(CiaStub));
if ((BuildCiaHeader(&(cia->header)) != 0) ||
(BuildCiaCert(cia->cert) != 0) ||
(BuildFakeTicket(&(cia->ticket), title_id) != 0) ||
(BuildFakeTmd(&(cia->tmd), title_id, content_count, save_size)) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0)) {
return 1;
}
// insert NCSD content
TmdContentChunk* chunk = cia->content_list;
for (u32 i = 0; i < 3; i++) {
NcchPartition* partition = ncsd.partitions + i;
u32 offset = partition->offset * NCSD_MEDIA_UNIT;
u32 size = partition->size * NCSD_MEDIA_UNIT;
if (!size) continue;
memset(chunk, 0, sizeof(TmdContentChunk));
chunk->id[3] = chunk->index[1] = i;
if (InsertCiaContent(path_cia, path_ncsd, offset, size, chunk++, NULL, false, (i == 0)) != 0)
return 1;
}
// optional stuff (proper titlekey / meta data)
SearchTitleKeysBin(&(cia->ticket), title_id);
if ((BuildCiaMeta(meta, exthdr, NULL) == 0) &&
(LoadExeFsFile(meta->smdh, path_ncsd, NCSD_CNT0_OFFSET, "icon", sizeof(meta->smdh)) == 0) &&
(InsertCiaMeta(path_cia, meta) == 0))
cia->header.size_meta = CIA_META_SIZE;
// write the CIA stub (take #2)
if ((FixTmdHashes(&(cia->tmd)) != 0) ||
(FixCiaHeaderForTmd(&(cia->header), &(cia->tmd)) != 0) ||
(WriteCiaStub(cia, path_cia) != 0))
return 1;
return 0;
}
u32 BuildCiaFromGameFile(const char* path, bool force_legit) { u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
u32 filetype = IdentifyFileType(path); u32 filetype = IdentifyFileType(path);
char dest[256]; char dest[256];
@ -828,6 +970,7 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
// destination path // destination path
if (GetOutputPath(dest, path, force_legit ? "legit.cia" : "cia") != 0) return 1; if (GetOutputPath(dest, path, force_legit ? "legit.cia" : "cia") != 0) return 1;
if (!CheckWritePermissions(dest)) return 1; if (!CheckWritePermissions(dest)) return 1;
f_unlink(dest); // remove the file if it already exists
// ensure the output dir exists // ensure the output dir exists
if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK)) if ((f_stat(OUTPUT_PATH, NULL) != FR_OK) && (f_mkdir(OUTPUT_PATH) != FR_OK))
@ -836,6 +979,10 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
// build CIA from game file // build CIA from game file
if (filetype & GAME_TMD) if (filetype & GAME_TMD)
ret = BuildCiaFromTmdFile(path, dest, force_legit); ret = BuildCiaFromTmdFile(path, dest, force_legit);
else if (filetype & GAME_NCCH)
ret = BuildCiaFromNcchFile(path, dest);
else if (filetype & GAME_NCSD)
ret = BuildCiaFromNcsdFile(path, dest);
else ret = 1; else ret = 1;
if (ret != 0) // try to get rid of the borked file if (ret != 0) // try to get rid of the borked file

View File

@ -318,3 +318,22 @@ u32 DecryptNcchSequential(u8* data, u32 offset, u32 size) {
return DecryptNcch(data, offset, size, ncchptr, exefsptr); return DecryptNcch(data, offset, size, ncchptr, exefsptr);
} }
u32 SetNcchSdFlag(u8* data) { // data must be at least 0x600 byte and start with NCCH header
NcchHeader* ncch = (NcchHeader*) (void*) data;
NcchExtHeader* exthdr = (NcchExtHeader*) (void*) (data + NCCH_EXTHDR_OFFSET);
NcchExtHeader exthdr_dec;
if ((ValidateNcchHeader(ncch) != 0) || (!ncch->size_exthdr))
return 0; // no extheader
memcpy(&exthdr_dec, exthdr, sizeof(NcchExtHeader));
if (DecryptNcch((u8*) &exthdr_dec, NCCH_EXTHDR_OFFSET, sizeof(NcchExtHeader), ncch, NULL) != 0)
return 1;
if (exthdr_dec.flag & (1<<1)) return 0; // flag already set
exthdr_dec.flag |= (1<<1);
exthdr->flag ^= (1<<1);
sha_quick(ncch->hash_exthdr, &exthdr_dec, 0x400, SHA256_MODE);
return 0;
}

View File

@ -16,7 +16,7 @@ typedef struct {
char name[8]; char name[8];
u8 reserved[0x5]; u8 reserved[0x5];
u8 flag; // bit 1 for SD u8 flag; // bit 1 for SD
u32 remaster_version; u16 remaster_version;
u8 sci_data[0x30]; u8 sci_data[0x30];
u8 dependencies[0x180]; u8 dependencies[0x180];
u8 sys_info[0x40]; u8 sys_info[0x40];
@ -63,3 +63,4 @@ u32 ValidateNcchHeader(NcchHeader* header);
u32 SetupNcchCrypto(NcchHeader* ncch); u32 SetupNcchCrypto(NcchHeader* ncch);
u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs); u32 DecryptNcch(u8* data, u32 offset, u32 size, NcchHeader* ncch, ExeFsHeader* exefs);
u32 DecryptNcchSequential(u8* data, u32 offset, u32 size); u32 DecryptNcchSequential(u8* data, u32 offset, u32 size);
u32 SetNcchSdFlag(u8* data);

View File

@ -8,6 +8,7 @@
#define NCSD_CINFO_SIZE 0x1000 #define NCSD_CINFO_SIZE 0x1000
#define NCSD_DINFO_OFFSET 0x1200 #define NCSD_DINFO_OFFSET 0x1200
#define NCSD_DINFO_SIZE 0x300 #define NCSD_DINFO_SIZE 0x300
#define NCSD_CNT0_OFFSET 0x4000
typedef struct { typedef struct {
u32 offset; u32 offset;

View File

@ -562,6 +562,9 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
DirEntry* curr_entry = &(current_dir->entry[*cursor]); DirEntry* curr_entry = &(current_dir->entry[*cursor]);
const char* optionstr[8]; const char* optionstr[8];
// check for file lock
if (!FileUnlock(curr_entry->path)) return 1;
u32 filetype = IdentifyFileType(curr_entry->path); u32 filetype = IdentifyFileType(curr_entry->path);
u32 drvtype = DriveType(curr_entry->path); u32 drvtype = DriveType(curr_entry->path);
@ -576,9 +579,6 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
char pathstr[32 + 1]; char pathstr[32 + 1];
TruncateString(pathstr, curr_entry->path, 32, 8); TruncateString(pathstr, curr_entry->path, 32, 8);
// check for file lock
if (!FileUnlock(curr_entry->path)) return 1;
// main menu processing // main menu processing
int n_opt = 0; int n_opt = 0;
int special = (filetype && (drvtype & DRV_FAT)) ? ++n_opt : -1; int special = (filetype && (drvtype & DRV_FAT)) ? ++n_opt : -1;