From 2d36460a02015b1647acdebf5268f9d8e8c43e51 Mon Sep 17 00:00:00 2001 From: d0k3 Date: Mon, 30 Oct 2017 14:46:37 +0100 Subject: [PATCH] Virtual VRAM drive, based on TAR --- source/filesys/fsdrive.c | 2 + source/filesys/fsdrive.h | 14 +++-- source/game/3dsx.h | 2 +- source/godmode.c | 2 +- source/system/tar.c | 89 ++++++++++++++++++++++++++++ source/system/tar.h | 35 +++++++++++ source/system/vram0.h | 31 ++++++++++ source/utils/scripting.c | 2 +- source/virtual/vgame.c | 5 +- source/virtual/vgame.h | 2 +- source/virtual/virtual.c | 22 +++++-- source/virtual/virtual.h | 5 +- source/virtual/vvram.c | 123 +++++++++++++++++++++++++++++++++++++++ source/virtual/vvram.h | 12 ++++ 14 files changed, 326 insertions(+), 20 deletions(-) create mode 100644 source/system/tar.c create mode 100644 source/system/tar.h create mode 100644 source/system/vram0.h create mode 100644 source/virtual/vvram.c create mode 100644 source/virtual/vvram.h diff --git a/source/filesys/fsdrive.c b/source/filesys/fsdrive.c index 7d12095..02cad0e 100644 --- a/source/filesys/fsdrive.c +++ b/source/filesys/fsdrive.c @@ -55,6 +55,8 @@ int DriveType(const char* path) { type = DRV_VIRTUAL | DRV_GAME | DRV_IMAGE; } else if (vsrc == VRT_CART) { type = DRV_VIRTUAL | DRV_CART; + } else if (vsrc == VRT_VRAM) { + type = DRV_VIRTUAL | DRV_VRAM; } } diff --git a/source/filesys/fsdrive.h b/source/filesys/fsdrive.h index d0652d3..eb52d8d 100644 --- a/source/filesys/fsdrive.h +++ b/source/filesys/fsdrive.h @@ -5,7 +5,7 @@ #define NORM_FS 10 #define IMGN_FS 3 // image normal filesystems -#define VIRT_FS 12 +#define VIRT_FS 13 // primary drive types #define DRV_UNKNOWN (0<<0) @@ -23,10 +23,11 @@ #define DRV_MEMORY (1UL<<10) #define DRV_GAME (1UL<<11) #define DRV_CART (1UL<<12) -#define DRV_ALIAS (1UL<<13) -#define DRV_BONUS (1UL<<14) -#define DRV_SEARCH (1UL<<15) -#define DRV_STDFAT (1UL<<16) // standard FAT drive without limitations +#define DRV_VRAM (1UL<<13) +#define DRV_ALIAS (1UL<<14) +#define DRV_BONUS (1UL<<15) +#define DRV_SEARCH (1UL<<16) +#define DRV_STDFAT (1UL<<17) // standard FAT drive without limitations #define FS_DRVNAME \ "SDCARD", \ @@ -36,11 +37,12 @@ "GAMECART", \ "GAME IMAGE", "AESKEYDB IMAGE", "TICKET.DB IMAGE", \ "MEMORY VIRTUAL", \ + "VRAM 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:", "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 **/ int DriveType(const char* path); diff --git a/source/game/3dsx.h b/source/game/3dsx.h index f8b5f36..6e748d2 100644 --- a/source/game/3dsx.h +++ b/source/game/3dsx.h @@ -19,5 +19,5 @@ typedef struct { u32 size_bss; u32 offset_smdh; u32 size_smdh; - u32 size_romfs_lv3; + u32 offset_romfs_lv3; } __attribute__((packed)) ThreedsxHeader; diff --git a/source/godmode.c b/source/godmode.c index 5e42cfe..4c04bbe 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -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_SYSNAND) ? "SysNAND" : (drvtype & DRV_EMUNAND) ? "EmuNAND" : (drvtype & DRV_IMAGE) ? "Image" : (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" : "")); ResizeString(tempstr, drvstr, len_info / FONT_WIDTH_EXT, 8, false); } else { diff --git a/source/system/tar.c b/source/system/tar.c new file mode 100644 index 0000000..1b316a9 --- /dev/null +++ b/source/system/tar.c @@ -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; +} diff --git a/source/system/tar.h b/source/system/tar.h new file mode 100644 index 0000000..740801c --- /dev/null +++ b/source/system/tar.h @@ -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); diff --git a/source/system/vram0.h b/source/system/vram0.h new file mode 100644 index 0000000..b94a27a --- /dev/null +++ b/source/system/vram0.h @@ -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) diff --git a/source/utils/scripting.c b/source/utils/scripting.c index 34e7250..947de19 100644 --- a/source/utils/scripting.c +++ b/source/utils/scripting.c @@ -827,7 +827,7 @@ bool ValidateText(const char* text, u32 len) { char c = text[i]; 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 == 0x7F) || (c == 0xFF)) return false; // other illegal char + if (c == 0xFF) return false; // other illegal char } return true; } diff --git a/source/virtual/vgame.c b/source/virtual/vgame.c index 8010bfc..4a7e60e 100644 --- a/source/virtual/vgame.c +++ b/source/virtual/vgame.c @@ -664,7 +664,7 @@ u64 InitVGameDrive(void) { // prerequisite: game file mounted as image return type; } -u32 CheckVGameDrive(void) { +u64 CheckVGameDrive(void) { if (vgame_type != GetMountState()) vgame_type = 0; // very basic sanity check return vgame_type; } @@ -799,7 +799,6 @@ bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry) { bool ReadVGameDirLv3(VirtualFile* vfile, VirtualDir* vdir) { vfile->name[0] = '\0'; - BuildLv3Index(&lv3idx, romfslv3); vfile->flags = VFLAG_LV3 | VFLAG_READONLY; vfile->keyslot = ((offset_ncch != (u64) -1) && NCCH_ENCRYPTED(ncch)) ? 0x2C : 0xFF; // actual keyslot may be different @@ -1038,7 +1037,7 @@ bool GetVGameFilename(char* name, const VirtualFile* vfile, u32 n_chars) { bool MatchVGameFilename(const char* name, const VirtualFile* vfile, u32 n_chars) { if (vfile->flags & VFLAG_LV3) { - char lv3_name[256]; + char lv3_name[256]; if (!GetVGameLv3Filename(lv3_name, vfile, 256)) return false; return (strncasecmp(name, lv3_name, n_chars) == 0); } else if (vfile->flags & VFLAG_NITRO) { diff --git a/source/virtual/vgame.h b/source/virtual/vgame.h index 63362fe..c6fb5b8 100644 --- a/source/virtual/vgame.h +++ b/source/virtual/vgame.h @@ -5,7 +5,7 @@ #include "virtual.h" u64 InitVGameDrive(void); -u32 CheckVGameDrive(void); +u64 CheckVGameDrive(void); bool OpenVGameDir(VirtualDir* vdir, VirtualFile* ventry); bool ReadVGameDir(VirtualFile* vfile, VirtualDir* vdir); diff --git a/source/virtual/virtual.c b/source/virtual/virtual.c index 48f3c53..16ccdd4 100644 --- a/source/virtual/virtual.c +++ b/source/virtual/virtual.c @@ -5,6 +5,7 @@ #include "vtickdb.h" #include "vkeydb.h" #include "vcart.h" +#include "vvram.h" typedef struct { char drv_letter; @@ -31,6 +32,8 @@ bool CheckVirtualDrive(const char* path) { u32 virtual_src = GetVirtualSource(path); if (virtual_src & (VRT_EMUNAND|VRT_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) return CheckVGameDrive(); else if (virtual_src & VRT_TICKDB) @@ -55,6 +58,8 @@ bool ReadVirtualDir(VirtualFile* vfile, VirtualDir* vdir) { ret = ReadVKeyDbDir(vfile, vdir); } else if (virtual_src & VRT_CART) { ret = ReadVCartDir(vfile, vdir); + } else if (virtual_src & VRT_VRAM) { + ret = ReadVVramDir(vfile, vdir); } vfile->flags |= virtual_src; // add source flag return ret; @@ -111,8 +116,9 @@ bool GetVirtualFile(VirtualFile* vfile, const char* path) { if (!(vdir.flags & VFLAG_LV3)) { // standard method while (true) { if (!ReadVirtualDir(vfile, &vdir)) return false; - if ((!(vfile->flags & VRT_GAME) && (strncasecmp(name, vfile->name, 32) == 0)) || - ((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256))) + if ((!(vfile->flags & (VRT_GAME|VRT_VRAM)) && (strncasecmp(name, vfile->name, 32) == 0)) || + ((vfile->flags & VRT_GAME) && MatchVGameFilename(name, vfile, 256)) || + ((vfile->flags & VRT_VRAM) && MatchVVramFilename(name, vfile))) break; // entry found } } 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) { - if (!(vfile->flags & VRT_GAME)) strncpy(name, vfile->name, n_chars); - else if (!GetVGameFilename(name, vfile, n_chars)) return false; + if (vfile->flags & VRT_GAME) return GetVGameFilename(name, vfile, n_chars); + else if (vfile->flags & VRT_VRAM) return GetVVramFilename(name, vfile); + + strncpy(name, vfile->name, n_chars); return true; } @@ -157,6 +165,8 @@ int ReadVirtualFile(const VirtualFile* vfile, void* buffer, u64 offset, u64 coun return ReadVKeyDbFile(vfile, buffer, offset, count); } else if (vfile->flags & VRT_CART) { return ReadVCartFile(vfile, buffer, offset, count); + } else if (vfile->flags & VRT_VRAM) { + return ReadVVramFile(vfile, buffer, offset, count); } return -1; @@ -176,7 +186,7 @@ int WriteVirtualFile(const VirtualFile* vfile, const void* buffer, u64 offset, u return WriteVNandFile(vfile, buffer, offset, count); } else if (vfile->flags & VRT_MEMORY) { 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; } @@ -208,5 +218,7 @@ u64 GetVirtualDriveSize(const char* path) { return GetVKeyDbDriveSize(); else if (virtual_src & VRT_CART) return GetVCartDriveSize(); + else if (virtual_src & VRT_VRAM) + return GetVVramDriveSize(); return 0; } diff --git a/source/virtual/virtual.h b/source/virtual/virtual.h index 5a958e4..5bc826e 100644 --- a/source/virtual/virtual.h +++ b/source/virtual/virtual.h @@ -12,8 +12,9 @@ #define VRT_TICKDB (1UL<<6) #define VRT_KEYDB (1UL<<7) #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_ROOT (1UL<<11) @@ -23,7 +24,7 @@ #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): // bits 0...3 : reserved for NAND virtual sources and info diff --git a/source/virtual/vvram.c b/source/virtual/vvram.c new file mode 100644 index 0000000..28d31d6 --- /dev/null +++ b/source/virtual/vvram.c @@ -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; +} diff --git a/source/virtual/vvram.h b/source/virtual/vvram.h new file mode 100644 index 0000000..6fb7c00 --- /dev/null +++ b/source/virtual/vvram.h @@ -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);