implement virtual mounting of BDRI files

This commit is contained in:
aspargas2 2020-05-23 21:04:18 -04:00 committed by d0k3
parent 2b94b585eb
commit c70546d5ca
10 changed files with 368 additions and 253 deletions

View File

@ -46,13 +46,13 @@ int DriveType(const char* path) {
type = DRV_VIRTUAL | DRV_SYSNAND; type = DRV_VIRTUAL | DRV_SYSNAND;
} else if (vsrc == VRT_EMUNAND) { } else if (vsrc == VRT_EMUNAND) {
type = DRV_VIRTUAL | DRV_EMUNAND; type = DRV_VIRTUAL | DRV_EMUNAND;
} else if ((vsrc == VRT_IMGNAND) || (vsrc == VRT_DISADIFF)) { } else if ((vsrc == VRT_IMGNAND) || (vsrc == VRT_DISADIFF) || (vsrc == VRT_BDRI)) {
type = DRV_VIRTUAL | DRV_IMAGE; type = DRV_VIRTUAL | DRV_IMAGE;
} else if (vsrc == VRT_XORPAD) { } else if (vsrc == VRT_XORPAD) {
type = DRV_VIRTUAL | DRV_XORPAD; type = DRV_VIRTUAL | DRV_XORPAD;
} else if (vsrc == VRT_MEMORY) { } else if (vsrc == VRT_MEMORY) {
type = DRV_VIRTUAL | DRV_MEMORY; type = DRV_VIRTUAL | DRV_MEMORY;
} else if ((vsrc == VRT_GAME) || (vsrc == VRT_KEYDB) || (vsrc == VRT_TICKDB)) { } else if ((vsrc == VRT_GAME) || (vsrc == VRT_KEYDB)) {
type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE; type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE;
} else if (vsrc == VRT_CART) { } else if (vsrc == VRT_CART) {
type = DRV_VIRTUAL | DRV_CART; type = DRV_VIRTUAL | DRV_CART;

View File

@ -36,7 +36,7 @@
"EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "EMUNAND SD", "EMUNAND VIRTUAL", \ "EMUNAND CTRNAND", "EMUNAND TWLN", "EMUNAND TWLP", "EMUNAND SD", "EMUNAND VIRTUAL", \
"IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", "IMGNAND VIRTUAL", \ "IMGNAND CTRNAND", "IMGNAND TWLN", "IMGNAND TWLP", "IMGNAND VIRTUAL", \
"GAMECART", \ "GAMECART", \
"GAME IMAGE", "AESKEYDB IMAGE", "TICKET.DB IMAGE", "DISA/DIFF IMAGE", \ "GAME IMAGE", "AESKEYDB IMAGE", "BDRI IMAGE", "DISA/DIFF IMAGE", \
"MEMORY VIRTUAL", \ "MEMORY VIRTUAL", \
"VRAM VIRTUAL", \ "VRAM VIRTUAL", \
"LAST SEARCH" \ "LAST SEARCH" \

View File

@ -689,7 +689,7 @@ bool PathMoveCopy(const char* dest, const char* orig, u32* flags, bool move) {
bool force_unmount = false; bool force_unmount = false;
// handle NAND image unmounts // handle NAND image unmounts
if (ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) { if ((ddrvtype & (DRV_SYSNAND|DRV_EMUNAND|DRV_IMAGE)) && !(GetVirtualSource(dest) & (VRT_DISADIFF | VRT_BDRI))) {
FILINFO fno; FILINFO fno;
// virtual NAND files over 4 MB require unmount, totally arbitrary limit (hacky!) // virtual NAND files over 4 MB require unmount, totally arbitrary limit (hacky!)
if ((fvx_stat(ldest, &fno) == FR_OK) && (fno.fsize > 4 * 1024 * 1024)) if ((fvx_stat(ldest, &fno) == FR_OK) && (fno.fsize > 4 * 1024 * 1024))
@ -733,7 +733,7 @@ bool PathCopy(const char* destdir, const char* orig, u32* flags) {
snprintf(dest, 255, "%s/%s", destdir, (++oname)); snprintf(dest, 255, "%s/%s", destdir, (++oname));
// virtual destination special handling // virtual destination special handling
if (GetVirtualSource(destdir)) { if (GetVirtualSource(destdir) & ~VRT_BDRI) {
u64 osize = FileGetSize(orig); u64 osize = FileGetSize(orig);
VirtualFile dvfile; VirtualFile dvfile;
if (!osize) return false; if (!osize) return false;

318
arm9/source/virtual/vbdri.c Normal file
View File

@ -0,0 +1,318 @@
#include "vbdri.h"
#include "image.h"
#include "disadiff.h"
#include "vdisadiff.h"
#include "bdri.h"
#include "vff.h"
#define VBDRI_MAX_ENTRIES 8192 // Completely arbitrary
#define VFLAG_UNKNOWN (1UL<<28)
#define VFLAG_HOMEBREW (1UL<<29)
#define VFLAG_ESHOP (1UL<<30)
#define VFLAG_SYSTEM (1UL<<31)
#define VFLAG_TICKDIR (VFLAG_UNKNOWN|VFLAG_HOMEBREW|VFLAG_ESHOP|VFLAG_SYSTEM)
#define NAME_TIE "%016llX"
#define NAME_TIK "%016llX.%08lX.tik" // title id / console id
#define PART_PATH "D:/partitionA.bin"
typedef struct {
u8 type; // 0 for eshop, 1 for homebrew, 2 for system, 3 for unknown
u32 size;
u8 console_id[4];
} PACKED_STRUCT TickInfoEntry;
// only for the main directory
static const VirtualFile VTickDbFileTemplates[] = {
{ "system" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_SYSTEM },
{ "homebrew", 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_HOMEBREW },
{ "eshop" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_ESHOP },
{ "unknown" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_UNKNOWN },
};
static bool is_tickdb;
static u32 num_entries = 0;
static u8* title_ids = NULL;
static TickInfoEntry* tick_info = NULL;
static u8* cached_entry = NULL;
static int cache_index;
//static u32 cache_size;
void DeinitVBDRIDrive(void) {
free(title_ids);
free(tick_info);
free(cached_entry);
title_ids = NULL;
tick_info = NULL;
cached_entry = NULL;
num_entries = 0;
cache_index = -1;
}
u64 InitVBDRIDrive(void) { // prerequisite: .db file mounted as virtual diff image
u64 mount_state = CheckVDisaDiffDrive();
if (!(mount_state & SYS_DIFF)) return 0;
is_tickdb = (mount_state & SYS_TICKDB);
DeinitVBDRIDrive();
num_entries = min(is_tickdb ? GetNumTickets(PART_PATH) : GetNumTitleInfoEntries(PART_PATH), VBDRI_MAX_ENTRIES);
title_ids = (u8*) malloc(num_entries * 8);
if (!title_ids ||
((is_tickdb ? ListTicketTitleIDs(PART_PATH, title_ids, num_entries) : ListTitleInfoEntryTitleIDs(PART_PATH, title_ids, num_entries)) != 0)) {
DeinitVBDRIDrive();
return 0;
}
if (is_tickdb) {
tick_info = (TickInfoEntry*) malloc(num_entries * sizeof(TickInfoEntry));
if (!tick_info) {
DeinitVBDRIDrive();
return 0;
}
for (u32 i = 0; i < num_entries; i++) {
Ticket* ticket;
if (ReadTicketFromDB(PART_PATH, title_ids + (i * 8), &ticket) != 0) {
DeinitVBDRIDrive();
return 0;
}
tick_info[i].type = (ticket->commonkey_idx == 0) ? ((ValidateTicketSignature(ticket) == 0) ? 0 : 1) : ((ticket->commonkey_idx == 1) ? 2 : 3);
tick_info[i].size = GetTicketSize(ticket);
memcpy(tick_info[i].console_id, ticket->console_id, 4);
free(ticket);
}
} else if ((cached_entry = malloc(sizeof(TitleInfoEntry))) == NULL)
return 0;
return mount_state;
}
u64 CheckVBDRIDrive(void) {
u64 mount_state = CheckVDisaDiffDrive();
return (title_ids && (mount_state & SYS_DIFF) && (!is_tickdb || ((mount_state & SYS_TICKDB) && tick_info))) ?
mount_state : 0;
}
bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir) {
if (!CheckVBDRIDrive())
return false;
if (vdir->flags & VFLAG_TICKDIR) { // ticket dir
if (!is_tickdb)
return false;
while (++vdir->index < (int) num_entries) {
u32 type = tick_info[vdir->index].type;
u64 tid = getbe64(title_ids + (vdir->index * 8));
if ((tid == 0) || !(
((vdir->flags & VFLAG_ESHOP) && (type == 0)) ||
((vdir->flags & VFLAG_HOMEBREW) && (type == 1)) ||
((vdir->flags & VFLAG_SYSTEM) && (type == 2)) ||
((vdir->flags & VFLAG_UNKNOWN) && (type == 3))))
continue;
memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_TIK, tid, getbe32(tick_info[vdir->index].console_id));
vfile->offset = vdir->index; // "offset" is the internal buffer index
vfile->size = tick_info[vdir->index].size;
vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true; // found
}
} else { // root dir
if (is_tickdb) {
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
}
} else { // title dbs display all entries in root
while (++vdir->index < (int) num_entries) {
u64 tid = getbe64(title_ids + (vdir->index * 8));
if (tid == 0)
continue;
memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_TIE, tid);
vfile->offset = vdir->index; // "offset" is the internal buffer index
vfile->size = sizeof(TitleInfoEntry);
vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true;
}
}
}
return false;
}
bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path) {
size_t len = strlen(path);
char buf[31];
strcpy(buf, path + len - (is_tickdb ? 30 : 17));
if (is_tickdb && ((strcmp(buf + 26, ".tik") != 0) || buf[17] != '.'))
return false;
for (char* ptr = buf + 1; ptr < buf + 17; ptr++)
*ptr = toupper(*ptr);
u64 tid;
if (sscanf(buf, "/%016llX", &tid) != 1) return false;
if (tid == 0)
return false;
tid = getbe64((u8*)&tid);
int entry_index = -1;
for (u32 i = 0; i < num_entries; i++) {
if ((entry_index == -1) && (*((u64*)(void*)(title_ids + 8 * i)) == 0))
entry_index = i;
if (memcmp(&tid, title_ids + 8 * i, 8) == 0)
return false;
}
if (entry_index == -1) {
if (num_entries == VBDRI_MAX_ENTRIES)
return false;
u32 new_num_entries = min(num_entries + 128, VBDRI_MAX_ENTRIES);
u8* new_title_ids = realloc(title_ids, new_num_entries * 8);
if (!new_title_ids)
return false;
if (is_tickdb) {
TickInfoEntry* new_tick_info = realloc(tick_info, new_num_entries * sizeof(TickInfoEntry));
if (!new_tick_info)
return false;
tick_info = new_tick_info;
}
entry_index = num_entries;
num_entries = new_num_entries;
title_ids = new_title_ids;
memset(title_ids + entry_index * 8, 0, (num_entries - entry_index) * 8);
}
u32 size = is_tickdb ? TICKET_COMMON_SIZE : sizeof(TitleInfoEntry);
u8 entry[size];
if (is_tickdb)
*((u32*)(void*)(entry + 0x2A8)) = 0xAC000000;
if ((is_tickdb ? AddTicketToDB(PART_PATH, (u8*)&tid, (Ticket*)(void*)entry, false) :
AddTitleInfoEntryToDB(PART_PATH, (u8*)&tid, (TitleInfoEntry*)(void*)entry, false)) != 0)
return false;
memcpy(title_ids + entry_index * 8, &tid, 8);
if (is_tickdb) {
tick_info[entry_index].type = 3;
tick_info[entry_index].size = TICKET_COMMON_SIZE;
memset(tick_info[entry_index].console_id, 0, 4);
}
memset(vfile, 0, sizeof(VirtualFile));
strcpy(vfile->name, buf);
vfile->offset = entry_index; // "offset" is the internal buffer index
vfile->size = size;
vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_DELETABLE) & ~VFLAG_DIR;
return true;
}
int ReadVBDRIFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
if ((int) vfile->offset == cache_index) {
memcpy(buffer, cached_entry + offset, count);
return 0;
}
if (is_tickdb && (cache_index != -1))
free(cached_entry);
if ((is_tickdb ? ReadTicketFromDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket**) &cached_entry) :
ReadTitleInfoEntryFromDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*) cached_entry)) != 0)
return 1;
cache_index = (int) vfile->offset;
memcpy(buffer, cached_entry + offset, count);
return 0;
}
int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count) {
bool resize = false;
if (offset + count > vfile->size) {
if (!is_tickdb)
return false;
vfile->size = offset + count;
resize = true;
}
if (is_tickdb && (cache_index != -1))
free(cached_entry);
if ((is_tickdb ? ReadTicketFromDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket**) &cached_entry) :
ReadTitleInfoEntryFromDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*) cached_entry)) != 0) {
if (resize) vfile->size = tick_info[vfile->offset].size;
return 1;
}
cache_index = (int) vfile->offset;
if (resize) {
u8* new_cached_entry = realloc(cached_entry, vfile->size);
if (!new_cached_entry) {
vfile->size = tick_info[vfile->offset].size;
return 1;
}
cached_entry = new_cached_entry;
if (RemoveTicketFromDB(PART_PATH, title_ids + vfile->offset * 8) != 0) {
vfile->size = tick_info[vfile->offset].size;
return 1;
}
}
memcpy(cached_entry + offset, buffer, count);
if ((is_tickdb ? AddTicketToDB(PART_PATH, title_ids + vfile->offset * 8, (Ticket*)(void*)cached_entry, true) :
AddTitleInfoEntryToDB(PART_PATH, title_ids + vfile->offset * 8, (TitleInfoEntry*)(void*)cached_entry, true)) != 0) {
if (resize) vfile->size = tick_info[vfile->offset].size;
return 1;
}
if (resize) tick_info[vfile->offset].size = vfile->size;
if (is_tickdb && ((offset <= 0x1F1 && offset + count > 0x1F1) || (cached_entry[0x1F1] == 0 && offset <= 0x104 && offset + count > 4)))
tick_info[vfile->offset].type = (cached_entry[0x1F1] == 0) ? ((ValidateTicketSignature((Ticket*)(void*)cached_entry) == 0) ? 0 : 1) : ((cached_entry[0x1F1] == 1) ? 2 : 3);
return 0;
}
int DeleteVBDRIFile(const VirtualFile* vfile) {
int ret = (int) (is_tickdb ? RemoveTicketFromDB(PART_PATH, title_ids + vfile->offset * 8) : RemoveTitleInfoEntryFromDB(PART_PATH, title_ids + vfile->offset * 8));
if (ret == 0) {
if ((int) vfile->offset == cache_index) {
cache_index = -1;
if (is_tickdb) {
free(cached_entry);
cached_entry = NULL;
}
}
memset(title_ids + vfile->offset * 8, 0, 8);
}
return ret;
}
u64 GetVBDRIDriveSize(void) {
return CheckVBDRIDrive() ? fvx_qsize(PART_PATH) : 0;
}

