Virtual VRAM drive, based on TAR

This commit is contained in:
d0k3 2017-10-30 14:46:37 +01:00
parent 284fda27bc
commit 2d36460a02
14 changed files with 326 additions and 20 deletions

View File

@ -55,6 +55,8 @@ int DriveType(const char* path) {
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;
} else if (vsrc == VRT_VRAM) {
type = DRV_VIRTUAL | DRV_VRAM;
} }
} }

View File

@ -5,7 +5,7 @@
#define NORM_FS 10 #define NORM_FS 10
#define IMGN_FS 3 // image normal filesystems #define IMGN_FS 3 // image normal filesystems
#define VIRT_FS 12 #define VIRT_FS 13
// primary drive types // primary drive types
#define DRV_UNKNOWN (0<<0) #define DRV_UNKNOWN (0<<0)
@ -23,10 +23,11 @@
#define DRV_MEMORY (1UL<<10) #define DRV_MEMORY (1UL<<10)
#define DRV_GAME (1UL<<11) #define DRV_GAME (1UL<<11)
#define DRV_CART (1UL<<12) #define DRV_CART (1UL<<12)
#define DRV_ALIAS (1UL<<13) #define DRV_VRAM (1UL<<13)
#define DRV_BONUS (1UL<<14) #define DRV_ALIAS (1UL<<14)
#define DRV_SEARCH (1UL<<15) #define DRV_BONUS (1UL<<15)
#define DRV_STDFAT (1UL<<16) // standard FAT drive without limitations #define DRV_SEARCH (1UL<<16)
#define DRV_STDFAT (1UL<<17) // standard FAT drive without limitations
#define FS_DRVNAME \ #define FS_DRVNAME \
"SDCARD", \ "SDCARD", \
@ -36,11 +37,12 @@
"GAMECART", \ "GAMECART", \
"GAME IMAGE", "AESKEYDB IMAGE", "TICKET.DB IMAGE", \ "GAME IMAGE", "AESKEYDB IMAGE", "TICKET.DB IMAGE", \
"MEMORY VIRTUAL", \ "MEMORY VIRTUAL", \
"VRAM VIRTUAL", \
"NAND XORPADS", \ "NAND XORPADS", \
"LAST SEARCH" \ "LAST SEARCH" \
#define FS_DRVNUM \ #define FS_DRVNUM \
"0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "K:", "T:", "M:", "X:", "Z:" "0:", "1:", "2:", "3:", "A:", "S:", "4:", "5:", "6:", "B:", "E:", "7:", "8:", "9:", "I:", "C:", "G:", "K:", "T:", "M:", "V:", "X:", "Z:"
/** Function to identify the type of a drive **/ /** Function to identify the type of a drive **/
int DriveType(const char* path); int DriveType(const char* path);

View File

@ -19,5 +19,5 @@ typedef struct {
u32 size_bss; u32 size_bss;
u32 offset_smdh; u32 offset_smdh;
u32 size_smdh; u32 size_smdh;
u32 size_romfs_lv3; u32 offset_romfs_lv3;
} __attribute__((packed)) ThreedsxHeader; } __attribute__((packed)) ThreedsxHeader;

View File

