z_mirror/hare-fmm
git clone https://git.raiguard.me/z_mirror/hare-fmm
74b341b
trunk
1use encoding::json; 2use errors; 3use fmt; 4use fs; 5use io; 6use os; 7use path; 8use strings; 9 10// A mods directory, containing a mod-list.json and any number of zipped or 11// unzipped mods. 12type dir = struct { 13 modfiles: []modfile, 14 modlist: modlist, 15 path: str, 16}; 17 18// All possible errors returned by dir functions. 19type dir_error = !(fs::error | json::error | errors::error | nomem); 20 21// Disables all mods in the directory except for the base mod. 22fn dir_disable_all(dir: *dir) void = { 23 for (let entry &.. dir.modlist) { 24 if (entry.name != "base") 25 entry.enabled = false; 26 }; 27 fmt::println("Disabled all mods")!; 28}; 29 30// Locates the requested mod in a [[dir]], returning its directory entry and 31// modlist entry. 32fn dir_find( 33 dir: *dir, 34 mod: *modident 35) ((*modfile, *modlist_entry) | !void) = { 36 const name = mod.0; 37 const ver = mod.1; 38 let file: (*modfile | void) = void; 39 for (let mod &.. dir.modfiles) { 40 if (mod.ident.0 == name) { 41 if (ver is version && version_cmp( 42 ver as version, 43 mod.ident.1 as version 44 ) != version_cmpres::EQ) 45 continue; 46 file = mod; 47 break; 48 }; 49 }; 50 let entry: (*modlist_entry | void) = void; 51 for (let mod &.. dir.modlist) { 52 if (mod.name == name) { 53 entry = mod; 54 break; 55 }; 56 }; 57 if (file is *modfile && entry is *modlist_entry) 58 return (file as *modfile, entry as *modlist_entry); 59}; 60 61// Frees resources related to a [[dir]]. 62fn dir_free(dir: *dir) void = { 63 free(dir.path); 64 for (let modfile &.. dir.modfiles) 65 modfile_free(modfile); 66 modlist_free(dir.modlist); 67}; 68 69// Parses a mods directory. The return value must be freed with [[dir_free]]. 70fn dir_load(path: str) (dir | dir_error) = { 71 // Parse mod list first in case it's not there 72 const modlist_path = path::init(path, "mod-list.json")!; 73 const mod_list = os::open(path::string(&modlist_path))?; 74 defer io::close(mod_list)!; 75 const modlist_json = json::load(mod_list)?; 76 defer json::finish(modlist_json); 77 const modlist = modlist_unmarshal(modlist_json); 78 // Construct modfiles 79 const dirents = os::readdir(path)?; 80 let modfiles: []modfile = []; 81 for (let entry &.. dirents) { 82 const file = modfile_load(path, entry); 83 // TODO: Print errors here 84 if (file is modfile) 85 append(modfiles, file as modfile)!; 86 }; 87 88 return dir { 89 modfiles = modfiles, 90 modlist = modlist, 91 path = strings::dup(path)!, 92 }; 93}; 94 95// Saves changes made to mod-list.json 96fn dir_save(dir: *dir) void = { 97 let modlist_path = path::init(dir.path, "mod-list.json")!; 98 let file = os::open( 99 path::string(&modlist_path), 100 fs::flag::TRUNC | fs::flag::WRONLY 101 )!; 102 const json = modlist_marshal(dir.modlist); 103 json::dump(file, json)!; 104 json::finish(json); 105 io::close(file)!; 106}; 107 108// Converts a [[dir_error]] into a user-friendly string. 109fn dir_strerror(err: dir_error) str = { 110 match (err) { 111 case let err: fs::error => 112 return fs::strerror(err); 113 case let err: json::error => 114 return json::strerror(err); 115 case let err: errors::error => 116 return errors::strerror(err); 117 }; 118}; 119 120// Updates the enabled status of the given mods in mod-list.json. 121fn dir_update_enabled(dir: *dir, mods: []modident, enabled: bool) void = { 122 for (let mod &.. mods) { 123 match (dir_find(dir, mod)) { 124 case let entry: (*modfile, *modlist_entry) => 125 entry.1.enabled = enabled; 126 if (enabled) { 127 const modstr = modident_str(&entry.0.ident); 128 fmt::println("Enabled", modstr)!; 129 free(modstr); 130 } else { 131 fmt::println("Disabled", mod.0)!; 132 }; 133 case => 134 const modstr = modident_str(mod); 135 defer free(modstr); 136 fmt::errorln(modstr, 137 "was not found in the mods directory")!; 138 }; 139 }; 140};