mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
Preliminary ticket.db mount support
This commit is contained in:
parent
f5694cc16d
commit
ea00a3217a
@ -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)) &&
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
141
source/virtual/vtickdb.c
Normal 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
12
source/virtual/vtickdb.h
Normal 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);
|
Loading…
x
Reference in New Issue
Block a user