View File

@ -0,0 +1,15 @@
#pragma once
#include "common.h"
#include "virtual.h"
void DeinitVBDRIDrive(void);
u64 InitVBDRIDrive(void);
u64 CheckVBDRIDrive(void);
bool ReadVBDRIDir(VirtualFile* vfile, VirtualDir* vdir);
bool GetNewVBDRIFile(VirtualFile* vfile, VirtualDir* vdir, const char* path);
int ReadVBDRIFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count);
int WriteVBDRIFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count);
int DeleteVBDRIFile(const VirtualFile* vfile);
u64 GetVBDRIDriveSize(void);

View File

@ -2,7 +2,7 @@
#include "disadiff.h" #include "disadiff.h"
#include "common.h" #include "common.h"
#include "image.h" #include "image.h"
#include "vtickdb.h" // So we can mount a file as vdisadiff and vtickdb simeltaneously #include "vbdri.h" // So we can mount a file as vdisadiff and vbdri simeltaneously
#define VFLAG_PARTITION_B (1 << 31) #define VFLAG_PARTITION_B (1 << 31)
@ -194,15 +194,15 @@ u64 InitVDisaDiffDrive(void) {
partitionB_info->rw_info = info; partitionB_info->rw_info = info;
} }
InitVTickDbDrive(); InitVBDRIDrive();
return type; return type;
} }
u64 CheckVDisaDiffDrive(void) { u64 CheckVDisaDiffDrive(void) {
u64 type = GetMountState() & (SYS_DISA | SYS_DIFF); u64 type = GetMountState();
return (type && partitionA_info) ? type : 0; return ((type & (SYS_DISA | SYS_DIFF)) && partitionA_info) ? type : 0;
} }
// Can be very lazy here because there are only two files that can appear // Can be very lazy here because there are only two files that can appear

