aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.envrc7
-rw-r--r--.gitignore6
-rw-r--r--README.md3
-rw-r--r--build.zig95
-rw-r--r--build.zig.zon61
-rw-r--r--examples/game.zig24
-rw-r--r--examples/map.tmj66
-rw-r--r--flake.lock168
-rw-r--r--flake.nix53
-rw-r--r--src/test/map.tmj66
-rw-r--r--src/tmz.zig377
11 files changed, 926 insertions, 0 deletions
diff --git a/.envrc b/.envrc
new file mode 100644
index 0000000..3116522
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,7 @@
+dotenv
+
+if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
+fi
+
+use flake
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c706ca6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# direnv
+.direnv
+
+# Zig
+zig-cache
+zig-out
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2574b2a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# tmz
+
+A library for loading Tiled Maps and Tilesets
diff --git a/build.zig b/build.zig
new file mode 100644
index 0000000..a3b1b10
--- /dev/null
+++ b/build.zig
@@ -0,0 +1,95 @@
+const std = @import("std");
+
+// Although this function looks imperative, note that its job is to
+// declaratively construct a build graph that will be executed by an external
+// runner.
+pub fn build(b: *std.Build) void {
+ // Standard target options allows the person running `zig build` to choose
+ // what target to build for. Here we do not override the defaults, which
+ // means any target is allowed, and the default is native. Other options
+ // for restricting supported target set are available.
+ const target = b.standardTargetOptions(.{});
+
+ // Standard optimization options allow the person running `zig build` to select
+ // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
+ // set a preferred release mode, allowing the user to decide how to optimize.
+ const optimize = b.standardOptimizeOption(.{});
+
+ // const tmz_module = b.createModule(.{ .root_source_file = .{ .path = "src/tmz.zig" } });
+
+ const lib = b.addStaticLibrary(.{
+ .name = "tmz",
+ // In this case the main source file is merely a path, however, in more
+ // complicated build scripts, this could be a generated file.
+ .root_source_file = .{ .path = "src/tmz.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // This declares intent for the library to be installed into the standard
+ // location when the user invokes the "install" step (the default step when
+ // running `zig build`).
+ b.installArtifact(lib);
+
+ const game = b.addExecutable(.{
+ .name = "tmz",
+ .root_source_file = .{ .path = "examples/game.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ game.root_module.addImport("tmz", &lib.root_module);
+
+ // This declares intent for the executable to be installed into the
+ // standard location when the user invokes the "install" step (the default
+ // step when running `zig build`).
+ b.installArtifact(game);
+
+ // This *creates* a Run step in the build graph, to be executed when another
+ // step is evaluated that depends on it. The next line below will establish
+ // such a dependency.
+ const run_cmd = b.addRunArtifact(game);
+
+ // By making the run step depend on the install step, it will be run from the
+ // installation directory rather than directly from within the cache directory.
+ // This is not necessary, however, if the application depends on other installed
+ // files, this ensures they will be present and in the expected location.
+ run_cmd.step.dependOn(b.getInstallStep());
+
+ // This allows the user to pass arguments to the application in the build
+ // command itself, like this: `zig build run -- arg1 arg2 etc`
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+
+ // This creates a build step. It will be visible in the `zig build --help` menu,
+ // and can be selected like this: `zig build run`
+ // This will evaluate the `run` step rather than the default, which is "install".
+ const run_step = b.step("run", "Run the app");
+ run_step.dependOn(&run_cmd.step);
+
+ // Creates a step for unit testing. This only builds the test executable
+ // but does not run it.
+ const lib_unit_tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/tmz.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
+
+ const game_unit_tests = b.addTest(.{
+ .root_source_file = .{ .path = "examples/game.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const run_game_unit_tests = b.addRunArtifact(game_unit_tests);
+
+ // Similar to creating the run step earlier, this exposes a `test` step to
+ // the `zig build --help` menu, providing a way for the user to request
+ // running the unit tests.
+ const test_step = b.step("test", "Run unit tests");
+ test_step.dependOn(&run_lib_unit_tests.step);
+ test_step.dependOn(&run_game_unit_tests.step);
+}
diff --git a/build.zig.zon b/build.zig.zon
new file mode 100644
index 0000000..4638b33
--- /dev/null
+++ b/build.zig.zon
@@ -0,0 +1,61 @@
+.{
+ .name = "tmz",
+ // This is a [Semantic Version](https://semver.org/).
+ // In a future version of Zig it will be used for package deduplication.
+ .version = "0.1.0",
+
+ // This field is optional.
+ // This is currently advisory only; Zig does not yet do anything
+ // with this value.
+ .minimum_zig_version = "0.12.0",
+
+ // This field is optional.
+ // Each dependency must either provide a `url` and `hash`, or a `path`.
+ // `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
+ // Once all dependencies are fetched, `zig build` no longer requires
+ // internet connectivity.
+ .dependencies = .{
+ // See `zig fetch --save <url>` for a command-line interface for adding dependencies.
+ //.example = .{
+ // // When updating this field to a new URL, be sure to delete the corresponding
+ // // `hash`, otherwise you are communicating that you expect to find the old hash at
+ // // the new URL.
+ // .url = "https://example.com/foo.tar.gz",
+ //
+ // // This is computed from the file contents of the directory of files that is
+ // // obtained after fetching `url` and applying the inclusion rules given by
+ // // `paths`.
+ // //
+ // // This field is the source of truth; packages do not come from a `url`; they
+ // // come from a `hash`. `url` is just one of many possible mirrors for how to
+ // // obtain a package matching this `hash`.
+ // //
+ // // Uses the [multihash](https://multiformats.io/multihash/) format.
+ // .hash = "...",
+ //
+ // // When this is provided, the package is found in a directory relative to the
+ // // build root. In this case the package's hash is irrelevant and therefore not
+ // // computed. This field and `url` are mutually exclusive.
+ // .path = "foo",
+
+ // // When this is set to `true`, a package is declared to be lazily
+ // // fetched. This makes the dependency only get fetched if it is
+ // // actually used.
+ // .lazy = false,
+ //},
+ },
+
+ // Specifies the set of files and directories that are included in this package.
+ // Only files and directories listed here are included in the `hash` that
+ // is computed for this package.
+ // Paths are relative to the build root. Use the empty string (`""`) to refer to
+ // the build root itself.
+ // A directory listed here means that all files within, recursively, are included.
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ //"LICENSE",
+ //"README.md",
+ },
+}
diff --git a/examples/game.zig b/examples/game.zig
new file mode 100644
index 0000000..e86eede
--- /dev/null
+++ b/examples/game.zig
@@ -0,0 +1,24 @@
+const std = @import("std");
+const tmz = @import("tmz");
+
+pub fn main() !void {
+ var buffer: [10_000]u8 = undefined;
+ const file = try std.fs.cwd().readFile("examples/map.tmj", &buffer);
+
+ var gpa = std.heap.GeneralPurposeAllocator(.{}){};
+ const allocator = gpa.allocator();
+ defer _ = gpa.deinit();
+
+ var arena = std.heap.ArenaAllocator.init(allocator);
+ defer arena.deinit();
+
+ const map = try tmz.parse(file, arena.allocator());
+
+ const stdout_file = std.io.getStdOut().writer();
+ var bw = std.io.bufferedWriter(stdout_file);
+ const stdout = bw.writer();
+
+ try stdout.print("Map: {any}\n", .{map});
+
+ try bw.flush(); // don't forget to flush!
+}
diff --git a/examples/map.tmj b/examples/map.tmj
new file mode 100644
index 0000000..ddfa9f3
--- /dev/null
+++ b/examples/map.tmj
@@ -0,0 +1,66 @@
+{
+ "compressionlevel": -1,
+ "height": 30,
+ "class": "bar",
+ "infinite": false,
+ "layers": [
+ {
+ "class": "bar",
+ "data": [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 19, 15, 19, 15, 19, 15, 19, 15, 19, 15,
+ 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14, 15, 15, 15, 14, 15, 15, 15, 14,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 14, 15, 19, 15, 19, 15, 19, 15, 19, 15,
+ 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14, 15, 14, 15, 15, 15, 15, 15, 14,
+ 4, 4, 4, 4, 4, 4, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 3, 3, 14, 14, 14, 15, 19, 15, 19, 15, 19, 15,
+ 5, 5, 5, 5, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 3, 14, 14, 15, 14, 15, 14, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 16, 16, 16, 16, 14, 15, 19, 15, 19, 15, 19, 15,
+ 6, 6, 6, 6, 6, 6, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 16, 16, 16, 16, 15, 14, 15, 15, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 3, 16, 16, 16, 16, 14, 14, 19, 15, 19, 15, 19, 15,
+ 7, 7, 7, 7, 7, 7, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 13, 13, 13, 13, 8, 9, 8, 14, 15, 14, 15, 14,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 8, 19, 15, 19, 15,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 8, 14, 15, 14,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2147483659, 10, 17, 15, 19, 15,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 8, 14, 15, 14,
+ 4, 4, 4, 4, 4, 4, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 10, 17, 15, 19, 15,
+ 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 10, 17, 15, 15, 15, 14,
+ 6, 6, 6, 6, 6, 6, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 15, 19, 15, 19, 15,
+ 6, 6, 6, 6, 6, 6, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 15, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 3, 1, 3, 1, 3, 1, 2, 1, 2, 1, 3, 1, 3, 1, 2, 1, 3, 1, 3, 1, 2, 19, 15, 19, 15,
+ 7, 7, 7, 7, 7, 7, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 15, 14, 15, 14,
+ 1, 2, 1, 3, 1, 3, 1, 3, 1, 2, 1, 2, 1, 3, 1, 2, 1, 2, 1, 2, 1, 3, 1, 2, 1, 2, 1, 2, 1, 2, 19, 15,
+ 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 15, 14,
+ 1, 3, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 19, 15,
+ 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1],
+ "height": 30,
+ "id": 1,
+ "name": "Tile Layer 1",
+ "opacity": 1,
+ "type": "tilelayer",
+ "visible": true,
+ "width": 32,
+ "x": 0,
+ "y": 0
+ }
+ ],
+ "nextlayerid": 2,
+ "nextobjectid": 1,
+ "orientation": "orthogonal",
+ "renderorder": "right-down",
+ "tiledversion": "1.10.0",
+ "tileheight": 8,
+ "tilesets": [
+ {
+ "firstgid": 1,
+ "source": "tiles.tsj"
+ }
+ ],
+ "tilewidth": 8,
+ "type": "map",
+ "version": "1.10",
+ "width": 32
+}
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..9697d8d
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,168 @@
+{
+ "nodes": {
+ "devshell": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1713532798,
+ "narHash": "sha256-wtBhsdMJA3Wa32Wtm1eeo84GejtI43pMrFrmwLXrsEc=",
+ "owner": "numtide",
+ "repo": "devshell",
+ "rev": "12e914740a25ea1891ec619bb53cf5e6ca922e40",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "devshell",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1712014858,
+ "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1701680307,
+ "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1704161960,
+ "narHash": "sha256-QGua89Pmq+FBAro8NriTuoO/wNaUtugt29/qqA8zeeM=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "63143ac2c9186be6d9da6035fa22620018c85932",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "dir": "lib",
+ "lastModified": 1711703276,
+ "narHash": "sha256-iMUFArF0WCatKK6RzfUJknjem0H9m4KgorO/p3Dopkk=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "d8fe5e6c92d0d190646fb9f1056741a229980089",
+ "type": "github"
+ },
+ "original": {
+ "dir": "lib",
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1713714899,
+ "narHash": "sha256-+z/XjO3QJs5rLE5UOf015gdVauVRQd2vZtsFkaXBq2Y=",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "6143fc5eeb9c4f00163267708e26191d1e918932",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs_3": {
+ "locked": {
+ "lastModified": 1708475490,
+ "narHash": "sha256-g1v0TsWBQPX97ziznfJdWhgMyMGtoBFs102xSYO4syU=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "0e74ca98a74bc7270d28838369593635a5db3260",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "devshell": "devshell",
+ "flake-parts": "flake-parts",
+ "nixpkgs": "nixpkgs_2",
+ "treefmt-nix": "treefmt-nix"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ },
+ "treefmt-nix": {
+ "inputs": {
+ "nixpkgs": "nixpkgs_3"
+ },
+ "locked": {
+ "lastModified": 1711963903,
+ "narHash": "sha256-N3QDhoaX+paWXHbEXZapqd1r95mdshxToGowtjtYkGI=",
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "rev": "49dc4a92b02b8e68798abd99184f228243b6e3ac",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "treefmt-nix",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..53dec5f
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,53 @@
+{
+ description = "tmz - a library for loading Tiled Maps and Tilesets";
+
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ flake-parts.url = "github:hercules-ci/flake-parts";
+
+ devshell.url = "github:numtide/devshell";
+ treefmt-nix.url = "github:numtide/treefmt-nix";
+ };
+
+ outputs = inputs @ {flake-parts, ...}:
+ flake-parts.lib.mkFlake {inherit inputs;} {
+ imports = [
+ inputs.devshell.flakeModule
+ inputs.treefmt-nix.flakeModule
+ ];
+ systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"];
+ perSystem = {
+ config,
+ self',
+ inputs',
+ pkgs,
+ system,
+ ...
+ }: {
+ treefmt.config = {
+ projectRootFile = "flake.nix";
+
+ flakeCheck = true;
+ flakeFormatter = true;
+
+ programs.zig.enable = true;
+ programs.alejandra.enable = true;
+ };
+
+ devshells.default = {
+ commands = [
+ {
+ name = "fmt";
+ help = "format the repo";
+ command = "nix fmt";
+ }
+ ];
+
+ packages = [
+ pkgs.zig_0_12
+ ];
+ };
+ };
+ };
+}
diff --git a/src/test/map.tmj b/src/test/map.tmj
new file mode 100644
index 0000000..ddfa9f3
--- /dev/null
+++ b/src/test/map.tmj
@@ -0,0 +1,66 @@
+{
+ "compressionlevel": -1,
+ "height": 30,
+ "class": "bar",
+ "infinite": false,
+ "layers": [
+ {
+ "class": "bar",
+ "data": [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 19, 15, 19, 15, 19, 15, 19, 15, 19, 15,
+ 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14, 15, 15, 15, 14, 15, 15, 15, 14,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 14, 15, 19, 15, 19, 15, 19, 15, 19, 15,
+ 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14, 15, 14, 15, 15, 15, 15, 15, 14,
+ 4, 4, 4, 4, 4, 4, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 3, 3, 14, 14, 14, 15, 19, 15, 19, 15, 19, 15,
+ 5, 5, 5, 5, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 3, 14, 14, 15, 14, 15, 14, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 16, 16, 16, 16, 14, 15, 19, 15, 19, 15, 19, 15,
+ 6, 6, 6, 6, 6, 6, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 16, 16, 16, 16, 15, 14, 15, 15, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 3, 3, 16, 16, 16, 16, 14, 14, 19, 15, 19, 15, 19, 15,
+ 7, 7, 7, 7, 7, 7, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 8, 9, 13, 13, 13, 13, 8, 9, 8, 14, 15, 14, 15, 14,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 8, 19, 15, 19, 15,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 10, 10, 8, 14, 15, 14,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2147483659, 10, 17, 15, 19, 15,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 8, 14, 15, 14,
+ 4, 4, 4, 4, 4, 4, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 10, 17, 15, 19, 15,
+ 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 1073741835, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 3221225483, 10, 10, 17, 15, 15, 15, 14,
+ 6, 6, 6, 6, 6, 6, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 18, 17, 15, 19, 15, 19, 15,
+ 6, 6, 6, 6, 6, 6, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 15, 15, 14, 15, 14,
+ 6, 6, 6, 6, 6, 6, 1, 3, 1, 3, 1, 3, 1, 2, 1, 2, 1, 3, 1, 3, 1, 2, 1, 3, 1, 3, 1, 2, 19, 15, 19, 15,
+ 7, 7, 7, 7, 7, 7, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 15, 14, 15, 14,
+ 1, 2, 1, 3, 1, 3, 1, 3, 1, 2, 1, 2, 1, 3, 1, 2, 1, 2, 1, 2, 1, 3, 1, 2, 1, 2, 1, 2, 1, 2, 19, 15,
+ 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 1, 1, 15, 14,
+ 1, 3, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 19, 15,
+ 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 15, 14,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 3, 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 2,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1],
+ "height": 30,
+ "id": 1,
+ "name": "Tile Layer 1",
+ "opacity": 1,
+ "type": "tilelayer",
+ "visible": true,
+ "width": 32,
+ "x": 0,
+ "y": 0
+ }
+ ],
+ "nextlayerid": 2,
+ "nextobjectid": 1,
+ "orientation": "orthogonal",
+ "renderorder": "right-down",
+ "tiledversion": "1.10.0",
+ "tileheight": 8,
+ "tilesets": [
+ {
+ "firstgid": 1,
+ "source": "tiles.tsj"
+ }
+ ],
+ "tilewidth": 8,
+ "type": "map",
+ "version": "1.10",
+ "width": 32
+}
diff --git a/src/tmz.zig b/src/tmz.zig
new file mode 100644
index 0000000..ac16d75
--- /dev/null
+++ b/src/tmz.zig
@@ -0,0 +1,377 @@
+const std = @import("std");
+
+pub const flipped_horizontally: u32 = 0x80000000;
+pub const flipped_vertically: u32 = 0x40000000;
+pub const flipped_diagonally: u32 = 0x20000000;
+pub const rotated_hexagonal_120: u32 = 0x10000000;
+
+/// [https://doc.mapeditor.org/en/stable/reference/json-map-format/#map]
+pub const Map = struct {
+ backgroundcolor: ?[]const u8 = null,
+ class: ?[]const u8 = null,
+ compressionlevel: i32 = -1,
+ height: u32,
+ hexsidelength: ?u32 = null,
+ infinite: bool,
+ layers: []Layer,
+ nextlayerid: u32,
+ nextobjectid: u32,
+ orientation: Orientation,
+ parallaxoriginx: f32 = 0,
+ parallaxoriginy: f32 = 0,
+ properties: ?[]const Property = null,
+ renderorder: RenderOrder,
+ staggeraxis: ?StaggerAxis = null,
+ staggerindex: ?StaggerIndex = null,
+ tiledversion: []const u8,
+ tileheight: u32,
+ tilesets: []Tileset,
+ tilewidth: u32,
+ type: Type,
+ version: []const u8,
+ width: u32,
+
+ pub fn globalTileId(self: @This(), index: u32) !Gid {
+ const is_flipped_horizontally = (index & flipped_horizontally > 0);
+ const is_flipped_vertically = (index & flipped_vertically > 0);
+ const is_flipped_diagonally = (index & flipped_diagonally > 0);
+ const is_rotated = (index & rotated_hexagonal_120 > 0);
+
+ // clear flags
+ const gtid = index & ~flipped_horizontally | flipped_vertically | flipped_diagonally | rotated_hexagonal_120;
+
+ for (self.tilesets) |tileset| {
+ if (tileset.firstgid <= gtid) {
+ return .{ .gid = gtid - tileset.firstgid, .flipped_horizontally = is_flipped_horizontally, .flipped_vertically = is_flipped_vertically, .flipped_diagonally = is_flipped_diagonally, .rotated = is_rotated };
+ }
+ }
+ return error.GidNotFound;
+ }
+
+ pub const Gid = struct {
+ gid: u32,
+ flipped_horizontally: bool,
+ flipped_vertically: bool,
+ flipped_diagonally: bool,
+ rotated: bool,
+ };
+
+ pub const Orientation = enum { orthogonal, isometric, staggered, hexagonal };
+ pub const RenderOrder = enum { @"right-down", @"right-up", @"left-down", @"left-up" };
+ pub const StaggerAxis = enum { x, y };
+ pub const StaggerIndex = enum { odd, even };
+ pub const Type = enum { map };
+};
+
+inline fn get(source: std.json.Value, name: []const u8) ?[]const u8 {
+ if (source.object.get(name)) |o| {
+ return o.string;
+ }
+ return null;
+}
+
+// pub const DataEncoding = union(enum) {
+// csv: ?[]const u32,
+// base64: ?[]const u8,
+
+// pub fn jsonParse(allocator: Allocator, source: std.json.Value, options: std.json.ParseOptions) !@This() {
+// _ = source;
+// _ = options;
+// _ = allocator;
+// // if (source == .string) {
+// // // const beep = source.string;
+// // // return .{ .base64 = beep };
+// // return @This(){ .base64 = "hi" };
+// // } else if (source == .array) {
+// // // return .{ .csv = &[_]u32{ 2, 3 } };
+// // // return .{ .base64 = "fff" };
+// // return error.UnexpectedToken;
+// // } else {
+// // return error.UnexpectedToken;
+// // }
+// return .{ .base64 = "foo" };
+// }
+// };
+
+pub const Layer = struct {
+ // chunks: ?[]const ChunkEncoding = null,
+ class: ?[]const u8 = null,
+ compression: ?enum { zlib, gzip, zstd } = null,
+ data: ?[]const u32 = null,
+ draworder: ?enum { topdown, index } = .topdown,
+ encoding: ?Encoding = .csv,
+ // height: ?u32 = null,
+ // id: u32,
+ // image: ?[]const u8 = null,
+ // layers: ?[]Layer = null,
+ // locked: bool = false,
+ // name: []const u8,
+ // objects: ?[]Object = null,
+ // offsetx: f32 = 0,
+ // offsety: f32 = 0,
+ // opacity: f32,
+ // parallaxx: f32 = 1,
+ // parallaxy: f32 = 1,
+ // properties: ?[]Property = null,
+ // repeatx: ?bool = null,
+ // repeaty: ?bool = null,
+ // startx: ?i32 = null,
+ // starty: ?i32 = null,
+ // tintcolor: ?[]const u8 = null,
+ // transparentcolor: ?[]const u8 = null,
+ type: Type,
+ // visible: bool,
+ // width: ?u32 = null,
+ // x: i32,
+ // y: i32,
+
+ pub fn jsonParseFromValue(allocator: std.mem.Allocator, source: std.json.Value, options: std.json.ParseOptions) !@This() {
+ _ = allocator; // autofix
+ _ = options; // autofix
+ if (source.object.get("type")) |layer_type| {
+ if (std.mem.eql(u8, layer_type.string, "tilelayer")) {
+ if (source.object.get("encoding")) |encoding| {
+ if (std.mem.eql(u8, encoding.string, "csv")) {
+ return .{ .type = Type.fromString(layer_type.string) };
+ }
+ } else {
+ return .{ .type = Type.fromString(layer_type.string) };
+ }
+ }
+ } else {
+ return error.MissingField;
+ }
+ return error.MissingField;
+ }
+
+ pub const Type = enum {
+ tilelayer,
+ objectgroup,
+ imagelayer,
+ group,
+
+ pub fn fromString(string: []const u8) Type {
+ if (std.mem.eql(u8, string, "tilelayer")) {
+ return .tilelayer;
+ }
+ return .tilelayer;
+ }
+ };
+ pub const Encoding = enum { csv, base64 };
+
+ // pub fn jsonParseFromValue(allocator: Allocator, source: std.json.Value, options: std.json.ParseOptions) !@This() {
+ // if (source != .object) return error.UnexpectedToken;
+ // if (source.object.get("data")) |source_data| {
+ // std.log.info("yayyyyyyyyyyyy : {s}\n\n\n", .{source.object.keys()});
+ // const encoding_value: std.json.Value = source.object.get("encoding") orelse .{ .string = "csv" };
+ // if (encoding_value != .string) return error.UnexpectedToken;
+ // const encoding = encoding_value.string;
+ // var data: []const u32 = undefined;
+ // if (std.mem.eql(u8, encoding, "base64")) {
+ // var base64 = try std.json.parseFromValueLeaky([]const u8, allocator, source_data, options);
+
+ // // var buffer = allocator.alloc(u8, std.base64.standard.Decoder.calcSizeForSlice(base64) catch return error.UnexpectedToken);
+ // var buffer: [0x10000]u8 = undefined;
+ // var decoded = buffer[0 .. std.base64.standard.Decoder.calcSizeForSlice(base64) catch return error.UnexpectedToken];
+ // std.base64.standard.Decoder.decode(decoded, base64) catch return error.UnexpectedToken;
+
+ // data = &[_]u32{ 1, 4 };
+ // } else {
+ // data = try std.json.parseFromValueLeaky([]const u32, allocator, source_data, options);
+ // }
+ // return .{
+ // .id = @intCast(source.object.get("id").?.integer),
+ // .name = source.object.get("name").?.string,
+ // .opacity = 0,
+ // .type = .tilelayer,
+ // .visible = true,
+ // .x = 0,
+ // .y = 0,
+ // .data = data,
+ // };
+ // } else {
+ // std.log.err("brooooo : {s}\n\n\n", .{source.object.keys()});
+ // return error.UnexpectedToken;
+ // }
+ // }
+};
+
+pub const Data = union(enum) {
+ csv: []const u32,
+ base64: []const u8,
+};
+
+pub const Chunk = struct {
+ data: []const u32,
+ height: u32,
+ width: u32,
+ x: u32,
+ y: u32,
+};
+
+pub const Object = struct {
+ ellipse: ?bool = null,
+ gid: ?u32 = null,
+ height: f32,
+ id: u32,
+ name: []const u8,
+ point: ?bool = null,
+ polygon: ?[]Point = null,
+ polyline: ?[]Point = null,
+ properties: ?[]Property = null,
+ rotation: f32,
+ template: ?[]const u8 = null,
+ text: ?Text = null,
+ type: ?[]const u8 = null,
+ visible: bool,
+ width: f32,
+ x: f32,
+ y: f32,
+};
+
+pub const Point = struct {
+ x: f32,
+ y: f32,
+};
+
+pub const Text = struct {
+ bold: bool,
+ color: []const u8,
+ fontfamily: []const u8 = "sans-serif",
+ halign: enum { center, right, justify, left } = .left,
+ italic: bool = false,
+ kerning: bool = true,
+ pixelsize: usize = 16,
+ strikeout: bool = false,
+ text: []const u8,
+ underline: bool = false,
+ valign: enum { center, bottom, top } = .top,
+ wrap: bool = false,
+};
+
+pub const Property = struct {
+ name: []const u8,
+ type: enum {
+ string,
+ int,
+ float,
+ bool,
+ color,
+ file,
+ object,
+ class,
+ } = .string,
+ propertytype: []const u8 = "",
+};
+
+pub const Tileset = struct {
+ backgroundcolor: ?[]const u8 = null,
+ class: ?[]const u8 = null,
+ columns: ?u32 = null,
+ fillmode: ?enum { stretch, @"preserve-aspect-fit" } = .stretch,
+ firstgid: u32,
+ grid: ?Grid = null,
+ image: ?[]const u8 = null,
+ imageheight: ?u32 = null,
+ imagewidth: ?u32 = null,
+ margin: ?u32 = null,
+ name: ?[]const u8 = null,
+ objectalignment: ?enum { unspecified, topleft, top, topright, left, center, right, bottomleft, bottom, bottomright } = .unspecified,
+ properties: ?[]Property = null,
+ source: []const u8,
+ spacing: ?i32 = null,
+ terrains: ?[]Terrain = null,
+ tilecount: ?u32 = null,
+ tileversion: ?[]const u8 = null,
+ tileheight: ?u32 = null,
+ tileoffset: ?Point = null,
+ tilerendersize: ?enum { tile, grid } = .tile,
+ tiles: ?[]Tile = null,
+ tilewidth: ?u32 = null,
+ transformations: ?Transformations = null,
+ transparentcolor: ?[]const u8 = null,
+ type: ?enum { tileset } = .tileset,
+ version: ?[]const u8 = null,
+ wangsets: ?[]Wangset = null,
+};
+
+pub const Grid = struct {
+ orientation: enum { orthogonal, isometric } = .orthogonal,
+ height: i32,
+ width: i32,
+};
+
+pub const Terrain = struct {
+ name: []const u8,
+ properties: []Property,
+ tile: i32,
+};
+
+pub const Tile = struct {
+ animiation: []Frame,
+ id: i32,
+ name: ?[]const u8,
+ imageheight: u32,
+ imagewidth: u32,
+ x: i32,
+ y: i32,
+ width: u32,
+ height: u32,
+ objectgroup: ?Layer,
+ probability: f32,
+ properties: []Property,
+ terrain: []i32,
+ type: []const u8,
+};
+
+pub const Frame = struct {
+ duration: i32,
+ tileid: i32,
+};
+
+pub const Transformations = struct {
+ hflip: bool,
+ vflip: bool,
+ rotate: bool,
+ preferuntransformed: bool,
+};
+
+pub const Wangset = struct {
+ class: ?[]const u8,
+ colors: []Wangcolor,
+ name: []const u8,
+ properties: []Property,
+ tile: i32,
+ type: []const u8,
+ wangtiles: []Wangtile,
+};
+
+pub const Wangcolor = struct {
+ class: ?[]const u8,
+ color: []const u8,
+ name: []const u8,
+ probability: f32,
+ properties: []Property,
+ tile: i32,
+};
+
+pub const Wangtile = struct {
+ tileid: i32,
+ wangid: []i32,
+};
+
+pub fn parse(bytes: []const u8, allocator: std.mem.Allocator) !Map {
+ const map = try std.json.parseFromSliceLeaky(std.json.Value, allocator, bytes, .{});
+ return try std.json.parseFromValueLeaky(Map, allocator, map, .{ .ignore_unknown_fields = true });
+}
+
+test "parse works" {
+ const json_map = @embedFile("test/map.tmj");
+
+ var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
+ defer arena.deinit();
+ const map = try parse(json_map, arena.allocator());
+ try std.testing.expectEqualStrings("1.10", map.version);
+
+ // try std.testing.expectEqual(0, map.globalTileId(0));
+}