summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules6
-rw-r--r--build.zig4
m---------lib/ini0
m---------lib/loca0
m---------lib/zeal0
-rw-r--r--src/Game.h4
-rw-r--r--src/GameInitDispose.cpp82
-rw-r--r--src/config.h21
-rw-r--r--src/config.zig117
-rw-r--r--src/main.zig20
11 files changed, 181 insertions, 74 deletions
diff --git a/.gitignore b/.gitignore
index 2f12ded..21f8acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
 zig-cache/
 zig-out/
-config.txt
 highscore.txt
diff --git a/.gitmodules b/.gitmodules
index e0c1036..1c3b457 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,9 @@
 [submodule "lib/zeal"]
 	path = lib/zeal
 	url = https://git.sr.ht/~cnx/zeal
+[submodule "lib/loca"]
+	path = lib/loca
+	url = https://git.sr.ht/~cnx/zig-loca
+[submodule "lib/ini"]
+	path = lib/ini
+	url = https://github.com/ziglibs/ini
diff --git a/build.zig b/build.zig
index ada71ad..86a81db 100644
--- a/build.zig
+++ b/build.zig
@@ -26,7 +26,9 @@ pub fn build(b: *std.build.Builder) void {
     exe.addCSourceFile("src/Timer.cpp", &cxxflags);
     exe.addCSourceFile("src/Textures.cpp", &cxxflags);
 
-    exe.addPackage(.{ .name = "zeal", .path = "lib/zeal/src/main.zig" });
+    exe.addPackage(.{ .name = "ini", .path = "lib/ini/src/ini.zig" });
+    exe.addPackage(.{ .name = "loca", .path = "lib/loca/src/main.zig" });
+    exe.addPackage(.{ .name = "zeal", .path = "lib/zeal/src/zeal.zig" });
     exe.linkSystemLibrary("GL");
     exe.linkSystemLibrary("GLU");
     exe.linkSystemLibrary("SDL");
diff --git a/lib/ini b/lib/ini
new file mode 160000
+Subproject cceafc18d7f72a532224be80b36ea1b3b9e2bfd
diff --git a/lib/loca b/lib/loca
new file mode 160000
+Subproject d88de4ac1b70517be1de905d62f11ae74353166
diff --git a/lib/zeal b/lib/zeal
-Subproject a0238a32993194b121d2f32845e4a20c667b75f
+Subproject 735e9aff1d1443abc8107a9895d4b71e43fdf63
diff --git a/src/Game.h b/src/Game.h
index b4dbcc5..0d819c6 100644
--- a/src/Game.h
+++ b/src/Game.h
@@ -22,6 +22,8 @@
 #ifndef BLACKSHADES_GAME_H
 #define BLACKSHADES_GAME_H
 
+#include "config.h"
+
 #ifdef __cplusplus
 #include <GL/gl.h>
 #include <GL/glu.h>
@@ -205,7 +207,7 @@ typedef struct Game Game;
 #ifdef __cplusplus
 extern "C" {
 #endif // __cplusplus
-	Game* makeGame();
+	Game* makeGame(struct Config config);
 	void initGl(Game*);
 	void initGame(Game*);
 	void eventLoop(Game*);
diff --git a/src/GameInitDispose.cpp b/src/GameInitDispose.cpp
index 782cf6a..b208df5 100644
--- a/src/GameInitDispose.cpp
+++ b/src/GameInitDispose.cpp
@@ -22,6 +22,7 @@
 
 #include <AL/alc.h>
 
+#include "config.h"
 #include "Textures.h"
 #include "Game.h"
 
@@ -57,9 +58,21 @@ extern int aimkey;
 extern int psychicaimkey;
 extern int psychickey;
 
-Game* makeGame()
+Game* makeGame(Config config)
 {
-	return new Game();
+	auto game = new Game();
+	game->screenwidth = config.screen_width;
+	game->screenheight = config.screen_height;
+	game->usermousesensitivity = config.mouse_sensitivity;
+	game->debug = config.debug;
+	game->vblsync = config.vsync;
+	blood = config.blood;
+	game->blurness = config.blur;
+	game->mainmenuness = config.menu;
+	game->customlevels = config.custom_levels;
+	game->musictoggle = config.music;
+	game->azertykeyboard = config.azerty;
+	return game;
 }
 
 void LoadSounds(bool musictoggle)
@@ -1781,69 +1794,6 @@ void initGl(Game* game)
 	// Config
 	game->mousesensitivity = 1.0f;
 	if (!game->initialized) {
-		ifstream ipstream {"config.txt"};
-		// If no config, write one
-		if (!ipstream) {
-			ofstream opstream("config.txt");
-			opstream << "Screenwidth:\n";
-			opstream << (game->screenwidth = 640);
-			opstream << "\nScreenheight:\n";
-			opstream << (game->screenheight = 480);
-			opstream << "\nMouse sensitivity:\n";
-			opstream << (game->usermousesensitivity = 0.7f);
-			opstream << "\nShow fps and other info:\n";
-			opstream << (game->debug = false);
-			opstream << "\nVBL sync:\n";
-			opstream << (game->vblsync = true);
-			opstream << "\nBlood:\n";
-			opstream << (blood = true);
-			opstream << "\nBlur:\n";
-			opstream << (game->blurness = false);
-			opstream << "\nMain Menu:\n";
-			opstream << (game->mainmenuness = true);
-			opstream << "\nCustom levels:\n";
-			opstream << (game->customlevels = false);
-			opstream << "\nMusic:\n";
-			opstream << (game->musictoggle = true);
-			opstream << "\azerty keyboard:\n";
-			opstream << (game->azertykeyboard = false) << endl;
-			opstream.close();
-		} else {
-			ipstream.ignore(256,'\n');
-			ipstream >> game->screenwidth;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->screenheight;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->usermousesensitivity;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->debug;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->vblsync;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> blood;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->blurness;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->mainmenuness;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->customlevels;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->musictoggle;
-			ipstream.ignore(256,'\n');
-			ipstream.ignore(256,'\n');
-			ipstream >> game->azertykeyboard;
-			ipstream.close();
-		}
-
 		// TODO: Read high score
 		ifstream ipstream2 {"highscore.txt"};
 		if (!ipstream2) {
@@ -1853,7 +1803,7 @@ void initGl(Game* game)
 			opstream.close();
 		} else {
 			ipstream2 >> game->highscore;
-			ipstream.ignore(256,'\n');
+			ipstream2.ignore(256,'\n');
 			ipstream2 >> game->beatgame;
 			ipstream2.close();
 		}
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 0000000..fa456b2
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,21 @@
+#ifndef BLACKSHADES_CONFIG_H
+#define BLACKSHADES_CONFIG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+struct Config {
+	int screen_width;
+	int screen_height;
+	float mouse_sensitivity;
+	bool debug;
+	bool vsync;
+	bool blood;
+	bool blur;
+	bool menu;
+	bool custom_levels;
+	bool music;
+	bool azerty;
+};
+
+#endif // BLACKSHADES_CONFIG_H
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;
+}
diff --git a/src/main.zig b/src/main.zig
index 34e3844..baca0e0 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -16,14 +16,24 @@
 // 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 legacy = @cImport({ @cInclude("Game.h"); });
-const al = @import("zeal");
+const Loca = @import("loca").Loca;
+const allocator = @import("std").heap.c_allocator;
+const legacy = @cImport(@cInclude("Game.h"));
+const configuration = @import("config.zig");
+const zeal = @import("zeal");
 
 pub fn main() !void {
-    try al.init(null);
-    defer al.deinit() catch unreachable;
+    const loca = try Loca.init(allocator, .{});
+    defer loca.deinit();
+    const config = try configuration.parse(allocator, loca.user_config);
 
-    const game = legacy.makeGame();
+    const device = try zeal.Device.init(null);
+    defer device.deinit() catch unreachable;
+    const context = try zeal.Context.init(device, &.{ });
+    defer context.deinit() catch unreachable;
+    try zeal.useContext(context);
+
+    const game = legacy.makeGame(@bitCast(legacy.Config, config));
     legacy.initGl(game);
     legacy.initGame(game);
     defer legacy.closeGame(game);