From ae440a8108ec8ed4b01b6f53fa2aab4d7f0df661 Mon Sep 17 00:00:00 2001 From: sadbeast Date: Mon, 15 Apr 2024 18:08:28 -0700 Subject: initial mistake --- src/test/map.tmj | 66 ++++++++++ src/tmz.zig | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 443 insertions(+) create mode 100644 src/test/map.tmj create mode 100644 src/tmz.zig (limited to 'src') 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)); +} -- cgit v1.2.3