Fix CMD & NCSD handling

This commit is contained in:
d0k3 2020-07-24 17:22:55 +02:00
parent b8798f2aff
commit d8aeb056cb
6 changed files with 57 additions and 49 deletions

View File

@ -124,7 +124,7 @@ u64 IdentifyFileType(const char* path) {
(memcmp(data, threedsx_magic, sizeof(threedsx_magic)) == 0)) { (memcmp(data, threedsx_magic, sizeof(threedsx_magic)) == 0)) {
return GAME_3DSX; // 3DSX (executable) file return GAME_3DSX; // 3DSX (executable) file
} else if ((fsize > sizeof(CmdHeader)) && } else if ((fsize > sizeof(CmdHeader)) &&
CheckCmdSize((CmdHeader*) data, fsize) == 0) { (CMD_SIZE((CmdHeader*) data) == fsize)) {
return GAME_CMD; // CMD file return GAME_CMD; // CMD file
} else if ((fsize > sizeof(NcchInfoHeader)) && } else if ((fsize > sizeof(NcchInfoHeader)) &&
(GetNcchInfoVersion((NcchInfoHeader*) data)) && (GetNcchInfoVersion((NcchInfoHeader*) data)) &&

View File

@ -1,50 +1,62 @@
#include "cmd.h" #include "cmd.h"
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize) { CmdHeader* BuildAllocCmdData(TitleMetaData* tmd) {
u64 cmdsize = sizeof(CmdHeader) + CmdHeader proto;
(cmd->n_entries * sizeof(u32)) + CmdHeader* cmd = NULL;
(cmd->n_cmacs * sizeof(u32)) +
(cmd->n_entries * 0x10);
return (fsize == cmdsize) ? 0 : 1;
}
u32 BuildCmdData(CmdHeader* cmd, TitleMetaData* tmd) {
u32 content_count = getbe16(tmd->content_count); u32 content_count = getbe16(tmd->content_count);
u32 max_cnt_id = 0;
// header basic info // sanity check
cmd->cmd_id = 0x1; if (!content_count)
cmd->n_entries = content_count; return NULL;
cmd->n_cmacs = content_count;
// find max content id
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++)
if (getbe32(chunk->id) > max_cnt_id) max_cnt_id = getbe32(chunk->id);
// allocate memory for CMD / basic setup
proto.cmd_id = 1;
proto.n_entries = max_cnt_id + 1;
proto.n_cmacs = content_count;
proto.unknown = 1;
cmd = (CmdHeader*) malloc(CMD_SIZE(&proto));
if (!cmd) return NULL;
memcpy(cmd, &proto, sizeof(CmdHeader));
cmd->unknown = 0x0; // this means no CMACs, only valid for NAND cmd->unknown = 0x0; // this means no CMACs, only valid for NAND
// copy content ids // copy content ids
u32* cnt_id = (u32*) (cmd + 1); u32* cnt_id = (u32*) (cmd + 1);
u32* cnt_id_cpy = cnt_id + content_count; u32* cnt_id_2nd = cnt_id + cmd->n_entries;
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1); chunk = (TmdContentChunk*) (tmd + 1);
memset(cnt_id, 0xFF, cmd->n_entries * sizeof(u32));
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) {
cnt_id[i] = getbe32(chunk->id); u32 chunk_id = getbe32(chunk->id);
cnt_id_cpy[i] = cnt_id[i]; cnt_id[chunk_id] = chunk_id;
*(cnt_id_2nd++) = chunk_id;
} }
// bubble sort the second content id list // bubble sort the second content id list
u32 b = 0; bool bs_finished = false;
while ((b < content_count) && (b < TMD_MAX_CONTENTS)) { cnt_id_2nd = cnt_id + cmd->n_entries;
for (b = 1; (b < content_count) && (b < TMD_MAX_CONTENTS); b++) { while (!bs_finished) {
if (cnt_id_cpy[b] < cnt_id_cpy[b-1]) { bs_finished = true;
u32 swp = cnt_id_cpy[b]; for (u32 b = 1; b < cmd->n_cmacs; b++) {
cnt_id_cpy[b] = cnt_id_cpy[b-1]; if (cnt_id_2nd[b] < cnt_id_2nd[b-1]) {
cnt_id_cpy[b-1] = swp; u32 swp = cnt_id_2nd[b];
cnt_id_2nd[b] = cnt_id_2nd[b-1];
cnt_id_2nd[b-1] = swp;
bs_finished = false;
} }
} }
} }
// set CMACs to 0xFF // set CMACs to 0xFF
u8* cnt_cmac = (u8*) (cnt_id + (2*cmd->n_entries)); u8* cnt_cmac = (u8*) (cnt_id_2nd + cmd->n_cmacs);
memset(cmd->cmac, 0xFF, 0x10); memset(cmd->cmac, 0xFF, 0x10);
memset(cnt_cmac, 0xFF, 0x10 * content_count); memset(cnt_cmac, 0xFF, 0x10 * cmd->n_entries);
// we still need to fix / set the CMACs inside the CMD file! // we still need to fix / set the CMACs inside the CMD file!
return 0; return cmd;
} }