@ -235,7 +235,7 @@ void DrawUserInterface(const char* curr_path, DirEntry* curr_entry, u32 curr_pan
((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" : ((drvtype & DRV_SDCARD) ? "SD" : (drvtype & DRV_RAMDRIVE) ? "RAMdrive" : (drvtype & DRV_GAME) ? "Game" :
(drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" : (drvtype & DRV_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" :
(drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" : (drvtype & DRV_XORPAD) ? "XORpad" : (drvtype & DRV_MEMORY) ? "Memory" : (drvtype & DRV_ALIAS) ? "Alias" :
(drvtype & DRV_CART) ? "Gamecart" : (drvtype & DRV_SEARCH) ? "Search" : ""), (drvtype & DRV_CART) ? "Gamecart" : (drvtype & DRV_VRAM) ? "VRAM" : (drvtype & DRV_SEARCH) ? "Search" : ""),
((drvtype & DRV_FAT) ? " FAT" : (drvtype & DRV_VIRTUAL) ? " Virtual" : "")); ((drvtype & DRV_FAT) ? " FAT" : (drvtype & DRV_VIRTUAL) ? " Virtual" : ""));
ResizeString(tempstr, drvstr, len_info / FONT_WIDTH_EXT, 8, false); ResizeString(tempstr, drvstr, len_info / FONT_WIDTH_EXT, 8, false);
} else { } else {

89
source/system/tar.c Normal file
View File

@ -0,0 +1,89 @@
#include "tar.h"
u64 ReadAsciiOctal(char* num, u32 len) {
u64 res = 0;
if ((num[len-1] != '\0') && (num[len-1] != ' '))
return (u64) -1;
for (u32 i = 0; i < (len-1); i++) {
res <<= 3;
if ((num[i] >= '0') && (num[i] < '8')) res |= (num[i] - '0');
else return (u64) -1;
}
return res;
}
u32 ValidateTarHeader(void* tardata, void* tardata_end) {
TarHeader* tar = tardata;
// available space
if ((u8*) tardata_end < (u8*) tardata + sizeof(TarHeader))
return 1;
// filename prefix is not supported here
if (*(tar->fname_prefix)) return 1;
// ustar magic
if (strncmp(tar->magic, USTAR_MAGIC, 5) != 0)
return 1;
// check filesize
u64 fsize_max = ((u8*) tardata_end - (u8*) tardata) - sizeof(TarHeader);
if (ReadAsciiOctal(tar->fsize, 12) > fsize_max)
return 1;
// type can only be standard file or dir
if ((tar->ftype != '0') && (tar->ftype != '5'))
return 1;
// checksum
u8* data = (u8*) tardata;
u64 checksum = 0;
for (u32 i = 0; i < sizeof(TarHeader); i++)
checksum += ((i < 148) || (i >= 156)) ? data[i] : (u64) ' ';
if (checksum != ReadAsciiOctal(tar->checksum, 7))
return 1;
return 0;
}
void* GetTarFileInfo(void* tardata, char* fname, u64* fsize, bool* is_dir) {
// this assumes a valid TAR header
TarHeader* tar = tardata;
if (fname) snprintf(fname, 101, "%.100s", tar->fname);
if (fsize) *fsize = ReadAsciiOctal(tar->fsize, 12);
if (is_dir) *is_dir = (tar->ftype == '5');
return (void*) (tar + 1);
}
void* NextTarEntry(void* tardata, void* tardata_end) {
// this assumes a valid TAR header
TarHeader* tar = tardata;
u8* data = (u8*) tardata;
u64 fsize = ReadAsciiOctal(tar->fsize, 12);
data += sizeof(TarHeader) + align(fsize, 512);
if (ValidateTarHeader(data, tardata_end) != 0)
return NULL;
return data;
}
void* FindTarFileInfo(void* tardata, void* tardata_end, const char* fname, u64* fsize, bool* is_dir) {
while (tardata && (tardata < tardata_end)) {
TarHeader* tar = tardata;
if (ValidateTarHeader(tardata, tardata_end) != 0) break;
ShowPrompt(false, "%s\n%s", tar->fname, fname);
if ((strncasecmp(tar->fname, fname, 100) == 0) && (tar->ftype == '0'))
return GetTarFileInfo(tardata, NULL, fsize, is_dir);
tardata = ((u8*) tardata) + sizeof(TarHeader) + align(ReadAsciiOctal(tar->fsize, 12), 512);
}
return NULL;
}

35
source/system/tar.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include "common.h"
#define USTAR_MAGIC "ustar"
// see: https://en.wikipedia.org/wiki/Tar_(computing)
// all numeric values in ASCII / octal
typedef struct {
char fname[100];
char fmode[8];
char owner_id[8];
char group_id[8];
char fsize[12];
char last_modified[12];
char checksum[8];
char ftype;
char link_name[100];
// ustar extension
char magic[6]; // "ustar"
char version[2]; // "00"
char owner_name[32];
char group_name[32];
char dev_major[8];
char dev_minor[8];
char fname_prefix[155];
char unused[12];
} __attribute__((packed)) TarHeader;
u32 ValidateTarHeader(void* tardata, void* tardata_end);
void* GetTarFileInfo(void* tardata, char* fname, u64* fsize, bool* is_dir);
void* NextTarEntry(void* tardata, void* tardata_end);
void* FindTarFileInfo(void* tardata, void* tardata_end, const char* fname, u64* fsize, bool* is_dir);

31
source/system/vram0.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include "common.h"
#include "tar.h"
#define VRAM0_OFFSET 0x18000000
#define VRAM0_LIMIT 0x00300000
#define TARDATA ((void*) VRAM0_OFFSET)
#define TARDATA_(off) ((void*) (u32) (VRAM0_OFFSET + (off)))
#define TARDATA_END TARDATA_(VRAM0_LIMIT)
#define CheckVram0Tar() \
(ValidateTarHeader(TARDATA, TARDATA_END) == 0)
#define FirstVTarEntry() \
TARDATA
#define OffsetVTarEntry(off) \
TARDATA_(off)
#define NextVTarEntry(tardata) \
NextTarEntry(tardata, TARDATA_END)
#define GetVTarFileInfo(tardata, fname, fsize, is_dir) \
GetTarFileInfo(tardata, fname, fsize, is_dir)
#define FindVTarFileInfo(fname, fsize, is_dir) \
FindTarFileInfo(TARDATA, TARDATA_END, fname, fsize, is_dir)

View File

@ -827,7 +827,7 @@ bool ValidateText(const char* text, u32 len) {
char c = text[i]; char c = text[i];
if ((c == '\r') && ((i+1) < len) && (text[i+1] != '\n')) return false; // CR without LF if ((c == '\r') && ((i+1) < len) && (text[i+1] != '\n')) return false; // CR without LF
if ((c < 0x20) && (c != '\t') && (c != '\r') && (c != '\n')) return false; // illegal control char if ((c < 0x20) && (c != '\t') && (c != '\r') && (c != '\n')) return false; // illegal control char
if ((c == 0x7F) || (c == 0xFF)) return false; // other illegal char if (c == 0xFF) return false; // other illegal char
} }
return true; return true;
} }

