z_mirror/hare-fmm

git clone https://git.raiguard.me/z_mirror/hare-fmm

Caleb HeuerConvert to for-each loops and fix some potential memory issues74b341b

trunk
3.5 KiB140 linesraw
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};