View File

@ -3,9 +3,10 @@
#include "common.h" #include "common.h"
#include "tmd.h" #include "tmd.h"
#define CMD_SIZE_N(n) (sizeof(CmdHeader) + ((n)*(sizeof(u32)+sizeof(u32)+0x10))) #define CMD_SIZE(cmd) (sizeof(CmdHeader) + \
#define CMD_SIZE_NS(n) (sizeof(CmdHeader) + ((n)*(sizeof(u32)+sizeof(u32)))) (((cmd)->n_entries) * sizeof(u32)) + \
(((cmd)->n_cmacs) * sizeof(u32)) + \
(((cmd)->unknown) ? (((cmd)->n_entries) * 0x10) : 0))
// from: http://3dbrew.org/wiki/Titles#Data_Structure // from: http://3dbrew.org/wiki/Titles#Data_Structure
typedef struct { typedef struct {
@ -19,5 +20,4 @@ typedef struct {
// followed by <n_entries> CMACs (may contain garbage) // followed by <n_entries> CMACs (may contain garbage)
} __attribute__((packed, aligned(4))) CmdHeader; } __attribute__((packed, aligned(4))) CmdHeader;
u32 CheckCmdSize(CmdHeader* cmd, u64 fsize); CmdHeader* BuildAllocCmdData(TitleMetaData* tmd);
u32 BuildCmdData(CmdHeader* cmd, TitleMetaData* tmd);

View File

@ -25,7 +25,7 @@ u32 BuildTitleInfoEntryTmd(TitleInfoEntry* tie, TitleMetaData* tmd, bool sd) {
tie->title_size = tie->title_size =
(align_size * 3) + // base folder + 'content' + 'cmd' (align_size * 3) + // base folder + 'content' + 'cmd'
align(TMD_SIZE_N(content_count), align_size) + // TMD align(TMD_SIZE_N(content_count), align_size) + // TMD
align(CMD_SIZE_N(content_count), align_size); // CMD align_size; // CMD, placeholder
for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++, chunk++) {
if (getbe32(chunk->id) == 1) has_id1 = true; // will be useful later if (getbe32(chunk->id) == 1) has_id1 = true; // will be useful later
tie->title_size += align(getbe64(chunk->size), align_size); tie->title_size += align(getbe64(chunk->size), align_size);

View File

@ -1402,10 +1402,9 @@ u32 InstallCiaSystemData(CiaStub* cia, const char* drv) {
return 1; return 1;
// build the cmd // build the cmd
cmd = (CmdHeader*) malloc(CMD_SIZE_N(content_count)); cmd = BuildAllocCmdData(tmd);
if (!cmd) return 1; if (!cmd) return 1;
BuildCmdData(cmd, tmd); cmd->unknown = 0xFFFFFFFE; // mark this as custom built
if (!syscmd) cmd->unknown = 0xFFFFFFFE; // mark this as custom built
// generate all the paths // generate all the paths
snprintf(path_titledb, 32, "%2.2s/dbs/title.db", snprintf(path_titledb, 32, "%2.2s/dbs/title.db",
@ -1423,8 +1422,7 @@ u32 InstallCiaSystemData(CiaStub* cia, const char* drv) {
fvx_rmkpath(path_tmd); fvx_rmkpath(path_tmd);
fvx_rmkpath(path_cmd); fvx_rmkpath(path_cmd);
if ((fvx_qwrite(path_tmd, tmd, 0, TMD_SIZE_N(content_count), NULL) != FR_OK) || if ((fvx_qwrite(path_tmd, tmd, 0, TMD_SIZE_N(content_count), NULL) != FR_OK) ||
(fvx_qwrite(path_cmd, cmd, 0, (fvx_qwrite(path_cmd, cmd, 0, CMD_SIZE(cmd), NULL) != FR_OK)) {
syscmd ? CMD_SIZE_NS(content_count) : CMD_SIZE_N(content_count), NULL) != FR_OK)) {
free(cmd); free(cmd);
return 1; return 1;
} }
@ -1963,7 +1961,7 @@ u32 BuildInstallFromNcsdFile(const char* path_ncsd, const char* path_dest, bool
if ((!install && (InsertCiaContent(path_dest, path_ncsd, if ((!install && (InsertCiaContent(path_dest, path_ncsd,
offset, size, chunk++, NULL, false, (i == 0), false) != 0)) || offset, size, chunk++, NULL, false, (i == 0), false) != 0)) ||
(install && (InstallCiaContent(path_dest, path_ncsd, (install && (InstallCiaContent(path_dest, path_ncsd,
offset, size, chunk, title_id, NULL, (i == 0)) != 0))) { offset, size, chunk++, title_id, NULL, (i == 0)) != 0))) {
free(cia); free(cia);
return 1; return 1;
} }
@ -2161,9 +2159,8 @@ u32 InstallGameFile(const char* path, bool to_emunand, bool force_nand) {
to_sd = true; to_sd = true;
// does the title.db exist? // does the title.db exist?
if (to_sd && !fvx_qsize(to_emunand ? "B:/dbs/title.db" : "A:/dbs/title.db")) if ((to_sd && !fvx_qsize(to_emunand ? "B:/dbs/title.db" : "A:/dbs/title.db")) ||
to_sd = false; (!to_sd && !fvx_qsize(to_emunand ? "4:/dbs/title.db" : "1:/dbs/title.db")))
if (!to_sd && !fvx_qsize(to_emunand ? "4:/dbs/title.db" : "1:/dbs/title.db"))
return 1; return 1;
// now we know the correct drive // now we know the correct drive
@ -2411,7 +2408,6 @@ u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) {
if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh), NULL) == 0) return 0; if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh), NULL) == 0) return 0;
} else if (filetype & GAME_CIA) { // CIA file } else if (filetype & GAME_CIA) { // CIA file
CiaInfo info; CiaInfo info;
if ((fvx_qread(path, &info, 0, 0x20, NULL) != FR_OK) || if ((fvx_qread(path, &info, 0, 0x20, NULL) != FR_OK) ||
(GetCiaInfo(&info, (CiaHeader*) &info) != 0)) return 1; (GetCiaInfo(&info, (CiaHeader*) &info) != 0)) return 1;
if ((info.offset_meta) && (fvx_qread(path, smdh, info.offset_meta + 0x400, sizeof(Smdh), NULL) == FR_OK)) return 0; if ((info.offset_meta) && (fvx_qread(path, smdh, info.offset_meta + 0x400, sizeof(Smdh), NULL) == FR_OK)) return 0;

View File

@ -374,7 +374,7 @@ u32 CheckFixCmdCmac(const char* path, bool fix) {
// read the full file to memory and check it (we may write it back later) // read the full file to memory and check it (we may write it back later)
if ((fvx_qread(path, cmd_data, 0, cmd_size, NULL) != FR_OK) || if ((fvx_qread(path, cmd_data, 0, cmd_size, NULL) != FR_OK) ||
(CheckCmdSize(cmd, cmd_size) != 0)) { (CMD_SIZE(cmd) != cmd_size)) {
free(cmd_data); free(cmd_data);
return 1; return 1;
} }