View File

@ -664,7 +664,7 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image
return type; return type;
} }
u32 CheckVGameDrive(void) { u64 CheckVGameDrive(void) {
if (vgame_type != GetMountState()) vgame_type = 0; // very basic sanity check if (vgame_type != GetMountState()) vgame_type = 0; // very basic sanity check
return vgame_type; return vgame_type;
} }
@ -799,7 +799,6 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) {
bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) { bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) {
vfile->name[0] = '\0'; vfile->name[0] = '\0';
BuildLv3Index(&lv3idx, romfslv3);
vfile->flags = VFLAG_LV3 | VFLAG_READONLY; vfile->flags = VFLAG_LV3 | VFLAG_READONLY;
vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ? vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ?
0x2C : 0xFF; // actual keyslot may be different 0x2C : 0xFF; // actual keyslot may be different

View File

@ -5,7 +5,7 @@
#include "virtual.h" #include "virtual.h"
u64 InitVGameDrive(void); u64 InitVGameDrive(void);
u32 CheckVGameDrive(void); u64 CheckVGameDrive(void);
bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry); bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry);
bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir); bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir);

View File

@ -5,6 +5,7 @@
#include "vtickdb.h" #include "vtickdb.h"
#include "vkeydb.h" #include "vkeydb.h"
#include "vcart.h" #include "vcart.h"
#include "vvram.h"
typedef struct { typedef struct {
char drv_letter; char drv_letter;
@ -31,6 +32,8 @@ bool CheckVirtualDrive(const char* path) {
u32 virtual_src = GetVirtualSource(path); u32 virtual_src = GetVirtualSource(path);
if (virtual_src & (VRT_EMUNAND|VRT_IMGNAND)) if (virtual_src & (VRT_EMUNAND|VRT_IMGNAND))
return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND return CheckVNandDrive(virtual_src); // check virtual NAND drive for EmuNAND / ImgNAND
else if (virtual_src & VRT_VRAM)
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_TICKDB)
@ -55,6 +58,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) {
ret = ReadVKeyDbDir(vfile, vdir); ret = ReadVKeyDbDir(vfile, vdir);
} else if (virtual_src & VRT_CART) { } else if (virtual_src & VRT_CART) {
ret = ReadVCartDir(vfile, vdir); ret = ReadVCartDir(vfile, vdir);
} else if (virtual_src & VRT_VRAM) {
ret = ReadVVramDir(vfile, vdir);
} }
vfile->flags |= virtual_src; // add source flag vfile->flags |= virtual_src; // add source flag
return ret; return ret;
@ -111,8 +116,9 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path) {
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 false;
if ((!(vfile->flags & VRT_GAME) && (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)))
break; // entry found break; // entry found
} }
} else { // use lv3 hashes for quicker search } else { // use lv3 hashes for quicker search
@ -132,8 +138,10 @@ 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) {
if (!(vfile->flags & VRT_GAME)) strncpy(name, vfile->name, n_chars); if (vfile->flags & VRT_GAME) return GetVGameFilename(name, vfile, n_chars);
else if (!GetVGameFilename(name, vfile, n_chars)) return false; else if (vfile->flags & VRT_VRAM) return GetVVramFilename(name, vfile);
strncpy(name, vfile->name, n_chars);
return true; return true;
} }
@ -157,6 +165,8 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun
return ReadVKeyDbFile(vfile, buffer, offset, count); return ReadVKeyDbFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_CART) { } else if (vfile->flags & VRT_CART) {
return ReadVCartFile(vfile, buffer, offset, count); return ReadVCartFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_VRAM) {
return ReadVVramFile(vfile, buffer, offset, count);
} }
return -1; return -1;
@ -176,7 +186,7 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u
return WriteVNandFile(vfile, buffer, offset, count); return WriteVNandFile(vfile, buffer, offset, count);
} else if (vfile->flags & VRT_MEMORY) { } else if (vfile->flags & VRT_MEMORY) {
return WriteVMemFile(vfile, buffer, offset, count); return WriteVMemFile(vfile, buffer, offset, count);
} // no write support for virtual game / tickdb / keydb / cart files } // no write support for virtual game / tickdb / keydb / cart / vram files
return -1; return -1;
} }
@ -208,5 +218,7 @@ u64 GetVirtualDriveSize(const char* path) {
return GetVKeyDbDriveSize(); return GetVKeyDbDriveSize();
else if (virtual_src & VRT_CART) else if (virtual_src & VRT_CART)
return GetVCartDriveSize(); return GetVCartDriveSize();
else if (virtual_src & VRT_VRAM)
return GetVVramDriveSize();
return 0; return 0;
} }

