diff options
Diffstat (limited to 'src/config.zig')
-rw-r--r-- | src/config.zig | 117 |
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; +} |