summary refs log tree commit diff
path: root/src/config.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.zig')
-rw-r--r--src/config.zig117
1 files changed, 117 insertions, 0 deletions
diff --git a/src/config.zig b/src/config.zig
new file mode 100644
index 0000000..4bde82c
--- /dev/null
+++ b/src/config.zig
@@ -0,0 +1,117 @@
+// Configuration parser
+// Copyright (C) 2021  Nguyễn Gia Phong
+//
+// This file is part of Black Shades.
+//
+// Black Shades is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published
+// by the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Black Shades is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Black Shades.  If not, see <https://www.gnu.org/licenses/>.
+
+const Allocator = std.mem.Allocator;
+const createFile = std.fs.createFileAbsolute;
+const eql = std.mem.eql;
+const ini = @import("ini");
+const join = std.fs.path.join;
+const mkdir = std.os.mkdir;
+const openFile = std.fs.openFileAbsolute;
+const parseFloat = std.fmt.parseFloat;
+const parseInt = std.fmt.parseInt;
+const std = @import("std");
+
+/// Game configuration.
+pub const Config = extern struct {
+    screen_width: c_int = 640,
+    screen_height: c_int = 480,
+    mouse_sensitivity: f32 = 0.7,
+    debug: bool = false,
+    vsync: bool = true,
+    blood: bool = true,
+    blur: bool = false,
+    menu: bool = true,
+    custom_levels: bool = false,
+    music: bool = true,
+    azerty: bool = false,
+};
+
+/// Parse boolean values.
+fn parseBool(s: []const u8) !bool {
+    if (eql(u8, s, "false"))
+        return false;
+    if (eql(u8, s, "true"))
+        return true;
+    return error.InvalidCharacter;
+}
+
+/// Parse settings.ini in the given base directory.
+pub fn parse(allocator: *Allocator, base_dir: []const u8) !Config {
+    const config_dir = try join(allocator, &.{ base_dir, "blackshades" });
+    defer allocator.free(config_dir);
+    const path = try join(allocator, &.{ config_dir, "settings.ini" });
+    defer allocator.free(path);
+
+    var config = Config{};
+    const input = openFile(path, .{}) catch {
+        mkdir(config_dir, 0o644) catch |err| switch (err) {
+            error.PathAlreadyExists => {},
+            else => return err,
+        };
+        const output = try createFile(path, .{});
+        defer output.close();
+        const writer = output.writer();
+        try writer.print("screen width = {}\n", .{ config.screen_width });
+        try writer.print("screen height = {}\n", .{ config.screen_height });
+        try writer.print("mouse sensitivity = {d:.1}\n",
+                         .{ config.mouse_sensitivity });
+        try writer.print("debug = {}\n", .{ config.debug });
+        try writer.print("vsync = {}\n", .{ config.vsync });
+        try writer.print("blood = {}\n", .{ config.blood });
+        try writer.print("blur = {}\n", .{ config.blur });
+        try writer.print("menu = {}\n", .{ config.menu });
+        try writer.print("custom levels = {}\n", .{ config.custom_levels });
+        try writer.print("music = {}\n", .{ config.music });
+        try writer.print("azerty = {}\n", .{ config.azerty });
+        return config;
+    };
+    defer input.close();
+
+    var parser = ini.parse(allocator, input.reader());
+    defer parser.deinit();
+
+    while (try parser.next()) |record|
+        switch (record) {
+            .property => |kv| if (eql(u8, kv.key, "screen width")) {
+                config.screen_width = try parseInt(c_int, kv.value, 10);
+            } else if (eql(u8, kv.key, "screen height")) {
+                config.screen_height = try parseInt(c_int, kv.value, 10);
+            } else if (eql(u8, kv.key, "mouse_sensitivity")) {
+                config.mouse_sensitivity = try parseFloat(f32, kv.value);
+            } else if (eql(u8, kv.key, "debug")) {
+                config.debug = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "vsync")) {
+                config.vsync = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "blood")) {
+                config.blood = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "blur")) {
+                config.blur = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "menu")) {
+                config.menu = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "custom levels")) {
+                config.custom_levels = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "music")) {
+                config.music = try parseBool(kv.value);
+            } else if (eql(u8, kv.key, "azerty")) {
+                config.azerty = try parseBool(kv.value);
+            },
+            else => unreachable,
+        };
+    return config;
+}