diff --git a/autogen/common.py b/autogen/common.py index 6b1886a7d..2a76f2dd2 100644 --- a/autogen/common.py +++ b/autogen/common.py @@ -148,6 +148,9 @@ def translate_type_to_lot(ptype, allowArrays=True): if ptype == 'const char*': return 'LOT_NONE' + if ptype == 'ByteString': + return 'LOT_NONE' + if 'unsigned' not in ptype and (ptype == 'char*' or ('char' in ptype and '[' in ptype)): return 'LOT_NONE' @@ -215,6 +218,9 @@ def translate_type_to_lua(ptype): if ptype == 'const char*': return '`string`', None + if ptype == 'ByteString': + return '`string`', None + if 'unsigned' not in ptype and (ptype == 'char*' or ('char' in ptype and '[' in ptype)): return '`string`', None @@ -253,9 +259,15 @@ def translate_type_to_lua(ptype): if ptype == 'int': return '`integer`', None + if ptype == 'lua_Integer': + return '`integer`', None + if ptype == 'float': return '`number`', None + if ptype == 'lua_Number': + return '`number`', None + if ptype == 'double': return '`number`', None diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index dcf8e6048..9b7d695f7 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -46,6 +46,7 @@ in_files = [ "src/audio/external.h", "src/game/envfx_snow.h", "src/pc/mods/mod_storage.h", + "src/pc/mods/mod_fs.h", "src/game/first_person_cam.h", "src/pc/djui/djui_console.h", "src/game/player_palette.h", @@ -65,7 +66,8 @@ exclude_constants = { "src/game/obj_behaviors.c": [ "^o$" ], "src/pc/djui/djui_console.h": [ "CONSOLE_MAX_TMP_BUFFER" ], "src/pc/lua/smlua_hooks.h": [ "MAX_HOOKED_MOD_MENU_ELEMENTS", "^HOOK_RETURN_.*", "^ACTION_HOOK_.*", "^MOD_MENU_ELEMENT_.*" ], - "src/pc/djui/djui_panel_menu.h": [ "RAINBOW_TEXT_LEN" ] + "src/pc/djui/djui_panel_menu.h": [ "RAINBOW_TEXT_LEN" ], + "src/pc/mods/mod_fs.h": [ "MOD_FS_DIRECTORY", "MOD_FS_EXTENSION", "MOD_FS_VERSION", "INT_TYPE_MAX", "FLOAT_TYPE_MAX", "FILE_SEEK_MAX" ] } include_constants = { diff --git a/autogen/convert_functions.py b/autogen/convert_functions.py index d1f698fde..da8676e42 100644 --- a/autogen/convert_functions.py +++ b/autogen/convert_functions.py @@ -7,8 +7,8 @@ from vec_types import * verbose = len(sys.argv) > 1 and (sys.argv[1] == "-v" or sys.argv[1] == "--verbose") rejects = "" -integer_types = ["u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64", "int"] -number_types = ["f32", "float", "f64", "double"] +integer_types = ["u8", "u16", "u32", "u64", "s8", "s16", "s32", "s64", "int", "lua_Integer"] +number_types = ["f32", "float", "f64", "double", "lua_Number"] out_filename = 'src/pc/lua/smlua_functions_autogen.c' out_filename_docs = 'docs/lua/functions%s.md' out_filename_defs = 'autogen/lua_definitions/functions.lua' @@ -69,6 +69,7 @@ in_files = [ "src/game/behavior_actions.h", "src/game/mario_misc.h", "src/pc/mods/mod_storage.h", + "src/pc/mods/mod_fs.h", "src/pc/utils/misc.h", "src/game/level_update.h", "src/game/area.h", @@ -156,6 +157,11 @@ lua_function_params = { "src/pc/lua/utils/smlua_obj_utils.h::spawn_object_sync::objSetupFunction": [ "struct Object*" ] } +parameter_keywords = [ + "OUT", + "OPTIONAL" +] + ########################################################### template = """/* THIS FILE IS AUTOGENERATED */ @@ -803,6 +809,8 @@ def build_param(fid, param, i): return ' %s %s = smlua_to_number(L, %d);\n' % (ptype, pid, i) elif ptype == 'const char*': return ' %s %s = smlua_to_string(L, %d);\n' % (ptype, pid, i) + elif ptype == 'ByteString': + return ' %s %s = smlua_to_bytestring(L, %d);\n' % (ptype, pid, i) elif ptype == 'LuaFunction': return ' %s %s = smlua_to_lua_function(L, %d);\n' % (ptype, pid, i) elif translate_type_to_lot(ptype) == 'LOT_POINTER': @@ -822,7 +830,7 @@ def build_param(fid, param, i): def build_param_after(param, i): ptype = param['type'] pid = param['identifier'] - is_output = param.get('out', False) + is_output = 'OUT' in param if ptype in VEC_TYPES and is_output: return (vec_type_after % (ptype.lower())).replace('$[IDENTIFIER]', str(pid)).replace('$[INDEX]', str(i)) @@ -858,6 +866,8 @@ def build_call(function): lfunc = 'lua_pushstring' elif ftype == 'const char*': lfunc = 'lua_pushstring' + elif ftype == 'ByteString': + lfunc = 'smlua_push_bytestring' elif translate_type_to_lot(ftype) == 'LOT_POINTER': lvt = translate_type_to_lvt(ftype) return ' smlua_push_pointer(L, %s, (void*)%s, NULL);\n' % (lvt, ccall) @@ -884,19 +894,39 @@ def build_function(function, do_extern): if 'bhv_' in fid: s += ' if (!gCurrentObject) { return 0; }\n' - s += """ if (L == NULL) { return 0; }\n + params_max = len(function['params']) + params_min = len([param for param in function['params'] if 'OPTIONAL' not in param]) + if params_min == params_max: + s += """ if (L == NULL) { return 0; }\n int top = lua_gettop(L); if (top != %d) { LOG_LUA_LINE("Improper param count for '%%s': Expected %%u, Received %%u", "%s", %d, top); return 0; - }\n\n""" % (len(function['params']), function['identifier'], len(function['params'])) + }\n\n""" % (params_max, function['identifier'], params_max) + else: + s += """ if (L == NULL) { return 0; }\n + int top = lua_gettop(L); + if (top < %d || top > %d) { + LOG_LUA_LINE("Improper param count for '%%s': Expected between %%u and %%u, Received %%u", "%s", %d, %d, top); + return 0; + }\n\n""" % (params_min, params_max, function['identifier'], params_min, params_max) is_interact_func = fid.startswith('interact_') and fname == 'interaction.h' i = 1 for param in function['params']: - if is_interact_func and param['identifier'] == 'interactType': + pid = param['identifier'] + if is_interact_func and pid == 'interactType': s += " // interactType skipped so mods can't lie about what interaction it is\n" + elif 'OPTIONAL' in param: + sparam = build_param(fid, param, i) + param_var, param_value = sparam.split('=') + param_type = param_var.replace(pid, '').strip() + s += ' %s = (%s) NULL;\n' % (param_var.strip(), param_type) + s += ' if (top >= %d) {\n' % (i) + s += ' %s = %s\n' % (pid, param_value.strip()) + s += ' if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %%u for function \'%%s\'", %d, "%s"); return 0; }\n' % (i, fid) + s += ' }\n' else: s += build_param(fid, param, i) s += ' if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %%u for function \'%%s\'", %d, "%s"); return 0; }\n' % (i, fid) @@ -921,7 +951,7 @@ def build_function(function, do_extern): # To allow chaining vector functions calls, return the table corresponding to the `OUT` parameter if function['type'] in VECP_TYPES: for i, param in enumerate(function['params']): - if param.get('out', False): + if 'OUT' in param: s += ' lua_settop(L, %d);\n' % (i + 1) break @@ -1020,13 +1050,16 @@ def process_function(fname, line, description): pass else: param_index = 0 + last_param_optional = None for param_str in params_str.split(','): param = {} param_str = param_str.strip() - if param_str.startswith('OUT '): - param['out'] = True - param_str = param_str[len('OUT'):].strip() + for param_keyword in parameter_keywords: + keyword_index = param_str.find(param_keyword + ' ') + if keyword_index != -1: + param[param_keyword] = True + param_str = (param_str[:keyword_index] + param_str[keyword_index+len(param_keyword)+1:]).strip() if param_str.endswith('*') or ' ' not in param_str: param['type'] = normalize_type(param_str) @@ -1038,6 +1071,12 @@ def process_function(fname, line, description): param['type'] = normalize_type(param_str[0:match.span()[0]]) param['identifier'] = match.group() + if 'OPTIONAL' in param: + last_param_optional = param['identifier'] + elif last_param_optional is not None: + print(f"REJECTED: {function['identifier']} -> mandatory parameter `{param['identifier']}` is following optional parameter `{last_param_optional}`") + return None + # override Vec3s/f if param['identifier'] == 'pos': if param['type'].replace(' ', '') == 'f32*': @@ -1354,7 +1393,7 @@ def def_function(fname, function): if ptype.startswith('Pointer_') and ptype not in def_pointers: def_pointers.append(ptype) - s += '--- @param %s %s\n' % (pid, ptype) + s += '--- @param %s%s %s\n' % (pid, ('?' if 'OPTIONAL' in param else ''), ptype) rtype = translate_to_def(rtype) if rtype.startswith('Pointer_') and rtype not in def_pointers: diff --git a/autogen/convert_structs.py b/autogen/convert_structs.py index 4f53c2906..8a5e0f722 100644 --- a/autogen/convert_structs.py +++ b/autogen/convert_structs.py @@ -27,6 +27,7 @@ in_files = [ "src/pc/network/network.h", "src/game/hardcoded.h", "src/pc/mods/mod.h", + "src/pc/mods/mod_fs.h", "src/pc/lua/utils/smlua_audio_utils.h", "src/game/paintings.h", "src/pc/djui/djui_types.h", @@ -97,7 +98,9 @@ override_field_invisible = { "FnGraphNode": [ "luaTokenIndex" ], "Object": [ "firstSurface" ], "ModAudio": [ "sound", "decoder", "buffer", "bufferSize", "sampleCopiesTail" ], - "DialogEntry": [ "str" ] + "DialogEntry": [ "str" ], + "ModFsFile": [ "data", "capacity" ], + "ModFs": [ "files" ], } override_field_deprecated = { @@ -137,7 +140,9 @@ override_field_immutable = { "FirstPersonCamera": [ "enabled" ], "ModAudio": [ "isStream", "loaded" ], "Gfx": [ "w0", "w1" ], # to protect from invalid type conversions - "DialogEntry": [ "unused", "linesPerBox", "leftOffset", "width", "str", "text"] + "DialogEntry": [ "unused", "linesPerBox", "leftOffset", "width", "str", "text"], + "ModFsFile": [ "*" ], + "ModFs": [ "*" ], } override_field_version_excludes = { diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 25e23c918..bc7b3aa49 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -4533,6 +4533,50 @@ GRAB_POS_BOWSER = 3 --- @type MarioGrabPosGSCId --- | `GRAB_POS_HEAVY_OBJ` --- | `GRAB_POS_BOWSER` +--- @type integer +MOD_FS_MAX_SIZE = 0x1000000 + +--- @type integer +MOD_FS_MAX_FILES = 0x100 + +--- @type integer +MOD_FS_MAX_PATH = 0x100 + +INT_TYPE_U8 = 0 --- @type ModFsFileIntType +INT_TYPE_U16 = 1 --- @type ModFsFileIntType +INT_TYPE_U32 = 2 --- @type ModFsFileIntType +INT_TYPE_U64 = 3 --- @type ModFsFileIntType +INT_TYPE_S8 = 4 --- @type ModFsFileIntType +INT_TYPE_S16 = 5 --- @type ModFsFileIntType +INT_TYPE_S32 = 6 --- @type ModFsFileIntType +INT_TYPE_S64 = 7 --- @type ModFsFileIntType + +--- @alias ModFsFileIntType +--- | `INT_TYPE_U8` +--- | `INT_TYPE_U16` +--- | `INT_TYPE_U32` +--- | `INT_TYPE_U64` +--- | `INT_TYPE_S8` +--- | `INT_TYPE_S16` +--- | `INT_TYPE_S32` +--- | `INT_TYPE_S64` + +FLOAT_TYPE_F32 = 0 --- @type ModFsFileFloatType +FLOAT_TYPE_F64 = 1 --- @type ModFsFileFloatType + +--- @alias ModFsFileFloatType +--- | `FLOAT_TYPE_F32` +--- | `FLOAT_TYPE_F64` + +FILE_SEEK_SET = 0 --- @type ModFsFileSeek +FILE_SEEK_CUR = 1 --- @type ModFsFileSeek +FILE_SEEK_END = 2 --- @type ModFsFileSeek + +--- @alias ModFsFileSeek +--- | `FILE_SEEK_SET` +--- | `FILE_SEEK_CUR` +--- | `FILE_SEEK_END` + --- @type integer MAX_KEYS = 4096 diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index e9c5d54ff..bde2be249 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -7340,6 +7340,260 @@ function delta_interpolate_vec3s(res, a, b, delta) -- ... end +--- @param modPath? string +--- @return boolean +--- Checks the existence of a modfs at path `modPath` or for the active mod if not provided. Checking for the existence of a private modfs will return false, even if it exists +function mod_fs_exists(modPath) + -- ... +end + +--- @param modPath? string +--- @return ModFs +--- Gets the modfs object at path `modPath` or the active mod one if not provided. This function will return nil for a private modfs, even if it exists +function mod_fs_get(modPath) + -- ... +end + +--- @param modPath? string +--- @return ModFs +--- Reloads the modfs object at path `modPath`. This function will return nil for a private modfs, even if it exists +function mod_fs_reload(modPath) + -- ... +end + +--- @return ModFs +--- Creates a modfs object for the active mod if it doesn't exist. Returns the modfs object on success +function mod_fs_create() + -- ... +end + +--- @return boolean +--- Deletes the modfs object of the active mod if it exists. Returns true on success +function mod_fs_delete() + -- ... +end + +--- @return boolean +--- Saves the modfs object of the active mod if it exists. Returns true on success +function mod_fs_save() + -- ... +end + +--- @param pub boolean +--- @return boolean +--- Marks the modfs object of the active mod as public (i.e. readable by other mods) if it exists. Returns true on success +function mod_fs_set_public(pub) + -- ... +end + +--- @param modFs ModFs +--- @param index integer +--- @return string +--- Gets the filename at position `index` of the provided `modFs` +function mod_fs_get_filename(modFs, index) + -- ... +end + +--- @param modFs ModFs +--- @param filepath string +--- @return ModFsFile +--- Gets the file object at path `filepath` of the provided `modFs`. This function will return nil for a private modfs file, even if it exists +function mod_fs_get_file(modFs, filepath) + -- ... +end + +--- @param modFs ModFs +--- @param filepath string +--- @param text boolean +--- @return ModFsFile +--- Creates a new file at path `filepath` for the provided `modFs`. Set `text` to true to treat the file as a pure text file, not a binary file. Returns the created file on success +function mod_fs_create_file(modFs, filepath, text) + -- ... +end + +--- @param modFs ModFs +--- @param oldpath string +--- @param newpath string +--- @param overwriteExisting boolean +--- @return boolean +--- Moves the file at path `oldpath` to `newpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `newpath` if it exists. Returns true on success +function mod_fs_move_file(modFs, oldpath, newpath, overwriteExisting) + -- ... +end + +--- @param modFs ModFs +--- @param srcpath string +--- @param dstpath string +--- @param overwriteExisting boolean +--- @return boolean +--- Copies the file at path `srcpath` to `dstpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `dstpath` if it exists. Returns true on success +function mod_fs_copy_file(modFs, srcpath, dstpath, overwriteExisting) + -- ... +end + +--- @param modFs ModFs +--- @param filepath string +--- @return boolean +--- Deletes the file at path `filepath` of the provided `modFs`. Returns true on success +function mod_fs_delete_file(modFs, filepath) + -- ... +end + +--- @param modFs ModFs +--- @return boolean +--- Deletes all files of the provided `modFs`. Returns true on success +function mod_fs_clear(modFs) + -- ... +end + +--- @param file ModFsFile +--- @return boolean +--- Reads a boolean from a binary modfs `file` +function mod_fs_file_read_bool(file) + -- ... +end + +--- @param file ModFsFile +--- @param intType ModFsFileIntType +--- @return integer +--- Reads an integer from a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants +function mod_fs_file_read_integer(file, intType) + -- ... +end + +--- @param file ModFsFile +--- @param floatType ModFsFileFloatType +--- @return number +--- Reads an floating-point number from a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants +function mod_fs_file_read_number(file, floatType) + -- ... +end + +--- @param file ModFsFile +--- @param length integer +--- @return string +--- Reads a bytestring of `length` bytes from a binary modfs `file` +function mod_fs_file_read_bytes(file, length) + -- ... +end + +--- @param file ModFsFile +--- @return string +--- Reads a string from a binary modfs `file`, or read the whole content of a text modfs `file` +function mod_fs_file_read_string(file) + -- ... +end + +--- @param file ModFsFile +--- @return string +--- Reads a line from a text modfs `file` +function mod_fs_file_read_line(file) + -- ... +end + +--- @param file ModFsFile +--- @param value boolean +--- @return boolean +--- Writes a boolean to a binary modfs `file`. Returns true on success +function mod_fs_file_write_bool(file, value) + -- ... +end + +--- @param file ModFsFile +--- @param value integer +--- @param intType ModFsFileIntType +--- @return boolean +--- Writes an integer to a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants. Returns true on success +function mod_fs_file_write_integer(file, value, intType) + -- ... +end + +--- @param file ModFsFile +--- @param value number +--- @param floatType ModFsFileFloatType +--- @return boolean +--- Writes an floating-point number to a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants. Returns true on success +function mod_fs_file_write_number(file, value, floatType) + -- ... +end + +--- @param file ModFsFile +--- @param bytestring string +--- @return boolean +--- Writes a bytestring to a modfs `file`. Returns true on success +function mod_fs_file_write_bytes(file, bytestring) + -- ... +end + +--- @param file ModFsFile +--- @param str string +--- @return boolean +--- Writes a string to a modfs `file`. Returns true on success +function mod_fs_file_write_string(file, str) + -- ... +end + +--- @param file ModFsFile +--- @param str string +--- @return boolean +--- Writes a line to a text modfs `file`. Returns true on success +function mod_fs_file_write_line(file, str) + -- ... +end + +--- @param file ModFsFile +--- @param offset integer +--- @param origin ModFsFileSeek +--- @return boolean +--- Sets the current position of a modfs `file`. If `origin` is `FILE_SEEK_SET`, file position is set to `offset`. If `origin` is `FILE_SEEK_CUR`, `offset` is added to file current position. If `origin` is `FILE_SEEK_END`, file position is set to `end of file + offset`. Returns true on success +function mod_fs_file_seek(file, offset, origin) + -- ... +end + +--- @param file ModFsFile +--- @return boolean +--- Returns true if the provided modfs `file` has reached its end of file +function mod_fs_file_is_eof(file) + -- ... +end + +--- @param file ModFsFile +--- @param byte integer +--- @param length integer +--- @return boolean +--- Fills a modfs `file` with `byte` repeated `length` times. Returns true on success +function mod_fs_file_fill(file, byte, length) + -- ... +end + +--- @param file ModFsFile +--- @param length integer +--- @return boolean +--- Erases `length` bytes or characters from a modfs `file`. Returns true on success +function mod_fs_file_erase(file, length) + -- ... +end + +--- @param file ModFsFile +--- @param pub boolean +--- @return boolean +--- Marks the provided modfs `file` as public (i.e. readable by other mods). Returns true on success +function mod_fs_file_set_public(file, pub) + -- ... +end + +--- @param hide boolean +--- Hides script errors raised by `mod_fs` functions. Errors messages are still generated and can be retrieved with `mod_fs_get_last_error()` +function mod_fs_hide_errors(hide) + -- ... +end + +--- @return string +--- Returns the last error message generated by `mod_fs` functions or nil if no error occurred +function mod_fs_get_last_error() + -- ... +end + --- @param key string --- @param value string --- @return boolean diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 46f71ca88..5d2d5d832 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -1289,6 +1289,21 @@ --- @field public relativePath string --- @field public wroteBytes integer +--- @class ModFs +--- @field public isPublic boolean +--- @field public mod Mod +--- @field public modPath string +--- @field public numFiles integer +--- @field public totalSize integer + +--- @class ModFsFile +--- @field public filepath string +--- @field public isPublic boolean +--- @field public isText boolean +--- @field public modFs ModFs +--- @field public offset integer +--- @field public size integer + --- @class ModeTransitionInfo --- @field public frame integer --- @field public lastMode integer diff --git a/developer/dir2modfs.py b/developer/dir2modfs.py new file mode 100644 index 000000000..af41a2abd --- /dev/null +++ b/developer/dir2modfs.py @@ -0,0 +1,190 @@ +import os, sys, re + + +MOD_FS_MAGIC = "MODFSSM64COOPDX" +MOD_FS_HEADER_SIZE = 32 +MOD_FS_EXTENSION = ".modfs" +MOD_FS_VERSION = 1 +MOD_FS_MAX_SIZE = 0x1000000 +MOD_FS_MAX_FILES = 0x100 +MOD_FS_MAX_PATH = 0x100 + + +def usage(): + print(""" +Directory to modfs: + + python dir2modfs.py [--set-public] [--set-file-public ...] + + Parameters: + dirpath Path to directory to turn into a .modfs file + + Options: + --set-public Set modfs file as public (readable by other mods) + --set-file-public Set the provided files as public (readable by other mods) + +modfs to directory: + + python dir2modfs.py --extract + + Parameters: + filepath Path to modfs file to extract files from +""") + exit(0) + + +def is_binary_file(bytes: bytes): + textchars = bytearray({0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0D, 0x1B} | set(range(0x20, 0x100)) - {0x7F}) + return bool(bytes.translate(None, textchars)) + + +def get_files(dirpath: str, public_files: list): + files = [] + for root, _, filenames in os.walk(dirpath): + for filename in filenames: + relpath = os.path.join(root, filename) + filepath = relpath.removeprefix(dirpath).strip("/\\").replace('\\', '/') + is_public = False + for public_file in public_files: + if re.match(public_file, relpath) or re.match(public_file, filepath): + is_public = True + break + files.append({ + "relpath": relpath, + "filepath": filepath, + "is_public": is_public, + "is_text": False, + "data": None + }) + return files + + +def convert(dirpath: str, set_public: bool, public_files: list): + dirpath = dirpath.rstrip("/\\") + files = sorted(get_files(dirpath, public_files), key=lambda file: file["filepath"]) + if len(files) > MOD_FS_MAX_FILES: + raise Exception(f"Max number of files exceeded: {len(files)} (max is: {MOD_FS_MAX_FILES})") + + total_size = 0 + for file in files: + filepath = file["filepath"] + if len(filepath) >= MOD_FS_MAX_PATH: + raise Exception(f"{filepath} - Exceeded filepath length: {len(filepath)} (max is: {MOD_FS_MAX_PATH-1})") + + with open(file["relpath"], "rb") as f: + data = f.read() + total_size += len(data) + if total_size > MOD_FS_MAX_SIZE: + raise Exception(f"{filepath} - Total size exceeded: {total_size} (max is: {MOD_FS_MAX_SIZE})") + + file["data"] = data + file["is_text"] = not is_binary_file(data) + + # write file + destpath = dirpath + MOD_FS_EXTENSION + with open(destpath, "wb") as f: + + # magic + version + f.write(MOD_FS_MAGIC.encode()) + f.write(MOD_FS_VERSION.to_bytes(1, byteorder="little", signed=False)) + + # header + f.write(len(files).to_bytes(2, byteorder="little", signed=False)) + f.write(set_public.to_bytes(1, byteorder="little", signed=False)) + + # padding (empty space for future versions) + padding = MOD_FS_HEADER_SIZE - f.tell() + f.write(b'\0' * padding) + + # files + for file in files: + + # filepath + f.write(len(file["filepath"]).to_bytes(2, byteorder="little", signed=False)) + f.write(file["filepath"].encode()) + + # data + f.write(len(file["data"]).to_bytes(4, byteorder="little", signed=False)) + f.write(file["is_public"].to_bytes(1, byteorder="little", signed=False)) + f.write(file["is_text"].to_bytes(1, byteorder="little", signed=False)) + f.write(file["data"]) + + # summary + print("") + print(f"Directory: {dirpath}") + print(f"Num files: {len(files)}") + print(f"Total size: {total_size}") + print(f"Is public: {set_public}") + + filepaths_max = max(8, len(max([file["filepath"] for file in files], key=len))) + sizes_max = max(4, len(max([str(len(file["data"])) for file in files], key=len))) + print("") + print(f"{'FILEPATH'.ljust(filepaths_max)} {'SIZE'.rjust(sizes_max)} {'TEXT'.rjust(5)} {'PUBLIC'.rjust(6)}") + print(f"{'--------'.ljust(filepaths_max)} {'----'.rjust(sizes_max)} {'----'.rjust(5)} {'------'.rjust(6)}") + for file in files: + filepath = file["filepath"] + size = str(len(file["data"])) + is_text = str(file["is_text"]) + is_public = str(file["is_public"]) + print(f"{filepath.ljust(filepaths_max)} {size.rjust(sizes_max)} {is_text.rjust(5)} {is_public.rjust(6)}") + + +def extract(filepath: str): + if not filepath.endswith(MOD_FS_EXTENSION): + raise Exception("Not a modfs file") + + with open(filepath, "rb") as f: + + # magic + version + magic = f.read(len(MOD_FS_MAGIC)).decode() + if magic != MOD_FS_MAGIC: + raise Exception("Not a modfs file") + version = int.from_bytes(f.read(1), byteorder="little", signed=False) + if version != MOD_FS_VERSION: + raise Exception("Version mismatch") + + # header + num_files = int.from_bytes(f.read(2), byteorder="little", signed=False) + is_public = bool.from_bytes(f.read(1), byteorder="little", signed=False) + + # padding (empty space for future versions) + f.seek(MOD_FS_HEADER_SIZE, 0) + + # create directory + dirpath = filepath.removesuffix(MOD_FS_EXTENSION) + os.makedirs(dirpath, exist_ok=True) + + # files + for _ in range(num_files): + + # filepath + filepath_len = int.from_bytes(f.read(2), byteorder="little", signed=False) + filepath = os.path.join(dirpath, f.read(filepath_len).decode()) + + # data + file_size = int.from_bytes(f.read(4), byteorder="little", signed=False) + file_is_public = bool.from_bytes(f.read(1), byteorder="little", signed=False) + file_is_text = bool.from_bytes(f.read(1), byteorder="little", signed=False) + file_data = f.read(file_size) + + # write file + os.makedirs(os.path.dirname(filepath), exist_ok=True) + with open(filepath, "wb") as g: + g.write(file_data) + print(f"Extracted file of size {file_size} to: {filepath}") + + +def main(argc: int, argv: list): + if argc < 2: + usage() + + if "--extract" in argv: + extract(argv[1]) + else: + set_public = "--set-public" in argv + set_file_public_index = argv.index("--set-file-public") if "--set-file-public" in argv else argc + convert(argv[1], set_public, argv[set_file_public_index+1:]) + + +if __name__ == "__main__": + main(len(sys.argv), sys.argv) diff --git a/docs/lua/constants.md b/docs/lua/constants.md index e1ac36c14..ae3452319 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -52,6 +52,10 @@ - [enum MarioHandGSCId](#enum-MarioHandGSCId) - [enum MarioCapGSCId](#enum-MarioCapGSCId) - [enum MarioGrabPosGSCId](#enum-MarioGrabPosGSCId) +- [mod_fs.h](#mod_fsh) + - [enum ModFsFileIntType](#enum-ModFsFileIntType) + - [enum ModFsFileFloatType](#enum-ModFsFileFloatType) + - [enum ModFsFileSeek](#enum-ModFsFileSeek) - [mod_storage.h](#mod_storageh) - [network.h](#networkh) - [enum NetworkSystemType](#enum-NetworkSystemType) @@ -2129,6 +2133,40 @@
+## [mod_fs.h](#mod_fs.h) +- MOD_FS_MAX_SIZE +- MOD_FS_MAX_FILES +- MOD_FS_MAX_PATH + +### [enum ModFsFileIntType](#ModFsFileIntType) +| Identifier | Value | +| :--------- | :---- | +| INT_TYPE_U8 | 0 | +| INT_TYPE_U16 | 1 | +| INT_TYPE_U32 | 2 | +| INT_TYPE_U64 | 3 | +| INT_TYPE_S8 | 4 | +| INT_TYPE_S16 | 5 | +| INT_TYPE_S32 | 6 | +| INT_TYPE_S64 | 7 | + +### [enum ModFsFileFloatType](#ModFsFileFloatType) +| Identifier | Value | +| :--------- | :---- | +| FLOAT_TYPE_F32 | 0 | +| FLOAT_TYPE_F64 | 1 | + +### [enum ModFsFileSeek](#ModFsFileSeek) +| Identifier | Value | +| :--------- | :---- | +| FILE_SEEK_SET | 0 | +| FILE_SEEK_CUR | 1 | +| FILE_SEEK_END | 2 | + +[:arrow_up_small:](#) + +
+ ## [mod_storage.h](#mod_storage.h) - MAX_KEYS - MAX_KEY_VALUE_LENGTH diff --git a/docs/lua/functions-5.md b/docs/lua/functions-5.md index c83e04386..e29068ee5 100644 --- a/docs/lua/functions-5.md +++ b/docs/lua/functions-5.md @@ -246,6 +246,791 @@ Linearly interpolates `res` between `a` and `b` with `delta`
+--- +# functions from mod_fs.h + +
+ + +## [mod_fs_exists](#mod_fs_exists) + +### Description +Checks the existence of a modfs at path `modPath` or for the active mod if not provided. Checking for the existence of a private modfs will return false, even if it exists + +### Lua Example +`local booleanValue = mod_fs_exists(modPath)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modPath | `string` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_exists(OPTIONAL const char *modPath);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_get](#mod_fs_get) + +### Description +Gets the modfs object at path `modPath` or the active mod one if not provided. This function will return nil for a private modfs, even if it exists + +### Lua Example +`local ModFsValue = mod_fs_get(modPath)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modPath | `string` | + +### Returns +[ModFs](structs.md#ModFs) + +### C Prototype +`struct ModFs *mod_fs_get(OPTIONAL const char *modPath);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_reload](#mod_fs_reload) + +### Description +Reloads the modfs object at path `modPath`. This function will return nil for a private modfs, even if it exists + +### Lua Example +`local ModFsValue = mod_fs_reload(modPath)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modPath | `string` | + +### Returns +[ModFs](structs.md#ModFs) + +### C Prototype +`struct ModFs *mod_fs_reload(OPTIONAL const char *modPath);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_create](#mod_fs_create) + +### Description +Creates a modfs object for the active mod if it doesn't exist. Returns the modfs object on success + +### Lua Example +`local ModFsValue = mod_fs_create()` + +### Parameters +- None + +### Returns +[ModFs](structs.md#ModFs) + +### C Prototype +`struct ModFs *mod_fs_create();` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_delete](#mod_fs_delete) + +### Description +Deletes the modfs object of the active mod if it exists. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_delete()` + +### Parameters +- None + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_delete();` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_save](#mod_fs_save) + +### Description +Saves the modfs object of the active mod if it exists. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_save()` + +### Parameters +- None + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_save();` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_set_public](#mod_fs_set_public) + +### Description +Marks the modfs object of the active mod as public (i.e. readable by other mods) if it exists. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_set_public(pub)` + +### Parameters +| Field | Type | +| ----- | ---- | +| pub | `boolean` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_set_public(bool pub);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_get_filename](#mod_fs_get_filename) + +### Description +Gets the filename at position `index` of the provided `modFs` + +### Lua Example +`local stringValue = mod_fs_get_filename(modFs, index)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| index | `integer` | + +### Returns +- `string` + +### C Prototype +`const char *mod_fs_get_filename(struct ModFs *modFs, u16 index);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_get_file](#mod_fs_get_file) + +### Description +Gets the file object at path `filepath` of the provided `modFs`. This function will return nil for a private modfs file, even if it exists + +### Lua Example +`local ModFsFileValue = mod_fs_get_file(modFs, filepath)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| filepath | `string` | + +### Returns +[ModFsFile](structs.md#ModFsFile) + +### C Prototype +`struct ModFsFile *mod_fs_get_file(struct ModFs *modFs, const char *filepath);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_create_file](#mod_fs_create_file) + +### Description +Creates a new file at path `filepath` for the provided `modFs`. Set `text` to true to treat the file as a pure text file, not a binary file. Returns the created file on success + +### Lua Example +`local ModFsFileValue = mod_fs_create_file(modFs, filepath, text)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| filepath | `string` | +| text | `boolean` | + +### Returns +[ModFsFile](structs.md#ModFsFile) + +### C Prototype +`struct ModFsFile *mod_fs_create_file(struct ModFs *modFs, const char *filepath, bool text);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_move_file](#mod_fs_move_file) + +### Description +Moves the file at path `oldpath` to `newpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `newpath` if it exists. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_move_file(modFs, oldpath, newpath, overwriteExisting)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| oldpath | `string` | +| newpath | `string` | +| overwriteExisting | `boolean` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_move_file(struct ModFs *modFs, const char *oldpath, const char *newpath, bool overwriteExisting);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_copy_file](#mod_fs_copy_file) + +### Description +Copies the file at path `srcpath` to `dstpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `dstpath` if it exists. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_copy_file(modFs, srcpath, dstpath, overwriteExisting)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| srcpath | `string` | +| dstpath | `string` | +| overwriteExisting | `boolean` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_copy_file(struct ModFs *modFs, const char *srcpath, const char *dstpath, bool overwriteExisting);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_delete_file](#mod_fs_delete_file) + +### Description +Deletes the file at path `filepath` of the provided `modFs`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_delete_file(modFs, filepath)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | +| filepath | `string` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_delete_file(struct ModFs *modFs, const char *filepath);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_clear](#mod_fs_clear) + +### Description +Deletes all files of the provided `modFs`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_clear(modFs)` + +### Parameters +| Field | Type | +| ----- | ---- | +| modFs | [ModFs](structs.md#ModFs) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_clear(struct ModFs *modFs);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_bool](#mod_fs_file_read_bool) + +### Description +Reads a boolean from a binary modfs `file` + +### Lua Example +`local booleanValue = mod_fs_file_read_bool(file)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_read_bool(struct ModFsFile *file);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_integer](#mod_fs_file_read_integer) + +### Description +Reads an integer from a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants + +### Lua Example +`local integerValue = mod_fs_file_read_integer(file, intType)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| intType | [enum ModFsFileIntType](constants.md#enum-ModFsFileIntType) | + +### Returns +- `integer` + +### C Prototype +`lua_Integer mod_fs_file_read_integer(struct ModFsFile *file, enum ModFsFileIntType intType);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_number](#mod_fs_file_read_number) + +### Description +Reads an floating-point number from a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants + +### Lua Example +`local numberValue = mod_fs_file_read_number(file, floatType)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| floatType | [enum ModFsFileFloatType](constants.md#enum-ModFsFileFloatType) | + +### Returns +- `number` + +### C Prototype +`lua_Number mod_fs_file_read_number(struct ModFsFile *file, enum ModFsFileFloatType floatType);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_bytes](#mod_fs_file_read_bytes) + +### Description +Reads a bytestring of `length` bytes from a binary modfs `file` + +### Lua Example +`local stringValue = mod_fs_file_read_bytes(file, length)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| length | `integer` | + +### Returns +- `string` + +### C Prototype +`ByteString mod_fs_file_read_bytes(struct ModFsFile *file, u32 length);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_string](#mod_fs_file_read_string) + +### Description +Reads a string from a binary modfs `file`, or read the whole content of a text modfs `file` + +### Lua Example +`local stringValue = mod_fs_file_read_string(file)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | + +### Returns +- `string` + +### C Prototype +`const char *mod_fs_file_read_string(struct ModFsFile *file);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_read_line](#mod_fs_file_read_line) + +### Description +Reads a line from a text modfs `file` + +### Lua Example +`local stringValue = mod_fs_file_read_line(file)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | + +### Returns +- `string` + +### C Prototype +`const char *mod_fs_file_read_line(struct ModFsFile *file);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_bool](#mod_fs_file_write_bool) + +### Description +Writes a boolean to a binary modfs `file`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_bool(file, value)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| value | `boolean` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_bool(struct ModFsFile *file, bool value);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_integer](#mod_fs_file_write_integer) + +### Description +Writes an integer to a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_integer(file, value, intType)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| value | `integer` | +| intType | [enum ModFsFileIntType](constants.md#enum-ModFsFileIntType) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_integer(struct ModFsFile *file, lua_Integer value, enum ModFsFileIntType intType);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_number](#mod_fs_file_write_number) + +### Description +Writes an floating-point number to a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_number(file, value, floatType)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| value | `number` | +| floatType | [enum ModFsFileFloatType](constants.md#enum-ModFsFileFloatType) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_number(struct ModFsFile *file, lua_Number value, enum ModFsFileFloatType floatType);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_bytes](#mod_fs_file_write_bytes) + +### Description +Writes a bytestring to a modfs `file`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_bytes(file, bytestring)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| bytestring | `string` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_bytes(struct ModFsFile *file, ByteString bytestring);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_string](#mod_fs_file_write_string) + +### Description +Writes a string to a modfs `file`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_string(file, str)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| str | `string` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_string(struct ModFsFile *file, const char *str);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_write_line](#mod_fs_file_write_line) + +### Description +Writes a line to a text modfs `file`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_write_line(file, str)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| str | `string` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_write_line(struct ModFsFile *file, const char *str);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_seek](#mod_fs_file_seek) + +### Description +Sets the current position of a modfs `file`. If `origin` is `FILE_SEEK_SET`, file position is set to `offset`. If `origin` is `FILE_SEEK_CUR`, `offset` is added to file current position. If `origin` is `FILE_SEEK_END`, file position is set to `end of file + offset`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_seek(file, offset, origin)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| offset | `integer` | +| origin | [enum ModFsFileSeek](constants.md#enum-ModFsFileSeek) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_seek(struct ModFsFile *file, s32 offset, enum ModFsFileSeek origin);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_is_eof](#mod_fs_file_is_eof) + +### Description +Returns true if the provided modfs `file` has reached its end of file + +### Lua Example +`local booleanValue = mod_fs_file_is_eof(file)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_is_eof(struct ModFsFile *file);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_fill](#mod_fs_file_fill) + +### Description +Fills a modfs `file` with `byte` repeated `length` times. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_fill(file, byte, length)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| byte | `integer` | +| length | `integer` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_fill(struct ModFsFile *file, u8 byte, u32 length);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_erase](#mod_fs_file_erase) + +### Description +Erases `length` bytes or characters from a modfs `file`. Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_erase(file, length)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| length | `integer` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_erase(struct ModFsFile *file, u32 length);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_file_set_public](#mod_fs_file_set_public) + +### Description +Marks the provided modfs `file` as public (i.e. readable by other mods). Returns true on success + +### Lua Example +`local booleanValue = mod_fs_file_set_public(file, pub)` + +### Parameters +| Field | Type | +| ----- | ---- | +| file | [ModFsFile](structs.md#ModFsFile) | +| pub | `boolean` | + +### Returns +- `boolean` + +### C Prototype +`bool mod_fs_file_set_public(struct ModFsFile *file, bool pub);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_hide_errors](#mod_fs_hide_errors) + +### Description +Hides script errors raised by `mod_fs` functions. Errors messages are still generated and can be retrieved with `mod_fs_get_last_error()` + +### Lua Example +`mod_fs_hide_errors(hide)` + +### Parameters +| Field | Type | +| ----- | ---- | +| hide | `boolean` | + +### Returns +- None + +### C Prototype +`void mod_fs_hide_errors(bool hide);` + +[:arrow_up_small:](#) + +
+ +## [mod_fs_get_last_error](#mod_fs_get_last_error) + +### Description +Returns the last error message generated by `mod_fs` functions or nil if no error occurred + +### Lua Example +`local stringValue = mod_fs_get_last_error()` + +### Parameters +- None + +### Returns +- `string` + +### C Prototype +`const char *mod_fs_get_last_error();` + +[:arrow_up_small:](#) + +
+ --- # functions from mod_storage.h @@ -7418,690 +8203,6 @@ Gets a trajectory's length [:arrow_up_small:](#) -
- ---- -# functions from object_list_processor.h - -
- - -## [set_object_respawn_info_bits](#set_object_respawn_info_bits) - -### Description -Runs an OR operator on the `obj`'s respawn info with `bits` << 8. If `bits` is 0xFF, this prevents the object from respawning after leaving and re-entering the area - -### Lua Example -`set_object_respawn_info_bits(obj, bits)` - -### Parameters -| Field | Type | -| ----- | ---- | -| obj | [Object](structs.md#Object) | -| bits | `integer` | - -### Returns -- None - -### C Prototype -`void set_object_respawn_info_bits(struct Object *obj, u8 bits);` - -[:arrow_up_small:](#) - -
- ---- -# functions from platform_displacement.h - -
- - -## [apply_platform_displacement](#apply_platform_displacement) - -### Description -Apply one frame of platform rotation to the object using the given platform - -### Lua Example -`apply_platform_displacement(o, platform)` - -### Parameters -| Field | Type | -| ----- | ---- | -| o | [Object](structs.md#Object) | -| platform | [Object](structs.md#Object) | - -### Returns -- None - -### C Prototype -`void apply_platform_displacement(struct Object *o, struct Object *platform);` - -[:arrow_up_small:](#) - -
- ---- -# functions from rumble_init.h - -
- - -## [queue_rumble_data](#queue_rumble_data) - -### Description -Queues rumble data - -### Lua Example -`queue_rumble_data(a0, a1)` - -### Parameters -| Field | Type | -| ----- | ---- | -| a0 | `integer` | -| a1 | `integer` | - -### Returns -- None - -### C Prototype -`void queue_rumble_data(s16 a0, s16 a1);` - -[:arrow_up_small:](#) - -
- -## [queue_rumble_data_object](#queue_rumble_data_object) - -### Description -Queues rumble data for object, factoring in its distance from Mario - -### Lua Example -`queue_rumble_data_object(object, a0, a1)` - -### Parameters -| Field | Type | -| ----- | ---- | -| object | [Object](structs.md#Object) | -| a0 | `integer` | -| a1 | `integer` | - -### Returns -- None - -### C Prototype -`void queue_rumble_data_object(struct Object* object, s16 a0, s16 a1);` - -[:arrow_up_small:](#) - -
- -## [queue_rumble_data_mario](#queue_rumble_data_mario) - -### Description -Queues rumble data for Mario - -### Lua Example -`queue_rumble_data_mario(m, a0, a1)` - -### Parameters -| Field | Type | -| ----- | ---- | -| m | [MarioState](structs.md#MarioState) | -| a0 | `integer` | -| a1 | `integer` | - -### Returns -- None - -### C Prototype -`void queue_rumble_data_mario(struct MarioState* m, s16 a0, s16 a1);` - -[:arrow_up_small:](#) - -
- -## [reset_rumble_timers](#reset_rumble_timers) - -### Description -Resets rumble timers - -### Lua Example -`reset_rumble_timers(m)` - -### Parameters -| Field | Type | -| ----- | ---- | -| m | [MarioState](structs.md#MarioState) | - -### Returns -- None - -### C Prototype -`void reset_rumble_timers(struct MarioState* m);` - -[:arrow_up_small:](#) - -
- -## [reset_rumble_timers_2](#reset_rumble_timers_2) - -### Description -Resets rumble timers and sets a field based on `a0` - -### Lua Example -`reset_rumble_timers_2(m, a0)` - -### Parameters -| Field | Type | -| ----- | ---- | -| m | [MarioState](structs.md#MarioState) | -| a0 | `integer` | - -### Returns -- None - -### C Prototype -`void reset_rumble_timers_2(struct MarioState* m, s32 a0);` - -[:arrow_up_small:](#) - -
- ---- -# functions from save_file.h - -
- - -## [get_level_num_from_course_num](#get_level_num_from_course_num) - -### Description -Gets the course number's corresponding level number - -### Lua Example -`local integerValue = get_level_num_from_course_num(courseNum)` - -### Parameters -| Field | Type | -| ----- | ---- | -| courseNum | `integer` | - -### Returns -- `integer` - -### C Prototype -`s8 get_level_num_from_course_num(s16 courseNum);` - -[:arrow_up_small:](#) - -
- -## [get_level_course_num](#get_level_course_num) - -### Description -Gets the level number's corresponding course number - -### Lua Example -`local integerValue = get_level_course_num(levelNum)` - -### Parameters -| Field | Type | -| ----- | ---- | -| levelNum | `integer` | - -### Returns -- `integer` - -### C Prototype -`s8 get_level_course_num(s16 levelNum);` - -[:arrow_up_small:](#) - -
- -## [touch_coin_score_age](#touch_coin_score_age) - -### Description -Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress - -### Lua Example -`touch_coin_score_age(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- None - -### C Prototype -`void touch_coin_score_age(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_do_save](#save_file_do_save) - -### Description -Saves the current state of the game into a specified save file. Includes data verification and backup management. Useful for maintaining game progress during play or when saving manually - -### Lua Example -`save_file_do_save(fileIndex, forceSave)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| forceSave | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_do_save(s32 fileIndex, s8 forceSave);` - -[:arrow_up_small:](#) - -
- -## [save_file_erase](#save_file_erase) - -### Description -Erases all data in a specified save file, including backup slots. Marks the save file as modified and performs a save to apply the changes. Useful for resetting a save file to its default state - -### Lua Example -`save_file_erase(fileIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_erase(s32 fileIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_erase_current_backup_save](#save_file_erase_current_backup_save) - -### Description -Erases the backup data for the current save file without affecting the primary save data. Reloads the save file afterward - -### Lua Example -`save_file_erase_current_backup_save()` - -### Parameters -- None - -### Returns -- None - -### C Prototype -`void save_file_erase_current_backup_save(void);` - -[:arrow_up_small:](#) - -
- -## [save_file_reload](#save_file_reload) - -### Description -Reloads the save file data into memory, optionally resetting all save files. Marks the save file as modified. Useful for reloading state after data corruption or during development debugging - -### Lua Example -`save_file_reload(load_all)` - -### Parameters -| Field | Type | -| ----- | ---- | -| load_all | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_reload(u8 load_all);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_max_coin_score](#save_file_get_max_coin_score) - -### Description -Determines the maximum coin score for a course across all save files. Returns the score along with the file index of the save containing it. Useful for leaderboard-style comparisons and overall progress tracking - -### Lua Example -`local integerValue = save_file_get_max_coin_score(courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| courseIndex | `integer` | - -### Returns -- `integer` - -### C Prototype -`u32 save_file_get_max_coin_score(s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_course_star_count](#save_file_get_course_star_count) - -### Description -Calculates the total number of stars collected in a specific course for a given save file. Useful for determining completion status of individual levels - -### Lua Example -`local integerValue = save_file_get_course_star_count(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- `integer` - -### C Prototype -`s32 save_file_get_course_star_count(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_total_star_count](#save_file_get_total_star_count) - -### Description -Calculates the total number of stars collected across multiple courses within a specified range. Useful for determining the overall progress toward game completion - -### Lua Example -`local integerValue = save_file_get_total_star_count(fileIndex, minCourse, maxCourse)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| minCourse | `integer` | -| maxCourse | `integer` | - -### Returns -- `integer` - -### C Prototype -`s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse);` - -[:arrow_up_small:](#) - -
- -## [save_file_set_flags](#save_file_set_flags) - -### Description -Adds new flags to the save file's flag bitmask. Useful for updating progress or triggering new gameplay features - -### Lua Example -`save_file_set_flags(flags)` - -### Parameters -| Field | Type | -| ----- | ---- | -| flags | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_set_flags(u32 flags);` - -[:arrow_up_small:](#) - -
- -## [save_file_clear_flags](#save_file_clear_flags) - -### Description -Clears specific flags in the current save file. The flags are specified as a bitmask in the `flags` parameter. Ensures that the save file remains valid after clearing. Useful for removing specific game states, such as collected items or completed objectives, without resetting the entire save - -### Lua Example -`save_file_clear_flags(flags)` - -### Parameters -| Field | Type | -| ----- | ---- | -| flags | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_clear_flags(u32 flags);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_flags](#save_file_get_flags) - -### Description -Retrieves the bitmask of flags representing the current state of the save file. Flags indicate collected items, completed objectives, and other game states. Useful for checking specific game progress details - -### Lua Example -`local integerValue = save_file_get_flags()` - -### Parameters -- None - -### Returns -- `integer` - -### C Prototype -`u32 save_file_get_flags(void);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_star_flags](#save_file_get_star_flags) - -### Description -Retrieves the bitmask of stars collected in a specific course or castle secret stars (-1). Useful for evaluating level progress and completion - -### Lua Example -`local integerValue = save_file_get_star_flags(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- `integer` - -### C Prototype -`u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_set_star_flags](#save_file_set_star_flags) - -### Description -Adds specific star flags to the save file, indicating collected stars for a course or castle secret stars. Updates the save file flags as necessary. Useful for recording progress after star collection - -### Lua Example -`save_file_set_star_flags(fileIndex, courseIndex, starFlags)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | -| starFlags | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags);` - -[:arrow_up_small:](#) - -
- -## [save_file_remove_star_flags](#save_file_remove_star_flags) - -### Description -Removes specific star flags from the save file. This modifies the bitmask representing collected stars for a course or castle secret stars. Useful for undoing progress or debugging collected stars - -### Lua Example -`save_file_remove_star_flags(fileIndex, courseIndex, starFlagsToRemove)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | -| starFlagsToRemove | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_remove_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlagsToRemove);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_course_coin_score](#save_file_get_course_coin_score) - -### Description -Returns the highest coin score for a specified course in the save file. Performs checks to ensure the coin score is valid. Useful for tracking player achievements and high scores - -### Lua Example -`local integerValue = save_file_get_course_coin_score(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- `integer` - -### C Prototype -`s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_set_course_coin_score](#save_file_set_course_coin_score) - -### Description -Updates the coin score for a specific course in the save file. The new score is provided in the `coinScore` parameter. Useful for manually setting achievements such as high coin counts in individual levels - -### Lua Example -`save_file_set_course_coin_score(fileIndex, courseIndex, coinScore)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | -| coinScore | `integer` | - -### Returns -- None - -### C Prototype -`void save_file_set_course_coin_score(s32 fileIndex, s32 courseIndex, u8 coinScore);` - -[:arrow_up_small:](#) - -
- -## [save_file_is_cannon_unlocked](#save_file_is_cannon_unlocked) - -### Description -Checks whether the cannon in the specified course is unlocked. Returns true if the cannon is unlocked, otherwise false. Useful for tracking course-specific progress and enabling shortcuts - -### Lua Example -`local integerValue = save_file_is_cannon_unlocked(fileIndex, courseIndex)` - -### Parameters -| Field | Type | -| ----- | ---- | -| fileIndex | `integer` | -| courseIndex | `integer` | - -### Returns -- `integer` - -### C Prototype -`s32 save_file_is_cannon_unlocked(s32 fileIndex, s32 courseIndex);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_cap_pos](#save_file_get_cap_pos) - -### Description -Retrieves the current position of Mario's cap, if it is on the ground in the current level and area. The position is stored in the provided `capPos` parameter. Useful for tracking the cap's location after it has been dropped or lost - -### Lua Example -`local integerValue = save_file_get_cap_pos(capPos)` - -### Parameters -| Field | Type | -| ----- | ---- | -| capPos | [Vec3s](structs.md#Vec3s) | - -### Returns -- `integer` - -### C Prototype -`s32 save_file_get_cap_pos(OUT Vec3s capPos);` - -[:arrow_up_small:](#) - -
- -## [save_file_get_sound_mode](#save_file_get_sound_mode) - -### Description -Returns the current sound mode (e.g., stereo, mono) stored in the save file. Useful for checking the audio output preferences when loading a save - -### Lua Example -`local integerValue = save_file_get_sound_mode()` - -### Parameters -- None - -### Returns -- `integer` - -### C Prototype -`u16 save_file_get_sound_mode(void);` - -[:arrow_up_small:](#) -
--- diff --git a/docs/lua/functions-6.md b/docs/lua/functions-6.md index 031d8310a..3f808e027 100644 --- a/docs/lua/functions-6.md +++ b/docs/lua/functions-6.md @@ -5,6 +5,690 @@ [< prev](functions-5.md) | [1](functions.md) | [2](functions-2.md) | [3](functions-3.md) | [4](functions-4.md) | [5](functions-5.md) | 6] +--- +# functions from object_list_processor.h + +
+ + +## [set_object_respawn_info_bits](#set_object_respawn_info_bits) + +### Description +Runs an OR operator on the `obj`'s respawn info with `bits` << 8. If `bits` is 0xFF, this prevents the object from respawning after leaving and re-entering the area + +### Lua Example +`set_object_respawn_info_bits(obj, bits)` + +### Parameters +| Field | Type | +| ----- | ---- | +| obj | [Object](structs.md#Object) | +| bits | `integer` | + +### Returns +- None + +### C Prototype +`void set_object_respawn_info_bits(struct Object *obj, u8 bits);` + +[:arrow_up_small:](#) + +
+ +--- +# functions from platform_displacement.h + +
+ + +## [apply_platform_displacement](#apply_platform_displacement) + +### Description +Apply one frame of platform rotation to the object using the given platform + +### Lua Example +`apply_platform_displacement(o, platform)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o | [Object](structs.md#Object) | +| platform | [Object](structs.md#Object) | + +### Returns +- None + +### C Prototype +`void apply_platform_displacement(struct Object *o, struct Object *platform);` + +[:arrow_up_small:](#) + +
+ +--- +# functions from rumble_init.h + +
+ + +## [queue_rumble_data](#queue_rumble_data) + +### Description +Queues rumble data + +### Lua Example +`queue_rumble_data(a0, a1)` + +### Parameters +| Field | Type | +| ----- | ---- | +| a0 | `integer` | +| a1 | `integer` | + +### Returns +- None + +### C Prototype +`void queue_rumble_data(s16 a0, s16 a1);` + +[:arrow_up_small:](#) + +
+ +## [queue_rumble_data_object](#queue_rumble_data_object) + +### Description +Queues rumble data for object, factoring in its distance from Mario + +### Lua Example +`queue_rumble_data_object(object, a0, a1)` + +### Parameters +| Field | Type | +| ----- | ---- | +| object | [Object](structs.md#Object) | +| a0 | `integer` | +| a1 | `integer` | + +### Returns +- None + +### C Prototype +`void queue_rumble_data_object(struct Object* object, s16 a0, s16 a1);` + +[:arrow_up_small:](#) + +
+ +## [queue_rumble_data_mario](#queue_rumble_data_mario) + +### Description +Queues rumble data for Mario + +### Lua Example +`queue_rumble_data_mario(m, a0, a1)` + +### Parameters +| Field | Type | +| ----- | ---- | +| m | [MarioState](structs.md#MarioState) | +| a0 | `integer` | +| a1 | `integer` | + +### Returns +- None + +### C Prototype +`void queue_rumble_data_mario(struct MarioState* m, s16 a0, s16 a1);` + +[:arrow_up_small:](#) + +
+ +## [reset_rumble_timers](#reset_rumble_timers) + +### Description +Resets rumble timers + +### Lua Example +`reset_rumble_timers(m)` + +### Parameters +| Field | Type | +| ----- | ---- | +| m | [MarioState](structs.md#MarioState) | + +### Returns +- None + +### C Prototype +`void reset_rumble_timers(struct MarioState* m);` + +[:arrow_up_small:](#) + +
+ +## [reset_rumble_timers_2](#reset_rumble_timers_2) + +### Description +Resets rumble timers and sets a field based on `a0` + +### Lua Example +`reset_rumble_timers_2(m, a0)` + +### Parameters +| Field | Type | +| ----- | ---- | +| m | [MarioState](structs.md#MarioState) | +| a0 | `integer` | + +### Returns +- None + +### C Prototype +`void reset_rumble_timers_2(struct MarioState* m, s32 a0);` + +[:arrow_up_small:](#) + +
+ +--- +# functions from save_file.h + +
+ + +## [get_level_num_from_course_num](#get_level_num_from_course_num) + +### Description +Gets the course number's corresponding level number + +### Lua Example +`local integerValue = get_level_num_from_course_num(courseNum)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseNum | `integer` | + +### Returns +- `integer` + +### C Prototype +`s8 get_level_num_from_course_num(s16 courseNum);` + +[:arrow_up_small:](#) + +
+ +## [get_level_course_num](#get_level_course_num) + +### Description +Gets the level number's corresponding course number + +### Lua Example +`local integerValue = get_level_course_num(levelNum)` + +### Parameters +| Field | Type | +| ----- | ---- | +| levelNum | `integer` | + +### Returns +- `integer` + +### C Prototype +`s8 get_level_course_num(s16 levelNum);` + +[:arrow_up_small:](#) + +
+ +## [touch_coin_score_age](#touch_coin_score_age) + +### Description +Marks the coin score for a specific course as the newest among all save files. Adjusts the age of other scores to reflect the update. Useful for leaderboard tracking or displaying recent progress + +### Lua Example +`touch_coin_score_age(fileIndex, courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | + +### Returns +- None + +### C Prototype +`void touch_coin_score_age(s32 fileIndex, s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_do_save](#save_file_do_save) + +### Description +Saves the current state of the game into a specified save file. Includes data verification and backup management. Useful for maintaining game progress during play or when saving manually + +### Lua Example +`save_file_do_save(fileIndex, forceSave)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| forceSave | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_do_save(s32 fileIndex, s8 forceSave);` + +[:arrow_up_small:](#) + +
+ +## [save_file_erase](#save_file_erase) + +### Description +Erases all data in a specified save file, including backup slots. Marks the save file as modified and performs a save to apply the changes. Useful for resetting a save file to its default state + +### Lua Example +`save_file_erase(fileIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_erase(s32 fileIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_erase_current_backup_save](#save_file_erase_current_backup_save) + +### Description +Erases the backup data for the current save file without affecting the primary save data. Reloads the save file afterward + +### Lua Example +`save_file_erase_current_backup_save()` + +### Parameters +- None + +### Returns +- None + +### C Prototype +`void save_file_erase_current_backup_save(void);` + +[:arrow_up_small:](#) + +
+ +## [save_file_reload](#save_file_reload) + +### Description +Reloads the save file data into memory, optionally resetting all save files. Marks the save file as modified. Useful for reloading state after data corruption or during development debugging + +### Lua Example +`save_file_reload(load_all)` + +### Parameters +| Field | Type | +| ----- | ---- | +| load_all | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_reload(u8 load_all);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_max_coin_score](#save_file_get_max_coin_score) + +### Description +Determines the maximum coin score for a course across all save files. Returns the score along with the file index of the save containing it. Useful for leaderboard-style comparisons and overall progress tracking + +### Lua Example +`local integerValue = save_file_get_max_coin_score(courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| courseIndex | `integer` | + +### Returns +- `integer` + +### C Prototype +`u32 save_file_get_max_coin_score(s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_course_star_count](#save_file_get_course_star_count) + +### Description +Calculates the total number of stars collected in a specific course for a given save file. Useful for determining completion status of individual levels + +### Lua Example +`local integerValue = save_file_get_course_star_count(fileIndex, courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | + +### Returns +- `integer` + +### C Prototype +`s32 save_file_get_course_star_count(s32 fileIndex, s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_total_star_count](#save_file_get_total_star_count) + +### Description +Calculates the total number of stars collected across multiple courses within a specified range. Useful for determining the overall progress toward game completion + +### Lua Example +`local integerValue = save_file_get_total_star_count(fileIndex, minCourse, maxCourse)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| minCourse | `integer` | +| maxCourse | `integer` | + +### Returns +- `integer` + +### C Prototype +`s32 save_file_get_total_star_count(s32 fileIndex, s32 minCourse, s32 maxCourse);` + +[:arrow_up_small:](#) + +
+ +## [save_file_set_flags](#save_file_set_flags) + +### Description +Adds new flags to the save file's flag bitmask. Useful for updating progress or triggering new gameplay features + +### Lua Example +`save_file_set_flags(flags)` + +### Parameters +| Field | Type | +| ----- | ---- | +| flags | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_set_flags(u32 flags);` + +[:arrow_up_small:](#) + +
+ +## [save_file_clear_flags](#save_file_clear_flags) + +### Description +Clears specific flags in the current save file. The flags are specified as a bitmask in the `flags` parameter. Ensures that the save file remains valid after clearing. Useful for removing specific game states, such as collected items or completed objectives, without resetting the entire save + +### Lua Example +`save_file_clear_flags(flags)` + +### Parameters +| Field | Type | +| ----- | ---- | +| flags | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_clear_flags(u32 flags);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_flags](#save_file_get_flags) + +### Description +Retrieves the bitmask of flags representing the current state of the save file. Flags indicate collected items, completed objectives, and other game states. Useful for checking specific game progress details + +### Lua Example +`local integerValue = save_file_get_flags()` + +### Parameters +- None + +### Returns +- `integer` + +### C Prototype +`u32 save_file_get_flags(void);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_star_flags](#save_file_get_star_flags) + +### Description +Retrieves the bitmask of stars collected in a specific course or castle secret stars (-1). Useful for evaluating level progress and completion + +### Lua Example +`local integerValue = save_file_get_star_flags(fileIndex, courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | + +### Returns +- `integer` + +### C Prototype +`u32 save_file_get_star_flags(s32 fileIndex, s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_set_star_flags](#save_file_set_star_flags) + +### Description +Adds specific star flags to the save file, indicating collected stars for a course or castle secret stars. Updates the save file flags as necessary. Useful for recording progress after star collection + +### Lua Example +`save_file_set_star_flags(fileIndex, courseIndex, starFlags)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | +| starFlags | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_set_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlags);` + +[:arrow_up_small:](#) + +
+ +## [save_file_remove_star_flags](#save_file_remove_star_flags) + +### Description +Removes specific star flags from the save file. This modifies the bitmask representing collected stars for a course or castle secret stars. Useful for undoing progress or debugging collected stars + +### Lua Example +`save_file_remove_star_flags(fileIndex, courseIndex, starFlagsToRemove)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | +| starFlagsToRemove | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_remove_star_flags(s32 fileIndex, s32 courseIndex, u32 starFlagsToRemove);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_course_coin_score](#save_file_get_course_coin_score) + +### Description +Returns the highest coin score for a specified course in the save file. Performs checks to ensure the coin score is valid. Useful for tracking player achievements and high scores + +### Lua Example +`local integerValue = save_file_get_course_coin_score(fileIndex, courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | + +### Returns +- `integer` + +### C Prototype +`s32 save_file_get_course_coin_score(s32 fileIndex, s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_set_course_coin_score](#save_file_set_course_coin_score) + +### Description +Updates the coin score for a specific course in the save file. The new score is provided in the `coinScore` parameter. Useful for manually setting achievements such as high coin counts in individual levels + +### Lua Example +`save_file_set_course_coin_score(fileIndex, courseIndex, coinScore)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | +| coinScore | `integer` | + +### Returns +- None + +### C Prototype +`void save_file_set_course_coin_score(s32 fileIndex, s32 courseIndex, u8 coinScore);` + +[:arrow_up_small:](#) + +
+ +## [save_file_is_cannon_unlocked](#save_file_is_cannon_unlocked) + +### Description +Checks whether the cannon in the specified course is unlocked. Returns true if the cannon is unlocked, otherwise false. Useful for tracking course-specific progress and enabling shortcuts + +### Lua Example +`local integerValue = save_file_is_cannon_unlocked(fileIndex, courseIndex)` + +### Parameters +| Field | Type | +| ----- | ---- | +| fileIndex | `integer` | +| courseIndex | `integer` | + +### Returns +- `integer` + +### C Prototype +`s32 save_file_is_cannon_unlocked(s32 fileIndex, s32 courseIndex);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_cap_pos](#save_file_get_cap_pos) + +### Description +Retrieves the current position of Mario's cap, if it is on the ground in the current level and area. The position is stored in the provided `capPos` parameter. Useful for tracking the cap's location after it has been dropped or lost + +### Lua Example +`local integerValue = save_file_get_cap_pos(capPos)` + +### Parameters +| Field | Type | +| ----- | ---- | +| capPos | [Vec3s](structs.md#Vec3s) | + +### Returns +- `integer` + +### C Prototype +`s32 save_file_get_cap_pos(OUT Vec3s capPos);` + +[:arrow_up_small:](#) + +
+ +## [save_file_get_sound_mode](#save_file_get_sound_mode) + +### Description +Returns the current sound mode (e.g., stereo, mono) stored in the save file. Useful for checking the audio output preferences when loading a save + +### Lua Example +`local integerValue = save_file_get_sound_mode()` + +### Parameters +- None + +### Returns +- `integer` + +### C Prototype +`u16 save_file_get_sound_mode(void);` + +[:arrow_up_small:](#) + +
+ --- # functions from seqplayer.h diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 7ad8bc456..ffa53a803 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1338,6 +1338,43 @@
+- mod_fs.h + - [mod_fs_exists](functions-5.md#mod_fs_exists) + - [mod_fs_get](functions-5.md#mod_fs_get) + - [mod_fs_reload](functions-5.md#mod_fs_reload) + - [mod_fs_create](functions-5.md#mod_fs_create) + - [mod_fs_delete](functions-5.md#mod_fs_delete) + - [mod_fs_save](functions-5.md#mod_fs_save) + - [mod_fs_set_public](functions-5.md#mod_fs_set_public) + - [mod_fs_get_filename](functions-5.md#mod_fs_get_filename) + - [mod_fs_get_file](functions-5.md#mod_fs_get_file) + - [mod_fs_create_file](functions-5.md#mod_fs_create_file) + - [mod_fs_move_file](functions-5.md#mod_fs_move_file) + - [mod_fs_copy_file](functions-5.md#mod_fs_copy_file) + - [mod_fs_delete_file](functions-5.md#mod_fs_delete_file) + - [mod_fs_clear](functions-5.md#mod_fs_clear) + - [mod_fs_file_read_bool](functions-5.md#mod_fs_file_read_bool) + - [mod_fs_file_read_integer](functions-5.md#mod_fs_file_read_integer) + - [mod_fs_file_read_number](functions-5.md#mod_fs_file_read_number) + - [mod_fs_file_read_bytes](functions-5.md#mod_fs_file_read_bytes) + - [mod_fs_file_read_string](functions-5.md#mod_fs_file_read_string) + - [mod_fs_file_read_line](functions-5.md#mod_fs_file_read_line) + - [mod_fs_file_write_bool](functions-5.md#mod_fs_file_write_bool) + - [mod_fs_file_write_integer](functions-5.md#mod_fs_file_write_integer) + - [mod_fs_file_write_number](functions-5.md#mod_fs_file_write_number) + - [mod_fs_file_write_bytes](functions-5.md#mod_fs_file_write_bytes) + - [mod_fs_file_write_string](functions-5.md#mod_fs_file_write_string) + - [mod_fs_file_write_line](functions-5.md#mod_fs_file_write_line) + - [mod_fs_file_seek](functions-5.md#mod_fs_file_seek) + - [mod_fs_file_is_eof](functions-5.md#mod_fs_file_is_eof) + - [mod_fs_file_fill](functions-5.md#mod_fs_file_fill) + - [mod_fs_file_erase](functions-5.md#mod_fs_file_erase) + - [mod_fs_file_set_public](functions-5.md#mod_fs_file_set_public) + - [mod_fs_hide_errors](functions-5.md#mod_fs_hide_errors) + - [mod_fs_get_last_error](functions-5.md#mod_fs_get_last_error) + +
+ - mod_storage.h - [mod_storage_save](functions-5.md#mod_storage_save) - [mod_storage_save_number](functions-5.md#mod_storage_save_number) @@ -1698,46 +1735,46 @@
- object_list_processor.h - - [set_object_respawn_info_bits](functions-5.md#set_object_respawn_info_bits) + - [set_object_respawn_info_bits](functions-6.md#set_object_respawn_info_bits)
- platform_displacement.h - - [apply_platform_displacement](functions-5.md#apply_platform_displacement) + - [apply_platform_displacement](functions-6.md#apply_platform_displacement)
- rumble_init.h - - [queue_rumble_data](functions-5.md#queue_rumble_data) - - [queue_rumble_data_object](functions-5.md#queue_rumble_data_object) - - [queue_rumble_data_mario](functions-5.md#queue_rumble_data_mario) - - [reset_rumble_timers](functions-5.md#reset_rumble_timers) - - [reset_rumble_timers_2](functions-5.md#reset_rumble_timers_2) + - [queue_rumble_data](functions-6.md#queue_rumble_data) + - [queue_rumble_data_object](functions-6.md#queue_rumble_data_object) + - [queue_rumble_data_mario](functions-6.md#queue_rumble_data_mario) + - [reset_rumble_timers](functions-6.md#reset_rumble_timers) + - [reset_rumble_timers_2](functions-6.md#reset_rumble_timers_2)
- save_file.h - - [get_level_num_from_course_num](functions-5.md#get_level_num_from_course_num) - - [get_level_course_num](functions-5.md#get_level_course_num) - - [touch_coin_score_age](functions-5.md#touch_coin_score_age) - - [save_file_do_save](functions-5.md#save_file_do_save) - - [save_file_erase](functions-5.md#save_file_erase) - - [save_file_erase_current_backup_save](functions-5.md#save_file_erase_current_backup_save) - - [save_file_reload](functions-5.md#save_file_reload) - - [save_file_get_max_coin_score](functions-5.md#save_file_get_max_coin_score) - - [save_file_get_course_star_count](functions-5.md#save_file_get_course_star_count) - - [save_file_get_total_star_count](functions-5.md#save_file_get_total_star_count) - - [save_file_set_flags](functions-5.md#save_file_set_flags) - - [save_file_clear_flags](functions-5.md#save_file_clear_flags) - - [save_file_get_flags](functions-5.md#save_file_get_flags) - - [save_file_get_star_flags](functions-5.md#save_file_get_star_flags) - - [save_file_set_star_flags](functions-5.md#save_file_set_star_flags) - - [save_file_remove_star_flags](functions-5.md#save_file_remove_star_flags) - - [save_file_get_course_coin_score](functions-5.md#save_file_get_course_coin_score) - - [save_file_set_course_coin_score](functions-5.md#save_file_set_course_coin_score) - - [save_file_is_cannon_unlocked](functions-5.md#save_file_is_cannon_unlocked) - - [save_file_get_cap_pos](functions-5.md#save_file_get_cap_pos) - - [save_file_get_sound_mode](functions-5.md#save_file_get_sound_mode) + - [get_level_num_from_course_num](functions-6.md#get_level_num_from_course_num) + - [get_level_course_num](functions-6.md#get_level_course_num) + - [touch_coin_score_age](functions-6.md#touch_coin_score_age) + - [save_file_do_save](functions-6.md#save_file_do_save) + - [save_file_erase](functions-6.md#save_file_erase) + - [save_file_erase_current_backup_save](functions-6.md#save_file_erase_current_backup_save) + - [save_file_reload](functions-6.md#save_file_reload) + - [save_file_get_max_coin_score](functions-6.md#save_file_get_max_coin_score) + - [save_file_get_course_star_count](functions-6.md#save_file_get_course_star_count) + - [save_file_get_total_star_count](functions-6.md#save_file_get_total_star_count) + - [save_file_set_flags](functions-6.md#save_file_set_flags) + - [save_file_clear_flags](functions-6.md#save_file_clear_flags) + - [save_file_get_flags](functions-6.md#save_file_get_flags) + - [save_file_get_star_flags](functions-6.md#save_file_get_star_flags) + - [save_file_set_star_flags](functions-6.md#save_file_set_star_flags) + - [save_file_remove_star_flags](functions-6.md#save_file_remove_star_flags) + - [save_file_get_course_coin_score](functions-6.md#save_file_get_course_coin_score) + - [save_file_set_course_coin_score](functions-6.md#save_file_set_course_coin_score) + - [save_file_is_cannon_unlocked](functions-6.md#save_file_is_cannon_unlocked) + - [save_file_get_cap_pos](functions-6.md#save_file_get_cap_pos) + - [save_file_get_sound_mode](functions-6.md#save_file_get_sound_mode)
diff --git a/docs/lua/structs.md b/docs/lua/structs.md index f9fa72a49..5ace40985 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -77,6 +77,8 @@ - [ModAudio](#ModAudio) - [ModAudioSampleCopies](#ModAudioSampleCopies) - [ModFile](#ModFile) +- [ModFs](#ModFs) +- [ModFsFile](#ModFsFile) - [ModeTransitionInfo](#ModeTransitionInfo) - [NametagsSettings](#NametagsSettings) - [NetworkPlayer](#NetworkPlayer) @@ -1964,6 +1966,35 @@
+## [ModFs](#ModFs) + +| Field | Type | Access | +| ----- | ---- | ------ | +| isPublic | `boolean` | read-only | +| mod | [Mod](structs.md#Mod) | read-only | +| modPath | `string` | read-only | +| numFiles | `integer` | read-only | +| totalSize | `integer` | read-only | + +[:arrow_up_small:](#) + +
+ +## [ModFsFile](#ModFsFile) + +| Field | Type | Access | +| ----- | ---- | ------ | +| filepath | `string` | read-only | +| isPublic | `boolean` | read-only | +| isText | `boolean` | read-only | +| modFs | [ModFs](structs.md#ModFs) | read-only | +| offset | `integer` | read-only | +| size | `integer` | read-only | + +[:arrow_up_small:](#) + +
+ ## [ModeTransitionInfo](#ModeTransitionInfo) | Field | Type | Access | diff --git a/include/types.h b/include/types.h index a891f6cda..dc20060b2 100644 --- a/include/types.h +++ b/include/types.h @@ -40,10 +40,6 @@ struct Controller OSContPad *controllerData; }; -// A macro to tell autogen which function parameters are modified during the function call and should be pushed again -// Only works with Vec3, Mat4 and Color types -#define OUT - typedef f32 Vec2f[2]; // X, Y typedef s16 Vec2s[2]; typedef s32 Vec2i[2]; @@ -602,5 +598,6 @@ struct TextureInfo #include "game/characters.h" #include "data/dynos.c.h" +#include "src/pc/lua/smlua_autogen.h" #endif // _SM64_TYPES_H_ diff --git a/src/pc/lua/smlua_autogen.h b/src/pc/lua/smlua_autogen.h new file mode 100644 index 000000000..53b49e5ed --- /dev/null +++ b/src/pc/lua/smlua_autogen.h @@ -0,0 +1,16 @@ +#ifndef SMLUA_AUTOGEN_H +#define SMLUA_AUTOGEN_H + +// +// Autogen macros +// + +// A macro to tell autogen which function parameters are modified during the function call and should be pushed again +// Only works with Vec3, Mat4 and Color types +#define OUT + +// A macro to tell autogen this parameter can be omitted and replaced by 'nil' during a function call +// Optional parameters must be contiguous until the last parameter (a mandatory parameter following an optional parameter is not allowed) +#define OPTIONAL + +#endif // SMLUA_AUTOGEN_H diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index 4d77a56a0..5d0f3d606 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -21,6 +21,7 @@ #include "src/pc/network/network.h" #include "src/game/hardcoded.h" #include "src/pc/mods/mod.h" +#include "src/pc/mods/mod_fs.h" #include "src/pc/lua/utils/smlua_audio_utils.h" #include "src/game/paintings.h" #include "src/pc/djui/djui_types.h" @@ -1620,6 +1621,25 @@ static struct LuaObjectField sModFileFields[LUA_MOD_FILE_FIELD_COUNT] = { { "wroteBytes", LVT_U64, offsetof(struct ModFile, wroteBytes), true, LOT_NONE, 1, sizeof(u64) }, }; +#define LUA_MOD_FS_FIELD_COUNT 5 +static struct LuaObjectField sModFsFields[LUA_MOD_FS_FIELD_COUNT] = { + { "isPublic", LVT_BOOL, offsetof(struct ModFs, isPublic), true, LOT_NONE, 1, sizeof(bool) }, + { "mod", LVT_COBJECT_P, offsetof(struct ModFs, mod), true, LOT_MOD, 1, sizeof(struct Mod*) }, + { "modPath", LVT_STRING, offsetof(struct ModFs, modPath), true, LOT_NONE, 1, sizeof(char) }, + { "numFiles", LVT_U16, offsetof(struct ModFs, numFiles), true, LOT_NONE, 1, sizeof(u16) }, + { "totalSize", LVT_U32, offsetof(struct ModFs, totalSize), true, LOT_NONE, 1, sizeof(u32) }, +}; + +#define LUA_MOD_FS_FILE_FIELD_COUNT 6 +static struct LuaObjectField sModFsFileFields[LUA_MOD_FS_FILE_FIELD_COUNT] = { + { "filepath", LVT_STRING, offsetof(struct ModFsFile, filepath), true, LOT_NONE, 1, sizeof(char) }, + { "isPublic", LVT_BOOL, offsetof(struct ModFsFile, isPublic), true, LOT_NONE, 1, sizeof(bool) }, + { "isText", LVT_BOOL, offsetof(struct ModFsFile, isText), true, LOT_NONE, 1, sizeof(bool) }, + { "modFs", LVT_COBJECT_P, offsetof(struct ModFsFile, modFs), true, LOT_MODFS, 1, sizeof(struct ModFs*) }, + { "offset", LVT_U32, offsetof(struct ModFsFile, offset), true, LOT_NONE, 1, sizeof(u32) }, + { "size", LVT_U32, offsetof(struct ModFsFile, size), true, LOT_NONE, 1, sizeof(u32) }, +}; + #define LUA_MODE_TRANSITION_INFO_FIELD_COUNT 6 static struct LuaObjectField sModeTransitionInfoFields[LUA_MODE_TRANSITION_INFO_FIELD_COUNT] = { { "frame", LVT_S16, offsetof(struct ModeTransitionInfo, frame), false, LOT_NONE, 1, sizeof(s16) }, @@ -2925,6 +2945,8 @@ struct LuaObjectTable sLuaObjectAutogenTable[LOT_AUTOGEN_MAX - LOT_AUTOGEN_MIN] { LOT_MODAUDIO, sModAudioFields, LUA_MOD_AUDIO_FIELD_COUNT }, { LOT_MODAUDIOSAMPLECOPIES, sModAudioSampleCopiesFields, LUA_MOD_AUDIO_SAMPLE_COPIES_FIELD_COUNT }, { LOT_MODFILE, sModFileFields, LUA_MOD_FILE_FIELD_COUNT }, + { LOT_MODFS, sModFsFields, LUA_MOD_FS_FIELD_COUNT }, + { LOT_MODFSFILE, sModFsFileFields, LUA_MOD_FS_FILE_FIELD_COUNT }, { LOT_MODETRANSITIONINFO, sModeTransitionInfoFields, LUA_MODE_TRANSITION_INFO_FIELD_COUNT }, { LOT_NAMETAGSSETTINGS, sNametagsSettingsFields, LUA_NAMETAGS_SETTINGS_FIELD_COUNT }, { LOT_NETWORKPLAYER, sNetworkPlayerFields, LUA_NETWORK_PLAYER_FIELD_COUNT }, @@ -3051,6 +3073,8 @@ const char *sLuaLotNames[] = { [LOT_MODAUDIO] = "ModAudio", [LOT_MODAUDIOSAMPLECOPIES] = "ModAudioSampleCopies", [LOT_MODFILE] = "ModFile", + [LOT_MODFS] = "ModFs", + [LOT_MODFSFILE] = "ModFsFile", [LOT_MODETRANSITIONINFO] = "ModeTransitionInfo", [LOT_NAMETAGSSETTINGS] = "NametagsSettings", [LOT_NETWORKPLAYER] = "NetworkPlayer", diff --git a/src/pc/lua/smlua_cobject_autogen.h b/src/pc/lua/smlua_cobject_autogen.h index ff6fb9490..26351cf2c 100644 --- a/src/pc/lua/smlua_cobject_autogen.h +++ b/src/pc/lua/smlua_cobject_autogen.h @@ -96,6 +96,8 @@ enum LuaObjectAutogenType { LOT_MODAUDIO, LOT_MODAUDIOSAMPLECOPIES, LOT_MODFILE, + LOT_MODFS, + LOT_MODFSFILE, LOT_MODETRANSITIONINFO, LOT_NAMETAGSSETTINGS, LOT_NETWORKPLAYER, diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index d795f0dcb..bd85a46ca 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -2227,6 +2227,22 @@ char gSmluaConstants[] = "" "GRAB_POS_LIGHT_OBJ=1\n" "GRAB_POS_HEAVY_OBJ=2\n" "GRAB_POS_BOWSER=3\n" +"MOD_FS_MAX_SIZE=0x1000000\n" +"MOD_FS_MAX_FILES=0x100\n" +"MOD_FS_MAX_PATH=0x100\n" +"INT_TYPE_U8=0\n" +"INT_TYPE_U16=1\n" +"INT_TYPE_U32=2\n" +"INT_TYPE_U64=3\n" +"INT_TYPE_S8=4\n" +"INT_TYPE_S16=5\n" +"INT_TYPE_S32=6\n" +"INT_TYPE_S64=7\n" +"FLOAT_TYPE_F32=0\n" +"FLOAT_TYPE_F64=1\n" +"FILE_SEEK_SET=0\n" +"FILE_SEEK_CUR=1\n" +"FILE_SEEK_END=2\n" "MAX_KEYS=4096\n" "MAX_KEY_VALUE_LENGTH=1024\n" "SYNC_DISTANCE_ONLY_DEATH=-1\n" diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index b227a3ce1..8b547498b 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -43,6 +43,7 @@ #include "src/game/behavior_actions.h" #include "src/game/mario_misc.h" #include "src/pc/mods/mod_storage.h" +#include "src/pc/mods/mod_fs.h" #include "src/pc/utils/misc.h" #include "src/game/level_update.h" #include "src/game/area.h" @@ -22201,6 +22202,628 @@ int smlua_func_delta_interpolate_vec3s(lua_State* L) { return 1; } + ////////////// + // mod_fs.h // +////////////// + +int smlua_func_mod_fs_exists(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top < 0 || top > 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "mod_fs_exists", 0, 1, top); + return 0; + } + + const char* modPath = (const char*) NULL; + if (top >= 1) { + modPath = smlua_to_string(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_exists"); return 0; } + } + + lua_pushboolean(L, mod_fs_exists(modPath)); + + return 1; +} + +int smlua_func_mod_fs_get(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top < 0 || top > 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "mod_fs_get", 0, 1, top); + return 0; + } + + const char* modPath = (const char*) NULL; + if (top >= 1) { + modPath = smlua_to_string(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_get"); return 0; } + } + + smlua_push_object(L, LOT_MODFS, mod_fs_get(modPath), NULL); + + return 1; +} + +int smlua_func_mod_fs_reload(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top < 0 || top > 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected between %u and %u, Received %u", "mod_fs_reload", 0, 1, top); + return 0; + } + + const char* modPath = (const char*) NULL; + if (top >= 1) { + modPath = smlua_to_string(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_reload"); return 0; } + } + + smlua_push_object(L, LOT_MODFS, mod_fs_reload(modPath), NULL); + + return 1; +} + +int smlua_func_mod_fs_create(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_create", 0, top); + return 0; + } + + + smlua_push_object(L, LOT_MODFS, mod_fs_create(), NULL); + + return 1; +} + +int smlua_func_mod_fs_delete(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_delete", 0, top); + return 0; + } + + + lua_pushboolean(L, mod_fs_delete()); + + return 1; +} + +int smlua_func_mod_fs_save(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_save", 0, top); + return 0; + } + + + lua_pushboolean(L, mod_fs_save()); + + return 1; +} + +int smlua_func_mod_fs_set_public(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_set_public", 1, top); + return 0; + } + + bool pub = smlua_to_boolean(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_set_public"); return 0; } + + lua_pushboolean(L, mod_fs_set_public(pub)); + + return 1; +} + +int smlua_func_mod_fs_get_filename(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_get_filename", 2, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_get_filename"); return 0; } + u16 index = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_get_filename"); return 0; } + + lua_pushstring(L, mod_fs_get_filename(modFs, index)); + + return 1; +} + +int smlua_func_mod_fs_get_file(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_get_file", 2, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_get_file"); return 0; } + const char* filepath = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_get_file"); return 0; } + + smlua_push_object(L, LOT_MODFSFILE, mod_fs_get_file(modFs, filepath), NULL); + + return 1; +} + +int smlua_func_mod_fs_create_file(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 3) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_create_file", 3, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_create_file"); return 0; } + const char* filepath = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_create_file"); return 0; } + bool text = smlua_to_boolean(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_create_file"); return 0; } + + smlua_push_object(L, LOT_MODFSFILE, mod_fs_create_file(modFs, filepath, text), NULL); + + return 1; +} + +int smlua_func_mod_fs_move_file(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 4) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_move_file", 4, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_move_file"); return 0; } + const char* oldpath = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_move_file"); return 0; } + const char* newpath = smlua_to_string(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_move_file"); return 0; } + bool overwriteExisting = smlua_to_boolean(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "mod_fs_move_file"); return 0; } + + lua_pushboolean(L, mod_fs_move_file(modFs, oldpath, newpath, overwriteExisting)); + + return 1; +} + +int smlua_func_mod_fs_copy_file(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 4) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_copy_file", 4, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_copy_file"); return 0; } + const char* srcpath = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_copy_file"); return 0; } + const char* dstpath = smlua_to_string(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_copy_file"); return 0; } + bool overwriteExisting = smlua_to_boolean(L, 4); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 4, "mod_fs_copy_file"); return 0; } + + lua_pushboolean(L, mod_fs_copy_file(modFs, srcpath, dstpath, overwriteExisting)); + + return 1; +} + +int smlua_func_mod_fs_delete_file(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_delete_file", 2, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_delete_file"); return 0; } + const char* filepath = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_delete_file"); return 0; } + + lua_pushboolean(L, mod_fs_delete_file(modFs, filepath)); + + return 1; +} + +int smlua_func_mod_fs_clear(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_clear", 1, top); + return 0; + } + + struct ModFs* modFs = (struct ModFs*)smlua_to_cobject(L, 1, LOT_MODFS); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_clear"); return 0; } + + lua_pushboolean(L, mod_fs_clear(modFs)); + + return 1; +} + +int smlua_func_mod_fs_file_read_bool(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_bool", 1, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_bool"); return 0; } + + lua_pushboolean(L, mod_fs_file_read_bool(file)); + + return 1; +} + +int smlua_func_mod_fs_file_read_integer(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_integer", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_integer"); return 0; } + int intType = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_read_integer"); return 0; } + + lua_pushinteger(L, mod_fs_file_read_integer(file, intType)); + + return 1; +} + +int smlua_func_mod_fs_file_read_number(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_number", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_number"); return 0; } + int floatType = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_read_number"); return 0; } + + lua_pushnumber(L, mod_fs_file_read_number(file, floatType)); + + return 1; +} + +int smlua_func_mod_fs_file_read_bytes(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_bytes", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_bytes"); return 0; } + u32 length = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_read_bytes"); return 0; } + + smlua_push_bytestring(L, mod_fs_file_read_bytes(file, length)); + + return 1; +} + +int smlua_func_mod_fs_file_read_string(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_string", 1, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_string"); return 0; } + + lua_pushstring(L, mod_fs_file_read_string(file)); + + return 1; +} + +int smlua_func_mod_fs_file_read_line(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_read_line", 1, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_read_line"); return 0; } + + lua_pushstring(L, mod_fs_file_read_line(file)); + + return 1; +} + +int smlua_func_mod_fs_file_write_bool(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_bool", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_bool"); return 0; } + bool value = smlua_to_boolean(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_bool"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_bool(file, value)); + + return 1; +} + +int smlua_func_mod_fs_file_write_integer(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 3) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_integer", 3, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_integer"); return 0; } + lua_Integer value = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_integer"); return 0; } + int intType = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_file_write_integer"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_integer(file, value, intType)); + + return 1; +} + +int smlua_func_mod_fs_file_write_number(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 3) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_number", 3, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_number"); return 0; } + lua_Number value = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_number"); return 0; } + int floatType = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_file_write_number"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_number(file, value, floatType)); + + return 1; +} + +int smlua_func_mod_fs_file_write_bytes(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_bytes", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_bytes"); return 0; } + ByteString bytestring = smlua_to_bytestring(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_bytes"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_bytes(file, bytestring)); + + return 1; +} + +int smlua_func_mod_fs_file_write_string(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_string", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_string"); return 0; } + const char* str = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_string"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_string(file, str)); + + return 1; +} + +int smlua_func_mod_fs_file_write_line(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_write_line", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_write_line"); return 0; } + const char* str = smlua_to_string(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_write_line"); return 0; } + + lua_pushboolean(L, mod_fs_file_write_line(file, str)); + + return 1; +} + +int smlua_func_mod_fs_file_seek(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 3) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_seek", 3, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_seek"); return 0; } + s32 offset = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_seek"); return 0; } + int origin = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_file_seek"); return 0; } + + lua_pushboolean(L, mod_fs_file_seek(file, offset, origin)); + + return 1; +} + +int smlua_func_mod_fs_file_is_eof(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_is_eof", 1, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_is_eof"); return 0; } + + lua_pushboolean(L, mod_fs_file_is_eof(file)); + + return 1; +} + +int smlua_func_mod_fs_file_fill(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 3) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_fill", 3, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_fill"); return 0; } + u8 byte = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_fill"); return 0; } + u32 length = smlua_to_integer(L, 3); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 3, "mod_fs_file_fill"); return 0; } + + lua_pushboolean(L, mod_fs_file_fill(file, byte, length)); + + return 1; +} + +int smlua_func_mod_fs_file_erase(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_erase", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_erase"); return 0; } + u32 length = smlua_to_integer(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_erase"); return 0; } + + lua_pushboolean(L, mod_fs_file_erase(file, length)); + + return 1; +} + +int smlua_func_mod_fs_file_set_public(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 2) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_file_set_public", 2, top); + return 0; + } + + struct ModFsFile* file = (struct ModFsFile*)smlua_to_cobject(L, 1, LOT_MODFSFILE); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_file_set_public"); return 0; } + bool pub = smlua_to_boolean(L, 2); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 2, "mod_fs_file_set_public"); return 0; } + + lua_pushboolean(L, mod_fs_file_set_public(file, pub)); + + return 1; +} + +int smlua_func_mod_fs_hide_errors(lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 1) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_hide_errors", 1, top); + return 0; + } + + bool hide = smlua_to_boolean(L, 1); + if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "mod_fs_hide_errors"); return 0; } + + mod_fs_hide_errors(hide); + + return 1; +} + +int smlua_func_mod_fs_get_last_error(UNUSED lua_State* L) { + if (L == NULL) { return 0; } + + int top = lua_gettop(L); + if (top != 0) { + LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "mod_fs_get_last_error", 0, top); + return 0; + } + + + lua_pushstring(L, mod_fs_get_last_error()); + + return 1; +} + /////////////////// // mod_storage.h // /////////////////// @@ -36863,6 +37486,41 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "delta_interpolate_vec3f", smlua_func_delta_interpolate_vec3f); smlua_bind_function(L, "delta_interpolate_vec3s", smlua_func_delta_interpolate_vec3s); + // mod_fs.h + smlua_bind_function(L, "mod_fs_exists", smlua_func_mod_fs_exists); + smlua_bind_function(L, "mod_fs_get", smlua_func_mod_fs_get); + smlua_bind_function(L, "mod_fs_reload", smlua_func_mod_fs_reload); + smlua_bind_function(L, "mod_fs_create", smlua_func_mod_fs_create); + smlua_bind_function(L, "mod_fs_delete", smlua_func_mod_fs_delete); + smlua_bind_function(L, "mod_fs_save", smlua_func_mod_fs_save); + smlua_bind_function(L, "mod_fs_set_public", smlua_func_mod_fs_set_public); + smlua_bind_function(L, "mod_fs_get_filename", smlua_func_mod_fs_get_filename); + smlua_bind_function(L, "mod_fs_get_file", smlua_func_mod_fs_get_file); + smlua_bind_function(L, "mod_fs_create_file", smlua_func_mod_fs_create_file); + smlua_bind_function(L, "mod_fs_move_file", smlua_func_mod_fs_move_file); + smlua_bind_function(L, "mod_fs_copy_file", smlua_func_mod_fs_copy_file); + smlua_bind_function(L, "mod_fs_delete_file", smlua_func_mod_fs_delete_file); + smlua_bind_function(L, "mod_fs_clear", smlua_func_mod_fs_clear); + smlua_bind_function(L, "mod_fs_file_read_bool", smlua_func_mod_fs_file_read_bool); + smlua_bind_function(L, "mod_fs_file_read_integer", smlua_func_mod_fs_file_read_integer); + smlua_bind_function(L, "mod_fs_file_read_number", smlua_func_mod_fs_file_read_number); + smlua_bind_function(L, "mod_fs_file_read_bytes", smlua_func_mod_fs_file_read_bytes); + smlua_bind_function(L, "mod_fs_file_read_string", smlua_func_mod_fs_file_read_string); + smlua_bind_function(L, "mod_fs_file_read_line", smlua_func_mod_fs_file_read_line); + smlua_bind_function(L, "mod_fs_file_write_bool", smlua_func_mod_fs_file_write_bool); + smlua_bind_function(L, "mod_fs_file_write_integer", smlua_func_mod_fs_file_write_integer); + smlua_bind_function(L, "mod_fs_file_write_number", smlua_func_mod_fs_file_write_number); + smlua_bind_function(L, "mod_fs_file_write_bytes", smlua_func_mod_fs_file_write_bytes); + smlua_bind_function(L, "mod_fs_file_write_string", smlua_func_mod_fs_file_write_string); + smlua_bind_function(L, "mod_fs_file_write_line", smlua_func_mod_fs_file_write_line); + smlua_bind_function(L, "mod_fs_file_seek", smlua_func_mod_fs_file_seek); + smlua_bind_function(L, "mod_fs_file_is_eof", smlua_func_mod_fs_file_is_eof); + smlua_bind_function(L, "mod_fs_file_fill", smlua_func_mod_fs_file_fill); + smlua_bind_function(L, "mod_fs_file_erase", smlua_func_mod_fs_file_erase); + smlua_bind_function(L, "mod_fs_file_set_public", smlua_func_mod_fs_file_set_public); + smlua_bind_function(L, "mod_fs_hide_errors", smlua_func_mod_fs_hide_errors); + smlua_bind_function(L, "mod_fs_get_last_error", smlua_func_mod_fs_get_last_error); + // mod_storage.h smlua_bind_function(L, "mod_storage_save", smlua_func_mod_storage_save); smlua_bind_function(L, "mod_storage_save_number", smlua_func_mod_storage_save_number); diff --git a/src/pc/lua/smlua_utils.c b/src/pc/lua/smlua_utils.c index 4dd773ca0..aa4000f15 100644 --- a/src/pc/lua/smlua_utils.c +++ b/src/pc/lua/smlua_utils.c @@ -128,6 +128,18 @@ const char* smlua_to_string(lua_State* L, int index) { return lua_tostring(L, index); } +ByteString smlua_to_bytestring(lua_State* L, int index) { + ByteString bytestring = { NULL, 0 }; + if (lua_type(L, index) != LUA_TSTRING) { + LOG_LUA_LINE("smlua_to_string received improper type '%s'", luaL_typename(L, index)); + gSmLuaConvertSuccess = false; + return bytestring; + } + gSmLuaConvertSuccess = true; + bytestring.bytes = lua_tolstring(L, index, &bytestring.length); + return bytestring; +} + LuaFunction smlua_to_lua_function(lua_State* L, int index) { if (lua_type(L, index) == LUA_TNIL) { return 0; @@ -475,6 +487,14 @@ void smlua_push_table_field(int index, const char* name) { /////////////////////////////////////////////////////////////////////////////////////////// +void smlua_push_bytestring(lua_State* L, ByteString bytestring) { + if (bytestring.bytes) { + lua_pushlstring(L, bytestring.bytes, bytestring.length); + } else { + lua_pushnil(L); + } +} + void smlua_push_lnt(struct LSTNetworkType* lnt) { lua_State* L = gLuaState; switch (lnt->type) { diff --git a/src/pc/lua/smlua_utils.h b/src/pc/lua/smlua_utils.h index 26e2882a5..a0c310f01 100644 --- a/src/pc/lua/smlua_utils.h +++ b/src/pc/lua/smlua_utils.h @@ -6,6 +6,11 @@ typedef int LuaFunction; struct Packet; struct LSTNetworkType; +typedef struct ByteString { + const char *bytes; + size_t length; +} ByteString; + f32* smlua_get_vec3f_from_buffer(void); s16* smlua_get_vec3s_from_buffer(void); f32* smlua_get_vec4f_from_buffer(void); @@ -19,6 +24,7 @@ bool smlua_to_boolean(lua_State* L, int index); lua_Integer smlua_to_integer(lua_State* L, int index); lua_Number smlua_to_number(lua_State* L, int index); const char* smlua_to_string(lua_State* L, int index); +ByteString smlua_to_bytestring(lua_State* L, int index); LuaFunction smlua_to_lua_function(lua_State* L, int index); bool smlua_is_cobject(lua_State* L, int index, u16 lot); void* smlua_to_cobject(lua_State* L, int index, u16 lot); @@ -37,6 +43,7 @@ void smlua_push_string_field(int index, const char* name, const char* val); void smlua_push_nil_field(int index, const char* name); void smlua_push_table_field(int index, const char* name); +void smlua_push_bytestring(lua_State* L, ByteString bytestring); void smlua_push_lnt(struct LSTNetworkType* lnt); lua_Integer smlua_get_integer_field(int index, const char* name); diff --git a/src/pc/mods/mod_fs.cpp b/src/pc/mods/mod_fs.cpp new file mode 100644 index 000000000..cd0aeb30a --- /dev/null +++ b/src/pc/mods/mod_fs.cpp @@ -0,0 +1,1396 @@ +extern "C" { +#include "mod_fs.h" +#include "src/pc/fs/fs.h" +#include "src/pc/mods/mods_utils.h" +} +#include +#include +#include +#include +#include + +#define C_DEFINE extern "C" + +static std::vector sModFsList = {}; +static char sModFsFileReadStringBuf[MOD_FS_MAX_SIZE + 1]; + +#define MOD_FS_MAGIC "MODFSSM64COOPDX" +#define MOD_FS_HEADER_SIZE 32 + +// +// Error handling +// + +// Errors raised by `mod_fs` functions do not stop the code execution. +// When an error is raised: +// - the error message is printed to the console if `sModFsHideErrors` is false +// - `sModFsLastError` is filled with that error message +// - the function that raised it usually returns false or nil + +static bool sModFsHideErrors = false; +static char sModFsLastError[1024]; +static char sModFsErrorFunction[256]; + +#define mod_fs_reset_last_error() { \ + memset(sModFsLastError, 0, sizeof(sModFsLastError)); \ + snprintf(sModFsErrorFunction, sizeof(sModFsErrorFunction), "%s", __FUNCTION__); \ +} + +#define mod_fs_raise_error(fmt, ...) { \ + snprintf(sModFsLastError, sizeof(sModFsLastError), "%s: " fmt, sModFsErrorFunction, __VA_ARGS__); \ + if (!sModFsHideErrors) { \ + LOG_LUA_LINE("%s", sModFsLastError); \ + } \ +} + +// +// Pointers +// + +// Pointers to ModFs and ModFsFile must be referenced and checked +// to avoid mods keeping and accessing dangling pointers + +template +static std::set &mod_fs_get_pointers() { + static std::set sPointers = {}; + return sPointers; +} + +template +static bool mod_fs_is_valid_pointer(T *ptr) { + std::set &pointers = mod_fs_get_pointers(); + return pointers.find(ptr) != pointers.end(); +} + +template +static T *mod_fs_alloc() { + T *ptr = (T *) calloc(1, sizeof(T)); + if (ptr) { + std::set &pointers = mod_fs_get_pointers(); + pointers.insert(ptr); + } + return ptr; +} + +template +static bool mod_fs_free(T *ptr) { + if (mod_fs_is_valid_pointer(ptr)) { + std::set &pointers = mod_fs_get_pointers(); + pointers.erase(ptr); + free(ptr); + return true; + } + return false; +} + +// +// Utils +// + +static bool mod_fs_is_active_mod(struct ModFs *modFs) { + return gLuaActiveMod != NULL && modFs->mod == gLuaActiveMod; +} + +static bool mod_fs_get_modpath(const char *modPath, char *dest) { + if (modPath) { + snprintf(dest, SYS_MAX_PATH, "%s", modPath); + } else if (gLuaActiveMod) { + snprintf(dest, SYS_MAX_PATH, "%s", gLuaActiveMod->relativePath); + } else { + return false; + } + char *ext = strstr(dest, ".lua"); + if (ext) *ext = 0; + return true; +} + +static bool mod_fs_get_physical_filename(const char *modPath, char *dest) { + char realModPath[SYS_MAX_PATH]; + if (mod_fs_get_modpath(modPath, realModPath)) { + const char *path = fs_get_write_path(MOD_FS_DIRECTORY); + snprintf(dest, SYS_MAX_PATH, "%s/%s" MOD_FS_EXTENSION, path, realModPath); + normalize_path(dest); + return true; + } + return false; +} + +static FILE *mod_fs_get_file_handle(const char *modPath, const char *mode) { + char filename[SYS_MAX_PATH]; + if (mod_fs_get_physical_filename(modPath, filename)) { + return fopen(filename, mode); + } + return NULL; +} + +// +// ctor, dtor +// + +static struct ModFs *mod_fs_new() { + if (gLuaActiveMod) { + struct ModFs *modFs = mod_fs_alloc(); + if (!modFs) { + mod_fs_raise_error( + "failed to allocate modfs object", NULL + ); + return NULL; + } + modFs->mod = gLuaActiveMod; + mod_fs_get_modpath(NULL, modFs->modPath); + modFs->files = NULL; + modFs->numFiles = 0; + modFs->totalSize = 0; + modFs->isPublic = false; + return modFs; + } + return NULL; +} + +static void mod_fs_file_destroy(struct ModFsFile *file) { + if (file) { + free(file->data.bin); + memset(file, 0, sizeof(struct ModFsFile)); + } +} + +static void mod_fs_destroy(struct ModFs *modFs) { + for (u16 i = 0; modFs->files && i != modFs->numFiles; ++i) { + mod_fs_file_destroy(modFs->files[i]); + mod_fs_free(modFs->files[i]); + } + free(modFs->files); + memset(modFs, 0, sizeof(struct ModFs)); +} + +// +// Read +// + +#define mod_fs_read_raise_error(modFs, f, ...) { \ + mod_fs_destroy(modFs); \ + fclose(f); \ + mod_fs_raise_error(__VA_ARGS__); \ + return false; \ +} + +#define mod_fs_read_fread_or_fail(modFs, f, buf, size, count, ...) { \ + if (fread(buf, size, count, f) < count) { \ + mod_fs_read_raise_error(modFs, f, __VA_ARGS__); \ + } \ +} + +static bool mod_fs_read(const char *modPath, struct ModFs *modFs, bool readHeaderOnly) { + FILE *f = mod_fs_get_file_handle(modPath, "rb"); + if (f) { + + // get true modPath and mod + if (!mod_fs_get_modpath(modPath, modFs->modPath)) { + mod_fs_read_raise_error(modFs, f, + "unable to retrieve modPath from %s", modPath + ); + } + char activeModPath[SYS_MAX_PATH]; + if (mod_fs_get_modpath(NULL, activeModPath) && strcmp(modFs->modPath, activeModPath) == 0) { + modFs->mod = gLuaActiveMod; + } else { + modFs->mod = NULL; + } + + // read magic (last byte is version) + char magic[sizeof(MOD_FS_MAGIC)] = {0}; + mod_fs_read_fread_or_fail(modFs, f, magic, 1, sizeof(magic), + "modPath: %s - cannot read file magic", modFs->modPath + ); + if (memcmp(magic, MOD_FS_MAGIC, sizeof(MOD_FS_MAGIC) - 1) != 0) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - not a modfs file", modFs->modPath + ); + } + u8 version = (u8) magic[sizeof(MOD_FS_MAGIC) - 1]; + if (version == 0 || version > MOD_FS_VERSION) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - invalid version: %u (must be between 1 and %u)", modFs->modPath, version, MOD_FS_VERSION + ); + } + + // read header + mod_fs_read_fread_or_fail(modFs, f, &modFs->numFiles, sizeof(modFs->numFiles), 1, + "modPath: %s - cannot read number of files", modFs->modPath + ); + mod_fs_read_fread_or_fail(modFs, f, &modFs->isPublic, sizeof(modFs->isPublic), 1, + "modPath: %s - cannot read public flag", modFs->modPath + ); + + // check validity + if (modFs->numFiles > MOD_FS_MAX_FILES) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - exceeded number of files: %u (max is: %u)", modFs->modPath, modFs->numFiles, MOD_FS_MAX_FILES + ); + } + + // check access + if (!mod_fs_is_active_mod(modFs) && !modFs->isPublic) { + // don't raise an error, user should not know if a private modfs file exists + mod_fs_destroy(modFs); + fclose(f); + return false; + } + if (readHeaderOnly) { + fclose(f); + return true; + } + + // padding (empty space for future versions) + if (fseek(f, MOD_FS_HEADER_SIZE, SEEK_SET) != 0) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - cannot read header", modFs->modPath + ); + } + + // read files and compute total size + if (modFs->numFiles) { + modFs->files = (struct ModFsFile **) calloc(modFs->numFiles, sizeof(struct ModFsFile *)); + if (!modFs->files) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - failed to allocate buffer for modfs files", modFs->modPath + ); + } + } else { + modFs->files = NULL; + } + modFs->totalSize = 0; + for (u16 i = 0; i != modFs->numFiles; ++i) { + + // check filepath length + u16 filepathLength; + mod_fs_read_fread_or_fail(modFs, f, &filepathLength, sizeof(filepathLength), 1, + "modPath: %s - cannot read filepath length", modFs->modPath + ); + if (filepathLength == 0) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - filepath length cannot be 0", modFs->modPath + ); + } + if (filepathLength > MOD_FS_MAX_PATH - 1) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - exceeded filepath length: %u (max is: %u)", modFs->modPath, filepathLength, MOD_FS_MAX_PATH - 1 + ); + } + + // get filename + char filepath[MOD_FS_MAX_PATH] = {0}; + mod_fs_read_fread_or_fail(modFs, f, filepath, sizeof(char), filepathLength, + "modPath: %s - cannot read filepath", modFs->modPath + ); + + // read file header + struct ModFsFile fileHeader = {0}; + mod_fs_read_fread_or_fail(modFs, f, &fileHeader.size, sizeof(fileHeader.size), 1, + "modPath: %s, filepath: %s - cannot read file size", modFs->modPath, filepath + ); + mod_fs_read_fread_or_fail(modFs, f, &fileHeader.isPublic, sizeof(fileHeader.isPublic), 1, + "modPath: %s, filepath: %s - cannot read file public flag", modFs->modPath, filepath + ); + mod_fs_read_fread_or_fail(modFs, f, &fileHeader.isText, sizeof(fileHeader.isText), 1, + "modPath: %s, filepath: %s - cannot read file text flag", modFs->modPath, filepath + ); + + // don't create file if it's private + if (!mod_fs_is_active_mod(modFs) && !fileHeader.isPublic) { + modFs->numFiles--; + i--; + fseek(f, fileHeader.size, SEEK_CUR); + continue; + } + + // create file + struct ModFsFile *file = modFs->files[i] = mod_fs_alloc(); + if (!file) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s, filepath: %s - failed to allocate modfs file object", modFs->modPath, file->filepath + ); + } + memcpy(file, &fileHeader, sizeof(struct ModFsFile)); + snprintf(file->filepath, MOD_FS_MAX_PATH, "%s", filepath); + modFs->totalSize += file->size; + if (modFs->totalSize > MOD_FS_MAX_SIZE) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s - exceeded total size: %u (max is: %u)", modFs->modPath, modFs->totalSize, MOD_FS_MAX_SIZE + ); + } + file->offset = 0; + + // read file data + file->capacity = file->size; + if (file->size > 0) { + file->data.bin = (u8 *) malloc(file->capacity); + if (!file->data.bin) { + mod_fs_read_raise_error(modFs, f, + "modPath: %s, filepath: %s - failed to allocate buffer for modfs file data", modFs->modPath, file->filepath + ); + } + mod_fs_read_fread_or_fail(modFs, f, file->data.bin, 1, file->size, + "modPath: %s, filepath: %s - cannot read file data", modFs->modPath, file->filepath + ); + } + } + + fclose(f); + return true; + } + return false; +} + +// +// Write +// + +#define mod_fs_write_fwrite_or_fail(f, buf, size, count) { \ + if (fwrite(buf, size, count, f) < count) { \ + fclose(f); \ + return false; \ + } \ +} + +static bool mod_fs_write(struct ModFs *modFs) { + FILE *f = mod_fs_get_file_handle(modFs->modPath, "wb"); + if (f) { + + // magic + version + const u8 version = MOD_FS_VERSION; + mod_fs_write_fwrite_or_fail(f, MOD_FS_MAGIC, 1, sizeof(MOD_FS_MAGIC) - 1); + mod_fs_write_fwrite_or_fail(f, &version, sizeof(u8), 1); + + // header + mod_fs_write_fwrite_or_fail(f, &modFs->numFiles, sizeof(modFs->numFiles), 1); + mod_fs_write_fwrite_or_fail(f, &modFs->isPublic, sizeof(modFs->isPublic), 1); + + // padding (empty space for future versions) + const u8 padding[MOD_FS_HEADER_SIZE] = {0}; + u32 paddingLength = MOD_FS_HEADER_SIZE - ftell(f); + mod_fs_write_fwrite_or_fail(f, padding, 1, paddingLength); + + // files + for (u16 i = 0; i != modFs->numFiles; ++i) { + struct ModFsFile *file = modFs->files[i]; + + // filepath + u16 filepathLength = strlen(file->filepath); + mod_fs_write_fwrite_or_fail(f, &filepathLength, sizeof(filepathLength), 1); + mod_fs_write_fwrite_or_fail(f, file->filepath, sizeof(char), filepathLength); + + // data + mod_fs_write_fwrite_or_fail(f, &file->size, sizeof(file->size), 1); + mod_fs_write_fwrite_or_fail(f, &file->isPublic, sizeof(file->isPublic), 1); + mod_fs_write_fwrite_or_fail(f, &file->isText, sizeof(file->isText), 1); + if (file->data.bin) { + mod_fs_write_fwrite_or_fail(f, file->data.bin, 1, file->size); + } + } + + fclose(f); + return true; + } + return false; +} + +// +// Common checks +// + +template +static bool mod_fs_check_pointer(T *ptr, const char *typeName) { + if (!mod_fs_is_valid_pointer(ptr)) { + mod_fs_raise_error( + "pointer is not a valid %s object", typeName + ); + return false; + } + return true; +} + +static bool mod_fs_check_write(struct ModFs *modFs, const char *action) { + if (!mod_fs_is_active_mod(modFs)) { + mod_fs_raise_error( + "modPath: %s - %s files in other mods modfs is not allowed", modFs->modPath, action + ); + return false; + } + return true; +} + +static bool mod_fs_file_check_write(struct ModFsFile *file) { + if (!mod_fs_is_active_mod(file->modFs)) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - writing to files in other mods modfs is not allowed", file->modFs->modPath, file->filepath + ); + return false; + } + return true; +} + +static bool mod_fs_check_filepath_length(struct ModFs *modFs, u32 filepathLength) { + if (filepathLength == 0) { + mod_fs_raise_error( + "modPath: %s - filepath length cannot be 0", modFs->modPath + ); + return false; + } + if (filepathLength > MOD_FS_MAX_PATH - 1) { + mod_fs_raise_error( + "modPath: %s - exceeded filepath length: %u (max is: %u)", modFs->modPath, filepathLength, MOD_FS_MAX_PATH - 1 + ); + return false; + } + return true; +} + +static bool mod_fs_file_check_file_type(struct ModFsFile *file, bool isText, bool write, const char *dataName) { + if (file->isText != isText) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - cannot %s %s %s a %s file", file->modFs->modPath, file->filepath, + (write ? "write" : "read"), + dataName, + (write ? "to" : "from"), + (file->isText ? "text" : "binary") + ); + return false; + } + return true; +} + +static bool mod_fs_file_check_parameter(struct ModFsFile *file, u8 parameter, u8 parameterMax, const char *parameterName) { + if (parameter >= parameterMax) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - invalid %s: %u", file->modFs->modPath, file->filepath, + parameterName, + parameter + ); + return false; + } + return true; +} + + + + + + +// +// FS management +// + +static struct ModFs *mod_fs_get_or_load(const char *modPath, bool loadIfNotFound) { + for (auto &item : sModFsList) { + + // active mod fs + if (!modPath && mod_fs_is_active_mod(item)) { + return item; + } + + if (modPath && strcmp(modPath, item->modPath) == 0) { + return item; + } + } + + // try to load it + if (loadIfNotFound) { + struct ModFs temp = {0}; + if (mod_fs_read(modPath, &temp, false)) { + struct ModFs *modFs = mod_fs_alloc(); + if (!modFs) { + mod_fs_raise_error( + "failed to allocate modfs object", NULL + ); + return NULL; + } + memcpy(modFs, &temp, sizeof(struct ModFs)); + for (u16 i = 0; i != modFs->numFiles; ++i) { + modFs->files[i]->modFs = modFs; + } + sModFsList.push_back(modFs); + return modFs; + } + } + + return NULL; +} + +C_DEFINE bool mod_fs_exists(OPTIONAL const char *modPath) { + mod_fs_reset_last_error(); + + struct ModFs *modFs = mod_fs_get_or_load(modPath, false); + if (modFs) { + return true; + } + + struct ModFs header = {0}; + if (!mod_fs_read(modPath, &header, true)) { + return false; + } + + return true; +} + +C_DEFINE struct ModFs *mod_fs_get(OPTIONAL const char *modPath) { + mod_fs_reset_last_error(); + + return mod_fs_get_or_load(modPath, true); +} + +C_DEFINE struct ModFs *mod_fs_reload(OPTIONAL const char *modPath) { + mod_fs_reset_last_error(); + + // remove modfs object if already loaded + struct ModFs *modFs = mod_fs_get_or_load(modPath, false); + if (modFs) { + sModFsList.erase(std::find(sModFsList.begin(), sModFsList.end(), modFs)); + mod_fs_destroy(modFs); + mod_fs_free(modFs); + } + + // reload + return mod_fs_get_or_load(modPath, true); +} + +C_DEFINE struct ModFs *mod_fs_create() { + mod_fs_reset_last_error(); + + struct ModFs *modFs = mod_fs_get_or_load(NULL, false); + if (!modFs) { + modFs = mod_fs_new(); + if (!modFs) { + mod_fs_raise_error( + "cannot create modfs for the active mod", NULL + ); + return NULL; + } + + sModFsList.push_back(modFs); + return modFs; + } + + mod_fs_raise_error( + "a modfs already exists for the active mod; use `mod_fs_get()` instead", NULL + ); + return NULL; +} + +C_DEFINE bool mod_fs_delete() { + mod_fs_reset_last_error(); + + struct ModFs *modFs = mod_fs_get_or_load(NULL, true); + if (modFs) { + char filename[SYS_MAX_PATH]; + if (mod_fs_get_physical_filename(modFs->modPath, filename) && fs_sys_file_exists(filename)) { + remove(filename); + } + + sModFsList.erase(std::find(sModFsList.begin(), sModFsList.end(), modFs)); + mod_fs_destroy(modFs); + mod_fs_free(modFs); + return true; + } + + mod_fs_raise_error( + "there is no modfs for the active mod", NULL + ); + return false; +} + +C_DEFINE bool mod_fs_save() { + mod_fs_reset_last_error(); + + struct ModFs *modFs = mod_fs_get_or_load(NULL, true); + if (modFs) { + if (!mod_fs_write(modFs)) { + mod_fs_raise_error( + "cannot save modfs for the active mod", NULL + ); + return false; + } + return true; + } + + mod_fs_raise_error( + "there is no modfs for the active mod; use `mod_fs_create()` to create one", NULL + ); + return false; +} + +C_DEFINE bool mod_fs_set_public(bool pub) { + mod_fs_reset_last_error(); + + struct ModFs *modFs = mod_fs_get_or_load(NULL, true); + if (modFs) { + modFs->isPublic = pub; + return true; + } + + mod_fs_raise_error( + "there is no modfs for the active mod; use `mod_fs_create()` to create one", NULL + ); + return false; +} + +// +// File management +// + +static int mod_fs_compare_filepaths(const void *l, const void *r) { + const struct ModFsFile *lfile = (const struct ModFsFile *) l; + const struct ModFsFile *rfile = (const struct ModFsFile *) r; + return strcmp(lfile->filepath, rfile->filepath); +} + +C_DEFINE const char *mod_fs_get_filename(struct ModFs *modFs, u16 index) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return NULL; + } + + if (index >= modFs->numFiles) { + mod_fs_raise_error( + "modPath: %s - file index out of bounds: %u (num files: %u)", modFs->modPath, index, modFs->numFiles + ); + return NULL; + } + + return modFs->files[index]->filepath; +} + +C_DEFINE struct ModFsFile *mod_fs_get_file(struct ModFs *modFs, const char *filepath) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return NULL; + } + + for (u16 i = 0; i != modFs->numFiles; ++i) { + struct ModFsFile *file = modFs->files[i]; + if (strcmp(file->filepath, filepath) == 0) { + return file; + } + } + return NULL; +} + +C_DEFINE struct ModFsFile *mod_fs_create_file(struct ModFs *modFs, const char *filepath, bool text) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return NULL; + } + + // cannot create new files in other mods modfs + if (!mod_fs_check_write(modFs, "creating")) { + return NULL; + } + + // check number of files + if (modFs->numFiles == MOD_FS_MAX_FILES) { + mod_fs_raise_error( + "modPath: %s - reached max number of files: %u", modFs->modPath, MOD_FS_MAX_FILES + ); + return NULL; + } + + // check filepath + u32 filepathLength = strlen(filepath); + if (!mod_fs_check_filepath_length(modFs, filepathLength)) { + return NULL; + } + + // create file + struct ModFsFile *file = mod_fs_alloc(); + if (!file) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - failed to allocate modfs file object", modFs->modPath, filepath + ); + return NULL; + } + snprintf(file->filepath, MOD_FS_MAX_PATH, "%s", filepath); + file->data.bin = NULL; + file->size = 0; + file->capacity = 0; + file->offset = 0; + file->isText = text; + file->isPublic = false; + file->modFs = modFs; + + // add file and sort by filename + struct ModFsFile **files = (struct ModFsFile **) realloc(modFs->files, (modFs->numFiles + 1) * sizeof(struct ModFsFile *)); + if (!files) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - failed to reallocate buffer of modfs files", modFs->modPath, filepath + ); + mod_fs_free(file); + return NULL; + } + modFs->files = files; + modFs->files[modFs->numFiles] = file; + modFs->numFiles++; + qsort(modFs->files, modFs->numFiles, sizeof(struct ModFsFile *), mod_fs_compare_filepaths); + + return file; +} + +C_DEFINE bool mod_fs_move_file(struct ModFs *modFs, const char *oldpath, const char *newpath, bool overwriteExisting) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return false; + } + + // cannot move files in other mods modfs + if (!mod_fs_check_write(modFs, "moving")) { + return false; + } + + // check new filepath + u32 newpathLength = strlen(newpath); + if (!mod_fs_check_filepath_length(modFs, newpathLength)) { + return false; + } + + // get file + struct ModFsFile *oldfile = mod_fs_get_file(modFs, oldpath); + if (!oldfile) { + mod_fs_raise_error( + "modPath: %s - file %s doesn't exist", modFs->modPath, oldpath + ); + return false; + } + + // if overwriteExisting is not set, check if the newpath points to an existing file + struct ModFsFile *newfile = mod_fs_get_file(modFs, newpath); + if (newfile && !overwriteExisting) { + mod_fs_raise_error( + "modPath: %s - file %s already exists; set `overwriteExisting` to true to replace this file", modFs->modPath, newpath + ); + return false; + } + + // rename file + if (newfile && !mod_fs_delete_file(modFs, newpath)) { + return false; + } + snprintf(oldfile->filepath, MOD_FS_MAX_PATH, "%s", newpath); + return true; +} + +C_DEFINE bool mod_fs_copy_file(struct ModFs *modFs, const char *srcpath, const char *dstpath, bool overwriteExisting) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return false; + } + + // cannot copy files in other mods modfs + if (!mod_fs_check_write(modFs, "copying")) { + return false; + } + + // check dest filepath + u32 dstpathLength = strlen(dstpath); + if (!mod_fs_check_filepath_length(modFs, dstpathLength)) { + return false; + } + + // get file + struct ModFsFile *srcfile = mod_fs_get_file(modFs, srcpath); + if (!srcfile) { + mod_fs_raise_error( + "modPath: %s - file %s doesn't exist", modFs->modPath, srcpath + ); + return false; + } + + // if overwriteExisting is not set, check if the newpath points to an existing file + struct ModFsFile *dstfile = mod_fs_get_file(modFs, dstpath); + if (dstfile && !overwriteExisting) { + mod_fs_raise_error( + "modPath: %s - file %s already exists; set `overwriteExisting` to true to replace this file", modFs->modPath, dstpath + ); + return false; + } + + // compute new total size + u32 newTotalSize = modFs->totalSize + srcfile->size; + if (dstfile) { + newTotalSize -= dstfile->size; + } + if (newTotalSize > MOD_FS_MAX_SIZE) { + mod_fs_raise_error( + "modPath: %s - cannot copy file %s: exceeding total size: %u (max is: %u)", modFs->modPath, srcpath, newTotalSize, MOD_FS_MAX_SIZE + ); + return false; + } + modFs->totalSize = newTotalSize; + + // copy file + u8 *buffer = (u8 *) malloc(srcfile->capacity); + if (!buffer) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - failed to allocate buffer for modfs file data", modFs->modPath, dstfile->filepath + ); + return false; + } + if (dstfile) { + free(dstfile->data.bin); + } else { + dstfile = mod_fs_create_file(modFs, dstpath, srcfile->isText); + if (!dstfile) { + free(buffer); + return false; + } + } + memcpy(dstfile, srcfile, sizeof(struct ModFsFile)); + snprintf(dstfile->filepath, MOD_FS_MAX_PATH, "%s", dstpath); + memcpy(buffer, srcfile->data.bin, srcfile->size); + dstfile->data.bin = buffer; + dstfile->offset = 0; + return true; +} + +C_DEFINE bool mod_fs_delete_file(struct ModFs *modFs, const char *filepath) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return false; + } + + // cannot delete files in other mods modfs + if (!mod_fs_check_write(modFs, "deleting")) { + return false; + } + + // get file + for (u16 i = 0; i != modFs->numFiles; ++i) { + struct ModFsFile *file = modFs->files[i]; + if (strcmp(file->filepath, filepath) == 0) { + + // delete file + modFs->totalSize -= file->size; + mod_fs_file_destroy(file); + mod_fs_free(file); + + // remove file from list + memmove(modFs->files + i, modFs->files + (i + 1), (modFs->numFiles - i - 1) * sizeof(struct ModFsFile *)); + modFs->numFiles--; + return true; + } + } + + mod_fs_raise_error( + "modPath: %s - file %s doesn't exist", modFs->modPath, filepath + ); + return false; +} + +C_DEFINE bool mod_fs_clear(struct ModFs *modFs) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(modFs, "modfs")) { + return false; + } + + // cannot delete files in other mods modfs + if (!mod_fs_check_write(modFs, "deleting")) { + return false; + } + + // delete all files + for (u16 i = 0; i != modFs->numFiles; ++i) { + struct ModFsFile *file = modFs->files[i]; + mod_fs_file_destroy(file); + mod_fs_free(file); + } + free(modFs->files); + modFs->files = NULL; + modFs->numFiles = 0; + modFs->totalSize = 0; + return true; +} + +// +// Read data +// + +static bool mod_fs_file_read_check_eof(struct ModFsFile *file, u32 size) { + if (file->offset + size > file->size) { + file->offset = file->size; + mod_fs_raise_error( + "modPath: %s, filepath: %s - reached end of file", file->modFs->modPath, file->filepath + ); + return true; + } + return false; +} + +template +static T mod_fs_file_read_data(struct ModFsFile *file, T defaultValue) { + if (mod_fs_file_read_check_eof(file, sizeof(T))) { + return defaultValue; + } + T value; + memcpy(&value, file->data.bin + file->offset, sizeof(T)); + file->offset += sizeof(T); + return value; +} + +C_DEFINE bool mod_fs_file_read_bool(struct ModFsFile *file) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, false, "bool")) { + return false; + } + + return mod_fs_file_read_data(file, false); +} + +C_DEFINE lua_Integer mod_fs_file_read_integer(struct ModFsFile *file, enum ModFsFileIntType intType) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return 0; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, false, "integer")) { + return 0; + } + + // check intType + if (!mod_fs_file_check_parameter(file, intType, INT_TYPE_MAX, "intType")) { + return 0; + } + + switch (intType) { + case INT_TYPE_U8: return mod_fs_file_read_data(file, 0); + case INT_TYPE_U16: return mod_fs_file_read_data(file, 0); + case INT_TYPE_U32: return mod_fs_file_read_data(file, 0); + case INT_TYPE_U64: return mod_fs_file_read_data(file, 0); + case INT_TYPE_S8: return mod_fs_file_read_data(file, 0); + case INT_TYPE_S16: return mod_fs_file_read_data(file, 0); + case INT_TYPE_S32: return mod_fs_file_read_data(file, 0); + case INT_TYPE_S64: return mod_fs_file_read_data(file, 0); + } + return 0; +} + +C_DEFINE lua_Number mod_fs_file_read_number(struct ModFsFile *file, enum ModFsFileFloatType floatType) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return 0; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, false, "number")) { + return 0; + } + + // check intType + if (!mod_fs_file_check_parameter(file, floatType, FLOAT_TYPE_MAX, "floatType")) { + return 0; + } + + switch (floatType) { + case FLOAT_TYPE_F32: return mod_fs_file_read_data(file, 0); + case FLOAT_TYPE_F64: return mod_fs_file_read_data(file, 0); + } + return 0; +} + +C_DEFINE ByteString mod_fs_file_read_bytes(struct ModFsFile *file, u32 length) { + mod_fs_reset_last_error(); + ByteString bytestring = { NULL, 0 }; + + if (!mod_fs_check_pointer(file, "modfs file")) { + return bytestring; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, false, "bytes")) { + return bytestring; + } + + // check eof + if (mod_fs_file_read_check_eof(file, length)) { + return bytestring; + } + + bytestring.bytes = (const char *) (file->data.bin + file->offset); + bytestring.length = length; + file->offset += length; + return bytestring; +} + +C_DEFINE const char *mod_fs_file_read_string(struct ModFsFile *file) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return NULL; + } + + memset(sModFsFileReadStringBuf, 0, sizeof(sModFsFileReadStringBuf)); + if (mod_fs_file_read_check_eof(file, 1)) { + return NULL; + } + + // for text files, returns the whole content from offset + if (file->isText) { + memcpy(sModFsFileReadStringBuf, file->data.text + file->offset, file->size - file->offset); + file->offset = file->size; + return sModFsFileReadStringBuf; + } + + // for binary, stops at the first NULL char or at the end of the file + char *buf = sModFsFileReadStringBuf; + for (char *c = (char *) (file->data.bin + file->offset); *c && file->offset < file->size; c++, file->offset++, buf++) { + *buf = *c; + } + *buf = 0; + file->offset = MIN(file->offset + 1, file->size); + return sModFsFileReadStringBuf; +} + +C_DEFINE const char *mod_fs_file_read_line(struct ModFsFile *file) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return NULL; + } + + // text only + if (!mod_fs_file_check_file_type(file, true, false, "line")) { + return 0; + } + + memset(sModFsFileReadStringBuf, 0, sizeof(sModFsFileReadStringBuf)); + if (mod_fs_file_read_check_eof(file, 1)) { + return NULL; + } + + char *buf = sModFsFileReadStringBuf; + for (char *c = file->data.text + file->offset; *c != '\n' && file->offset < file->size; c++, file->offset++, buf++) { + *buf = *c; + } + *buf = 0; + file->offset = MIN(file->offset + 1, file->size); + return sModFsFileReadStringBuf; +} + +// +// Write data +// + +static bool mod_fs_file_write_resize_buffer(struct ModFsFile *file, u32 size) { + + // compute and check new sizes + file->offset = MIN(file->offset, file->size); + u32 newFileSize = MAX(file->offset + size, file->size); + u32 newTotalSize = file->modFs->totalSize + (newFileSize - file->size); + if (newTotalSize > MOD_FS_MAX_SIZE) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - cannot write to file: exceeding total size: %u (max is: %u)", file->modFs->modPath, file->filepath, newTotalSize, MOD_FS_MAX_SIZE + ); + return false; + } + + // resize data buffer + if (file->offset + size > file->capacity) { + u32 newCapacity = MAX(file->capacity * 2, 32); + u8 *buffer = (u8 *) realloc(file->data.bin, newCapacity); + if (!buffer) { + mod_fs_raise_error( + "modPath: %s, filepath: %s - failed to reallocate buffer of modfs file data", file->modFs->modPath, file->filepath + ); + return false; + } + file->data.bin = buffer; + file->capacity = newCapacity; + } + file->size = newFileSize; + file->modFs->totalSize = newTotalSize; + return true; +} + +template +static bool mod_fs_file_write_data(struct ModFsFile *file, T value) { + if (mod_fs_file_write_resize_buffer(file, sizeof(T))) { + memcpy(file->data.bin + file->offset, &value, sizeof(T)); + file->offset += sizeof(T); + return true; + } + return false; +} + +C_DEFINE bool mod_fs_file_write_bool(struct ModFsFile *file, bool value) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, true, "bool")) { + return false; + } + + return mod_fs_file_write_data(file, value); +} + +C_DEFINE bool mod_fs_file_write_integer(struct ModFsFile *file, lua_Integer value, enum ModFsFileIntType intType) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, true, "integer")) { + return false; + } + + // check intType + if (!mod_fs_file_check_parameter(file, intType, INT_TYPE_MAX, "intType")) { + return false; + } + + switch (intType) { + case INT_TYPE_U8: return mod_fs_file_write_data(file, value); + case INT_TYPE_U16: return mod_fs_file_write_data(file, value); + case INT_TYPE_U32: return mod_fs_file_write_data(file, value); + case INT_TYPE_U64: return mod_fs_file_write_data(file, value); + case INT_TYPE_S8: return mod_fs_file_write_data(file, value); + case INT_TYPE_S16: return mod_fs_file_write_data(file, value); + case INT_TYPE_S32: return mod_fs_file_write_data(file, value); + case INT_TYPE_S64: return mod_fs_file_write_data(file, value); + } + return false; +} + +C_DEFINE bool mod_fs_file_write_number(struct ModFsFile *file, lua_Number value, enum ModFsFileFloatType floatType) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, true, "number")) { + return false; + } + + // check floatType + if (!mod_fs_file_check_parameter(file, floatType, FLOAT_TYPE_MAX, "floatType")) { + return false; + } + + switch (floatType) { + case FLOAT_TYPE_F32: return mod_fs_file_write_data(file, value); + case FLOAT_TYPE_F64: return mod_fs_file_write_data(file, value); + } + return false; +} + +C_DEFINE bool mod_fs_file_write_bytes(struct ModFsFile *file, ByteString bytestring) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + // binary only + if (!mod_fs_file_check_file_type(file, false, true, "bytes")) { + return false; + } + + u32 length = bytestring.length; + if (mod_fs_file_write_resize_buffer(file, length)) { + memcpy(file->data.bin + file->offset, bytestring.bytes, length); + file->offset += length; + return true; + } + return false; +} + +C_DEFINE bool mod_fs_file_write_string(struct ModFsFile *file, const char *str) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + u32 length = strlen(str) + (file->isText ? 0 : 1); // binary writes the NULL char + if (mod_fs_file_write_resize_buffer(file, length)) { + memcpy(file->data.bin + file->offset, str, length); + file->offset += length; + return true; + } + return false; +} + +C_DEFINE bool mod_fs_file_write_line(struct ModFsFile *file, const char *str) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + // text only + if (!mod_fs_file_check_file_type(file, true, true, "line")) { + return false; + } + + u32 length = strlen(str); + if (mod_fs_file_write_resize_buffer(file, length + 1)) { // '\n' + memcpy(file->data.text + file->offset, str, length); + file->offset += length; + file->data.text[file->offset++] = '\n'; + return true; + } + return false; +} + +// +// File misc +// + +C_DEFINE bool mod_fs_file_seek(struct ModFsFile *file, s32 offset, enum ModFsFileSeek origin) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // check origin + if (!mod_fs_file_check_parameter(file, origin, FILE_SEEK_MAX, "origin")) { + return false; + } + + s32 start = 0; + switch (origin) { + case FILE_SEEK_SET: start = 0; break; + case FILE_SEEK_CUR: start = file->offset; break; + case FILE_SEEK_END: start = file->size; break; + } + file->offset = MIN(MAX(start + offset, 0), (s32) file->size); + return true; +} + +C_DEFINE bool mod_fs_file_is_eof(struct ModFsFile *file) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + return file->offset >= file->size; +} + +C_DEFINE bool mod_fs_file_fill(struct ModFsFile *file, u8 byte, u32 length) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot write to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + if (mod_fs_file_write_resize_buffer(file, length)) { + memset(file->data.bin + file->offset, byte, length); + file->offset += length; + return true; + } + return false; +} + +C_DEFINE bool mod_fs_file_erase(struct ModFsFile *file, u32 length) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot erase data from files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + length = MIN(length, file->size - file->offset); + memmove(file->data.bin + file->offset, file->data.bin + file->offset + length, file->size - (file->offset + length)); + file->size -= length; + file->modFs->totalSize -= length; + return true; +} + +C_DEFINE bool mod_fs_file_set_public(struct ModFsFile *file, bool pub) { + mod_fs_reset_last_error(); + + if (!mod_fs_check_pointer(file, "modfs file")) { + return false; + } + + // cannot change public flag to files in other mods modfs + if (!mod_fs_file_check_write(file)) { + return false; + } + + file->isPublic = pub; + return true; +} + +// +// Errors +// + +void mod_fs_hide_errors(bool hide) { + sModFsHideErrors = hide; +} + +const char *mod_fs_get_last_error() { + return *sModFsLastError ? sModFsLastError : NULL; +} diff --git a/src/pc/mods/mod_fs.h b/src/pc/mods/mod_fs.h new file mode 100644 index 000000000..90daa1838 --- /dev/null +++ b/src/pc/mods/mod_fs.h @@ -0,0 +1,236 @@ +#ifndef MOD_FS_H +#define MOD_FS_H + +#include "types.h" +#include "src/pc/lua/smlua.h" + +#define MOD_FS_MAX_SIZE 0x1000000 // 16 MB +#define MOD_FS_MAX_FILES 0x100 +#define MOD_FS_MAX_PATH 0x100 +#define MOD_FS_DIRECTORY "sav" +#define MOD_FS_EXTENSION ".modfs" +#define MOD_FS_VERSION 1 + +enum ModFsFileIntType { + INT_TYPE_U8, + INT_TYPE_U16, + INT_TYPE_U32, + INT_TYPE_U64, + INT_TYPE_S8, + INT_TYPE_S16, + INT_TYPE_S32, + INT_TYPE_S64, + + INT_TYPE_MAX +}; + +enum ModFsFileFloatType { + FLOAT_TYPE_F32, + FLOAT_TYPE_F64, + + FLOAT_TYPE_MAX +}; + +enum ModFsFileSeek { + FILE_SEEK_SET, + FILE_SEEK_CUR, + FILE_SEEK_END, + + FILE_SEEK_MAX +}; + +struct Mod; + +struct ModFsFile { + struct ModFs *modFs; + char filepath[MOD_FS_MAX_PATH]; + union { + u8 *bin; + char *text; + } data; + u32 size; + u32 capacity; + u32 offset; + bool isText; + bool isPublic; +}; + +struct ModFs { + struct Mod *mod; + char modPath[SYS_MAX_PATH]; + struct ModFsFile **files; + u16 numFiles; + u32 totalSize; + bool isPublic; +}; + +/* |description| +Checks the existence of a modfs at path `modPath` or for the active mod if not provided. Checking for the existence of a private modfs will return false, even if it exists +|descriptionEnd| */ +bool mod_fs_exists(OPTIONAL const char *modPath); + +/* |description| +Gets the modfs object at path `modPath` or the active mod one if not provided. This function will return nil for a private modfs, even if it exists +|descriptionEnd| */ +struct ModFs *mod_fs_get(OPTIONAL const char *modPath); + +/* |description| +Reloads the modfs object at path `modPath`. This function will return nil for a private modfs, even if it exists +|descriptionEnd| */ +struct ModFs *mod_fs_reload(OPTIONAL const char *modPath); + +/* |description| +Creates a modfs object for the active mod if it doesn't exist. Returns the modfs object on success +|descriptionEnd| */ +struct ModFs *mod_fs_create(); + +/* |description| +Deletes the modfs object of the active mod if it exists. Returns true on success +|descriptionEnd| */ +bool mod_fs_delete(); + +/* |description| +Saves the modfs object of the active mod if it exists. Returns true on success +|descriptionEnd| */ +bool mod_fs_save(); + +/* |description| +Marks the modfs object of the active mod as public (i.e. readable by other mods) if it exists. Returns true on success +|descriptionEnd| */ +bool mod_fs_set_public(bool pub); + +/* |description| +Gets the filename at position `index` of the provided `modFs` +|descriptionEnd| */ +const char *mod_fs_get_filename(struct ModFs *modFs, u16 index); + +/* |description| +Gets the file object at path `filepath` of the provided `modFs`. This function will return nil for a private modfs file, even if it exists +|descriptionEnd| */ +struct ModFsFile *mod_fs_get_file(struct ModFs *modFs, const char *filepath); + +/* |description| +Creates a new file at path `filepath` for the provided `modFs`. Set `text` to true to treat the file as a pure text file, not a binary file. Returns the created file on success +|descriptionEnd| */ +struct ModFsFile *mod_fs_create_file(struct ModFs *modFs, const char *filepath, bool text); + +/* |description| +Moves the file at path `oldpath` to `newpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `newpath` if it exists. Returns true on success +|descriptionEnd| */ +bool mod_fs_move_file(struct ModFs *modFs, const char *oldpath, const char *newpath, bool overwriteExisting); + +/* |description| +Copies the file at path `srcpath` to `dstpath` of the provided `modFs`. Set `overwriteExisting` to true to overwrite the file at path `dstpath` if it exists. Returns true on success +|descriptionEnd| */ +bool mod_fs_copy_file(struct ModFs *modFs, const char *srcpath, const char *dstpath, bool overwriteExisting); + +/* |description| +Deletes the file at path `filepath` of the provided `modFs`. Returns true on success +|descriptionEnd| */ +bool mod_fs_delete_file(struct ModFs *modFs, const char *filepath); + +/* |description| +Deletes all files of the provided `modFs`. Returns true on success +|descriptionEnd| */ +bool mod_fs_clear(struct ModFs *modFs); + +/* |description| +Reads a boolean from a binary modfs `file` +|descriptionEnd| */ +bool mod_fs_file_read_bool(struct ModFsFile *file); + +/* |description| +Reads an integer from a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants +|descriptionEnd| */ +lua_Integer mod_fs_file_read_integer(struct ModFsFile *file, enum ModFsFileIntType intType); + +/* |description| +Reads an floating-point number from a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants +|descriptionEnd| */ +lua_Number mod_fs_file_read_number(struct ModFsFile *file, enum ModFsFileFloatType floatType); + +/* |description| +Reads a bytestring of `length` bytes from a binary modfs `file` +|descriptionEnd| */ +ByteString mod_fs_file_read_bytes(struct ModFsFile *file, u32 length); + +/* |description| +Reads a string from a binary modfs `file`, or read the whole content of a text modfs `file` +|descriptionEnd| */ +const char *mod_fs_file_read_string(struct ModFsFile *file); + +/* |description| +Reads a line from a text modfs `file` +|descriptionEnd| */ +const char *mod_fs_file_read_line(struct ModFsFile *file); + +/* |description| +Writes a boolean to a binary modfs `file`. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_bool(struct ModFsFile *file, bool value); + +/* |description| +Writes an integer to a binary modfs `file`. `intType` must be one of the `INT_TYPE_*` constants. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_integer(struct ModFsFile *file, lua_Integer value, enum ModFsFileIntType intType); + +/* |description| +Writes an floating-point number to a binary modfs `file`. `floatType` must be one of the `FLOAT_TYPE_*` constants. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_number(struct ModFsFile *file, lua_Number value, enum ModFsFileFloatType floatType); + +/* |description| +Writes a bytestring to a modfs `file`. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_bytes(struct ModFsFile *file, ByteString bytestring); + +/* |description| +Writes a string to a modfs `file`. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_string(struct ModFsFile *file, const char *str); + +/* |description| +Writes a line to a text modfs `file`. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_write_line(struct ModFsFile *file, const char *str); + +/* |description| +Sets the current position of a modfs `file`. +If `origin` is `FILE_SEEK_SET`, file position is set to `offset`. +If `origin` is `FILE_SEEK_CUR`, `offset` is added to file current position. +If `origin` is `FILE_SEEK_END`, file position is set to `end of file + offset`. +Returns true on success +|descriptionEnd| */ +bool mod_fs_file_seek(struct ModFsFile *file, s32 offset, enum ModFsFileSeek origin); + +/* |description| +Returns true if the provided modfs `file` has reached its end of file +|descriptionEnd| */ +bool mod_fs_file_is_eof(struct ModFsFile *file); + +/* |description| +Fills a modfs `file` with `byte` repeated `length` times. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_fill(struct ModFsFile *file, u8 byte, u32 length); + +/* |description| +Erases `length` bytes or characters from a modfs `file`. Returns true on success +|descriptionEnd| */ +bool mod_fs_file_erase(struct ModFsFile *file, u32 length); + +/* |description| +Marks the provided modfs `file` as public (i.e. readable by other mods). Returns true on success +|descriptionEnd| */ +bool mod_fs_file_set_public(struct ModFsFile *file, bool pub); + +/* |description| +Hides script errors raised by `mod_fs` functions. Errors messages are still generated and can be retrieved with `mod_fs_get_last_error()` +|descriptionEnd| */ +void mod_fs_hide_errors(bool hide); + +/* |description| +Returns the last error message generated by `mod_fs` functions or nil if no error occurred +|descriptionEnd| */ +const char *mod_fs_get_last_error(); + +#endif // MOD_FS_H