From ae44478b30d890fe0fb04022f44d474dcdcc3f9d Mon Sep 17 00:00:00 2001 From: Lassi Pulkkinen Date: Thu, 31 Oct 2024 03:11:21 +0200 Subject: Initial commit (import old repo) --- resource.ha | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 resource.ha (limited to 'resource.ha') diff --git a/resource.ha b/resource.ha new file mode 100644 index 0000000..6b393f2 --- /dev/null +++ b/resource.ha @@ -0,0 +1,191 @@ +use bufio; +use dejson; +use encoding::json; +use errors; +use fs; +use io; +use os; +use path; +use strings; +use trace; + +def MISSINGNO = "minecraft:missingno"; +def BUILTIN_MISSING = "minecraft:builtin/missing"; + +type Pack = struct { + fs: *fs::fs, + kind: str, +}; + +fn resource_splitpath(path: str) (str, str, str, str, str) = { + const (base, path) = strings::cut(path, "/"); + const (ns, path) = strings::cut(path, "/"); + const (kind, path) = strings::cut(path, "/"); + const bnameindex = match (strings::rindex(path, '/')) { + case let index: size => + yield index + 1; + case void => + yield 0z; + }; + const extindex = match (strings::rindex( + strings::sub(path, bnameindex, strings::end), '.')) { + case let index: size => + yield bnameindex + index; + case void => + yield len(path); + }; + const name = strings::sub(path, 0, extindex); + const ext = strings::sub(path, extindex, strings::end); + return (base, ns, kind, name, ext); +}; + +fn resource_open( + pack: *Pack, + kind: str, + ident: str, + ext: str, + tr: *trace::tracer, +) (io::handle | trace::failed) = { + const (ns, name) = ident_split(ident); + const fname = strings::concat(name, ext); + defer free(fname); + let path = path::init()!; + if (path::set(&path, pack.kind, ns, kind, fname) is path::too_long) { + return trace::error(tr, "Path too long"); + }; + const path = path::string(&path); + + match (fs::open(pack.fs, path, fs::flag::RDONLY)) { + case let f: io::handle => + return f; + case let err: fs::error => + return trace::error(tr, "open: {}", fs::strerror(err)); + }; +}; + +fn resource_load_json( + pack: *Pack, + kind: str, + ident: str, + ext: str, + tr: *trace::tracer, +) (json::value | trace::failed) = { + const f = resource_open(pack, kind, ident, ext, tr)?; + + let rbuf: [os::BUFSZ]u8 = [0...]; + let buf = bufio::init(f, rbuf, []); + + const json = match (json::load(&buf)) { + case let json: json::value => + yield json; + case let err: json::error => + io::close(f): void; + return trace::error(tr, "Invalid JSON: {}", + json::strerror(err)); + }; + + match (io::close(f)) { + case void => void; + case let err: io::error => + json::finish(json); + return trace::error(tr, "close: {}", io::strerror(err)); + }; + + return json; +}; + +fn resource_search( + pack: *Pack, + kind: str, + exts: str... +) [](str, str) = { + const tr = trace::ctx(&trace::root, "resource search"); + + let out: [](str, str) = []; + let path = path::init()!; + + const it = match (fs::iter(pack.fs, pack.kind)) { + case let x: *fs::iterator => + yield x; + case let err: fs::error => + trace::error(&tr, "{}: fs::iter: {}", + pack.kind, fs::strerror(err)): void; + return out; + }; + defer fs::finish(it); + for (true) match (fs::next(it)) { + case let ent: fs::dirent => + if (!fs::isdir(ent.ftype)) continue; + if (strings::hasprefix(ent.name, ".")) continue; + if (path::set(&path, pack.kind, ent.name, kind) + is path::too_long) { + + trace::error(&tr, "{}/{}/{}: Path too long", + pack.kind, ent.name, kind): void; + continue; + }; + resource_search_ns(pack, &out, path::string(&path), &tr, exts...); + case done => break; + }; + + return out; +}; + +fn resource_search_ns( + pack: *Pack, + out: *[](str, str), + parent: str, + tr: *trace::tracer, + exts: str... +) void = { + let path = path::init()!; + + const it = match (fs::iter(pack.fs, parent)) { + case let x: *fs::iterator => + yield x; + case errors::noentry => + return; + case let err: fs::error => + trace::error(tr, "{}: fs::iter: {}", + parent, fs::strerror(err)): void; + return; + }; + defer fs::finish(it); + for (true) match (fs::next(it)) { + case let ent: fs::dirent => + if (strings::hasprefix(ent.name, ".")) continue; + if (path::set(&path, parent, ent.name) is path::too_long) { + trace::error(tr, "{}/{}: Path too long", + parent, ent.name): void; + continue; + }; + const path = path::string(&path); + if (fs::isdir(ent.ftype)) { + resource_search_ns(pack, out, path, tr, exts...); + } else if (fs::isfile(ent.ftype)) { + const (_, ns, _, name, ext) = + resource_splitpath(path); + for (let i = 0z; i < len(exts); i += 1) { + if (ext == exts[i]) { + const ident = ident_make(ns, name); + // yes, this borrow from input is + // intentional. needs to be documented + // later. + append(out, (ident, exts[i])); + break; + }; + }; + }; + case done => break; + }; +}; + +fn wassert_fields(de: *dejson::deser, names: str...) (void | dejson::error) = { + dejson::object(de)?; + match (dejson::assert_fields(de, names...)) { + case let err: dejson::error => + defer free(err); + trace::warn(&trace::root, "{}", err); + case void => void; + }; +}; -- cgit v1.2.3