diff options
Diffstat (limited to 'src/misc.zig')
-rw-r--r-- | src/misc.zig | 66 |
1 files changed, 58 insertions, 8 deletions
diff --git a/src/misc.zig b/src/misc.zig index 2e54416..e30c154 100644 --- a/src/misc.zig +++ b/src/misc.zig @@ -35,7 +35,9 @@ const data_dir = @import("build_options").data_dir ++ [_]u8{ sep }; const endsWith = std.mem.endsWith; const eql = std.mem.eql; const free = std.c.free; +const ini = @import("ini"); const join = std.fs.path.joinZ; +const hash = std.crypto.hash.Blake3.hash; const maxInt = std.math.maxInt; const parseFloat = std.fmt.parseFloat; const parseInt = std.fmt.parseInt; @@ -45,14 +47,12 @@ const startsWith = std.mem.startsWith; const std = @import("std"); const tokenize = std.mem.tokenize; -// Don't judge me, take care of me! -const max_size = maxInt(usize); - /// Read given file to heap, allocated by C allocator. fn readFile(dir: Dir, comptime fmt: []const u8, args: anytype) ![]const u8 { const filename = try allocPrint(allocator, fmt, args); defer allocator.free(filename); - return dir.readFileAlloc(allocator, filename, max_size); + // Don't judge me, take care of me! + return dir.readFileAlloc(allocator, filename, maxInt(usize)); } const Frame = extern struct { @@ -268,6 +268,36 @@ export fn loadMuscles(muscles: [*]Muscle) void { muscles[i].load(row) catch unreachable; } +pub const Scores = extern struct { + // Don't underestimate players! + high_score: usize = 0, + completed: bool = false, +}; + +/// Read scores from user data directory and return it. +pub fn loadScores(base_dir: []const u8) !Scores { + var dir = try cwd().openDir(base_dir, .{}); + defer dir.close(); + var scores = Scores{}; + const path = "blackshades" ++ [_]u8{ sep } ++ "scores.ini"; + const input = dir.openFile(path, .{}) catch return scores; + defer input.close(); + var parser = ini.parse(allocator, input.reader()); + defer parser.deinit(); + + while (try parser.next()) |record| + switch (record) { + .section => {}, + .property => |kv| if (eql(u8, kv.key, "high score")) { + scores.high_score = try parseInt(usize, kv.value, 10); + } else if (eql(u8, kv.key, "completed")) { + scores.completed = try parseBool(kv.value); + } else return error.InvalidData, + else => return error.InvalidData, + }; + return scores; +} + /// Load audio file into an OpenAL buffer and return it. export fn loadSound(filename: [*:0]const u8) u32 { const path = join(allocator, &.{ data_dir ++ "sounds", span(filename) }) @@ -287,10 +317,9 @@ fn check(errorString: fn (c_uint) callconv(.C) [*c]const u8, /// Load PNG file into an OpenGL buffer and return it. export fn loadTexture(filename: [*:0]const u8) GLuint { - var dir = cwd().openDir(data_dir ++ "textures", .{}) catch unreachable; - defer dir.close(); - const file = dir.readFileAlloc(allocator, span(filename), max_size) - catch unreachable; + const file = readFile(cwd(), data_dir ++ "textures{c}{s}", .{ + sep, filename, + }) catch unreachable; defer allocator.free(file); var data: [*c]u8 = undefined; @@ -325,3 +354,24 @@ export fn playSound(source: u32, x: f32, y: f32, z: f32) void { src.setPosition(.{ x / 32, y / 32, z / 32 }) catch unreachable; src.play() catch unreachable; } + +// TODO: replace with @maximum from Zig 0.9+ +fn maximum(a: anytype, b: @TypeOf(a)) @TypeOf(a) { + return if (a > b) a else b; +} + +/// Write scores to user data directory. +pub fn saveScores(base_dir: []const u8, current: Scores) !void { + const previous = try loadScores(base_dir); + const dir_path = try join(allocator, &.{ base_dir, "blackshades" }); + defer allocator.free(dir_path); + var dir = try cwd().openDir(dir_path, .{}); + defer dir.close(); + const format = "[scores]\nhigh score = {}\ncompleted = {}\n"; + const data = try allocPrint(allocator, format, .{ + maximum(previous.high_score, current.high_score), + previous.completed or current.completed, + }); + defer allocator.free(data); + try dir.writeFile("scores.ini", data); +} |