diff options
Diffstat (limited to 'src/config.zig')
-rw-r--r-- | src/config.zig | 138 |
1 files changed, 119 insertions, 19 deletions
diff --git a/src/config.zig b/src/config.zig index dbe0f7d..4a1840d 100644 --- a/src/config.zig +++ b/src/config.zig @@ -16,19 +16,116 @@ // 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 Dir = std.fs.Dir; +const File = std.fs.File; const allocator = std.heap.c_allocator; -const createFile = std.fs.createFileAbsolute; +const c = @import("main.zig").c; const cwd = std.fs.cwd; const data_dir = @import("build_options").data_dir; const eql = std.mem.eql; const ini = @import("ini"); const join = std.fs.path.join; +const maxInt = std.math.maxInt; 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"); +const tokenize = std.mem.tokenize; + +const default_levels_len = 13; + +/// Open the given file for reading, +/// on error copy it from the game's data and try again. +fn openData(dir: Dir, filename: []const u8) !File { + return dir.openFile(filename, .{}) catch { + var data = try cwd().makeOpenPath(data_dir, .{}); + defer data.close(); + try data.copyFile(filename, dir, filename, .{}); + return try dir.openFile(filename, .{}); + }; +} + +const Level = extern struct { + environment: c_int, + evil_weapons: u8, + evil_rarity: u8, + guard_weapon: u8, + guard_reloads: u8, + time: c_int, + difficulty: f32, +}; + +const Weapons = packed struct { + // TODO: remove nogun and sort + jaw: bool = false, + sniper_rifle: bool = false, + assault_rifle: bool = false, + magnum: bool = false, + handgun: bool = false, + grenade: bool = false, + knife: bool = false, + shotgun: bool = false, +}; + +fn parseLevels(dir_path: []const u8, length: usize) ![*]Level { + var dir = try cwd().makeOpenPath(dir_path, .{}); + defer dir.close(); + const input = try openData(dir, "levels.ini"); + defer input.close(); + const levels = try allocator.alloc(Level, length); + errdefer allocator.free(levels); + var parser = ini.parse(allocator, input.reader()); + defer parser.deinit(); + + var i: usize = maxInt(usize); + while (try parser.next()) |record| + switch (record) { + .section => i +%= 1, + .property => |kv| if (eql(u8, kv.key, "environment")) { + levels[i].environment = if (eql(u8, kv.value, "sunny")) + c.sunny_environment + else if (eql(u8, kv.value, "foggy")) + c.foggy_environment + else if (eql(u8, kv.value, "snowy")) + c.snowy_environment + else if (eql(u8, kv.value, "rainy")) + c.rainy_environment + else if (eql(u8, kv.value, "firey")) + c.firey_environment + else if (eql(u8, kv.value, "night")) + c.night_environment + else return error.InvalidData; + } else if (eql(u8, kv.key, "evil weapons")) { + var weapons = Weapons{}; + var enums = tokenize(kv.value, " "); + while (enums.next()) |weapon| + switch (try parseInt(u3, weapon, 10)) { + c.nogun => weapons.jaw = true, + c.sniperrifle => weapons.sniper_rifle = true, + c.assaultrifle => weapons.assault_rifle = true, + c.handgun1 => weapons.magnum = true, + c.handgun2 => weapons.handgun = true, + c.grenade => weapons.grenade = true, + c.knife => weapons.knife = true, + c.shotgun => weapons.shotgun = true, + }; + levels[i].evil_weapons = @bitCast(u8, weapons); + } else if (eql(u8, kv.key, "evil rarity")) { + levels[i].evil_rarity = try parseInt(u8, kv.value, 10); + } else if (eql(u8, kv.key, "guard weapon")) { + levels[i].guard_weapon = try parseInt(u3, kv.value, 10); + } else if (eql(u8, kv.key, "guard reloads")) { + levels[i].guard_reloads = try parseInt(u8, kv.value, 10); + } else if (eql(u8, kv.key, "time")) { + levels[i].time = try parseInt(c_int, kv.value, 10); + } else if (eql(u8, kv.key, "difficulty")) { + levels[i].difficulty = try parseFloat(f32, kv.value); + } else return error.InvalidData, + else => return error.InvalidData, + }; + return levels.ptr; +} /// Game configuration. pub const Config = extern struct { @@ -42,7 +139,10 @@ pub const Config = extern struct { mouse_sensitivity: f32 = 1.0, - custom_levels: bool = false, + levels: extern struct { + ptr: [*]Level, + len: usize, + } = .{ .ptr = undefined, .len = 0 }, debug: bool = false, }; @@ -52,19 +152,12 @@ pub fn parse(base_dir: []const u8) !Config { 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 blk: { - var source = try cwd().makeOpenPath(data_dir, .{}); - defer source.close(); - try source.copyFile("config.ini", dir, "config.ini", .{}); - break :blk try dir.openFile("config.ini", .{}); - }; + const input = try openData(dir, "config.ini"); defer input.close(); - var parser = ini.parse(allocator, input.reader()); defer parser.deinit(); + var config = Config{}; var section: []u8 = ""; defer allocator.free(section); while (try parser.next()) |record| @@ -84,23 +177,30 @@ pub fn parse(base_dir: []const u8) !Config { config.blur = try parseBool(kv.value) else if (eql(u8, kv.key, "blood")) config.blood = try parseBool(kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "audio")) { if (eql(u8, kv.key, "music")) config.music = try parseBool(kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "input")) { if (eql(u8, kv.key, "mouse sensitivity")) config.mouse_sensitivity = try parseFloat(f32, kv.value) - else unreachable; + else return error.InvalidData; } else if (eql(u8, section, "misc")) { if (eql(u8, kv.key, "custom levels")) - config.custom_levels = try parseBool(kv.value) + config.levels.len = try parseInt(usize, kv.value, 10) else if (eql(u8, kv.key, "debug")) config.debug = try parseBool(kv.value) - else unreachable; - } else unreachable, - else => unreachable, + else return error.InvalidData; + } else return error.InvalidData, + else => return error.InvalidData, }; + + if (config.levels.len > 0) { + config.levels.ptr = try parseLevels(config_dir, config.levels.len); + } else { + config.levels.len = default_levels_len; + config.levels.ptr = try parseLevels(data_dir, config.levels.len); + } return config; } |