* Test implementation of lua * Trust that lua knows what its doing with this Silence warnings * actually update top screen when Thingy is called, disable unnecessary ShowPrompt calls * readme * change init for a simple test, print error on top screen too * Readme * change init for a simple test, print error on top screen too * enable more lua libs, edit init.lua with string examples * one more readme edit before bed * Readme * change init for a simple test, print error on top screen too * enable more lua libs, edit init.lua with string examples * make lua a proper file type, add test UI library with two functions, remove luacmd command * remove old attempts at editing lauxlib and liolib * README * README * FS lib, new UI stuff * consistency with "type* ptr" maybe * add custom package searcher, reset package.path * new functions for UI including basic print output buffer, add "Lua scripts..." option to home/power menu * build vram0.tar including subdirs of data * move default path to GM9LUA_DEFAULT_PATH * FS_FileGetData * testfgd, add GM9VERSION global, update README, fix indentation * FS_FileGetData will return a nil instead if it fails * it's actually luaL_pushfail * os * use luaL_tolstring instead of lua_tostring * fix test/remove debugging showprompt * os.clock float attempt * fix print for real * fix swapped offset and size for FileGetData * finish OS stuff * fix os.clock * shorten table in/out * remove .vscode dir * enum test * support building without lua * NO_LUA hides menu options (except when you directly select a lua file) * update UI lib to better match the ideas on #1 * dockermake * add DrawPNG function * whoops its ShowPNG * minor fixes, add DrawPNG * fix AskPrompt, add all showprompts mentioned in #1 * add newly added functions to readme * try to keep separate code and data * update lua to 5.4.7 * remove test libraries now that i want to attempt to implement in a real api * add nix flake for building * add various lua functions, some of them taken from the old attempt but with new names * add dev shell to flake.nix * remove test lua scripts in root * add test lua scripts in data/luascripts * add a whole bunch more lua functions and stuff * add more test lua scripts * add more functions, add preload script, add test io compatibility module * add more functions and test scripts * more functions and stuff * more functions and stuff * more functions and stuff, plus a wip ctrcheck reimpl * yet more functions and stuff * even more functions and stuff * command comparison table.ods * update command comparison table.ods * update command comparison table.ods * Add files via upload * update command comparison table.ods * update ui.show_text to use DrawStringCenter, update ctrcheck rewrite * Split up the ARM9 code (.text, .vectors) and data (.rodata, .data, .bss) sections into their own ELFs. This allows us to use more ARM9 WRAM while leaving the 128k BootROM mirror intact. * use the makefile definition * add title module, move around some functions, update command comparison table * add readme for lua * remove liolib.c and loslib.c * more functions and things, use CheckWritePermissionsLuaError in place of more manual checks, update command comparison table * add missing constant * set CURRDIR to nil instead of "(null)" if not found * remove gm9enum (unused since the restart) * add ui.check_key * split fs module to lua overlay and _fs internal module, and add a check for fs.write_file in lua * add fs.ask_select_file and fs.ask_select_dir * add fs.key_dump, replace overwrite_all and append_all with overwrite and append * add fs.cart_dump and sys.emu_base * add ctrtool, update flake.lock * add io append mode * make sure io.open with write mode starts with an empty file, add os.remove and os.rename aliases * properly implement os.remove compatibility * add fs.verify_with_sha_file, fix PathIsDirectory by using stat instead of opendir * add util.running_as_module (untested) * move scripts over to https://github.com/ihaveamac/GM9-lua-script-experiments * remove ods and dockermake.sh * remove data/scripts * add lua autorun (untested) * fix syntax error * add sys.check_embedded_backup * remove accidental symlink * add sys.check_raw_rtc * fix ui.show_file_text_viewer not freeing memory or reporting an error if OOM happens * add todo notes for ui * work-in-progress lua doc * formatting fix * up heading level for all sections * Revert "up heading level for all sections" This reverts commit 6ef14b619536b4253e341ba40b4dea728358979d. * separators * fix name and error for fs.move * do explicit permission checks in fs.move * fix error string for fs.copy * fix function name for fs.dir_info * fix error string for fs.find and fs.find_not * fix function name for fs.img_umount * partial fs doc * finished fs doc, string fixes for fs module * document fs.cart_dump encrypted opt, remove stat from fs.verify_with_sha_file * title doc * sys doc, error string updates * util doc * add json.lua to lua-doc * add fs.find_all * add 3dstool to flake * make fs.find_all recursive actually recursive, document fs.find_all * add "for" to comparison table * change fs.find to return nil if no path was found, instead of raising an error * change ui.echo to automatically word wrap (untested) * Revert "change ui.echo to automatically word wrap (untested)" This reverts commit 2524e7707708e9818162c31f9f004b6301a3061b. * switch devkitNix to upstream * flake.lock: Update Flake lock file updates: • Updated input 'devkitNix': 'github:ihaveamac/devkitNix/883d173b94e3da8dc4cc0860cdda8c36b738817c' (2024-12-05) → 'github:bandithedoge/devkitNix/95fd44f4ac7cecf24edf22daa899a516df73c6b7' (2025-01-11) • Updated input 'devkitNix/nixpkgs': 'github:NixOS/nixpkgs/566e53c2ad750c84f6d31f9ccb9d00f823165550' (2024-12-03) → 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) • Updated input 'hax-nur': 'github:ihaveamac/nur-packages/c570b3830f7dd4d655afb109300529c896cd8855' (2024-12-05) → 'github:ihaveamac/nur-packages/cd49afba206c2eb10a349d92470fdf2cc942ae23' (2025-01-11) • Updated input 'hax-nur/nixpkgs': 'github:NixOS/nixpkgs/2c15aa59df0017ca140d9ba302412298ab4bf22a' (2024-12-02) → 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/566e53c2ad750c84f6d31f9ccb9d00f823165550' (2024-12-03) → 'github:NixOS/nixpkgs/32af3611f6f05655ca166a0b1f47b57c762b5192' (2025-01-09) * flake.lock: Update Flake lock file updates: • Updated input 'devkitNix': 'github:bandithedoge/devkitNix/95fd44f4ac7cecf24edf22daa899a516df73c6b7' (2025-01-11) → 'github:bandithedoge/devkitNix/a344b0200a044f2d2ff99685f13ff7c53106428e' (2025-02-06) • Updated input 'devkitNix/nixpkgs': 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) → 'github:NixOS/nixpkgs/5b2753b0356d1c951d7a3ef1d086ba5a71fff43c' (2025-02-05) • Updated input 'hax-nur': 'github:ihaveamac/nur-packages/cd49afba206c2eb10a349d92470fdf2cc942ae23' (2025-01-11) → 'github:ihaveamac/nur-packages/2ce890cab4e948109ad1ad82ba18e69240a0d352' (2025-02-06) • Updated input 'hax-nur/nixpkgs': 'github:NixOS/nixpkgs/4bc9c909d9ac828a039f288cf872d16d38185db8' (2025-01-08) → 'github:NixOS/nixpkgs/8532db2a88ba56de9188af72134d93e39fd825f3' (2025-02-02) • Added input 'hax-nur/treefmt-nix': 'github:numtide/treefmt-nix/bebf27d00f7d10ba75332a0541ac43676985dea3' (2025-01-28) • Added input 'hax-nur/treefmt-nix/nixpkgs': follows 'hax-nur/nixpkgs' • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/32af3611f6f05655ca166a0b1f47b57c762b5192' (2025-01-09) → 'github:NixOS/nixpkgs/5b2753b0356d1c951d7a3ef1d086ba5a71fff43c' (2025-02-05) * make devkitNix and hax-nur inputs follow nixpkgs * Also dump section headers on .dis file * prepare for upstream merge * Restore original README. * Remove flake, was only used for my own testing * fix accidental removal of LIBS * copy lua-doc.md into release archive * README: update to mention Lua in place of GM9Script, add credits * lua-doc: fix typo * add sample HelloScript * lua-doc: remove wip notice, since all gm9script features are replicated * remove accidental inclusion of language.inl * Fix mixture of tabs and spaces * remove accidental nix leftover * re-add @ for add2tar command --------- Co-authored-by: luigoalma <luigoalma@hotmail.com> Co-authored-by: Gruetzig <florianavilov@gmail.com> Co-authored-by: Florian <88926852+Gruetzig@users.noreply.github.com> Co-authored-by: Wolfvak <soherrera1@hotmail.com>
32 KiB
GodMode9 Lua documentation
GodMode9 includes a Lua 5.4.7 implementation.
Running scripts
There are four ways to run Lua scripts:
- Select it in the file browser and choose "
Execute Lua script
" - Place it in
0:/gm9/luascripts
, then open the HOME menu, choose "Lua scripts...
", then choose the script - Place it in
data/luascripts
, then build GodMode9 from source- This takes precedence over
0:/gm9/luascripts
- This takes precedence over
- Place it at
data/autorun.lua
, then build GodMode9 from source withSCRIPT_RUNNER=1
to automatically run it at launch
Packages
Lua scripts can load custom modules. The default search path (defined as package.path
) is:
0:/gm9/luapackages/?.lua;0:/gm9/luapackages/?/init.lua;V:/luapackages/?.lua;V:/luapackages/?/init.lua
For example, when a script calls require("foobar")
, the package searcher will look for the module in this order:
0:/gm9/luapackages/foobar.lua
0:/gm9/luapackages/foobar/init.lua
V:/luapackages/foobar.lua
V:/luapackages/foobar/init.lua
Comparison with GM9Script
These tables are provided to assist with converting from GM9Script to Lua.
Commands
GM9 | Lua | Notes |
---|---|---|
goto | http://lua-users.org/wiki/GotoStatement | |
labelsel | ui.ask_selection | |
for | fs.find_all | Recursive searching does not work exactly the same, it will search for the filename in all directories |
keychk | ui.check_key | |
echo | ui.echo | |
qr | ui.show_qr | |
ask | ui.ask | |
input | ui.ask_input | |
filesel | fs.ask_select_file | |
dirsel | fs.ask_select_dir | |
set | local var = value | More details on variables and scoping: https://www.lua.org/pil/4.2.html |
strsplit | string.find and string.sub | |
strrep | string.gsub | https://www.lua.org/manual/5.4/manual.html#pdf-string.gsub |
allow | fs.allow | |
cp | fs.copy | |
mv | fs.move | |
inject | fs.write_file | |
fill | fs.fill_file | |
fdummy | fs.make_dummy_file | |
rm | fs.remove | |
mkdir | fs.mkdir | |
mount | fs.img_mount | |
umount | fs.img_umount | |
find | fs.find | |
findnot | fs.find_not | |
fget | fs.write_file | |
fset | fs.write_file | |
sha | fs.hash_file OR fs.verify_with_sha_file | hash_file simply returns a hash, verify_with_sha_file compares it with a corresponding .sha file |
shaget | fs.hash_file | |
dumptxt | fs.write_file | Use "end" for offset to append data |
fixcmac | fs.fix_cmacs | |
verify | fs.verify | |
decrypt | title.decrypt | |
encrypt | title.encrypt | |
buildcia | title.build_cia | |
install | title.install | |
extrcode | title.extract_code | |
cmprcode | title.compress_code | |
sdump | fs.key_dump | |
applyips | target.apply_ips | |
applybps | target.apply_bps | |
applybpm | target.apply_bpm | |
textview | fs.show_file_text_viewer | fs.show_text_viewer can be used to show text from a variable |
cartdump | fs.cart_dump | |
isdir | fs.is_dir | |
exist | fs.exists | |
boot | sys.boot | |
switchsd | fs.sd_switch | |
nextemu | sys.next_emu | |
reboot | sys.reboot | |
poweroff | sys.power_off | |
bkpt | bkpt |
PREVIEW_MODE variable
Unlike the PREVIEW_MODE
GM9Script variable, this has been split into multiple functions.
Setting | Lua |
---|---|
“quick" and “full" | (There is no alternative to view a Lua script as it’s running.) |
“off" | ui.clear |
text | ui.show_text |
png file | ui.show_png |
game icon | ui.show_game_info |
Other constants
GM9 | Lua | Notes |
---|---|---|
DATESTAMP | util.get_datestamp() | Formatted like “241202", equivalent to os.date("%y%m%d") |
TIMESTAMP | util.get_timestamp() | Formatted like “010828", equivalent to os.date("%H%M%S") |
SYSID0 | sys.sys_id0 | |
EMUID0 | sys.emu_id0 | |
EMUBASE | sys.emu_base | |
SERIAL | sys.serial | |
REGION | sys.region | |
SDSIZE | fs.stat_fs("0:/").total | int instead of string (use util.format_bytes to format it) |
SDFREE | fs.stat_fs("0:/").free | int instead of string (use util.format_bytes to format it) |
NANDSIZE | NANDSIZE | int instead of string (use util.format_bytes to format it) |
GM9OUT | GM9OUT | |
CURRDIR | CURRDIR | nil instead of “(null)" if it can’t be found |
ONTYPE | CONSOLE_TYPE | “O3DS" or “N3DS" |
RDTYPE | IS_DEVKIT | boolean instead of a string |
HAX | HAX | |
GM9VER | GM9VER |
Comparisons with standard Lua
These original Lua 5.4 modules are fully available:
- Basic functions
print
will replace the top screen with an output log. Currently it does not implement some features such as line wrapping.- dofile and loadfile don't work yet.
- coroutine
- debug
- math
- string
- table
- utf8
These modules are partially available:
- os
- Only
os.clock
,os.time
,os.date
,os.difftime
, andos.remove
- Only
- io
- Only
io.open
; for open files, all butfile:setvbuf
andfile:lines
- This is a custom compatibility module that uses
fs
functions. If there are differences compared to the originalio
implementation, please report them as issues.
- Only
These modules work differently:
- package
package.cpath
andpackage.loadlib
are nonfunctional due to GM9 having no ability to load dynamic libraries.
Third-party libraries
rxi's json.lua is pre-included. To use it, first load it:
local json = require("json")
API reference
Constants
GM9VER
The version such as "v2.1.1-159-gff2cb913"
, the same string that is shown on the main screen.
SCRIPT
Path to the executed script, such as "0:/gm9/luascripts/myscript.lua"
.
CURRDIR
Directory of the executed script, such as "0:/gm9/luascripts"
.
GM9OUT
The value "0:/gm9/out"
.
HAX
Warning
This needs checking if it's accurate. One of three values:
- "ntrboot" if started from an ntrboot cart
- "sighax" if booted directly from a FIRM partition
- Empty string otherwise
NANDSIZE
Total size of SysNAND in bytes.
CONSOLE_TYPE
The string "O3DS"
or "N3DS"
.
IS_DEVKIT
true
if the console is a developer unit.
ui
module
Note
This assumes the default build is used, where the bottom screen is the main screen. If GodMode9 is compiled with
SWITCH_SCREENS=1
, then every instance where something appears on the bottom screen will actually be on the top screen and vice versa.
ui.echo
void ui.echo(string text)
Display text on the bottom screen and wait for the user to press A.
- Arguments
text
- Text to show the user
ui.ask
bool ui.ask(string text)
Prompt the user with a yes/no question.
- Arguments
text
- Text to ask the user
- Returns:
true
if the user accepts
ui.ask_hex
int ui.ask_hex(string text, int initial, int n_digits)
Ask the user to input a hex number.
- Arguments
text
- Text to ask the userinitial
- Starting valuen_digits
- Amount of hex digits allowed
- Returns: the number the user entered, or
nil
if canceled
ui.ask_number
int ui.ask_number(string text, int initial)
Ask the user to input a number.
- Arguments
text
- Text to ask the userinitial
- Starting value
- Returns: the number the user entered, or
nil
if canceled
ui.ask_text
string ui.ask_text(string prompt, string initial, int max_length)
Ask the user to input text.
- Arguments
prompt
- Text to ask the userinitial
- Starting valuemax_length
- Maximum length of the string
- Returns: the text the user entered, or
nil
if canceled
ui.ask_selection
int ui.ask_selection(string prompt, array options)
Ask the user to choose an option from a list. A maximum of 256 options are allowed.
- Arguments
prompt
- Text to ask the useroptions
- Table of options
- Returns: index of selected option, or
nil
if canceled
ui.clear
void ui.clear()
Clears the top screen.
ui.show_png
void ui.show_png(string path)
Displays a PNG file on the top screen.
The image must not be larger than 400 pixels horizontal or 240 pixels vertical. If SWITCH_SCREENS=1
is used, it must not be larger than 320 pixels horizontal.
- Arguments
path
- Path to PNG file
- Throws
"Could not read (file)"
- file does not exist or there was another read error"Invalid PNG file"
- file is not a valid PNG"PNG too large"
- too large horizontal or vertical, or an out-of-memory error
ui.show_text
void ui.show_text(string text)
Displays text on the top screen.
- Arguments
text
- Text to show the user
ui.show_game_info
void ui.show_game_info(string path)
Shows game file info. Accepts any files that include an SMDH, a DS game file, or GBA file.
- Arguments
path
- Path to game file (CIA, CCI/".3ds", SMDH, TMD, TIE (DSiWare export), Ticket, NDS, GBA)
- Throws
"ShowGameFileIcon failed on <path>"
- failed to get game info from path
ui.show_qr
void ui.show_qr(string text, string data)
Displays a QR code on the top screen, and a prompt on the bottom screen, and waits for the user to press A.
- Arguments
text
- Text to show the userdata
- Data to encode into the QR code
- Throws
"could not allocate memory"
- out-of-memory error when attempting to generate the QR code
ui.show_text_viewer
void ui.show_text_viewer(string text)
Display a scrollable text viewer.
- Arguments
text
- Text to display
- Throws
"text validation failed"
- given string contains invalid characters"failed to run MemTextViewer"
- internal memory viewer error
ui.show_file_text_viewer
void ui.show_file_text_viewer(string path)
Display a scrollable text viewer from a text file.
- Arguments
path
- Path to text file
- Throws
"could not allocate memory"
- out-of-memory error when attempting to create the text buffer"text validation failed"
- text file contains invalid characters"failed to run MemTextViewer"
- internal memory viewer error
ui.format_bytes
string ui.format_bytes(int bytes)
Format a number with Byte
, kB
, MB
, or GB
.
Note
This is affected by localization and may return different text if the language is not English.
- Arguments
bytes
- Size to format
- Returns: formatted string
ui.check_key
bool ui.check_key(string key)
Checks if the user is holding down a key.
- Arguments
key
- A button string:"A"
,"B"
,"SELECT"
,"START"
,"RIGHT"
,"LEFT"
,"UP"
,"DOWN"
,"R"
,"L"
,"X"
,"Y"
- Returns:
true
if currently held,false
if not
fs
module
fs.move
void fs.move(string src, string dst[, table opts {bool no_cancel, bool silent, bool overwrite, bool skip_all}])
Moves or renames a file or directory.
- Arguments
src
- Item to movedst
- Destination nameopts
(optional) - Option flagsno_cancel
- Don’t allow user to cancelsilent
- Don’t show progressoverwrite
- Overwrite filesskip_all
- Skip existing files
- Throws
"writing not allowed: <path>"
- user denied permission"destination already exists on <src> -> <dst> and {overwrite=true} was not used"
- attempted to move an item over an existing one without usingoverwrite
"PathMoveCopy failed on <src> -> <dst>"
- error when moving, or user canceled
fs.remove
void fs.remove(string path[, table opts {bool recursive}])
Delete a file or directory.
- Arguments
path
- Path to deleteopts
(optional) - Option flagsrecursive
- Remove directories recursively
- Throws
"writing not allowed: <path>"
- user denied permission"requested directory remove without {recursive=true} on <path>"
- attempted to delete a directory without usingrecursive
"PathDelete failed on %s"
- error when deleting
fs.copy
void fs.copy(string src, string dst[, table opts {bool calc_sha, bool sha1, bool no_cancel, bool silent, bool overwrite, bool skip_all, bool append, bool recursive}])
Copy a file or directory.
- Arguments
src
- Item to copydst
- Destination nameopts
(optional) - Option flagscalc_sha
- Write.sha
files containing a SHA-256 (default) or SHA-1 hashsha1
- Use SHA-1no_cancel
- Don’t allow user to cancelsilent
- Don’t show progressoverwrite
- Overwrite filesskip_all
- Skip existing filesappend
- Append to the end of existing files instead of overwritingrecursive
- Copy directories recursively
- Throws
"writing not allowed: <path>"
- user denied permission"requested directory copy without {recursive=true} on <src> -> <dst>"
- attempted to copy a directory without usingrecursive
"destination already exists on <src> -> <dst> and {overwrite=true} was not used"
- attempted to copy an item over an existing one without usingoverwrite
"PathMoveCopy failed on <src> -> <dst>"
- error when copying, or user canceled
fs.mkdir
void fs.mkdir(string path)
Create a directory. This creates intermediate directories as required, so fs.mkdir("a/b/c")
would create a
, then b
, then c
.
- Arguments
path
- Directory to create
- Throws
"writing not allowed: <path>"
- user denied permission"could not mkdir (<path>)"
- error when creating directories
fs.stat
array fs.stat(string path)
Get information about a file or directory. The result is a stat table with these keys:
- Arguments
path
- Directory to stat
- Returns: A stat table with keys:
name
(string)type
(string) -"dir"
or"file"
size
(number)read_only
(bool)
- Throws
"could not stat <path> (##)"
- error when attempting to stat item, with FatFs error number
fs.list_dir
array fs.list_dir(string path)
Get the contents of a directory. The result is a list of stat tables with these keys:
-
name
(string) -
type
(string) -"dir"
or"file"
-
size
(number) -
read_only
(bool) -
Arguments
path
- Directory to list
-
Returns: A list of stat tables, each with keys:
name
(string)type
(string) -"dir"
or"file"
size
(number)read_only
(bool)
-
Throws
"could not opendir <path> (##)"
- error when attempting to open directory, with FatFs error number"could not readdir <path> (##)"
- error when attempting to read directory, with FatFs error number
fs.stat_fs
array fs.stat_fs(string path)
Get information about a filesystem.
Note
This function can take several seconds before it returns an answer.
- Arguments
path
- Filesystem to stat
- Returns: A stat table with keys:
free
(number)total
(number)used
(number)
fs.dir_info
array fs.dir_info(string path)
Get information about a directory.
Note
This function can take several seconds before it returns an answer.
- Arguments
path
- Directory to check
- Returns: An info table with keys:
size
(number)dirs
(number)files
(number)
- Throws
"error when running DirInfo"
- error when scanning directory
fs.ask_select_file
string fs.ask_select_file(string prompt, string path[, bool opts {bool include_dirs, bool explorer}])
Prompt the user to select a file based on a pattern. Accepts a wildcard pattern like "0:/gm9/in/*_ctrtransfer_n3ds.bin"
.
- Arguments
prompt
- Text to ask the userpath
- Wildcard pattern to search foropts
(optional) - Option flagsinclude_dirs
- Include directories in selectionexplorer
- Use file explorer, including navigating subdirectories
- Returns: path selected, or
nil
if user canceled - Throws
"forbidden drive"
- attempted search onZ:
"invalid path"
- could not find/
in path
fs.ask_select_dir
string fs.ask_select_dir(string prompt, string path[, bool opts {bool explorer}])
Prompt the user to select a directory.
- Arguments
prompt
- Text to ask the userpath
- Directory to searchopts
(optional) - Option flagsexplorer
- Use file explorer, including navigating subdirectories
- Returns: path selected, or
nil
if user canceled - Throws
"forbidden drive"
- attempted search onZ:
fs.find
string fs.find(string pattern[, bool opts {bool first}])
Searches for a file based on a wildcard pattern. Returns the last result, unless first
is specified, or nil
if nothing was found.
Pattern can use ?
for search values, for example 00017?02
will match 00017002
, 00017102
, etc. Wildcards are also accepted.
- Arguments
pattern
- Pattern to search foropts
(optional) - Option flagsfirst
- Return first result instead of last
- Returns: found file, or
nil
if nothing is found - Throws
"failed to find <path> (##)"
- error when attempting to find path, with FatFs error number
fs.find_not
string fs.find(string pattern)
Searches for a free filename based on a pattern.
Pattern can use ?
for search values, for example nand_??.bin
will check to see if nand_00.bin
exists. If it doesn't, it returns this string. Otherwise, it checks if nand_01.bin
exists and keeps going until an unused filename can be found.
- Arguments
pattern
- Pattern to search for
- Returns: found file
- Throws
"failed to find <path> (##)"
- error when attempting to find path, with FatFs error number
fs.find_all
string fs.find_all(string dir, string pattern[, table opts {bool recursive}])
Search for all files that match a pattern.
- Arguments
dir
- Directory to searchpattern
- Filename patternopts
(optional) - Option flagsrecursive
- Remove directories recursively
- Throws
"could not open directory"
- failed to open directory
fs.allow
bool fs.allow(string path[, table flags {bool ask_all}])
Check for and request permission to write to a sensitive path.
- Arguments
path
- Path to request permission foropts
(optional) - Option flagsask_all
- Request to write to all files in directory
- Returns:
true
if granted,false
if user declines
fs.img_mount
void fs.img_mount(string path)
Mount an image file. Can be anything mountable through the file browser.
- Arguments
path
- Path to image file
- Throws
"failed to mount <path>"
- not a valid image file
fs.img_umount
void fs.img_umount()
Unmount the currently mounted image file.
fs.get_img_mount
string fs.get_img_mount()
Get the currently mounted image file.
- Returns: path to file, or
nil
if none is mounted
fs.hash_file
string fs.hash_file(string path, int offset, int size[, table opts {bool sha1}])
Calculate the hash for a file. Uses SHA-256 unless sha1
is specified. To hash an entire file, size
should be 0
.
Tip
- Use
fs.verify_with_sha_file
to compare with a corresponding.sha
file.- Use
util.bytes_to_hex
to convert the result to printable hex characters.
Note
Using an offset that is not
0
, with a size of0
(to hash to end of file), is currently undefined behavior. In the future this should work properly.
- Arguments
path
- File to hashoffset
- Data offsetsize
- Amount of data to hash, or0
to hash to end of fileopts
(optional) - Option flagssha1
- Use SHA-1
- Returns: SHA-256 or SHA-1 hash as byte string
- Throws
"failed to stat <path>"
- could not stat file to get size"FileGetSha failed on <path>"
- could not read file or user canceled
fs.hash_data
string fs.hash_data(string data[, table opts {bool sha1}])
Calculate the hash for some data. Uses SHA-256 unless sha1
is specified.
Tip
- Use
util.bytes_to_hex
to convert the result to printable hex characters.
- Arguments
data
- Data to hashopts
(optional) - Option flagssha1
- Use SHA-1
- Returns: SHA-256 or SHA-1 hash as byte string
fs.verify
bool fs.verify(string path)
Verify the integrity of a file.
Note
This is for files that have their own hashes built-in. For verifying against a corresponding
.sha
file, usefs.verify_with_sha_file
.
- Arguments
path
- File to verify
- Returns:
true
if successful,false
if failed or not verifiable
fs.verify_with_sha_file
bool fs.verify_with_sha_file(string path)
Calculate the hash of a file and compare it with a corresponding .sha
file.
Important
This currently assumes SHA-256. In the future this may automatically use SHA-1 when appropriate, based on the
.sha
file size.
TODO: add errors for fs.read_file here
- Argumens
path
- File to hash
- Returns:
true
if successful,false
if failed,nil
if.sha
file could not be read - Throws
"failed to stat <path>"
- could not stat file to get size"FileGetSha failed on <path>"
- could not read file or user canceled
fs.exists
bool fs.exists(string path)
Check if an item exists.
- Arguments
path
- Path to file or directory
- Returns:
true
if exists,false
otherwise
fs.is_dir
bool fs.is_dir(string path)
Check if an item exists, and is a directory.
- Arguments
path
- Path to directory
- Returns:
true
if exists and is a directory,false
otherwise
fs.is_file
bool fs.is_file(string path)
Check if an item exists, and is a file.
- Arguments
path
- Path to file
- Returns:
true
if exists and is a file,false
otherwise
fs.sd_is_mounted
bool fs.sd_is_mounted()
Check if the SD card is mounted.
- Returns:
true
if SD card is mounted
fs.sd_switch
void fs.sd_switch([string message])
Prompt the user to remove and insert an SD card.
- Arguments
message
(optional) - Text to prompt the user, defaults to"Please switch the SD card now."
- Throws
"user canceled"
- user canceled the switch
fs.fix_cmacs
void fs.fix_cmacs(string path)
Fix CMACs for a directory.
- Arguments
path
- Path to recursively fix CMACs for
- Throws
fixcmac failed
- user denied permission, or fixing failed
fs.read_file
string fs.read_file(string path, int offset, int/string size)
Read data from a file.
- Arguments
path
- File to readoffset
- Data offsetsize
- Amount of data to read
- Returns: string of data
- Throws
"could not allocate memory to read file"
- out-of-memory error when attempting to create the data buffer"could not read <path> (##)"
- error when attempting to read file, with FatFs error number
fs.write_file
int fs.write_file(string path, int offset, string data)
Write data to a file.
- Arguments
path
- File to writeoffset
- Offset to write to, or the string"end"
to write at the end of filedata
- Data to write
- Returns: amount of bytes written
- Throws
"writing not allowed: <path>"
- user denied permission"error writing <path> (##)"
- error when attempting to write file, with FatFs error number
fs.fill_file
void fs.fill_file(string path, int offset, int size, int byte)
Fill a file with a specified byte.
- Arguments
path
- File to writeoffset
- Offset to write tosize
- Amount of data to writebyte
- Number between0x00
and0xFF
(0
and255
) indicating the byte to writeopts
(optional) - Option flagsno_cancel
- Don’t allow user to cancel
- Throws
"writing not allowed: <path>"
- user denied permission"byte is not between 0x00 and 0xFF (got: ##)"
- byte value is not a single byte"FileSetByte failed on <path>"
- writing failed or user canceled
fs.make_dummy_file
void fs.make_dummy_file(string path, int size)
Create a dummy file.
Note
The file will contain data from the unused parts of the filesystem. If you need to ensure it's clean, use
fs.fill_file
.
- Arguments
path
- File to createsize
- File size to set
- Throws
"writing not allowed: <path>"
- user denied permission"FileCreateDummy failed on <path>"
- dummy creation failed
fs.truncate
void fs.truncate(string path, int size)
Truncate a file to a specific size.
Important
Does not work for virtual filesystems.
- Arguments
path
- File to createsize
- File size to set
- Throws
"writing not allowed: <path>"
- user denied permission"failed to open <path> (note: this only works on FAT filesystems, not virtual)"
- opening file failed, or a virtual filesystem was used"failed to seek on <path>"
- seeking file failed"failed to truncate on <path>"
- truncating file failed
fs.key_dump
void fs.key_dump(string file[, table opts {bool overwrite}])
Dumps title keys or seeds. Taken from both SysNAND and EmuNAND. The resulting file is saved to 0:/gm9/out
.
- Arguments
file
- One of three supported filenames:seeddb.bin
,encTitleKeys.bin
,decTitleKeys.bin
opts
(optional) - Option flagsoverwrite
- Overwrite files
- Throws
"building <file> failed"
- building failed or file already exists andoverwrite
was not used
fs.cart_dump
void fs.cart_dump(string path, int size[, table opts {bool encrypted}])
Dump the raw data from the inserted game card. No modifications are made to the data. This means for example, Card2 games will not have the save area cleared.
- Arguments
path
- File to write data tosize
- Amount of data to readopts
(optional) - Option flagsencrypted
- Dump game encrypted, only for DS/DSi games?
- Throws
"out of memory"
- out-of-memory error when attempting to create the data buffer"cart init fail"
- card is not inserted or some other failure when attempting to initialize"cart dump failed or canceled"
- cart read failed or used canceled
title
module
title.decrypt, title.encrypt
void title.decrypt(string path)
void title.decrypt(string path)
Decrypt or encrypt a title or key database, done in-place.
- Arguments
path
- Path to title or key database
- Throws
"CryptAesKeyDb failed on <path>"
- could not crypt key database"CryptGameFile failed on <path>"
- could not crypt title
title.install
void title.install(string path[, table opts {bool to_emunand}])
Install a title.
- Arguments
path
- File to installopts
(optional) - Option flagsto_emunand
- Install to EmuNAND
- Throws
"InstallGameFile failed on <path>"
- install failed or user canceled
title.build_cia
void title.build_cia(string path[, table opts {bool legit}])
Build title as a CIA. Resulting file is put in 0:/gm9/out
.
- Arguments
path
- File to build as CIAopts
(optional) - Option flagslegit
- Build as legit CIA if possible
- Throws
"BuildCiaFromGameFile failed on <path>"
- build failed or user canceled
title.extract_code
void title.extract_code(string src, string dst)
Extracts the .code
from a title and decompresses it.
- Arguments
src
- File containing.code
dst
- Destination to write decompressed.code
- Throws
"writing not allowed: <path>"
- user denied permission"<path> does not have code"
- invalid file type"failed to extract code from <path>"
- extraction failed
title.compress_code
void title.compress_code(string src, string dst)
Compress a .code
file.
- Arguments
src
- Extracted.code
, like fromtitle.extract_code
dst
- Destination to write compressed.code
- Throws
"writing not allowed: <path>"
- user denied permission"failed to compress code from <path>"
- compression failed
title.apply_ips, title.apply_bps, title.apply_bpm
void title.apply_ips(string patch, string src, string target)
void title.apply_bps(string patch, string src, string target)
void title.apply_bpm(string patch, string src, string target)
Apply an IPS, BPS, or BPM patch.
- Arguments
patch
- IPS, BPS, or BPM patch filesrc
- File to patchtarget
- Destination to write patched file to
- Throws
"ApplyIPSPatch failed"
- failed to apply IPS patch"ApplyBPSPatch failed"
- failed to apply BPS patch"ApplyBPMPatch failed"
- failed to apply BPM patch
sys
module
sys.boot
void sys.boot(string path)
Boot a FIRM file.
- Arguments
path
- FIRM file
- Throws
"out of memory"
- out-of-memory error when attempting to create the data buffer"not a bootable firm"
- file cannot be booted
sys.reboot
void sys.reboot()
Reboots the console.
sys.power_off
void sys.power_off()
Powers off the console.
sys.region
string sys.region
System region, based on SysNAND's SecureInfo_[AB]
file. Not affected by SecureInfo_C
.
Possible values: "JPN"
, "USA"
, "EUR"
, "AUS"
, "CHN"
, "KOR"
, "TWN"
, nil
(if it does not exist, or the region byte is invalid)
sys.serial
string sys.serial
Serial number, based on SysNAND's SecureInfo_[AB]
file. Not affected by SecureInfo_C
.
Can be nil
if the file does not exist.
sys.secureinfo_letter
string sys.secureinfo_letter
The letter at the end of the console's SecureInfo_[AB]
file.
Possible values: "A"
, "B"
, nil
(if it does not exist)
sys.sys_id0
string sys.sys_id0
ID0 of SysNAND as a printable hex string.
Can be nil
if the file does not exist.
sys.emu_id0
string sys.emu_id0
ID0 of EmuNAND as a printable hex string.
Can be nil
if the file does not exist.
sys.emu_base
int sys.emu_base
Current EmuNAND base.
sys.refresh_info
void sys.refresh_info()
Refresh the following variables:
sys.region
sys.serial
sys.secureinfo_letter
sys.sys_id0
sys.emu_id0
sys.emu_base
This function is automatically called at the beginning of Lua's execution, but errors are caught to prevent a premature exit. This could leave some of the variables at nil
.
- Throws
"could not read SecureInfo"
-SecureInfo_[AB]
is missing"SecureInfo region byte is invalid"
-SecureInfo_[AB]
is not between 0 and 6 inclusive
sys.next_emu
void sys.next_emu()
Switch to the next available EmuNAND.
This will automatically call sys.refresh_info
.
sys.check_embedded_backup
bool sys.check_embedded_backup()
Check if essential.exefs
is embedded into SysNAND, and prompts the user if it isn't. This is the same as the check that is done at boot for GodMode9, but this can be called in a build compiled with SCRIPT_RUNNER=1
for autorun scripts that require essential.exefs
to exist.
- Returns:
true
if it exists or was created,false
if user declines,nil
if console fails genuine NCSD check (modified partition table)
sys.check_raw_rtc
bool sys.check_raw_rtc()
Check if the Raw RTC is set correctly, and prompts the user if it isn't. This is the same as the check that is done at boot for GodMode9, but this can be called in a build compiled with SCRIPT_RUNNER=1
for autorun scripts that require essential.exefs
to exist.
- Returns:
true
if set,false
if user declines
util
module
util.bytes_to_hex
string util.bytes_to_hex(string data)
Convert a byte string to printable hex characters.
Example: "test\xaa\xbb\xcc\xdd"
-> "74657374aabbccdd"
- Arguments
data
- Data to convert
- Returns: Hex characters
util.hex_to_bytes
string util.hex_to_bytes(string hexstring)
Convert hex characters to a byte string.
Example: "74657374aabbccdd"
-> "test\xaa\xbb\xcc\xdd"
- Arguments
hexstring
- Hex characters to convert
- Returns: Byte string
- Throws
"bad argument #1 to 'char' (number expected, got nil)"
- invalid hex characters
util.get_datestamp
string util.get_datestamp()
Returns a date stamp formatted like "241202" for 2024 December 02. Equivalent to os.date("%y%m%d")
.
- Returns: Date stamp
util.get_timestamp
string util.get_timestamp()
Returns a date stamp formatted like "010828" for 01:08:28. Equivalent to os.date("%H%M%S")
.
- Returns: Time stamp
util.running_as_module
bool util.running_as_module()
Determines if the currently executing script was directly run, or was imported by another script. Useful for scripts that want to be usable directly, while also importable.
Note
This is not well tested.
- Returns:
true
if the current script was imported as a module