Preliminary ticket.db mount support

This commit is contained in:
d0k3 2017-02-11 16:52:36 +01:00
parent f5694cc16d
commit ea00a3217a
11 changed files with 199 additions and 20 deletions

View File

@ -6,6 +6,7 @@
u32 IdentifyFileType(const char* path) {
const u8 romfs_magic[] = { ROMFS_MAGIC };
const u8 tickdb_magic[] = { TICKDB_MAGIC };
u8 header[0x200] __attribute__((aligned(32))); // minimum required size
void* data = (void*) header;
size_t fsize = FileGetSize(path);
@ -54,6 +55,8 @@ u32 IdentifyFileType(const char* path) {
return GAME_TICKET; // Ticket file (not used for anything right now)
} else if (ValidateFirmHeader((FirmHeader*) data, fsize) == 0) {
return SYS_FIRM; // FIRM file
} else if (memcmp(header + 0x100, tickdb_magic, sizeof(tickdb_magic)) == 0) {
return SYS_TICKDB; // ticket.db
}
}
if ((fsize > sizeof(BossHeader)) &&

View File

@ -14,14 +14,15 @@
#define GAME_NUSCDN (1<<9)
#define GAME_TICKET (1<<10)
#define SYS_FIRM (1<<11)
#define BIN_NCCHNFO (1<<12)
#define BIN_LAUNCH (1<<13)
#define SYS_TICKDB (1<<12)
#define BIN_NCCHNFO (1<<13)
#define BIN_LAUNCH (1<<14)
#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types
#define FLAG_NUSCDN (1<<30)
#define FLAG_CXI (1<<31)
#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM))
#define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM|SYS_TICKDB))
#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))

View File

@ -45,6 +45,8 @@ int DriveType(const char* path) {
type = DRV_VIRTUAL | DRV_MEMORY;
} else if (vsrc == VRT_GAME) {
type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE;
} else if (vsrc == VRT_TICKDB) { // GAME (???)
type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE;
} else if (vsrc == VRT_CART) {
type = DRV_VIRTUAL | DRV_CART;
}

View File

@ -5,7 +5,7 @@
#define NORM_FS 10
#define IMGN_FS 3 // image normal filesystems
#define VIRT_FS 10
#define VIRT_FS 11
// primary drive types
#define DRV_UNKNOWN (0<<0)
@ -31,13 +31,13 @@
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "EMUNAND SD", "EMUNAND VIRTUAL", \
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", "IMGNAND VIRTUAL", \
"GAMECART", \
"GAME IMAGE", \
"GAME IMAGE", "TICKET.DB IMAGE", \
"MEMORY VIRTUAL", \
"NAND XORPADS", \
"LAST SEARCH" \
#define FS_DRVNUM \
"0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "M:", "X:", "Z:"
"0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "T:", "M:", "X:", "Z:"
/** Function to identify the type of a drive **/
int DriveType(const char* path);

View File

@ -72,6 +72,17 @@ u32 GetTitleKey(u8* titlekey, Ticket* ticket) {
return 0;
}
Ticket* TicketFromTickDbChunk(u8* chunk, u8* title_id, bool legit_pls) {
// chunk must be aligned to 0x200 byte in file and at least 0x400 byte big
Ticket* tick = (Ticket*) (chunk + 0x18);
if ((getle32(chunk + 0x10) == 0) || (getle32(chunk + 0x14) != sizeof(Ticket))) return NULL;
if (ValidateTicket(tick) != 0) return NULL; // ticket not validated
if (title_id && (memcmp(title_id, tick->title_id, 8) != 0)) return NULL; // title id not matching
if (legit_pls && (getbe64(tick->ticket_id) == 0)) return NULL; // legit check, not perfect
return tick;
}
u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) {
const char* path_db = TICKDB_PATH(emunand); // EmuNAND / SysNAND
const u32 area_offsets[] = { TICKDB_AREA_OFFSETS };
@ -86,13 +97,10 @@ u32 FindTicket(Ticket* ticket, u8* title_id, bool force_legit, bool emunand) {
for (u32 p = 0; p < 2; p++) {
u32 area_offset = area_offsets[p];
for (u32 i = 0; !found && (i < TICKDB_AREA_SIZE); i += 0x200) {
Ticket* tick = (Ticket*) (data + 0x18);
f_lseek(&file, area_offset + i);
if ((f_read(&file, data, 0x400, &btr) != FR_OK) || (btr != 0x400)) break;
if ((getle32(data + 0x10) == 0) || (getle32(data + 0x14) != sizeof(Ticket))) continue;
if (ValidateTicket(tick) != 0) continue; // ticket not validated
if (memcmp(title_id, tick->title_id, 8) != 0) continue; // title id not matching
if (force_legit && (getbe64(tick->ticket_id) == 0)) continue; // legit check
Ticket* tick = TicketFromTickDbChunk(data, title_id, force_legit);
if (!tick) continue;
memcpy(ticket, tick, sizeof(Ticket));
found = true;
break;

View File

@ -16,7 +16,7 @@
#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
#define TICKDB_AREA_SIZE 0x00200000 // the actual area size is around 0x0010C600
#define TICKDB_AREA_SIZE 0x00180000 // the actual area size is around 0x0010C600
#define TICKDB_MAGIC 0x44, 0x49, 0x46, 0x46, 0x00, 0x00, 0x03, 0x00, \
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
@ -58,6 +58,7 @@ typedef struct {
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 BuildFakeTicket(Ticket* ticket, u8* title_id);

View File

@ -653,6 +653,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(filetype & GAME_BOSS) ? "BOSS file options..." :
(filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" :
(filetype & SYS_FIRM) ? "FIRM image options..." :
(filetype & SYS_TICKDB) ? "Mount as ticket.db" :
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." :
(filetype & BIN_LAUNCH) ? "Launch as arm9 payload" : "???";
optionstr[hexviewer-1] = "Show in Hexeditor";
@ -795,7 +796,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
if (clipboard->n_entries && (DriveType(clipboard->entry[0].path) & DRV_IMAGE))
clipboard->n_entries = 0; // remove last mounted image clipboard entries
InitImgFS(curr_entry->path);
if (!(DriveType("7:")||DriveType("G:"))) {
if (!(DriveType("7:")||DriveType("G:")||DriveType("T:"))) {
ShowPrompt(false, "Mounting image: failed");
InitImgFS(NULL);
} else {
@ -803,7 +804,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
*current_path = '\0';
GetDirContents(current_dir, current_path);
for (u32 i = 0; i < current_dir->n_entries; i++) {
if (strspn(current_dir->entry[i].path, "7GI") == 0)
if (strspn(current_dir->entry[i].path, "7GTI") == 0)
continue;
strncpy(current_path, current_dir->entry[i].path, 256);
GetDirContents(current_dir, current_path);

View File

@ -2,6 +2,7 @@
#include "vnand.h"
#include "vmem.h"
#include "vgame.h"
#include "vtickdb.h"
#include "vcart.h"
typedef struct {
@ -22,7 +23,7 @@ u32 GetVirtualSource(const char* path) {
}
bool InitVirtualImageDrive(void) {
return InitVGameDrive();
return InitVGameDrive() || InitVTickDbDrive();
}
bool CheckVirtualDrive(const char* path) {
@ -31,7 +32,9 @@ bool CheckVirtualDrive(const char* path) {
return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND
else if (virtual_src & VRT_GAME)
return CheckVGameDrive();
return virtual_src; // this is safe for SysNAND, memory & cart
else if (virtual_src & VRT_TICKDB)
return CheckVTickDbDrive();
return virtual_src; // this is safe for SysNAND & memory
}
bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
@ -43,6 +46,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
ret = ReadVMemDir(vfile, vdir);
} else if (virtual_src & VRT_GAME) {
ret = ReadVGameDir(vfile, vdir);
} else if (virtual_src & VRT_TICKDB) {
ret = ReadVTickDbDir(vfile, vdir);
} else if (virtual_src & VRT_CART) {
ret = ReadVCartDir(vfile, vdir);
}
@ -170,6 +175,8 @@ int ReadVirtualFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count,
return ReadVMemFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_GAME) {
return ReadVGameFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_TICKDB) {
return ReadVTickDbFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_CART) {
return ReadVCartFile(vfile, buffer, offset, count);
}
@ -189,7 +196,7 @@ int WriteVirtualFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32
return WriteVNandFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_MEMORY) {
return WriteVMemFile(vfile, buffer, offset, count);
} // no write support for virtual game / cart files
} // no write support for virtual game / tickdb / cart files
return -1;
}
@ -200,6 +207,8 @@ u64 GetVirtualDriveSize(const char* path) {
return GetVNandDriveSize(virtual_src);
else if (virtual_src & VRT_GAME)
return GetVGameDriveSize();
else if (virtual_src & VRT_TICKDB)
return GetVTickDbDriveSize();
else if (virtual_src & VRT_CART)
return GetVCartDriveSize();
return 0;

View File

@ -10,9 +10,10 @@
#define VRT_XORPAD NAND_ZERONAND
#define VRT_MEMORY (1<<10)
#define VRT_GAME (1<<11)
#define VRT_CART (1<<12)
#define VRT_TICKDB (1<<12)
#define VRT_CART (1<<13)
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_CART)
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_TICKDB|VRT_CART)
#define VFLAG_DIR (1<<16)
#define VFLAG_ROOT (1<<17)
@ -20,7 +21,7 @@
#define VFLAG_LV3 (1<<19)
#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, {'X', VRT_XORPAD }, \
{'M', VRT_MEMORY}, {'G', VRT_GAME}, {'C', VRT_CART}
{'M', VRT_MEMORY}, {'G', VRT_GAME}, {'T', VRT_TICKDB}, {'C', VRT_CART}
// virtual file flag (subject to change):
// bits 0...9 : reserved for NAND virtual sources and info

141
source/virtual/vtickdb.c Normal file
View File

@ -0,0 +1,141 @@
#include "vtickdb.h"
#include "image.h"
#include "ticket.h"
#define VFLAG_UNKNOWN (1<<29)
#define VFLAG_ESHOP (1<<30)
#define VFLAG_SYSTEM (1<<31)
#define VFLAG_TICKDIR (VFLAG_UNKNOWN|VFLAG_ESHOP|VFLAG_SYSTEM)
#define NAME_TIK "%02lX.%016llX.%08lX" // index / title id / console id
typedef struct {
u32 commonkey_idx;
u32 offset;
u8 title_id[8];
u8 titlekey[16];
u8 ticket_id[8];
u8 console_id[4];
u8 eshop_id[4];
} __attribute__((packed)) TickDbEntry;
typedef struct {
u32 n_entries;
u8 reserved[12];
TickDbEntry entries[256]; // this number is only a placeholder
} __attribute__((packed)) TickDbInfo;
// only for the main directory
static const VirtualFile vTickDbFileTemplates[] = {
{ "system" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_SYSTEM },
{ "eshop" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_ESHOP },
{ "unknown" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_UNKNOWN }
};
static TickDbInfo* tick_info = (TickDbInfo*) VGAME_BUFFER; // full 1MB reserved (enough for ~20000 entries)
u32 AddTickDbInfo(TickDbInfo* info, Ticket* ticket, u32 offset) {
if (ValidateTicket(ticket) != 0) return 1;
// build ticket entry
TickDbEntry* entry = info->entries + info->n_entries;
entry->commonkey_idx = ticket->commonkey_idx;
entry->offset = offset;
memcpy(entry->title_id, ticket->title_id, 8);
memcpy(entry->titlekey, ticket->titlekey, 16);
memcpy(entry->ticket_id, ticket->ticket_id, 8);
memcpy(entry->console_id, ticket->console_id, 4);
memcpy(entry->eshop_id, ticket->eshop_id, 4);
// check for duplicate
u32 t = 0;
for (; t < info->n_entries; t++) {
TickDbEntry* entry0 = info->entries + t;
if (memcmp(entry->title_id, entry0->title_id, 8) != 0) continue;
if (!getbe64(entry0->console_id)) // replace this
memcpy(entry0, entry, sizeof(TickDbEntry));
break;
}
if (t >= info->n_entries)
info->n_entries++; // new entry
return 0;
}
u32 InitVTickDbDrive(void) { // prerequisite: ticket.db mounted as image
const u32 area_offsets[] = { TICKDB_AREA_OFFSETS };
if (!(GetMountState() & SYS_TICKDB)) return 1;
// reset internal db
memset(tick_info, 0, 16);
// parse file, sector by sector
for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) {
u32 offset_area = area_offsets[p];
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 (ReadImageBytes(data, offset_area + i, read_bytes) != 0) {
tick_info->n_entries = 0;
return 0;
}
for (; data + TICKET_SIZE < ((u8*) TEMP_BUFFER) + read_bytes; data += 0x200) {
Ticket* ticket = TicketFromTickDbChunk(data, NULL, false);
if (!ticket) continue;
AddTickDbInfo(tick_info, ticket, offset_area + i + 0x18);
}
}
}
return (tick_info->n_entries) ? SYS_TICKDB : 0;
}
u32 CheckVTickDbDrive(void) {
if ((GetMountState() & SYS_TICKDB) && tick_info->n_entries) // very basic sanity check
return SYS_TICKDB;
tick_info->n_entries = 0;
return 0;
}
bool ReadVTickDbDir(VirtualFile* vfile, VirtualDir* vdir) {
if (vdir->flags & VFLAG_TICKDIR) { // ticket dir
while (++vdir->index < (int) tick_info->n_entries) {
TickDbEntry* tick_entry = tick_info->entries + vdir->index;
u64 ticket_id = getbe64(tick_entry->ticket_id);
u32 ck_idx = tick_entry->commonkey_idx;
if (!(((vdir->flags & VFLAG_SYSTEM) && ticket_id && (ck_idx == 1)) ||
((vdir->flags & VFLAG_ESHOP) && ticket_id && (ck_idx < 6)) ||
((vdir->flags & VFLAG_UNKNOWN) && (!ticket_id || (ck_idx >= 6)))))
continue;
memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_TIK, tick_entry->commonkey_idx, getbe64(tick_entry->title_id), getbe32(tick_entry->console_id));
vfile->offset = tick_entry->offset;
vfile->size = sizeof(Ticket);
vfile->keyslot = 0xFF;
vfile->flags = vdir->flags & ~VFLAG_DIR;
return true; // found
}
} else { // root dir
int n_templates = sizeof(vTickDbFileTemplates) / sizeof(VirtualFile);
const VirtualFile* templates = vTickDbFileTemplates;
while (++vdir->index < n_templates) {
// copy current template to vfile
memcpy(vfile, templates + vdir->index, sizeof(VirtualFile));
return true; // found
}
}
return false;
}
int ReadVTickDbFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count) {
u32 foffset = vfile->offset + offset;
return (ReadImageBytes(buffer, foffset, count) == 0) ? 0 : 1;
}
u64 GetVTickDbDriveSize(void) {
return (tick_info->n_entries) ? GetMountSize() : 0;
}

12
source/virtual/vtickdb.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "common.h"
#include "virtual.h"
u32 InitVTickDbDrive(void);
u32 CheckVTickDbDrive(void);
bool ReadVTickDbDir(VirtualFile* vfile, VirtualDir* vdir);
int ReadVTickDbFile(const VirtualFile* vfile, u8* buffer, u32 offset, u32 count);
// int WriteVTickDbFile(const VirtualFile* vfile, const u8* buffer, u32 offset, u32 count); // no writing
u64 GetVTickDbDriveSize(void);