mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-25 21:22:47 +00:00
801 lines
23 KiB
C
801 lines
23 KiB
C
#ifndef NO_LUA
|
|
#include "gm9internalfs.h"
|
|
#include "fs.h"
|
|
#include "ui.h"
|
|
#include "utils.h"
|
|
#include "sha.h"
|
|
#include "nand.h"
|
|
#include "language.h"
|
|
#include "hid.h"
|
|
#include "game.h"
|
|
#include "gamecart.h"
|
|
|
|
#define _MAX_FOR_DEPTH 16
|
|
|
|
static u8 no_data_hash_256[32] = { SHA256_EMPTY_HASH };
|
|
static u8 no_data_hash_1[32] = { SHA1_EMPTY_HASH };
|
|
|
|
static bool PathIsDirectory(const char* path) {
|
|
FRESULT res;
|
|
FILINFO fno;
|
|
res = fvx_stat(path, &fno);
|
|
if (res != FR_OK) return false;
|
|
return fno.fattrib & AM_DIR;
|
|
}
|
|
|
|
static void CreateStatTable(lua_State* L, FILINFO* fno) {
|
|
lua_createtable(L, 0, 4); // create nested table
|
|
lua_pushstring(L, fno->fname);
|
|
lua_setfield(L, -2, "name");
|
|
lua_pushstring(L, (fno->fattrib & AM_DIR) ? "dir" : "file");
|
|
lua_setfield(L, -2, "type");
|
|
lua_pushinteger(L, fno->fsize);
|
|
lua_setfield(L, -2, "size");
|
|
lua_pushboolean(L, fno->fattrib & AM_RDO);
|
|
lua_setfield(L, -2, "read_only");
|
|
// ... and leave this table on the stack for the caller to deal with
|
|
}
|
|
|
|
static int internalfs_move(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.move");
|
|
const char* path_src = luaL_checkstring(L, 1);
|
|
const char* path_dst = luaL_checkstring(L, 2);
|
|
FILINFO fno;
|
|
|
|
CheckWritePermissionsLuaError(L, path_src);
|
|
CheckWritePermissionsLuaError(L, path_dst);
|
|
|
|
u32 flags = BUILD_PATH;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, NO_CANCEL | SILENT | OVERWRITE_ALL | SKIP_ALL);
|
|
}
|
|
|
|
if (!(flags & OVERWRITE_ALL) && (fvx_stat(path_dst, &fno) == FR_OK)) {
|
|
return luaL_error(L, "destination already exists on %s -> %s and {overwrite=true} was not used", path_src, path_dst);
|
|
}
|
|
|
|
if (!(PathMoveCopy(path_dst, path_src, &flags, true))) {
|
|
return luaL_error(L, "PathMoveCopy failed on %s -> %s", path_src, path_dst);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_remove(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 1, "_fs.remove");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 2, flags, RECURSIVE);
|
|
}
|
|
|
|
if (!(flags & RECURSIVE)) {
|
|
if (PathIsDirectory(path)) {
|
|
return luaL_error(L, "requested directory remove without {recursive=true} on %s", path);
|
|
}
|
|
}
|
|
|
|
if (!(PathDelete(path))) {
|
|
return luaL_error(L, "PathDelete failed on %s", path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_copy(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.copy");
|
|
const char* path_src = luaL_checkstring(L, 1);
|
|
const char* path_dst = luaL_checkstring(L, 2);
|
|
FILINFO fno;
|
|
|
|
CheckWritePermissionsLuaError(L, path_dst);
|
|
|
|
u32 flags = BUILD_PATH;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, CALC_SHA | USE_SHA1 | NO_CANCEL | SILENT | OVERWRITE_ALL | SKIP_ALL | APPEND_ALL | RECURSIVE);
|
|
}
|
|
|
|
if (!(flags & RECURSIVE)) {
|
|
if (PathIsDirectory(path_src)) {
|
|
return luaL_error(L, "requested directory copy without {recursive=true} on %s -> %s", path_src, path_dst);
|
|
}
|
|
}
|
|
|
|
if (!(flags & OVERWRITE_ALL) && (fvx_stat(path_dst, &fno) == FR_OK)) {
|
|
return luaL_error(L, "destination already exists on %s -> %s and {overwrite=true} was not used", path_src, path_dst);
|
|
}
|
|
|
|
if (!(PathMoveCopy(path_dst, path_src, &flags, false))) {
|
|
return luaL_error(L, "PathMoveCopy failed on %s -> %s", path_src, path_dst);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_mkdir(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.mkdir");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
FRESULT res = fvx_rmkdir(path);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "could not mkdir %s (%d)", path, res);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_list_dir(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.list_dir");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_newtable(L);
|
|
|
|
DIR dir;
|
|
FILINFO fno;
|
|
|
|
FRESULT res = fvx_opendir(&dir, path);
|
|
if (res != FR_OK) {
|
|
lua_pop(L, 1); // remove final table from stack
|
|
return luaL_error(L, "could not opendir %s (%d)", path, res);
|
|
}
|
|
|
|
for (int i = 1; true; i++) {
|
|
res = fvx_readdir(&dir, &fno);
|
|
if (res != FR_OK) {
|
|
fvx_closedir(&dir);
|
|
lua_pop(L, 1); // remove final table from stack
|
|
return luaL_error(L, "could not readdir %s (%d)", path, res);
|
|
}
|
|
if (fno.fname[0] == 0) break;
|
|
CreateStatTable(L, &fno);
|
|
lua_seti(L, -2, i); // add nested table to final table
|
|
}
|
|
fvx_closedir(&dir);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_stat(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.stat");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
FILINFO fno;
|
|
|
|
FRESULT res = fvx_stat(path, &fno);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "could not stat %s (%d)", path, res);
|
|
}
|
|
CreateStatTable(L, &fno);
|
|
return 1;
|
|
}
|
|
|
|
// TODO: make this manually check for permissions recursively
|
|
static int internalfs_fix_cmacs(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.fix_cmacs");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
ShowString("%s", STR_FIXING_CMACS_PLEASE_WAIT);
|
|
if (RecursiveFixFileCmac(path) != 0) {
|
|
return luaL_error(L, "fixcmac failed");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_stat_fs(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.stat_fs");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
u64 freespace = GetFreeSpace(path);
|
|
u64 totalspace = GetTotalSpace(path);
|
|
u64 usedspace = totalspace - freespace;
|
|
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushinteger(L, freespace);
|
|
lua_setfield(L, -2, "free");
|
|
lua_pushinteger(L, totalspace);
|
|
lua_setfield(L, -2, "total");
|
|
lua_pushinteger(L, usedspace);
|
|
lua_setfield(L, -2, "used");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_dir_info(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.dir_info");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
u64 tsize = 0;
|
|
u32 tdirs = 0;
|
|
u32 tfiles = 0;
|
|
if (!DirInfo(path, &tsize, &tdirs, &tfiles)) {
|
|
return luaL_error(L, "error when running DirInfo");
|
|
}
|
|
|
|
lua_createtable(L, 0, 3);
|
|
lua_pushinteger(L, tsize);
|
|
lua_setfield(L, -2, "size");
|
|
lua_pushinteger(L, tdirs);
|
|
lua_setfield(L, -2, "dirs");
|
|
lua_pushinteger(L, tfiles);
|
|
lua_setfield(L, -2, "files");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int FileDirSelector(lua_State* L, const char* path_orig, const char* prompt, bool is_dir, bool include_dirs, bool explorer) {
|
|
bool ret;
|
|
char path[_VAR_CNT_LEN] = { 0 };
|
|
char choice[_VAR_CNT_LEN] = { 0 };
|
|
strncpy(path, path_orig, _VAR_CNT_LEN);
|
|
if (strncmp(path, "Z:", 2) == 0) {
|
|
return luaL_error(L, "forbidden drive");
|
|
} else if (!is_dir) {
|
|
u32 flags_ext = include_dirs ? 0 : NO_DIRS;
|
|
char *npattern = strrchr(path, '/');
|
|
if (!npattern) {
|
|
return luaL_error(L, "invalid path");
|
|
}
|
|
*(npattern++) = '\0';
|
|
ret = FileSelector(choice, prompt, path, npattern, flags_ext, explorer);
|
|
} else {
|
|
ret = FileSelector(choice, prompt, path, NULL, NO_FILES | SELECT_DIRS, explorer);
|
|
}
|
|
|
|
if (ret) {
|
|
lua_pushstring(L, choice);
|
|
} else {
|
|
lua_pushnil(L);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_ask_select_file(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.ask_select_file");
|
|
const char* prompt = luaL_checkstring(L, 1);
|
|
const char* path = luaL_checkstring(L, 2);
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, INCLUDE_DIRS | EXPLORER);
|
|
};
|
|
|
|
return FileDirSelector(L, path, prompt, false, (flags & INCLUDE_DIRS), (flags & EXPLORER));
|
|
}
|
|
|
|
static int internalfs_ask_select_dir(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.ask_select_dir");
|
|
const char* prompt = luaL_checkstring(L, 1);
|
|
const char* path = luaL_checkstring(L, 2);
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, EXPLORER);
|
|
};
|
|
|
|
return FileDirSelector(L, path, prompt, true, true, (flags & EXPLORER));
|
|
}
|
|
|
|
static int internalfs_find(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 1, "_fs.find");
|
|
const char* pattern = luaL_checkstring(L, 1);
|
|
char path[_VAR_CNT_LEN] = { 0 };
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 2, flags, FIND_FIRST);
|
|
}
|
|
|
|
u8 mode = (flags & FIND_FIRST) ? FN_LOWEST : FN_HIGHEST;
|
|
FRESULT res = fvx_findpath(path, pattern, mode);
|
|
if (res == FR_NO_PATH) {
|
|
lua_pushnil(L);
|
|
} else if (res != FR_OK) {
|
|
return luaL_error(L, "failed to find %s (%d)", pattern, res);
|
|
} else {
|
|
lua_pushstring(L, path);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// this should probably be rewritten
|
|
static int internalfs_find_all(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.find_all");
|
|
const char* dir = luaL_checkstring(L, 1);
|
|
const char* pattern = luaL_checkstring(L, 2);
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, RECURSIVE);
|
|
}
|
|
|
|
char forpath[_VAR_CNT_LEN] = { 0 };
|
|
|
|
// without re-implementing for_handler, i need to give it a "*" pattern
|
|
// and then manually compare each filename to see if it matches
|
|
// so that a recursive search actually works
|
|
bool forstatus = for_handler(NULL, dir, "*", flags & RECURSIVE);
|
|
if (!forstatus) {
|
|
return luaL_error(L, "could not open directory");
|
|
}
|
|
|
|
lua_newtable(L);
|
|
int i = 1;
|
|
char* slash;
|
|
|
|
while (true) {
|
|
forstatus = for_handler(forpath, NULL, NULL, false);
|
|
if (!forstatus) {
|
|
forpath[0] = '\0';
|
|
}
|
|
if (!forpath[0]) {
|
|
// finish for_handler
|
|
for_handler(NULL, NULL, NULL, false);
|
|
break;
|
|
} else {
|
|
slash = strrchr(forpath, '/');
|
|
if (!slash) bkpt; // this should never, ever happen
|
|
if (fvx_match_name(slash+1, pattern) == FR_OK) {
|
|
lua_pushstring(L, forpath);
|
|
lua_seti(L, -2, i++);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_find_not(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.find_not");
|
|
const char* pattern = luaL_checkstring(L, 1);
|
|
char path[_VAR_CNT_LEN] = { 0 };
|
|
|
|
FRESULT res = fvx_findnopath(path, pattern);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "failed to find %s (%d)", pattern, res);
|
|
}
|
|
|
|
lua_pushstring(L, path);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_exists(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.exists");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
FILINFO fno;
|
|
|
|
lua_pushboolean(L, (fvx_stat(path, &fno) == FR_OK));
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_is_dir(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.is_dir");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
lua_pushboolean(L, PathIsDirectory(path));
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_is_file(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.is_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
FILINFO fno;
|
|
|
|
FRESULT res = fvx_stat(path, &fno);
|
|
if (res != FR_OK) {
|
|
lua_pushboolean(L, false);
|
|
} else {
|
|
lua_pushboolean(L, !(fno.fattrib & AM_DIR));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_read_file(lua_State* L) {
|
|
CheckLuaArgCount(L, 3, "_fs.read_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer offset = luaL_checkinteger(L, 2);
|
|
lua_Integer size = luaL_checkinteger(L, 3);
|
|
|
|
char *buf = malloc(size);
|
|
if (!buf) {
|
|
return luaL_error(L, "could not allocate memory to read file");
|
|
}
|
|
UINT bytes_read = 0;
|
|
FRESULT res = fvx_qread(path, buf, offset, size, &bytes_read);
|
|
if (res != FR_OK) {
|
|
free(buf);
|
|
return luaL_error(L, "could not read %s (%d)", path, res);
|
|
}
|
|
lua_pushlstring(L, buf, bytes_read);
|
|
free(buf);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_write_file(lua_State* L) {
|
|
CheckLuaArgCount(L, 3, "_fs.write_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer offset = luaL_checkinteger(L, 2);
|
|
size_t data_length = 0;
|
|
const char* data = luaL_checklstring(L, 3, &data_length);
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
UINT bytes_written = 0;
|
|
FRESULT res = fvx_qwrite(path, data, offset, data_length, &bytes_written);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "error writing %s (%d)", path, res);
|
|
}
|
|
|
|
lua_pushinteger(L, bytes_written);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_fill_file(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 4, "_fs.fill_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer offset = luaL_checkinteger(L, 2);
|
|
lua_Integer size = luaL_checkinteger(L, 3);
|
|
lua_Integer byte = luaL_checkinteger(L, 4);
|
|
|
|
u8 real_byte = byte & 0xFF;
|
|
|
|
u32 flags = ALLOW_EXPAND;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 4, flags, NO_CANCEL);
|
|
};
|
|
|
|
if ((byte < 0) || (byte > 0xFF)) {
|
|
return luaL_error(L, "byte is not between 0x00 and 0xFF (got: %I)", byte);
|
|
}
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
if (!(FileSetByte(path, offset, size, real_byte, &flags))) {
|
|
return luaL_error(L, "FileSetByte failed on %s", path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_make_dummy_file(lua_State* L) {
|
|
CheckLuaArgCount(L, 2, "_fs.make_dummy_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer size = luaL_checkinteger(L, 2);
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
if (!(FileCreateDummy(path, NULL, size))) {
|
|
return luaL_error(L, "FileCreateDummy failed on %s", path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_truncate(lua_State* L) {
|
|
CheckLuaArgCount(L, 2, "_fs.truncate");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer size = luaL_checkinteger(L, 2);
|
|
FIL fp;
|
|
FRESULT res;
|
|
|
|
CheckWritePermissionsLuaError(L, path);
|
|
|
|
res = f_open(&fp, path, FA_READ | FA_WRITE);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "failed to open %s (note: this only works on FAT filesystems, not virtual)", path);
|
|
}
|
|
|
|
res = f_lseek(&fp, size);
|
|
if (res != FR_OK) {
|
|
f_close(&fp);
|
|
return luaL_error(L, "failed to seek on %s", path);
|
|
}
|
|
|
|
res = f_truncate(&fp);
|
|
if (res != FR_OK) {
|
|
f_close(&fp);
|
|
return luaL_error(L, "failed to truncate %s", path);
|
|
}
|
|
|
|
f_close(&fp);
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_img_mount(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.img_mount");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
|
|
bool res = InitImgFS(path);
|
|
if (!res) {
|
|
return luaL_error(L, "failed to mount %s", path);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_img_umount(lua_State* L) {
|
|
CheckLuaArgCount(L, 0, "_fs.img_umount");
|
|
|
|
InitImgFS(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_get_img_mount(lua_State* L) {
|
|
CheckLuaArgCount(L, 0, "_fs.get_img_mount");
|
|
|
|
char path[256] = { 0 };
|
|
strncpy(path, GetMountPath(), 256);
|
|
if (path[0] == 0) {
|
|
// since lua treats "" as true, return a nil to make if/else easier
|
|
lua_pushnil(L);
|
|
} else {
|
|
lua_pushstring(L, path);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
// TODO: what if someone does offset != 0 but size = 0 (end of file)?
|
|
static int internalfs_hash_file(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 3, "_fs.hash_file");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
lua_Integer offset = luaL_checkinteger(L, 2);
|
|
lua_Integer size = luaL_checkinteger(L, 3);
|
|
FRESULT res;
|
|
FILINFO fno;
|
|
|
|
if (size == 0) {
|
|
res = fvx_stat(path, &fno);
|
|
if (res != FR_OK) {
|
|
return luaL_error(L, "failed to stat %s", path);
|
|
}
|
|
|
|
size = fno.fsize;
|
|
}
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 4, flags, USE_SHA1);
|
|
}
|
|
|
|
const u8 hashlen = (flags & USE_SHA1) ? 20 : 32;
|
|
u8 hash_fil[0x20];
|
|
|
|
if (size == 0) {
|
|
// shortcut by just returning the hash of empty data
|
|
memcpy(hash_fil, (flags & USE_SHA1) ? no_data_hash_1 : no_data_hash_256, hashlen);
|
|
} else if (!(FileGetSha(path, hash_fil, offset, size, (flags & USE_SHA1)))) {
|
|
return luaL_error(L, "FileGetSha failed on %s", path);
|
|
}
|
|
|
|
lua_pushlstring(L, (char*)hash_fil, hashlen);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_hash_data(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 1, "_fs.hash_data");
|
|
size_t data_length = 0;
|
|
const char* data = luaL_checklstring(L, 1, &data_length);
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 2, flags, USE_SHA1);
|
|
}
|
|
|
|
const u8 hashlen = (flags & USE_SHA1) ? 20 : 32;
|
|
u8 hash_fil[0x20];
|
|
|
|
if (data_length == 0) {
|
|
// shortcut by just returning the hash of empty data
|
|
memcpy(hash_fil, (flags & USE_SHA1) ? no_data_hash_1 : no_data_hash_256, hashlen);
|
|
} else {
|
|
sha_quick(hash_fil, data, data_length, (flags & USE_SHA1) ? SHA1_MODE : SHA256_MODE);
|
|
}
|
|
|
|
lua_pushlstring(L, (char*)hash_fil, hashlen);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_allow(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 1, "_fs.allow");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
u32 flags = 0;
|
|
bool allowed;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 2, 0, ASK_ALL);
|
|
}
|
|
|
|
if (flags & ASK_ALL) {
|
|
allowed = CheckDirWritePermissions(path);
|
|
} else {
|
|
allowed = CheckWritePermissions(path);
|
|
}
|
|
lua_pushboolean(L, allowed);
|
|
return 1;
|
|
};
|
|
|
|
static int internalfs_verify(lua_State* L) {
|
|
CheckLuaArgCount(L, 1, "_fs.verify");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
bool res;
|
|
|
|
u64 filetype = IdentifyFileType(path);
|
|
if (filetype & IMG_NAND) res = (ValidateNandDump(path) == 0);
|
|
else res = (VerifyGameFile(path) == 0);
|
|
|
|
lua_pushboolean(L, res);
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_sd_is_mounted(lua_State* L) {
|
|
CheckLuaArgCount(L, 0, "_fs.sd_is_mounted");
|
|
|
|
lua_pushboolean(L, CheckSDMountState());
|
|
return 1;
|
|
}
|
|
|
|
static int internalfs_sd_switch(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 0, "_fs.sd_switch");
|
|
const char* message;
|
|
|
|
if (extra) {
|
|
message = luaL_checkstring(L, 1);
|
|
} else {
|
|
message = "Please switch the SD card now.";
|
|
}
|
|
|
|
bool ret;
|
|
|
|
DeinitExtFS();
|
|
if (!(ret = CheckSDMountState())) {
|
|
return luaL_error(L, "%s", STR_SCRIPTERR_SD_NOT_MOUNTED);
|
|
}
|
|
|
|
u32 pad_state;
|
|
DeinitSDCardFS();
|
|
ShowString("%s\n \n%s", message, STR_EJECT_SD_CARD);
|
|
while (!((pad_state = InputWait(0)) & (BUTTON_B|SD_EJECT)));
|
|
if (pad_state & SD_EJECT) {
|
|
ShowString("%s\n \n%s", message, STR_INSERT_SD_CARD);
|
|
while (!((pad_state = InputWait(0)) & (BUTTON_B|SD_INSERT)));
|
|
}
|
|
if (pad_state & BUTTON_B) {
|
|
return luaL_error(L, "user canceled");
|
|
}
|
|
|
|
InitSDCardFS();
|
|
AutoEmuNandBase(true);
|
|
InitExtFS();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_key_dump(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 1, "_fs.key_dump");
|
|
const char* opts[] = {SEEDINFO_NAME, TIKDB_NAME_ENC, TIKDB_NAME_DEC, NULL};
|
|
int opt = luaL_checkoption(L, 1, NULL, opts);
|
|
bool ret = false;
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 2, 0, OVERWRITE_ALL);
|
|
}
|
|
|
|
if (opt == 1 || opt == 2) {
|
|
bool tik_dec = opt == 2;
|
|
if (flags & OVERWRITE_ALL) fvx_unlink(tik_dec ? OUTPUT_PATH "/" TIKDB_NAME_DEC : OUTPUT_PATH "/" TIKDB_NAME_ENC);
|
|
if (BuildTitleKeyInfo(NULL, tik_dec, false) == 0) {
|
|
ShowString(STR_BUILDING_TO_OUT_ARG, OUTPUT_PATH, opts[opt]);
|
|
if (((BuildTitleKeyInfo("1:/dbs/ticket.db", tik_dec, false) == 0) ||
|
|
(BuildTitleKeyInfo("4:/dbs/ticket.db", tik_dec, false) == 0)) &&
|
|
(BuildTitleKeyInfo(NULL, tik_dec, true) == 0))
|
|
ret = true;
|
|
}
|
|
} else if (opt == 0) {
|
|
if (flags & OVERWRITE_ALL) fvx_unlink(OUTPUT_PATH "/" SEEDINFO_NAME);
|
|
if (BuildSeedInfo(NULL, false) == 0) {
|
|
ShowString(STR_BUILDING_TO_OUT_ARG, OUTPUT_PATH, opts[opt]);
|
|
if (((BuildSeedInfo("1:", false) == 0) ||
|
|
(BuildSeedInfo("4:", false) == 0)) &&
|
|
(BuildSeedInfo(NULL, true) == 0))
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
if (!ret) {
|
|
return luaL_error(L, "building %s failed", opts[opt]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int internalfs_cart_dump(lua_State* L) {
|
|
bool extra = CheckLuaArgCountPlusExtra(L, 2, "_fs.cart_dump");
|
|
const char* path = luaL_checkstring(L, 1);
|
|
u64 fsize = (u64)luaL_checkinteger(L, 2);
|
|
bool ret = false;
|
|
const char* errstr = "";
|
|
|
|
u32 flags = 0;
|
|
if (extra) {
|
|
flags = GetFlagsFromTable(L, 3, flags, ENCRYPTED);
|
|
}
|
|
|
|
CartData* cdata = (CartData*) malloc(sizeof(CartData));
|
|
u8* buf = (u8*) malloc(STD_BUFFER_SIZE);
|
|
ret = false;
|
|
if (!cdata || !buf) {
|
|
errstr = "out of memory";
|
|
} else if (InitCartRead(cdata) != 0){
|
|
errstr = "cart init fail";
|
|
} else {
|
|
SetSecureAreaEncryption(flags & ENCRYPTED);
|
|
fvx_unlink(path);
|
|
ret = true;
|
|
errstr = "cart dump failed or canceled";
|
|
for (u64 p = 0; p < fsize; p += STD_BUFFER_SIZE) {
|
|
u64 len = min((fsize - p), STD_BUFFER_SIZE);
|
|
ShowProgress(p, fsize, path);
|
|
if (!ShowProgress(p, fsize, path) ||
|
|
(ReadCartBytes(buf, p, len, cdata, false) != 0) ||
|
|
(fvx_qwrite(path, buf, p, len, NULL) != FR_OK)) {
|
|
ret = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
free(buf);
|
|
free(cdata);
|
|
|
|
if (!ret) {
|
|
return luaL_error(L, "%s", errstr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const luaL_Reg internalfs_lib[] = {
|
|
{"move", internalfs_move},
|
|
{"remove", internalfs_remove},
|
|
{"copy", internalfs_copy},
|
|
{"mkdir", internalfs_mkdir},
|
|
{"list_dir", internalfs_list_dir},
|
|
{"stat", internalfs_stat},
|
|
{"stat_fs", internalfs_stat_fs},
|
|
{"dir_info", internalfs_dir_info},
|
|
{"ask_select_file", internalfs_ask_select_file},
|
|
{"ask_select_dir", internalfs_ask_select_dir},
|
|
{"find", internalfs_find},
|
|
{"find_all", internalfs_find_all},
|
|
{"find_not", internalfs_find_not},
|
|
{"exists", internalfs_exists},
|
|
{"is_dir", internalfs_is_dir},
|
|
{"is_file", internalfs_is_file},
|
|
{"read_file", internalfs_read_file},
|
|
{"write_file", internalfs_write_file},
|
|
{"fill_file", internalfs_fill_file},
|
|
{"make_dummy_file", internalfs_make_dummy_file},
|
|
{"truncate", internalfs_truncate},
|
|
{"img_mount", internalfs_img_mount},
|
|
{"img_umount", internalfs_img_umount},
|
|
{"get_img_mount", internalfs_get_img_mount},
|
|
{"hash_file", internalfs_hash_file},
|
|
{"hash_data", internalfs_hash_data},
|
|
{"verify", internalfs_verify},
|
|
{"allow", internalfs_allow},
|
|
{"sd_is_mounted", internalfs_sd_is_mounted},
|
|
{"sd_switch", internalfs_sd_switch},
|
|
{"fix_cmacs", internalfs_fix_cmacs},
|
|
{"key_dump", internalfs_key_dump},
|
|
{"cart_dump", internalfs_cart_dump},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
int gm9lua_open_internalfs(lua_State* L) {
|
|
luaL_newlib(L, internalfs_lib);
|
|
return 1;
|
|
}
|
|
#endif
|