1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
// 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 cwd = std.fs.cwd;
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 parseBool = @import("misc.zig").parseBool;
const parseFloat = std.fmt.parseFloat;
const parseInt = std.fmt.parseInt;
const std = @import("std");
/// Game configuration.
pub const Config = extern struct {
width: c_int = 800,
height: c_int = 600,
vsync: bool = true,
blur: bool = false,
blood: bool = true,
music: bool = true,
mouse_sensitivity: f32 = 1.0,
custom_levels: bool = false,
debug: bool = false,
};
/// Parse config.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);
var dir = try cwd().makeOpenPath(config_dir, .{});
defer dir.close();
var config = Config{};
const input = dir.openFile("config.ini", .{}) catch {
try dir.writeFile("config.ini", @embedFile("config.ini"));
return config;
};
defer input.close();
var parser = ini.parse(allocator, input.reader());
defer parser.deinit();
var section: []u8 = "";
defer allocator.free(section);
while (try parser.next()) |record|
switch (record) {
.section => |heading| {
allocator.free(section);
section = try allocator.dupe(u8, heading);
},
.property => |kv| if (eql(u8, section, "graphics")) {
if (eql(u8, kv.key, "width"))
config.width = try parseInt(c_int, kv.value, 10)
else if (eql(u8, kv.key, "height"))
config.height = try parseInt(c_int, kv.value, 10)
else if (eql(u8, kv.key, "vsync"))
config.vsync = try parseBool(kv.value)
else if (eql(u8, kv.key, "blur"))
config.blur = try parseBool(kv.value)
else if (eql(u8, kv.key, "blood"))
config.blood = try parseBool(kv.value)
else unreachable;
} else if (eql(u8, section, "audio")) {
if (eql(u8, kv.key, "music"))
config.music = try parseBool(kv.value)
else unreachable;
} else if (eql(u8, section, "input")) {
if (eql(u8, kv.key, "mouse sensitivity"))
config.mouse_sensitivity = try parseFloat(f32, kv.value)
else unreachable;
} else if (eql(u8, section, "misc")) {
if (eql(u8, kv.key, "custom levels"))
config.custom_levels = try parseBool(kv.value)
else if (eql(u8, kv.key, "debug"))
config.debug = try parseBool(kv.value)
else unreachable;
} else unreachable,
else => unreachable,
};
return config;
}
|