xiao.fs

Read and write files in the C:\xiaohook folder, plus string helpers for paths.

xiao.fs reads and writes files inside the C:\xiaohook folder and offers string helpers to build paths.

Paths are relative to C:\xiaohook, both / and \ work, a single leading / or \ is ignored, and .. is allowed as long as the resolved path stays inside C:\xiaohook. Every failure raises an error: wrap calls in pcall when a missing file is a normal case.

At a glance

Function Description
xiao.fs.read(path) whole file as a string
xiao.fs.write(path, data) create or replace a file
xiao.fs.append(path, data) append to a file, create it if needed
xiao.fs.isfile(path) true for an existing file
xiao.fs.isfolder(path) true for an existing folder
xiao.fs.listfiles(path?) sorted content of a folder
xiao.fs.makefolder(path) create a folder and its parents
xiao.fs.delfolder(path) delete a folder and its content
xiao.fs.delfile(path) delete a file
forward, backward, join, extname, filename, dirname, sanitize path helpers, no disk access

Disk functions

xiao.fs.read(path): string

xiao.fs.read(path: string): string

Returns the whole file. The content is binary safe, so it works for fonts and images too.

Name Type Description
path string file path relative to C:\xiaohook

Returns: the bytes of the file. Errors: a refused path (see At a glance); cannot read file: ... when the file does not exist, is a folder or cannot be opened.

local ok, data = pcall(xiao.fs.read, "config/settings.txt")
if not ok then
    data = ""
end

xiao.fs.write(path, data)

xiao.fs.write(path: string, data: string)

Creates the file, or replaces its whole content. Bytes are written as is, without newline conversion. The folder must already exist: create it first with makefolder.

Name Type Description
path string file path relative to C:\xiaohook
data string new content

Returns: nothing. Errors: a refused path; cannot write file: ... when the folder is missing or the file is locked.

xiao.fs.write("notes.txt", "first line\n")

xiao.fs.append(path, data)

xiao.fs.append(path: string, data: string)

Appends data at the end of the file, creating it if needed. The folder must already exist.

Name Type Description
path string file path relative to C:\xiaohook
data string bytes to add

Returns: nothing. Errors: a refused path; cannot write file: ....

xiao.fs.append("notes.txt", "another line\n")

xiao.fs.isfile(path): boolean

xiao.fs.isfile(path: string): boolean
Name Type Description
path string path relative to C:\xiaohook

Returns: true if path is an existing file, false otherwise. Errors: a refused path only.

if xiao.fs.isfile("notes.txt") then print("found") end

xiao.fs.isfolder(path): boolean

xiao.fs.isfolder(path: string): boolean
Name Type Description
path string path relative to C:\xiaohook; "" or "." is C:\xiaohook itself

Returns: true if path is an existing folder, false otherwise. Errors: a refused path only.

print(xiao.fs.isfolder("lua"))

xiao.fs.listfiles(path): string[]

xiao.fs.listfiles(path?: string): string[]

Lists the files and folders directly inside path, without recursion.

Name Type Description
path string optional folder, C:\xiaohook itself when omitted

Returns: the entries as paths relative to C:\xiaohook, written with / ("lua/main.lua"), sorted by byte value. Errors: a refused path; not a folder: ....

for _, entry in ipairs(xiao.fs.listfiles("lua")) do
    print(entry, xiao.fs.isfolder(entry))
end

xiao.fs.makefolder(path)

xiao.fs.makefolder(path: string)

Creates the folder and every missing parent. An existing folder is not an error.

Name Type Description
path string folder path relative to C:\xiaohook

Returns: nothing. Errors: a refused path; cannot create folder: ....

xiao.fs.makefolder("cache/session")

xiao.fs.delfolder(path)

xiao.fs.delfolder(path: string)

Deletes the folder and everything inside it.

Name Type Description
path string folder path relative to C:\xiaohook

Returns: nothing. Errors: a refused path; not a folder: ...; the workspace itself cannot be deleted for C:\xiaohook; cannot delete: ....

xiao.fs.delfolder("cache/session")

xiao.fs.delfile(path)

xiao.fs.delfile(path: string)
Name Type Description
path string file path relative to C:\xiaohook

Returns: nothing. Errors: a refused path; not a file: ...; cannot delete: ....

pcall(xiao.fs.delfile, "notes.txt")

Path helpers

These helpers only transform strings. They never touch the disk, never raise a path error and are not subject to the restrictions above.

xiao.fs.forward(path): string

xiao.fs.forward(path: string): string

Returns: path with every \ replaced by /.

xiao.fs.backward(path): string

xiao.fs.backward(path: string): string

Returns: path with every / replaced by \.

xiao.fs.join(a, b): string

xiao.fs.join(a: string, b: string): string
Name Type Description
a string first part
b string second part, its leading separators are dropped

Returns: a and b joined by a single separator: / is added when a does not already end with / or \. With an empty a, returns b without its leading separators.

print(xiao.fs.join("cache/", "/log.txt"))
cache/log.txt

xiao.fs.extname(path, replace): string

xiao.fs.extname(path: string, replace?: string): string
Name Type Description
path string a path or a file name
replace string optional new extension, with or without the dot; "" removes the extension

Returns: without replace, the extension without the dot ("gz" for a.tar.gz, "" for .hidden or README). With replace, the whole path with that extension.

print(xiao.fs.extname("lua/main.lua"), xiao.fs.extname("lua/main.lua", "txt"))
lua	lua/main.txt

xiao.fs.filename(path, replace): string

xiao.fs.filename(path: string, replace?: string): string
Name Type Description
path string a path
replace string optional new file name

Returns: without replace, the part after the last separator. With replace, the path with that file name.

xiao.fs.dirname(path, replace): string

xiao.fs.dirname(path: string, replace?: string): string
Name Type Description
path string a path
replace string optional new folder; "" keeps only the file name

Returns: without replace, the part before the last separator, "" when there is none. With replace, the path moved to that folder.

print(xiao.fs.dirname("cache/session/log.txt"), xiao.fs.dirname("cache/session/log.txt", "backup"))
cache/session	backup/log.txt

xiao.fs.sanitize(name): string

xiao.fs.sanitize(name: string): string

Replaces control characters and < > : " | ? * with _. / and \ are kept: sanitize each name that comes from outside (a player name, a chat message) before joining it into a path.

Returns: the cleaned string, same length as name.

local safe = xiao.fs.sanitize("a:b?c")
print(safe)
a_b_c

Complete example

Keep a log and load a font from the disk, then draw with it (see xiao.paint). For a Lua table that must survive restarts, xiao.workspace does the file handling for you.

local fs = xiao.fs

fs.makefolder("cache/session")
fs.write("cache/session/log.txt", os.date("%x %X") .. "\n")
fs.append("cache/session/log.txt", "started\n")

for _, path in ipairs(fs.listfiles("cache/session")) do
    print(path, fs.extname(path))
end

local ok, font = pcall(fs.read, "lua/fonts/proggy.ttf")
if ok and xiao.paint.text.bake("Proggy", font, 14) then
    local white = xiao.paint.color.rgb(255, 255, 255)
    local origin = xiao.paint.point.vector2(20, 20)
    xiao.paint.add("proggy", function()
        xiao.paint.text.font("Proggy")
        xiao.paint.text.left(origin, "hello", white)
        xiao.paint.text.font("default")
    end)
end