mirror of
https://github.com/skawo/GodMode9-with-Cartridge-Fixer.git
synced 2025-06-18 17:05:48 -04:00
Add LARGEDLC mode for titles with > 1024 contents
Fixes #703 and is only active with `make LARGEDLC=1` and will break compatibility with other titles and CIAs. Thanks @luigoalma for new ticket builder code!
This commit is contained in:
parent
33a115b75c
commit
c9b6a335f7
@ -25,6 +25,12 @@ else ifeq ($(FLAVOR),ZuishMode9)
|
||||
CFLAGS += -DDEFAULT_FONT=\"font_zuish_8x8.pbm\"
|
||||
endif
|
||||
|
||||
ifeq ($(LARGEDLC),1)
|
||||
CFLAGS += -DTITLE_MAX_CONTENTS=1536
|
||||
else
|
||||
CFLAGS += -DTITLE_MAX_CONTENTS=1024
|
||||
endif
|
||||
|
||||
ifeq ($(SALTMODE),1)
|
||||
CFLAGS += -DSALTMODE
|
||||
endif
|
||||
|
@ -41,28 +41,91 @@ u32 ValidateTicketSignature(Ticket* ticket) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
|
||||
static const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256
|
||||
static const u8 ticket_cnt_index[] = { // whatever this is
|
||||
0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84,
|
||||
0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
// set ticket all zero for a clean start
|
||||
memset(ticket, 0x00, TICKET_COMMON_SIZE); // 0xAC being size of this fake ticket's content index
|
||||
// fill ticket values
|
||||
memcpy(ticket->sig_type, sig_type, 4);
|
||||
memset(ticket->signature, 0xFF, 0x100);
|
||||
snprintf((char*) ticket->issuer, 0x40, IS_DEVKIT ? TICKET_ISSUER_DEV : TICKET_ISSUER);
|
||||
memset(ticket->ecdsa, 0xFF, 0x3C);
|
||||
ticket->version = 0x01;
|
||||
memset(ticket->titlekey, 0xFF, 16);
|
||||
memcpy(ticket->title_id, title_id, 8);
|
||||
ticket->commonkey_idx = 0x00; // eshop
|
||||
ticket->audit = 0x01; // whatever
|
||||
memcpy(ticket->content_index, ticket_cnt_index, sizeof(ticket_cnt_index));
|
||||
memset(&ticket->content_index[sizeof(ticket_cnt_index)], 0xFF, 0x80); // 1024 content indexes
|
||||
u32 BuildVariableFakeTicket(Ticket** ticket, u32* ticket_size, const u8* title_id, u32 index_max) {
|
||||
if (!ticket || !ticket_size)
|
||||
return 1;
|
||||
|
||||
static const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256
|
||||
|
||||
// calculate sizes and determine pointers to use
|
||||
u32 rights_field_count = (min(index_max, 0x10000) + 1023) >> 10; // round up to 1024 and cap at 65536, and div by 1024
|
||||
u32 content_index_size = sizeof(TicketContentIndexMainHeader) + sizeof(TicketContentIndexDataHeader) + sizeof(TicketRightsField) * rights_field_count;
|
||||
u32 _ticket_size = sizeof(Ticket) + content_index_size;
|
||||
Ticket *_ticket;
|
||||
|
||||
if (*ticket) { // if a pointer was pregiven
|
||||
if (*ticket_size < _ticket_size) { // then check given boundary size
|
||||
*ticket_size = _ticket_size; // if not enough, inform the actual needed size
|
||||
return 2; // indicate a size error
|
||||
}
|
||||
_ticket = *ticket; // get the pointer if we good to go
|
||||
} else // if not pregiven, allocate one
|
||||
_ticket = (Ticket*)malloc(_ticket_size);
|
||||
|
||||
if (!_ticket)
|
||||
return 1;
|
||||
|
||||
// set ticket all zero for a clean start
|
||||
memset(_ticket, 0x00, _ticket_size);
|
||||
// fill ticket values
|
||||
memcpy(_ticket->sig_type, sig_type, 4);
|
||||
memset(_ticket->signature, 0xFF, 0x100);
|
||||
snprintf((char*) _ticket->issuer, 0x40, IS_DEVKIT ? TICKET_ISSUER_DEV : TICKET_ISSUER);
|
||||
memset(_ticket->ecdsa, 0xFF, 0x3C);
|
||||
_ticket->version = 0x01;
|
||||
memset(_ticket->titlekey, 0xFF, 16);
|
||||
memcpy(_ticket->title_id, title_id, 8);
|
||||
_ticket->commonkey_idx = 0x00; // eshop
|
||||
_ticket->audit = 0x01; // whatever
|
||||
|
||||
// fill in rights
|
||||
TicketContentIndexMainHeader* mheader = (TicketContentIndexMainHeader*)&_ticket->content_index[0];
|
||||
TicketContentIndexDataHeader* dheader = (TicketContentIndexDataHeader*)&_ticket->content_index[0x14];
|
||||
TicketRightsField* rights = (TicketRightsField*)&_ticket->content_index[0x28];
|
||||
|
||||
// first main data header
|
||||
mheader->unk1[1] = 0x1; mheader->unk2[1] = 0x14;
|
||||
mheader->content_index_size[3] = (u8)(content_index_size >> 0);
|
||||
mheader->content_index_size[2] = (u8)(content_index_size >> 8);
|
||||
mheader->content_index_size[1] = (u8)(content_index_size >> 16);
|
||||
mheader->content_index_size[0] = (u8)(content_index_size >> 24);
|
||||
mheader->data_header_relative_offset[3] = 0x14; // relative offset for TicketContentIndexDataHeader
|
||||
mheader->unk3[1] = 0x1; mheader->unk4[1] = 0x14;
|
||||
|
||||
// then the data header
|
||||
dheader->data_relative_offset[3] = 0x28; // relative offset for TicketRightsField
|
||||
dheader->max_entry_count[3] = (u8)(rights_field_count >> 0);
|
||||
dheader->max_entry_count[2] = (u8)(rights_field_count >> 8);
|
||||
dheader->max_entry_count[1] = (u8)(rights_field_count >> 16);
|
||||
dheader->max_entry_count[0] = (u8)(rights_field_count >> 24);
|
||||
dheader->size_per_entry[3] = (u8)sizeof(TicketRightsField); // sizeof should be 0x84
|
||||
dheader->total_size_used[3] = (u8)((sizeof(TicketRightsField) * rights_field_count) >> 0);
|
||||
dheader->total_size_used[2] = (u8)((sizeof(TicketRightsField) * rights_field_count) >> 8);
|
||||
dheader->total_size_used[1] = (u8)((sizeof(TicketRightsField) * rights_field_count) >> 16);
|
||||
dheader->total_size_used[0] = (u8)((sizeof(TicketRightsField) * rights_field_count) >> 24);
|
||||
dheader->data_type[1] = 3; // right fields
|
||||
|
||||
// now the right fields
|
||||
// indexoffets must be in accending order to have the desired effect
|
||||
for (u32 i = 0; i < rights_field_count; ++i) {
|
||||
rights[i].indexoffset[1] = (u8)((1024 * i) >> 0);
|
||||
rights[i].indexoffset[0] = (u8)((1024 * i) >> 8);
|
||||
memset(&rights[i].rightsbitfield[0], 0xFF, sizeof(rights[0].rightsbitfield));
|
||||
}
|
||||
|
||||
*ticket = _ticket;
|
||||
*ticket_size = _ticket_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 BuildFakeTicket(Ticket* ticket, const u8* title_id) {
|
||||
Ticket* tik;
|
||||
u32 ticket_size = sizeof(TicketCommon);
|
||||
u32 res = BuildVariableFakeTicket(&tik, &ticket_size, title_id, TICKET_MAX_CONTENTS);
|
||||
if (res != 0) return res;
|
||||
memcpy(ticket, tik, ticket_size);
|
||||
free(tik);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "tmd.h"
|
||||
|
||||
#define TICKET_COMMON_SIZE sizeof(TicketCommon)
|
||||
#define TICKET_MINIMUM_SIZE sizeof(TicketMinimum)
|
||||
#define TICKET_TWL_SIZE sizeof(Ticket)
|
||||
#define TICKET_CDNCERT_SIZE 0x700
|
||||
#define TICKET_MAX_CONTENTS TITLE_MAX_CONTENTS // should be TMD_MAX_CONTENTS
|
||||
#define TICKET_COMMON_CNT_INDEX_SIZE (0x28 + (((TICKET_MAX_CONTENTS + 1023) >> 10) * 0x84))
|
||||
|
||||
#define TICKET_ISSUER "Root-CA00000003-XS0000000c"
|
||||
#define TICKET_ISSUER_DEV "Root-CA00000004-XS00000009"
|
||||
@ -54,7 +57,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
TICKETBASE;
|
||||
u8 content_index[0xAC];
|
||||
u8 content_index[TICKET_COMMON_CNT_INDEX_SIZE];
|
||||
} PACKED_STRUCT TicketCommon;
|
||||
|
||||
// minimum allowed content_index is 0x14
|
||||
@ -97,7 +100,7 @@ typedef struct {
|
||||
u32 ValidateTicket(Ticket* ticket);
|
||||
u32 ValidateTwlTicket(Ticket* ticket);
|
||||
u32 ValidateTicketSignature(Ticket* ticket);
|
||||
u32 BuildFakeTicket(Ticket* ticket, u8* title_id);
|
||||
u32 BuildFakeTicket(Ticket* ticket, const u8* title_id);
|
||||
u32 GetTicketContentIndexSize(const Ticket* ticket);
|
||||
u32 GetTicketSize(const Ticket* ticket);
|
||||
u32 BuildTicketCert(u8* tickcert);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define TMD_MAX_CONTENTS 1000 // 383 // theme CIAs contain maximum 100 themes + 1 index content
|
||||
#define TMD_MAX_CONTENTS TITLE_MAX_CONTENTS // 1024 // 383 // theme CIAs contain maximum 100 themes + 1 index content
|
||||
|
||||
#define TMD_SIZE_MIN sizeof(TitleMetaData)
|
||||
#define TMD_SIZE_MAX (sizeof(TitleMetaData) + (TMD_MAX_CONTENTS*sizeof(TmdContentChunk)))
|
||||
|
@ -225,6 +225,10 @@ u32 LoadTmdFile(TitleMetaData* tmd, const char* path) {
|
||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_STUB, NULL) != FR_OK)
|
||||
return 1;
|
||||
|
||||
// sanity check
|
||||
if (getbe16(tmd->content_count) > TMD_MAX_CONTENTS)
|
||||
return 1;
|
||||
|
||||
// second part (read full size)
|
||||
if (ValidateTmd(tmd) == 0) {
|
||||
if (fvx_qread(path, tmd, 0, TMD_SIZE_N(getbe16(tmd->content_count)), NULL) != FR_OK)
|
||||
@ -348,7 +352,7 @@ u32 GetTmdContentPath(char* path_content, const char* path_tmd) {
|
||||
free(tmd);
|
||||
return 1;
|
||||
}
|
||||
snprintf(name_content, 256 - (name_content - path_content), cdn ? "%08lx" :
|
||||
snprintf(name_content, 255 - (name_content - path_content), cdn ? "%08lx" :
|
||||
(memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
|
||||
|
||||
free(tmd);
|
||||
@ -2173,19 +2177,12 @@ u32 BuildCiaLegitTicket(Ticket* ticket, u8* title_id, const char* path_cnt, bool
|
||||
bool copy = true;
|
||||
|
||||
if ((cdn && (LoadCdnTicketFile(&ticket_tmp, path_cnt) != 0)) ||
|
||||
(!cdn && (FindTicket(&ticket_tmp, title_id, true, src_emunand) != 0))) {
|
||||
(!cdn && (FindTicket(&ticket_tmp, title_id, true, src_emunand) != 0)) ||
|
||||
(GetTicketSize(ticket_tmp) != TICKET_COMMON_SIZE)) {
|
||||
FindTitleKey(ticket, title_id);
|
||||
copy = false;
|
||||
}
|
||||
|
||||
// either, it's a ticket without ways to check ownership data, smaller sized
|
||||
// or, it's title ticket with > 1024 contents, of which can't make it work with current CiaStub
|
||||
if (copy && GetTicketSize(ticket_tmp) != TICKET_COMMON_SIZE) {
|
||||
ShowPrompt(false, "ID %016llX\nLegit ticket of unsupported size.", getbe64(title_id));
|
||||
free(ticket_tmp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// check the tickets' console id, warn if it isn't zero
|
||||
if (copy && getbe32(ticket_tmp->console_id)) {
|
||||
static u32 default_action = 0;
|
||||
@ -2392,9 +2389,9 @@ u32 BuildInstallFromTmdFileBuffered(const char* path_tmd, const char* path_dest,
|
||||
TicketRightsCheck rights_ctx;
|
||||
TicketRightsCheck_InitContext(&rights_ctx, (Ticket*)&(cia->ticket));
|
||||
snprintf(name_content, 256 - (name_content - path_content),
|
||||
(cdn) ? "%08lx" : (dlc && !cdn) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
|
||||
(cdn) ? "%08lx" : (dlc) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
|
||||
if ((fvx_stat(path_content, &fno) != FR_OK) || (fno.fsize != (u32) getbe64(chunk->size)) ||
|
||||
!TicketRightsCheck_CheckIndex(&rights_ctx, getbe16(chunk->index))) {
|
||||
(!cdn && !TicketRightsCheck_CheckIndex(&rights_ctx, getbe16(chunk->index)))) {
|
||||
present[i / 8] ^= 1 << (i % 8);
|
||||
|
||||
u16 index = getbe16(chunk->index);
|
||||
|
@ -108,11 +108,11 @@ static u64 offset_ccnt = (u64) -1;
|
||||
static u64 offset_tad = (u64) -1;
|
||||
static u32 index_ccnt = (u32) -1;
|
||||
|
||||
static CiaStub* cia = NULL;
|
||||
// static CiaStub* cia = NULL; *unused*
|
||||
static TwlHeader* twl = NULL;
|
||||
static NcsdHeader* ncsd = NULL;
|
||||
static FirmA9LHeader* a9l = NULL;
|
||||
static FirmHeader* firm = NULL;
|
||||
static NcsdHeader* ncsd = NULL;
|
||||
static NcchHeader* ncch = NULL;
|
||||
static ExeFsHeader* exefs = NULL;
|
||||
static RomFsLv3Index lv3idx;
|
||||
@ -357,7 +357,7 @@ bool BuildVGameNcsdDir(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildVGameCiaDir(void) {
|
||||
bool BuildVGameCiaDir(CiaStub* cia) {
|
||||
CiaInfo info;
|
||||
VirtualFile* templates = templates_cia;
|
||||
u32 n = 0;
|
||||
@ -777,21 +777,20 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image
|
||||
vgame_buffer = (void*) malloc(0x40000);
|
||||
if (!vgame_buffer) return 0;
|
||||
|
||||
templates_cia = (void*) ((u8*) vgame_buffer); // first 184kb reserved (enough for 3364 entries)
|
||||
templates_firm = (void*) (((u8*) vgame_buffer) + 0x2E000); // 2kb reserved (enough for 36 entries)
|
||||
templates_ncsd = (void*) (((u8*) vgame_buffer) + 0x2E800); // 2kb reserved (enough for 36 entries)
|
||||
templates_ncch = (void*) (((u8*) vgame_buffer) + 0x2F000); // 1kb reserved (enough for 18 entries)
|
||||
templates_nds = (void*) (((u8*) vgame_buffer) + 0x2F400); // 1kb reserved (enough for 18 entries)
|
||||
templates_exefs = (void*) (((u8*) vgame_buffer) + 0x2F800); // 1kb reserved (enough for 18 entries)
|
||||
templates_tad = (void*) (((u8*) vgame_buffer) + 0x2FC00); // 1kb reserved (enough for 18 entries)
|
||||
cia = (CiaStub*) (void*) (((u8*) vgame_buffer) + 0x30000); // 61kB reserved - should be enough by far
|
||||
twl = (TwlHeader*) (void*) (((u8*) vgame_buffer) + 0x3F400); // 512 byte reserved (not the full thing)
|
||||
a9l = (FirmA9LHeader*) (void*) (((u8*) vgame_buffer) + 0x3F600); // 512 byte reserved
|
||||
firm = (FirmHeader*) (void*) (((u8*) vgame_buffer) + 0x3F800); // 512 byte reserved
|
||||
ncsd = (NcsdHeader*) (void*) (((u8*) vgame_buffer) + 0x3FA00); // 512 byte reserved
|
||||
ncch = (NcchHeader*) (void*) (((u8*) vgame_buffer) + 0x3FC00); // 512 byte reserved
|
||||
exefs = (ExeFsHeader*) (void*) (((u8*) vgame_buffer) + 0x3FE00); // 512 byte reserved
|
||||
// filesystem stuff (RomFS / NitroFS) will be allocated on demand
|
||||
templates_cia = (void*) ((u8*) vgame_buffer); // first 180kb reserved (enough for 3291 entries)
|
||||
templates_firm = (void*) (((u8*) vgame_buffer) + 0x2D000); // 2kb reserved (enough for 36 entries)
|
||||
templates_ncsd = (void*) (((u8*) vgame_buffer) + 0x2D800); // 2kb reserved (enough for 36 entries)
|
||||
templates_ncch = (void*) (((u8*) vgame_buffer) + 0x2E000); // 1kb reserved (enough for 18 entries)
|
||||
templates_nds = (void*) (((u8*) vgame_buffer) + 0x2E400); // 1kb reserved (enough for 18 entries)
|
||||
templates_exefs = (void*) (((u8*) vgame_buffer) + 0x2E800); // 1kb reserved (enough for 18 entries)
|
||||
templates_tad = (void*) (((u8*) vgame_buffer) + 0x2EC00); // 1kb reserved (enough for 18 entries)
|
||||
twl = (TwlHeader*) (void*) (((u8*) vgame_buffer) + 0x2F000); // 512 byte reserved (not the full thing)
|
||||
a9l = (FirmA9LHeader*) (void*) (((u8*) vgame_buffer) + 0x2F200); // 512 byte reserved
|
||||
firm = (FirmHeader*) (void*) (((u8*) vgame_buffer) + 0x2F400); // 512 byte reserved
|
||||
ncsd = (NcsdHeader*) (void*) (((u8*) vgame_buffer) + 0x2F600); // 512 byte reserved
|
||||
ncch = (NcchHeader*) (void*) (((u8*) vgame_buffer) + 0x2F800); // 512 byte reserved
|
||||
exefs = (ExeFsHeader*) (void*) (((u8*) vgame_buffer) + 0x2FA00); // 512 byte reserved (1kb reserve)
|
||||
// filesystem stuff (RomFS / NitroFS) and CIA/TADX will be allocated on demand
|
||||
|
||||
vgame_type = type;
|
||||
return type;
|
||||
@ -842,14 +841,24 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
|
||||
if (!BuildVGameTadDir()) return false;
|
||||
} else if ((vdir->flags & VFLAG_CIA) && (offset_cia != vdir->offset)) {
|
||||
CiaInfo info;
|
||||
if ((ReadImageBytes((u8*) cia, 0, 0x20) != 0) ||
|
||||
(ValidateCiaHeader(&(cia->header)) != 0) ||
|
||||
(GetCiaInfo(&info, &(cia->header)) != 0) ||
|
||||
(ReadImageBytes((u8*) cia, 0, info.offset_content) != 0))
|
||||
CiaStub* cia;
|
||||
u8 __attribute__((aligned(32))) hdr[0x20];
|
||||
if ((ReadImageBytes(hdr, 0, 0x20) != 0) ||
|
||||
(ValidateCiaHeader((CiaHeader*) (void*) hdr) != 0) ||
|
||||
(GetCiaInfo(&info, (CiaHeader*) (void*) hdr) != 0) ||
|
||||
!(cia = (CiaStub*) malloc(info.offset_content)))
|
||||
return false;
|
||||
if (ReadImageBytes((u8*) cia, 0, info.offset_content) != 0) {
|
||||
free(cia);
|
||||
return false;
|
||||
}
|
||||
offset_cia = vdir->offset; // always zero(!)
|
||||
GetTitleKey(cia_titlekey, (Ticket*)&(cia->ticket));
|
||||
if (!BuildVGameCiaDir()) return false;
|
||||
if (!BuildVGameCiaDir(cia)) {
|
||||
free(cia);
|
||||
return false;
|
||||
}
|
||||
free(cia);
|
||||
} else if ((vdir->flags & VFLAG_NCSD) && (offset_ncsd != vdir->offset)) {
|
||||
if ((ReadImageBytes((u8*) ncsd, 0, sizeof(NcsdHeader)) != 0) ||
|
||||
(ValidateNcsdHeader(ncsd) != 0))
|
||||
|
Loading…
Reference in New Issue
Block a user