View File

@ -2,7 +2,7 @@
#include "vnand.h" #include "vnand.h"
#include "vmem.h" #include "vmem.h"
#include "vgame.h" #include "vgame.h"
#include "vtickdb.h" #include "vbdri.h"
#include "vkeydb.h" #include "vkeydb.h"
#include "vcart.h" #include "vcart.h"
#include "vvram.h" #include "vvram.h"
@ -27,7 +27,7 @@ u32 GetVirtualSource(const char* path) {
void DeinitVirtualImageDrive(void) { void DeinitVirtualImageDrive(void) {
DeinitVGameDrive(); DeinitVGameDrive();
DeinitVTickDbDrive(); DeinitVBDRIDrive();
DeinitVKeyDbDrive(); DeinitVKeyDbDrive();
DeinitVDisaDiffDrive(); DeinitVDisaDiffDrive();
} }
@ -45,8 +45,8 @@ bool CheckVirtualDrive(const char* path) {
return CheckVVramDrive(); return CheckVVramDrive();
else if (virtual_src & VRT_GAME) else if (virtual_src & VRT_GAME)
return CheckVGameDrive(); return CheckVGameDrive();
else if (virtual_src & VRT_TICKDB) else if (virtual_src & VRT_BDRI)
return CheckVTickDbDrive(); return CheckVBDRIDrive();
else if (virtual_src & VRT_KEYDB) else if (virtual_src & VRT_KEYDB)
return CheckVKeyDbDrive(); return CheckVKeyDbDrive();
else if (virtual_src & VRT_DISADIFF) else if (virtual_src & VRT_DISADIFF)
@ -63,8 +63,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
ret = ReadVMemDir(vfile, vdir); ret = ReadVMemDir(vfile, vdir);
} else if (virtual_src & VRT_GAME) { } else if (virtual_src & VRT_GAME) {
ret = ReadVGameDir(vfile, vdir); ret = ReadVGameDir(vfile, vdir);
} else if (virtual_src & VRT_TICKDB) { } else if (virtual_src & VRT_BDRI) {
ret = ReadVTickDbDir(vfile, vdir); ret = ReadVBDRIDir(vfile, vdir);
} else if (virtual_src & VRT_KEYDB) { } else if (virtual_src & VRT_KEYDB) {
ret = ReadVKeyDbDir(vfile, vdir); ret = ReadVKeyDbDir(vfile, vdir);
} else if (virtual_src & VRT_CART) { } else if (virtual_src & VRT_CART) {
@ -129,7 +129,7 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path) {
for (name = strtok(lpath + 3, "/"); name && vdir.flags; name = strtok(NULL, "/")) { for (name = strtok(lpath + 3, "/"); name && vdir.flags; name = strtok(NULL, "/")) {
if (!(vdir.flags & VFLAG_LV3)) { // standard method if (!(vdir.flags & VFLAG_LV3)) { // standard method
while (true) { while (true) {
if (!ReadVirtualDir(vfile, &vdir)) return false; if (!ReadVirtualDir(vfile, &vdir)) return ((vdir.flags & VRT_BDRI) && GetNewVBDRIFile(vfile, &vdir, path));
if ((!(vfile->flags & (VRT_GAME|VRT_VRAM)) && (strncasecmp(name, vfile->name, 32) == 0)) || if ((!(vfile->flags & (VRT_GAME|VRT_VRAM)) && (strncasecmp(name, vfile->name, 32) == 0)) ||
((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256)) || ((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256)) ||
((vfile->flags & VRT_VRAM) && MatchVVramFilename(name, vfile))) ((vfile->flags & VRT_VRAM) && MatchVVramFilename(name, vfile)))
@ -173,8 +173,8 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun
return ReadVMemFile(vfile, buffer, offset, count); return ReadVMemFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_GAME) { } else if (vfile->flags & VRT_GAME) {
return ReadVGameFile(vfile, buffer, offset, count); return ReadVGameFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_TICKDB) { } else if (vfile->flags & VRT_BDRI) {
return ReadVTickDbFile(vfile, buffer, offset, count); return ReadVBDRIFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_KEYDB) { } else if (vfile->flags & VRT_KEYDB) {
return ReadVKeyDbFile(vfile, buffer, offset, count); return ReadVKeyDbFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_CART) { } else if (vfile->flags & VRT_CART) {
@ -188,11 +188,11 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun
return -1; return -1;
} }
int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count, u32* bytes_written) { int WriteVirtualFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count, u32* bytes_written) {
// basic check of offset / count // basic check of offset / count
if (offset >= vfile->size) if (offset >= vfile->size)
return 0; return 0;
else if ((offset + count) > vfile->size) else if (!(vfile->flags & VRT_BDRI) && ((offset + count) > vfile->size))
count = vfile->size - offset; count = vfile->size - offset;
if (bytes_written) *bytes_written = count; if (bytes_written) *bytes_written = count;
@ -206,7 +206,9 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u
return WriteVDisaDiffFile(vfile, buffer, offset, count); return WriteVDisaDiffFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_CART) { } else if (vfile->flags & VRT_CART) {
return WriteVCartFile(vfile, buffer, offset, count); return WriteVCartFile(vfile, buffer, offset, count);
} // no write support for virtual game / tickdb / keydb / vram files } else if (vfile->flags & VRT_BDRI) {
return WriteVBDRIFile(vfile, buffer, offset, count);
} // no write support for virtual game / keydb / vram files
return -1; return -1;
} }
@ -214,6 +216,11 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u
int DeleteVirtualFile(const VirtualFile* vfile) { int DeleteVirtualFile(const VirtualFile* vfile) {
if (!(vfile->flags & VFLAG_DELETABLE)) return -1; if (!(vfile->flags & VFLAG_DELETABLE)) return -1;
// Special handling for deleting BDRI entries
if (vfile->flags & VRT_BDRI)
return DeleteVBDRIFile(vfile);
// For anything else, "deleting" is just filling with 0s
u32 zeroes_size = STD_BUFFER_SIZE; u32 zeroes_size = STD_BUFFER_SIZE;
u8* zeroes = (u8*) malloc(zeroes_size); u8* zeroes = (u8*) malloc(zeroes_size);
if (!zeroes) return -1; if (!zeroes) return -1;
@ -222,7 +229,7 @@ int DeleteVirtualFile(const VirtualFile* vfile) {
int result = 0; int result = 0;
for (u64 pos = 0; pos < vfile->size; pos += zeroes_size) { for (u64 pos = 0; pos < vfile->size; pos += zeroes_size) {
u64 wipe_bytes = min(zeroes_size, vfile->size - pos); u64 wipe_bytes = min(zeroes_size, vfile->size - pos);
result = WriteVirtualFile(vfile, zeroes, pos, wipe_bytes, NULL); result = WriteVirtualFile((VirtualFile*)vfile, zeroes, pos, wipe_bytes, NULL);
if (result != 0) break; if (result != 0) break;
} }
@ -236,8 +243,8 @@ u64 GetVirtualDriveSize(const char* path) {
return GetVNandDriveSize(virtual_src); return GetVNandDriveSize(virtual_src);
else if (virtual_src & VRT_GAME) else if (virtual_src & VRT_GAME)
return GetVGameDriveSize(); return GetVGameDriveSize();
else if (virtual_src & VRT_TICKDB) else if (virtual_src & VRT_BDRI)
return GetVTickDbDriveSize(); return GetVBDRIDriveSize();
else if (virtual_src & VRT_KEYDB) else if (virtual_src & VRT_KEYDB)
return GetVKeyDbDriveSize(); return GetVKeyDbDriveSize();
else if (virtual_src & VRT_CART) else if (virtual_src & VRT_CART)

View File

@ -9,13 +9,13 @@
#define VRT_XORPAD NAND_ZERONAND #define VRT_XORPAD NAND_ZERONAND
#define VRT_MEMORY (1UL<<4) #define VRT_MEMORY (1UL<<4)
#define VRT_GAME (1UL<<5) #define VRT_GAME (1UL<<5)
#define VRT_TICKDB (1UL<<6) #define VRT_BDRI (1UL<<6)
#define VRT_KEYDB (1UL<<7) #define VRT_KEYDB (1UL<<7)
#define VRT_CART (1UL<<8) #define VRT_CART (1UL<<8)
#define VRT_VRAM (1UL<<9) #define VRT_VRAM (1UL<<9)
#define VRT_DISADIFF (1UL<<10) #define VRT_DISADIFF (1UL<<10)
#define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_TICKDB|VRT_KEYDB|VRT_CART|VRT_VRAM|VRT_DISADIFF) #define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_BDRI|VRT_KEYDB|VRT_CART|VRT_VRAM|VRT_DISADIFF)
#define VFLAG_DIR (1UL<<11) #define VFLAG_DIR (1UL<<11)
#define VFLAG_ROOT (1UL<<12) #define VFLAG_ROOT (1UL<<12)
@ -25,7 +25,7 @@
#define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, {'X', VRT_XORPAD }, \ #define VRT_DRIVES {'S', VRT_SYSNAND}, {'E', VRT_EMUNAND}, {'I', VRT_IMGNAND}, {'X', VRT_XORPAD }, \
{'M', VRT_MEMORY}, {'G', VRT_GAME}, {'K', VRT_KEYDB}, {'T', VRT_TICKDB}, {'C', VRT_CART}, {'V', VRT_VRAM}, {'D', VRT_DISADIFF} {'M', VRT_MEMORY}, {'G', VRT_GAME}, {'K', VRT_KEYDB}, {'T', VRT_BDRI}, {'C', VRT_CART}, {'V', VRT_VRAM}, {'D', VRT_DISADIFF}
// virtual file flag (subject to change): // virtual file flag (subject to change):
// bits 0...3 : reserved for NAND virtual sources and info // bits 0...3 : reserved for NAND virtual sources and info
@ -62,7 +62,7 @@ bool GetVirtualDir(VirtualDir* vdir, const char* path);
bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars); bool GetVirtualFilename(char* name, const VirtualFile* vfile, u32 n_chars);
int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count, u32* bytes_read); int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count, u32* bytes_read);
int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u64 count, u32* bytes_written); int WriteVirtualFile(VirtualFile* vfile, const void* buffer, u64 offset, u64 count, u32* bytes_written);
int DeleteVirtualFile(const VirtualFile* vfile); int DeleteVirtualFile(const VirtualFile* vfile);
u64 GetVirtualDriveSize(const char* path); u64 GetVirtualDriveSize(const char* path);

View File

@ -1,212 +0,0 @@
#include "vtickdb.h"
#include "image.h"
#include "disadiff.h"
#include "ticketdb.h"
#include "ui.h" // this takes long - we need a way to keep the user in check
#define VTICKDB_BUFFER_SIZE 0x100000 // 1MB, enough for ~20000 entries
#define VFLAG_HIDDEN (1UL<<28)
#define VFLAG_UNKNOWN (1UL<<29)
#define VFLAG_ESHOP (1UL<<30)
#define VFLAG_SYSTEM (1UL<<31)
#define VFLAG_TICKDIR (VFLAG_HIDDEN|VFLAG_UNKNOWN|VFLAG_ESHOP|VFLAG_SYSTEM)
#define OFLAG_RAW (1lu << 31)
#define NAME_TIK "%016llX.%08lX.tik" // title id / console id
typedef struct {
u32 commonkey_idx;
u32 offset;
u32 size;
u8 title_id[8];
u8 titlekey[16];
u8 ticket_id[8];
u8 console_id[4];
u8 eshop_id[4];
} PACKED_STRUCT TickDbEntry;
typedef struct {
u32 n_entries;
u8 reserved[12];
TickDbEntry entries[256]; // this number is only a placeholder (dangerous?)
} PACKED_STRUCT 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 },
{ "hidden" , 0x00000000, 0x00000000, 0xFF, VFLAG_DIR | VFLAG_HIDDEN }
};
static TickDbInfo* tick_info = NULL;
static u8* lvl2_cache = NULL;
static DisaDiffRWInfo diff_info;
static bool scanned_raw = false;
u32 AddTickDbInfo(TickDbInfo* info, Ticket* ticket, u32 offset, bool replace) {
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;
entry->size = GetTicketSize(ticket);
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 (replace && !getbe32(entry0->console_id)) // replace this
memcpy(entry0, entry, sizeof(TickDbEntry));
break;
}
if (t >= info->n_entries)
info->n_entries++; // new entry
return 0;
}
void ScanTickDb(bool raw_mode, bool replace) {
// set up buffer
u8* data = (u8*) malloc(TICKDB_AREA_SIZE);
if (!data) return;
if (!raw_mode) { // proper DIFF decoding
// read and decode ticket.db DIFF partition
ShowString("Loading DIFF data...");
if (ReadDisaDiffIvfcLvl4(NULL, &diff_info, TICKDB_AREA_OFFSET, TICKDB_AREA_SIZE, data) == TICKDB_AREA_SIZE) {
// parse the decoded data for valid tickets
for (u32 i = 0; i <= TICKDB_AREA_SIZE - 0x400; i += 0x200) {
if (!(i % 0x10000) && !ShowProgress(i, TICKDB_AREA_SIZE, "Scanning for tickets")) break;
Ticket* ticket = TicketFromTickDbChunk(data + i, NULL, true);
if (!ticket) continue;
AddTickDbInfo(tick_info, ticket, TICKDB_AREA_OFFSET + i + 0x18, replace);
}
}
} else { // scan RAW data
const u32 area_offsets[] = { TICKDB_AREA_RAW };
for (u32 p = 0; p < sizeof(area_offsets) / sizeof(u32); p++) {
u32 offset_area = area_offsets[p];
ShowString("Loading raw data (%lu)...", p);
if (ReadImageBytes(data, offset_area, TICKDB_AREA_SIZE) != 0)
continue;
for (u32 i = 0; i <= TICKDB_AREA_SIZE - 0x400; i += 0x200) {
if (!(i % 0x10000) && !ShowProgress(i, TICKDB_AREA_SIZE, "Scanning for tickets")) break;
Ticket* ticket = TicketFromTickDbChunk(data + i, NULL, true);
if (!ticket) continue;
AddTickDbInfo(tick_info, ticket, (offset_area + i + 0x18) | OFLAG_RAW, replace);
}
}
}
ClearScreenF(true, false, COLOR_STD_BG);
free(data);
}
void DeinitVTickDbDrive(void) {
free(tick_info);
free(lvl2_cache);
tick_info = NULL;
lvl2_cache = NULL;
scanned_raw = false;
}
u64 InitVTickDbDrive(void) { // prerequisite: ticket.db mounted as image
if (!(GetMountState() & SYS_TICKDB)) return 0;
// set up drive buffer / internal db
DeinitVTickDbDrive();
tick_info = (TickDbInfo*) malloc(VTICKDB_BUFFER_SIZE);
if (!tick_info) return 0;
memset(tick_info, 0, 16);
// setup DIFF reading
if ((GetDisaDiffRWInfo(NULL, &diff_info, false) != 0) ||
!(lvl2_cache = (u8*) malloc(diff_info.size_dpfs_lvl2)) ||
(BuildDisaDiffDpfsLvl2Cache(NULL, &diff_info, lvl2_cache, diff_info.size_dpfs_lvl2) != 0)) {
DeinitVTickDbDrive();
return 0;
}
ScanTickDb(false, true);
if (!tick_info->n_entries)
DeinitVTickDbDrive();
return tick_info ? SYS_TICKDB : 0;
}
u64 CheckVTickDbDrive(void) {
if ((GetMountState() & SYS_TICKDB) && tick_info) // very basic sanity check
return SYS_TICKDB;
return 0;
}
bool ReadVTickDbDir(VirtualFile* vfile, VirtualDir* vdir) {
if (!tick_info)
return false;
if (vdir->flags & VFLAG_TICKDIR) { // ticket dir
// raw scan required?
if ((vdir->flags & VFLAG_HIDDEN) && !scanned_raw) {
ScanTickDb(true, false);
scanned_raw = true;
}
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;
bool hidden = tick_entry->offset & OFLAG_RAW;
if (hidden && !(vdir->flags & VFLAG_HIDDEN)) continue;
if (!(((vdir->flags & VFLAG_HIDDEN) && hidden) ||
((vdir->flags & VFLAG_ESHOP) && ticket_id && (ck_idx == 0)) ||
((vdir->flags & VFLAG_SYSTEM) && ticket_id && (ck_idx == 1)) ||
((vdir->flags & VFLAG_UNKNOWN) && (!ticket_id || (ck_idx >= 2)))))
continue;
memset(vfile, 0, sizeof(VirtualFile));
snprintf(vfile->name, 32, NAME_TIK, getbe64(tick_entry->title_id), getbe32(tick_entry->console_id));
vfile->offset = tick_entry->offset & ~OFLAG_RAW;
vfile->size = tick_entry->size;
vfile->keyslot = 0xFF;
vfile->flags = (vdir->flags | VFLAG_READONLY) & ~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));
vfile->flags |= VFLAG_READONLY;
return true; // found
}
}
return false;
}
int ReadVTickDbFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
u64 foffset = vfile->offset+ offset;
bool hidden = vfile->flags & VFLAG_HIDDEN;
if (hidden) return (ReadImageBytes(buffer, foffset, count) == 0) ? 0 : 1;
else return (ReadDisaDiffIvfcLvl4(NULL, &diff_info, (u32) foffset, (u32) count, buffer) == (u32) count) ? 0 : 1;
}
u64 GetVTickDbDriveSize(void) {
return (tick_info->n_entries) ? GetMountSize() : 0;
}

View File

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