View File

@ -12,8 +12,9 @@
#define VRT_TICKDB (1UL<<6) #define VRT_TICKDB (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_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_TICKDB|VRT_KEYDB|VRT_CART) #define VRT_SOURCE (VRT_SYSNAND|VRT_EMUNAND|VRT_IMGNAND|VRT_XORPAD|VRT_MEMORY|VRT_GAME|VRT_TICKDB|VRT_KEYDB|VRT_CART|VRT_VRAM)
#define VFLAG_DIR (1UL<<10) #define VFLAG_DIR (1UL<<10)
#define VFLAG_ROOT (1UL<<11) #define VFLAG_ROOT (1UL<<11)
@ -23,7 +24,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} {'M', VRT_MEMORY}, {'G', VRT_GAME}, {'K', VRT_KEYDB}, {'T', VRT_TICKDB}, {'C', VRT_CART}, {'V', VRT_VRAM}
// 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

123
source/virtual/vvram.c Normal file
View File

@ -0,0 +1,123 @@
#include "vvram.h"
#include "vram0.h"
bool SplitTarFName(char* tar_fname, char** dir, char** name) {
u32 len = strnlen(tar_fname, 100 + 1);
if (!len || (len == 101)) return false;
// remove trailing slash
if (tar_fname[len-1] == '/') tar_fname[--len] = '\0';
// find last slash
char* slash = strrchr(tar_fname, '/');
// relative root dir entry
if (!slash) {
*name = tar_fname;
*dir = NULL;
} else {
*slash = '\0';
*name = slash + 1;
*dir = tar_fname;
}
return true;
}
bool CheckVVramDrive(void) {
return CheckVram0Tar();
}
bool ReadVVramDir(VirtualFile* vfile, VirtualDir* vdir) {
vfile->name[0] = '\0';
vfile->flags = VFLAG_READONLY;
vfile->keyslot = 0xFF;
// get current dir name
char curr_dir[100 + 1];
if (vdir->offset == (u64) -1) return false; // end of the dir?
else if (!vdir->offset) *curr_dir = '\0'; // relative root?
else {
// vdir->offset is offset of dir entry + 0x200
TarHeader* tar = (TarHeader*) OffsetVTarEntry(vdir->offset - 0x200);
strncpy(curr_dir, tar->fname, 100);
u32 len = strnlen(curr_dir, 100 + 1);
if (len == 101) return false; // path error
if (curr_dir[len-1] == '/') curr_dir[len-1] = '\0';
}
// using vdir index to signify the position limits us to 1TiB TARs
void* tardata = NULL;
if (vdir->index < 0) tardata = FirstVTarEntry();
else tardata = NextVTarEntry(OffsetVTarEntry(vdir->index << 9));
do {
TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1];
char *name, *dir;
strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name)) return false;
if ((!dir && !*curr_dir) || (dir && (strncmp(dir, curr_dir, 100) == 0))) break;
} while ((tardata = NextVTarEntry(tardata)));
// match found?
if (tardata) {
u64 fsize;
bool is_dir;
void* fdata = GetVTarFileInfo(tardata, NULL, &fsize, &is_dir);
vfile->offset = (u32) fdata - VRAM0_OFFSET;
vfile->size = fsize;
if (is_dir) vfile->flags |= VFLAG_DIR;
vdir->index = (vfile->offset >> 9) - 1;
} else { // not found
vdir->offset = (u64) -1;
return false;
}
return true;
}
int ReadVVramFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count) {
if (vfile->flags & VFLAG_DIR) return -1;
void* fdata = (u8*) VRAM0_OFFSET + vfile->offset;
// range checks in virtual.c
memcpy(buffer, (u8*) fdata + offset, count);
return 0;
}
bool GetVVramFilename(char* name, const VirtualFile* vfile) {
void* tardata = OffsetVTarEntry(vfile->offset - 0x200);
TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1];
char *name_tmp, *dir;
strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false;
strncpy(name, name_tmp, 100);
return true;
}
bool MatchVVramFilename(const char* name, const VirtualFile* vfile) {
void* tardata = OffsetVTarEntry(vfile->offset - 0x200);
TarHeader* tar = (TarHeader*) tardata;
char tar_fname[100 + 1];
char *name_tmp, *dir;
strncpy(tar_fname, tar->fname, 100);
if (!SplitTarFName(tar_fname, &dir, &name_tmp)) return false;
return (strncasecmp(name, name_tmp, 100) == 0);
}
u64 GetVVramDriveSize(void) {
return VRAM0_LIMIT;
}

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

@ -0,0 +1,12 @@
#include "common.h"
#include "virtual.h"
bool CheckVVramDrive(void);
bool ReadVVramDir(VirtualFile* vfile, VirtualDir* vdir);
int ReadVVramFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 count);
bool GetVVramFilename(char* name, const VirtualFile* vfile);
bool MatchVVramFilename(const char* name, const VirtualFile* vfile);
u64 GetVVramDriveSize(void);