From 56f05ca61f7befe8d460d31ef9618c50fcd916b9 Mon Sep 17 00:00:00 2001 From: Klemen Plestenjak Date: Wed, 1 Nov 2023 06:40:14 +0100 Subject: [PATCH] Add test tiled map and support for loading it --- CMakeLists.txt | 20 +- assets/maps/test.tmj | 241 +++ libs/cute_tiled.c | 3 + libs/cute_tiled/cute_tiled.h | 3036 ++++++++++++++++++++++++++++++++++ main.c | 28 - src/common.h | 7 + src/main.c | 54 + 7 files changed, 3360 insertions(+), 29 deletions(-) create mode 100644 assets/maps/test.tmj create mode 100644 libs/cute_tiled.c create mode 100644 libs/cute_tiled/cute_tiled.h delete mode 100644 main.c create mode 100644 src/common.h create mode 100644 src/main.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 469232d..5753add 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,5 +7,23 @@ set(CMAKE_C_STANDARD 11) add_subdirectory(libs/raylib-4.5.0) -add_executable(PixelDefense main.c) +set( + lib_sources + libs/cute_tiled.c +) + +set( + lib_dirs + libs/cute_tiled +) + + +add_executable(PixelDefense + ${lib_sources} + + src/main.c + src/common.h +) +target_include_directories(PixelDefense PUBLIC ${lib_dirs}) target_link_libraries(PixelDefense raylib) + diff --git a/assets/maps/test.tmj b/assets/maps/test.tmj new file mode 100644 index 0000000..4aa3b84 --- /dev/null +++ b/assets/maps/test.tmj @@ -0,0 +1,241 @@ +{ "compressionlevel":-1, + "height":40, + "infinite":false, + "layers":[ + { + "data":[137, 137, 137, 137, 137, 137, 137, 137, 137, 143, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 137, 137, 137, 137, 137, 137, 137, 140, 164, 165, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 137, 137, 137, 137, 140, 164, 164, 165, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 137, 137, 137, 140, 165, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 137, 137, 140, 165, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 164, 164, 165, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 278, 279, 279, 279, 279, 279, 280, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 278, 279, 279, 281, 309, 333, 333, 333, 308, 282, 279, 279, 280, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 278, 279, 281, 309, 333, 333, 334, 56, 57, 55, 332, 333, 333, 308, 282, 279, 279, 279, 279, 280, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 278, 281, 309, 333, 334, 2, 3, 1, 2, 3, 1, 2, 3, 1, 332, 333, 333, 333, 333, 308, 282, 279, 280, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 278, 281, 309, 334, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 332, 333, 308, 282, 279, 280, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 278, 281, 309, 334, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 332, 333, 308, 282, 280, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 278, 281, 309, 334, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 332, 308, 307, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 305, 309, 334, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 305, 282, 280, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 278, 281, 307, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 332, 308, 282, 280, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 305, 309, 334, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 332, 308, 307, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 305, 307, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 305, 307, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 278, 281, 307, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 305, 282, 280, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 305, 309, 334, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 332, 308, 307, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 305, 307, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 305, 307, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 305, 307, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 305, 307, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 305, 307, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 305, 282, 280, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 305, 307, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 332, 308, 307, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 278, 281, 307, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 305, 307, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 305, 309, 334, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 305, 307, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 305, 307, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 305, 307, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 305, 307, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 305, 307, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 305, 307, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 305, 307, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, + 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 305, 307, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, 305, 282, 280, 28, 29, 30, 28, 29, 30, 28, 29, 30, 28, 29, 30, + 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 305, 307, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, 332, 308, 307, 55, 56, 57, 55, 56, 57, 55, 56, 57, 55, 56, 57, + 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 305, 307, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 305, 307, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3], + "height":40, + "id":1, + "name":"Terrain", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":60, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 876, 878, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 708, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 708, 0, 0, 0, 713, 714, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 740, 741, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 708, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":40, + "id":3, + "name":"Buildings", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":60, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":8, + "name":"Trees", + "objects":[ + { + "gid":811, + "height":16, + "id":6, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":866.333, + "y":482 + }, + { + "gid":811, + "height":16, + "id":7, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":873.417, + "y":478 + }, + { + "gid":811, + "height":16, + "id":8, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":867.458, + "y":473.417 + }, + { + "gid":811, + "height":16, + "id":9, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":862.333, + "y":479.667 + }, + { + "gid":811, + "height":16, + "id":10, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":859.667, + "y":474 + }, + { + "gid":811, + "height":16, + "id":11, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":861, + "y":459.667 + }, + { + "gid":811, + "height":16, + "id":12, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":870.667, + "y":453 + }, + { + "gid":811, + "height":16, + "id":13, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":866.667, + "y":461 + }, + { + "gid":811, + "height":16, + "id":14, + "name":"", + "rotation":0, + "type":"", + "visible":true, + "width":16, + "x":872.667, + "y":459.083 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":15, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.10.2", + "tileheight":16, + "tilesets":[ + { + "firstgid":1, + "source":"..\/..\/tiled\/punyworld-overworld-tiles.tsx" + }], + "tilewidth":16, + "type":"map", + "version":"1.10", + "width":60 +} \ No newline at end of file diff --git a/libs/cute_tiled.c b/libs/cute_tiled.c new file mode 100644 index 0000000..a9b736f --- /dev/null +++ b/libs/cute_tiled.c @@ -0,0 +1,3 @@ + +#define CUTE_TILED_IMPLEMENTATION +#include "cute_tiled/cute_tiled.h" diff --git a/libs/cute_tiled/cute_tiled.h b/libs/cute_tiled/cute_tiled.h new file mode 100644 index 0000000..105a067 --- /dev/null +++ b/libs/cute_tiled/cute_tiled.h @@ -0,0 +1,3036 @@ +/* + ------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. + ------------------------------------------------------------------------------ + + cute_tiled.h - v1.06 + + To create implementation (the function definitions) + #define CUTE_TILED_IMPLEMENTATION + in *one* C/CPP file (translation unit) that includes this file + + SUMMARY + + Parses Tiled (http://www.mapeditor.org/) files saved as the JSON file + format. See http://doc.mapeditor.org/en/latest/reference/json-map-format/ + for a complete description of the JSON Tiled format. An entire map file + is loaded up in entirety and used to fill in a set of structs. The entire + struct collection is then handed to the user. + + This header is up to date with Tiled's documentation Revision 40049fd5 and + verified to work with Tiled stable version 1.4.1. + http://doc.mapeditor.org/en/latest/reference/json-map-format/ + + Here is a past discussion thread on this header: + https://www.reddit.com/r/gamedev/comments/87680n/cute_tiled_tiled_json_map_parserloader_in_c/ + + Revision history: + 1.00 (03/24/2018) initial release + 1.01 (05/04/2018) tile descriptors in tilesets for collision geometry + 1.02 (05/07/2018) reverse lists for ease of use, incorporate fixes by ZenToad + 1.03 (01/11/2019) support for Tiled 1.2.1 with the help of dpeter99 and tanis2000 + 1.04 (04/30/2020) support for Tiled 1.3.3 with the help of aganm + 1.05 (07/19/2020) support for Tiled 1.4.1 and tileset tile animations + 1.06 (04/05/2021) support for Tiled 1.5.0 parallax + 1.07 (03/01/2022) support for c89 +*/ + +/* + Contributors: + ZenToad 1.02 - Bug reports and goto statement errors for g++ + dpeter99 1.03 - Help with updating to Tiled 1.2.1 JSON format + tanis2000 1.03 - Help with updating to Tiled 1.2.1 JSON format + aganm 1.04 - Help with updating to Tiled 1.3.3 JSON format + mupf 1.07 - Adding support for C89 +*/ + +/* + DOCUMENTATION + + Load up a Tiled json exported file, either from disk or memory, like so: + + cute_tiled_map_t* map = cute_tiled_load_map_from_memory(memory, size, 0); + + Then simply access the map's fields like so: + + // get map width and height + int w = map->width; + int h = map->height; + + // loop over the map's layers + cute_tiled_layer_t* layer = map->layers; + while (layer) + { + int* data = layer->data; + int data_count = layer->data_count; + + // do something with the tile data + UserFunction_HandleTiles(data, data_count); + + layer = layer->next; + } + + Finally, free it like so: + + cute_tiled_free_map(map); + + LIMITATIONS + + More uncommon fields are not supported, and are annotated in this header. + Search for "Not currently supported." without quotes to find them. cute_tiled + logs a warning whenever a known unsupported field is encountered, and will + attempt to gracefully skip the field. If a field with completely unknown + syntax is encountered (which can happen if cute_tiled is used on a newer + and unsupported version of Tiled), undefined behavior may occur (crashes). + + If you would like a certain feature to be supported simply open an issue on + GitHub and provide a JSON exported map with the unsupported features. Changing + the parser to support new fields and objects is quite easy, as long as a map + file is provided for debugging and testing! + + GitHub : https://github.com/RandyGaul/cute_headers/ + + Compression of the tile GIDs is *not* supported in this header. Exporting + a map from Tiled will create a JSON file. This JSON file itself can very + trivially be compressed in its entirety, thus making Tiled's internal + compression exporting not a useful feature for this header to support. + Simply wrap calls to `cute_tiled_load_map_from_file` in a decompression + routine. Here is an example (assuming `zlib_uncompress` is already imp- + lemented somewhere in the user's codebase): + + int size; + void* uncompressed_data = zlib_uncompress(path_to_zipped_map_file, &size); + cute_tiled_map_t* map = cute_tiled_load_map_from_memory(uncompressed_data, size, 0); +*/ + +#if !defined(CUTE_TILED_H) + +// Read this in the event of errors +extern const char* cute_tiled_error_reason; +extern int cute_tiled_error_line; + + +typedef struct cute_tiled_map_t cute_tiled_map_t; +typedef struct cute_tiled_tileset_t cute_tiled_tileset_t; + +/*! + * Load a map from disk, placed into heap allocated memory. \p mem_ctx can be + * NULL. It is used for custom allocations. + */ +cute_tiled_map_t* cute_tiled_load_map_from_file(const char* path, void* mem_ctx); + +/*! + * Load a map from memory. \p mem_ctx can be NULL. It is used for custom allocations. + */ +cute_tiled_map_t* cute_tiled_load_map_from_memory(const void* memory, int size_in_bytes, void* mem_ctx); + +/*! + * Reverses the layers order, so they appear in reverse-order from what is shown in the Tiled editor. + */ +void cute_tiled_reverse_layers(cute_tiled_map_t* map); + +/*! + * Free all dynamic memory associated with this map. + */ +void cute_tiled_free_map(cute_tiled_map_t* map); + +/*! + * Load an external tileset from disk, placed into heap allocated memory. \p mem_ctx can be + * NULL. It is used for custom allocations. + * + * Please note this function is *entirely optional*, and only useful if you want to intentionally + * load tilesets externally from your map. If so, please also consider defining + * `CUTE_TILED_NO_EXTERNAL_TILESET_WARNING` to disable warnings about missing embedded tilesets. + */ +cute_tiled_tileset_t* cute_tiled_load_external_tileset(const char* path, void* mem_ctx); + +/*! + * Load an external tileset from memory. \p mem_ctx can be NULL. It is used for custom allocations. + * + * Please note this function is *entirely optional*, and only useful if you want to intentionally + * load tilesets externally from your map. If so, please also consider defining + * `CUTE_TILED_NO_EXTERNAL_TILESET_WARNING` to disable warnings about missing embedded tilesets. + */ +cute_tiled_tileset_t* cute_tiled_load_external_tileset_from_memory(const void* memory, int size_in_bytes, void* mem_ctx); + +/*! + * Free all dynamic memory associated with this external tileset. + */ +void cute_tiled_free_external_tileset(cute_tiled_tileset_t* tileset); + +#if !defined(CUTE_TILED_U64) + #define CUTE_TILED_U64 unsigned long long +#endif + +#if !defined(CUTE_TILED_INLINE) + #if defined(_MSC_VER) + #define CUTE_TILED_INLINE __inline + #else + #define CUTE_TILED_INLINE __inline__ + #endif +#endif + +typedef struct cute_tiled_layer_t cute_tiled_layer_t; +typedef struct cute_tiled_object_t cute_tiled_object_t; +typedef struct cute_tiled_frame_t cute_tiled_frame_t; +typedef struct cute_tiled_tile_descriptor_t cute_tiled_tile_descriptor_t; +typedef struct cute_tiled_property_t cute_tiled_property_t; +typedef union cute_tiled_string_t cute_tiled_string_t; + +/*! + * To access a string, simply do: object->name.ptr; this union is needed + * as a workaround for 32-bit builds where the size of a pointer is only + * 32 bits. + * + * More info: + * This unions is needed to support a single-pass parser, with string + * interning, where the parser copies value directly into the user-facing + * structures. As opposed to the parser copying values into an intermediate + * structure, and finally copying the intermediate values into the + * user-facing struct at the end. The latter option requires more code! + */ +union cute_tiled_string_t +{ + const char* ptr; + CUTE_TILED_U64 hash_id; +}; + +typedef enum CUTE_TILED_PROPERTY_TYPE +{ + CUTE_TILED_PROPERTY_NONE, + CUTE_TILED_PROPERTY_INT, + CUTE_TILED_PROPERTY_BOOL, + CUTE_TILED_PROPERTY_FLOAT, + CUTE_TILED_PROPERTY_STRING, + + // Note: currently unused! File properties are reported as strings in + // this header, and it is up to users to know a-priori which strings + // contain file paths. + CUTE_TILED_PROPERTY_FILE, + + CUTE_TILED_PROPERTY_COLOR, +} CUTE_TILED_PROPERTY_TYPE; + +struct cute_tiled_property_t +{ + union + { + int integer; + int boolean; + float floating; + cute_tiled_string_t string; + cute_tiled_string_t file; + int color; + } data; + CUTE_TILED_PROPERTY_TYPE type; + cute_tiled_string_t name; +}; + +struct cute_tiled_object_t +{ + int ellipse; // 0 or 1. Used to mark an object as an ellipse. + int gid; // GID, only if object comes from a Tilemap. + float height; // Height in pixels. Ignored if using a gid. + int id; // Incremental id - unique across all objects. + cute_tiled_string_t name; // String assigned to name field in editor. + int point; // 0 or 1. Used to mark an object as a point. + + // Example to index each vert of a polygon/polyline: + /* + float x, y; + for(int i = 0; i < vert_count * 2; i += 2) + { + x = vertices[i]; + y = vertices[i + 1]; + } + */ + int vert_count; + float* vertices; // Represents both type `polyline` and `polygon`. + int vert_type; // 1 for `polygon` and 0 for `polyline`. + + int property_count; // Number of elements in the `properties` array. + cute_tiled_property_t* properties; // Array of properties. + float rotation; // Angle in degrees clockwise. + /* template */ // Not currently supported. + /* text */ // Not currently supported. + cute_tiled_string_t type; // String assigned to type field in editor. + /* class */ // Not currently supported. + int visible; // 0 or 1. Whether object is shown in editor. + float width; // Width in pixels. Ignored if using a gid. + float x; // x coordinate in pixels. + float y; // y coordinate in pixels. + cute_tiled_object_t* next; // Pointer to next object. NULL if final object. +}; + +/*! + * Example of using both helper functions below to process the `data` pointer of a layer, + * containing an array of `GID`s. + * + * for (int i = 0; i < layer->data_count; i++) + * { + * int hflip, vflip, dflip; + * int tile_id = layer->data[i]; + * cute_tiled_get_flags(tile_id, &hflip, &vflip, &dflip); + * tile_id = cute_tiled_unset_flags(tile_id); + * DoSomethingWithFlags(tile_id, flip, vflip, dlfip); + * DoSomethingWithTileID(tile_id); + * } + */ + +#define CUTE_TILED_FLIPPED_HORIZONTALLY_FLAG 0x80000000 +#define CUTE_TILED_FLIPPED_VERTICALLY_FLAG 0x40000000 +#define CUTE_TILED_FLIPPED_DIAGONALLY_FLAG 0x20000000 + +/*! + * Helper for processing tile data in /ref `cute_tiled_layer_t` `data`. Unsets all of + * the image flipping flags in the higher bit of /p `tile_data_gid`. + */ +static CUTE_TILED_INLINE int cute_tiled_unset_flags(int tile_data_gid) +{ + const int flags = ~(CUTE_TILED_FLIPPED_HORIZONTALLY_FLAG | CUTE_TILED_FLIPPED_VERTICALLY_FLAG | CUTE_TILED_FLIPPED_DIAGONALLY_FLAG); + return tile_data_gid & flags; +} + +/*! + * Helper for processing tile data in /ref `cute_tiled_layer_t` `data`. Flags are + * stored in the GID array `data` for flipping the image. Retrieves all three flag types. + */ +static CUTE_TILED_INLINE void cute_tiled_get_flags(int tile_data_gid, int* flip_horizontal, int* flip_vertical, int* flip_diagonal) +{ + *flip_horizontal = !!(tile_data_gid & CUTE_TILED_FLIPPED_HORIZONTALLY_FLAG); + *flip_vertical = !!(tile_data_gid & CUTE_TILED_FLIPPED_VERTICALLY_FLAG); + *flip_diagonal = !!(tile_data_gid & CUTE_TILED_FLIPPED_DIAGONALLY_FLAG); +} + +struct cute_tiled_layer_t +{ + /* chunks */ // Not currently supported. + /* compression; */ // Not currently supported. + int data_count; // Number of integers in `data`. + int* data; // Array of GIDs. `tilelayer` only. Only support CSV style exports. + cute_tiled_string_t draworder; // `topdown` (default) or `index`. `objectgroup` only. + /* encoding; */ // Not currently supported. + int height; // Row count. Same as map height for fixed-size maps. + cute_tiled_layer_t* layers; // Linked list of layers. Only appears if `type` is `group`. + cute_tiled_string_t name; // Name assigned to this layer. + cute_tiled_object_t* objects; // Linked list of objects. `objectgroup` only. + float offsetx; // Horizontal layer offset. + float offsety; // Vertical layer offset. + float opacity; // Value between 0 and 1. + int property_count; // Number of elements in the `properties` array. + cute_tiled_property_t* properties; // Array of properties. + int transparentcolor; // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional). + int tintcolor; // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional). + cute_tiled_string_t type; // `tilelayer`, `objectgroup`, `imagelayer` or `group`. + cute_tiled_string_t image; // An image filepath. Used if layer is type `imagelayer`. + int visible; // 0 or 1. Whether layer is shown or hidden in editor. + int width; // Column count. Same as map width for fixed-size maps. + int x; // Horizontal layer offset in tiles. Always 0. + int y; // Vertical layer offset in tiles. Always 0. + float parallaxx; // X axis parallax factor. + float parallaxy; // Y axis parallax factor. + int repeatx; // Repeat image in the X direction + int repeaty; // Repeat image in the Y direction + int id; // ID of the layer. + cute_tiled_layer_t* next; // Pointer to the next layer. NULL if final layer. +}; + +struct cute_tiled_frame_t +{ + int duration; // Frame duration in milliseconds. + int tileid; // Local tile ID representing this frame. +}; + +struct cute_tiled_tile_descriptor_t +{ + int tile_index; // ID of the tile local to the associated tileset. + cute_tiled_string_t type; // String assigned to type field in editor. + int frame_count; // The number of animation frames in the `animation` array. + cute_tiled_frame_t* animation; // An array of `cute_tiled_frame_t`'s. Can be NULL. + cute_tiled_string_t image; // Image used for a tile in a tileset of type collection of images (relative path from map file to source image). + // Tileset is a collection of images if image.ptr isn't NULL. + int imageheight; // Image height of a tile in a tileset of type collection of images. + int imagewidth; // Image width of a tile in a tileset of type collection of images. + cute_tiled_layer_t* objectgroup; // Linked list of layers of type `objectgroup` only. Useful for holding collision info. + int property_count; // Number of elements in the `properties` array. + cute_tiled_property_t* properties; // Array of properties. + /* terrain */ // Not currently supported. + float probability; // The probability used when painting with the terrain brush in `Random Mode`. + cute_tiled_tile_descriptor_t* next; // Pointer to the next tile descriptor. NULL if final tile descriptor. +}; + +// IMPORTANT NOTE +// If your tileset is not embedded you will get a warning -- to disable this warning simply define +// this macro CUTE_TILED_NO_EXTERNAL_TILESET_WARNING. +// +// Here is an example. +// +// #define CUTE_TILED_NO_EXTERNAL_TILESET_WARNING +// #define CUTE_TILED_IMPLEMENTATION +// #include +struct cute_tiled_tileset_t +{ + int backgroundcolor; // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional). + int columns; // The number of tile columns in the tileset. + int firstgid; // GID corresponding to the first tile in the set. + /* grid */ // Not currently supported. + cute_tiled_string_t image; // Image used for tiles in this set (relative path from map file to source image). + int imagewidth; // Width of source image in pixels. + int imageheight; // Height of source image in pixels. + int margin; // Buffer between image edge and first tile (pixels). + cute_tiled_string_t name; // Name given to this tileset. + cute_tiled_string_t objectalignment; // Alignment to use for tile objects (unspecified (default), topleft, top, topright, left, center, right, bottomleft, bottom or bottomright) (since 1.4). + int property_count; // Number of elements in the `properties` array. + cute_tiled_property_t* properties; // Array of properties. + int spacing; // Spacing between adjacent tiles in image (pixels). + /* terrains */ // Not currently supported. + int tilecount; // The number of tiles in this tileset. + cute_tiled_string_t tiledversion; // The Tiled version used to save the tileset. + int tileheight; // Maximum height of tiles in this set. + int tileoffset_x; // Pixel offset to align tiles to the grid. + int tileoffset_y; // Pixel offset to align tiles to the grid. + cute_tiled_tile_descriptor_t* tiles; // Linked list of tile descriptors. Can be NULL. + int tilewidth; // Maximum width of tiles in this set. + int transparentcolor; // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional). + cute_tiled_string_t type; // `tileset` (for tileset files, since 1.0). + cute_tiled_string_t source; // Relative path to tileset, when saved externally from the map file. + cute_tiled_tileset_t* next; // Pointer to next tileset. NULL if final tileset. + float version; // The JSON format version (like 1.2). + void* _internal; // For internal use only. Don't touch. +}; + +struct cute_tiled_map_t +{ + int backgroundcolor; // Hex-formatted color (#RRGGBB or #AARRGGBB) (optional). + int height; // Number of tile rows. + /* hexsidelength */ // Not currently supported. + int infinite; // Whether the map has infinite dimensions. + cute_tiled_layer_t* layers; // Linked list of layers. Can be NULL. + int nextobjectid; // Auto-increments for each placed object. + cute_tiled_string_t orientation; // `orthogonal`, `isometric`, `staggered` or `hexagonal`. + int property_count; // Number of elements in the `properties` array. + cute_tiled_property_t* properties; // Array of properties. + cute_tiled_string_t renderorder; // Rendering direction (orthogonal maps only). + /* staggeraxis */ // Not currently supported. + /* staggerindex */ // Not currently supported. + cute_tiled_string_t tiledversion; // The Tiled version used to save the file. + int tileheight; // Map grid height. + cute_tiled_tileset_t* tilesets; // Linked list of tilesets. + int tilewidth; // Map grid width. + cute_tiled_string_t type; // `map` (since 1.0). + float version; // The JSON format version (like 1.2). + int width; // Number of tile columns. + int nextlayerid; // The ID of the following layer. +}; + +#define CUTE_TILED_H +#endif + +#ifdef CUTE_TILED_IMPLEMENTATION +#ifndef CUTE_TILED_IMPLEMENTATION_ONCE +#define CUTE_TILED_IMPLEMENTATION_ONCE + +#ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#if !defined(CUTE_TILED_ALLOC) + #include + #define CUTE_TILED_ALLOC(size, ctx) malloc(size) + #define CUTE_TILED_FREE(mem, ctx) free(mem) +#endif + + +#ifndef STRPOOL_EMBEDDED_MALLOC + #define STRPOOL_EMBEDDED_MALLOC(ctx, size) CUTE_TILED_ALLOC(size, ctx) +#endif + +#ifndef STRPOOL_EMBEDDED_FREE + #define STRPOOL_EMBEDDED_FREE(ctx, ptr) CUTE_TILED_FREE(ptr, ctx) +#endif + +#define STRPOOL_EMBEDDED_IMPLEMENTATION + +/* + begin embedding modified strpool.h +*/ + +/* +------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. +------------------------------------------------------------------------------ + +strpool.h - v1.4 - Highly efficient string pool for C/C++. + +Do this: + #define STRPOOL_EMBEDDED_IMPLEMENTATION +before you include this file in *one* C/C++ file to create the implementation. +*/ + +#ifndef strpool_embedded_h +#define strpool_embedded_h + +#ifndef STRPOOL_EMBEDDED_U32 + #define STRPOOL_EMBEDDED_U32 unsigned int +#endif +#ifndef STRPOOL_EMBEDDED_U64 + #define STRPOOL_EMBEDDED_U64 unsigned long long +#endif + +typedef struct strpool_embedded_t strpool_embedded_t; + +typedef struct strpool_embedded_config_t + { + void* memctx; + int ignore_case; + int counter_bits; + int index_bits; + int entry_capacity; + int block_capacity; + int block_size; + int min_length; + } strpool_embedded_config_t; + +extern strpool_embedded_config_t const strpool_embedded_default_config; + +void strpool_embedded_init( strpool_embedded_t* pool, strpool_embedded_config_t const* config ); +void strpool_embedded_term( strpool_embedded_t* pool ); + +STRPOOL_EMBEDDED_U64 strpool_embedded_inject( strpool_embedded_t* pool, char const* string, int length ); +char const* strpool_embedded_cstr( strpool_embedded_t const* pool, STRPOOL_EMBEDDED_U64 handle ); + +#endif /* strpool_embedded_h */ + +/* +---------------------- + IMPLEMENTATION +---------------------- +*/ + +#ifndef strpool_embedded_impl +#define strpool_embedded_impl + +struct strpool_embedded_internal_hash_slot_t; +struct strpool_embedded_internal_entry_t; +struct strpool_embedded_internal_handle_t; +struct strpool_embedded_internal_block_t; + +struct strpool_embedded_t + { + void* memctx; + int ignore_case; + int counter_shift; + STRPOOL_EMBEDDED_U64 counter_mask; + STRPOOL_EMBEDDED_U64 index_mask; + + int initial_entry_capacity; + int initial_block_capacity; + int block_size; + int min_data_size; + + struct strpool_embedded_internal_hash_slot_t* hash_table; + int hash_capacity; + + struct strpool_embedded_internal_entry_t* entries; + int entry_capacity; + int entry_count; + + struct strpool_embedded_internal_handle_t* handles; + int handle_capacity; + int handle_count; + int handle_freelist_head; + int handle_freelist_tail; + + struct strpool_embedded_internal_block_t* blocks; + int block_capacity; + int block_count; + int current_block; + }; + + +#endif /* strpool_embedded_impl */ + + +#ifdef STRPOOL_EMBEDDED_IMPLEMENTATION +#ifndef STRPOOL_EMBEDDED_IMPLEMENTATION_ONCE +#define STRPOOL_EMBEDDED_IMPLEMENTATION_ONCE + +#include + +#ifndef STRPOOL_EMBEDDED_ASSERT + #include + #define STRPOOL_EMBEDDED_ASSERT( x ) assert( x ) +#endif + +#ifndef STRPOOL_EMBEDDED_MEMSET + #include + #define STRPOOL_EMBEDDED_MEMSET( ptr, val, cnt ) ( memset( ptr, val, cnt ) ) +#endif + +#ifndef STRPOOL_EMBEDDED_MEMCPY + #include + #define STRPOOL_EMBEDDED_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) +#endif + +#ifndef STRPOOL_EMBEDDED_MEMCMP + #include + #define STRPOOL_EMBEDDED_MEMCMP( pr1, pr2, cnt ) ( memcmp( pr1, pr2, cnt ) ) +#endif + +#ifndef STRPOOL_EMBEDDED_STRNICMP + #ifdef _WIN32 + #include + #define STRPOOL_EMBEDDED_STRNICMP( s1, s2, len ) ( _strnicmp( s1, s2, len ) ) + #else + #include + #define STRPOOL_EMBEDDED_STRNICMP( s1, s2, len ) ( strncasecmp( s1, s2, len ) ) + #endif +#endif + +#ifndef STRPOOL_EMBEDDED_MALLOC + #include + #define STRPOOL_EMBEDDED_MALLOC( ctx, size ) ( malloc( size ) ) + #define STRPOOL_EMBEDDED_FREE( ctx, ptr ) ( free( ptr ) ) +#endif + + +typedef struct strpool_embedded_internal_hash_slot_t + { + STRPOOL_EMBEDDED_U32 hash_key; + int entry_index; + int base_count; + } strpool_embedded_internal_hash_slot_t; + + +typedef struct strpool_embedded_internal_entry_t + { + int hash_slot; + int handle_index; + char* data; + int size; + int length; + int refcount; + } strpool_embedded_internal_entry_t; + + +typedef struct strpool_embedded_internal_handle_t + { + int entry_index; + int counter; + } strpool_embedded_internal_handle_t; + + +typedef struct strpool_embedded_internal_block_t + { + int capacity; + char* data; + char* tail; + int free_list; + } strpool_embedded_internal_block_t; + + +typedef struct strpool_embedded_internal_free_block_t + { + int size; + int next; + } strpool_embedded_internal_free_block_t; + + +strpool_embedded_config_t const strpool_embedded_default_config = + { + /* memctx = */ 0, + /* ignore_case = */ 0, + /* counter_bits = */ 32, + /* index_bits = */ 32, + /* entry_capacity = */ 4096, + /* block_capacity = */ 32, + /* block_size = */ 256 * 1024, + /* min_length = */ 23, + }; + + + +static STRPOOL_EMBEDDED_U32 strpool_embedded_internal_pow2ceil( STRPOOL_EMBEDDED_U32 v ) + { + --v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + v += ( v == 0 ); + return v; + } + + +static int strpool_embedded_internal_add_block( strpool_embedded_t* pool, int size ) + { + if( pool->block_count >= pool->block_capacity ) + { + strpool_embedded_internal_block_t* new_blocks; + pool->block_capacity *= 2; + new_blocks = (strpool_embedded_internal_block_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->block_capacity * sizeof( *pool->blocks ) ); + STRPOOL_EMBEDDED_ASSERT( new_blocks ); + STRPOOL_EMBEDDED_MEMCPY( new_blocks, pool->blocks, pool->block_count * sizeof( *pool->blocks ) ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->blocks ); + pool->blocks = new_blocks; + } + pool->blocks[ pool->block_count ].capacity = size; + pool->blocks[ pool->block_count ].data = (char*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, (size_t) size ); + STRPOOL_EMBEDDED_ASSERT( pool->blocks[ pool->block_count ].data ); + pool->blocks[ pool->block_count ].tail = pool->blocks[ pool->block_count ].data; + pool->blocks[ pool->block_count ].free_list = -1; + return pool->block_count++; + } + + +void strpool_embedded_init( strpool_embedded_t* pool, strpool_embedded_config_t const* config ) + { + if( !config ) config = &strpool_embedded_default_config; + + pool->memctx = config->memctx; + pool->ignore_case = config->ignore_case; + + STRPOOL_EMBEDDED_ASSERT( config->counter_bits + config->index_bits <= 64 ); + pool->counter_shift = config->index_bits; + pool->counter_mask = ( 1ULL << (STRPOOL_EMBEDDED_U64) config->counter_bits ) - 1; + pool->index_mask = ( 1ULL << (STRPOOL_EMBEDDED_U64) config->index_bits ) - 1; + + pool->initial_entry_capacity = + (int) strpool_embedded_internal_pow2ceil( config->entry_capacity > 1 ? (STRPOOL_EMBEDDED_U32)config->entry_capacity : 2U ); + pool->initial_block_capacity = + (int) strpool_embedded_internal_pow2ceil( config->block_capacity > 1 ? (STRPOOL_EMBEDDED_U32)config->block_capacity : 2U ); + pool->block_size = + (int) strpool_embedded_internal_pow2ceil( config->block_size > 256 ? (STRPOOL_EMBEDDED_U32)config->block_size : 256U ); + pool->min_data_size = + (int) ( sizeof( int ) * 2 + 1 + ( config->min_length > 8 ? (STRPOOL_EMBEDDED_U32)config->min_length : 8U ) ); + + pool->hash_capacity = pool->initial_entry_capacity * 2; + pool->entry_capacity = pool->initial_entry_capacity; + pool->handle_capacity = pool->initial_entry_capacity; + pool->block_capacity = pool->initial_block_capacity; + + pool->handle_freelist_head = -1; + pool->handle_freelist_tail = -1; + pool->block_count = 0; + pool->handle_count = 0; + pool->entry_count = 0; + + pool->hash_table = (strpool_embedded_internal_hash_slot_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->hash_capacity * sizeof( *pool->hash_table ) ); + STRPOOL_EMBEDDED_ASSERT( pool->hash_table ); + STRPOOL_EMBEDDED_MEMSET( pool->hash_table, 0, pool->hash_capacity * sizeof( *pool->hash_table ) ); + pool->entries = (strpool_embedded_internal_entry_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->entry_capacity * sizeof( *pool->entries ) ); + STRPOOL_EMBEDDED_ASSERT( pool->entries ); + pool->handles = (strpool_embedded_internal_handle_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->handle_capacity * sizeof( *pool->handles ) ); + STRPOOL_EMBEDDED_ASSERT( pool->handles ); + pool->blocks = (strpool_embedded_internal_block_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->block_capacity * sizeof( *pool->blocks ) ); + STRPOOL_EMBEDDED_ASSERT( pool->blocks ); + + pool->current_block = strpool_embedded_internal_add_block( pool, pool->block_size ); + } + + +void strpool_embedded_term( strpool_embedded_t* pool ) + { + int i; +#if 0 + // Debug statistics + printf( "\n\n" ); + printf( "Handles: %d/%d\n", pool->handle_count, pool->handle_capacity ); + printf( "Entries: %d/%d\n", pool->entry_count, pool->entry_capacity ); + printf( "Hashtable: %d/%d\n", pool->entry_count, pool->hash_capacity ); + printf( "Blocks: %d/%d\n", pool->block_count, pool->block_capacity ); + for( int i = 0; i < pool->block_count; ++i ) + { + printf( "\n" ); + printf( "BLOCK: %d\n", i ); + printf( "Capacity: %d\n", pool->blocks[ i ].capacity ); + printf( "Free: [ %d ]", pool->blocks[ i ].capacity - ( pool->blocks[ i ].tail - pool->blocks[ i ].data ) ); + int fl = pool->blocks[ i ].free_list; + int count = 0; + int size = 0; + int total = 0; + while( fl >= 0 ) + { + strpool_embedded_free_block_t* free_entry = (strpool_embedded_free_block_t*) ( pool->blocks[ i ].data + fl ); + total += free_entry->size; + if( size == 0 ) { size = free_entry->size; } + if( size != free_entry->size ) + { + printf( ", %dx%d", count, size ); + count = 1; + size = free_entry->size; + } + else + { + ++count; + } + fl = free_entry->next; + } + if( size != 0 ) printf( ", %dx%d", count, size ); + printf( ", { %d }\n", total ); + } + printf( "\n\n" ); +#endif + + for( i = 0; i < pool->block_count; ++i ) STRPOOL_EMBEDDED_FREE( pool->memctx, pool->blocks[ i ].data ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->blocks ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->handles ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->entries ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->hash_table ); + } + + +static STRPOOL_EMBEDDED_U64 strpool_embedded_internal_make_handle( int index, int counter, STRPOOL_EMBEDDED_U64 index_mask, int counter_shift, + STRPOOL_EMBEDDED_U64 counter_mask ) + { + STRPOOL_EMBEDDED_U64 i = (STRPOOL_EMBEDDED_U64) ( index + 1 ); + STRPOOL_EMBEDDED_U64 c = (STRPOOL_EMBEDDED_U64) counter; + return ( ( c & counter_mask ) << counter_shift ) | ( i & index_mask ); + } + + +static int strpool_embedded_internal_counter_from_handle( STRPOOL_EMBEDDED_U64 handle, int counter_shift, STRPOOL_EMBEDDED_U64 counter_mask ) + { + return (int) ( ( handle >> counter_shift ) & counter_mask ) ; + } + + +static int strpool_embedded_internal_index_from_handle( STRPOOL_EMBEDDED_U64 handle, STRPOOL_EMBEDDED_U64 index_mask ) + { + return ( (int) ( handle & index_mask ) ) - 1; + } + + +static strpool_embedded_internal_entry_t* strpool_embedded_internal_get_entry( strpool_embedded_t const* pool, STRPOOL_EMBEDDED_U64 handle ) + { + int index = strpool_embedded_internal_index_from_handle( handle, pool->index_mask ); + int counter = strpool_embedded_internal_counter_from_handle( handle, pool->counter_shift, pool->counter_mask ); + + if( index >= 0 && index < pool->handle_count && + counter == (int) ( pool->handles[ index ].counter & pool->counter_mask ) ) + return &pool->entries[ pool->handles[ index ].entry_index ]; + + return 0; + } + + +static STRPOOL_EMBEDDED_U32 strpool_embedded_internal_find_in_blocks( strpool_embedded_t const* pool, char const* string, int length ) + { + int i; + for( i = 0; i < pool->block_count; ++i ) + { + strpool_embedded_internal_block_t* block = &pool->blocks[ i ]; + // Check if string comes from pool + if( string >= block->data + 2 * sizeof( STRPOOL_EMBEDDED_U32 ) && string < block->data + block->capacity ) + { + STRPOOL_EMBEDDED_U32* ptr = (STRPOOL_EMBEDDED_U32*) string; + int stored_length = (int)( *( ptr - 1 ) ); // Length is stored immediately before string + STRPOOL_EMBEDDED_U32 hash; + if( stored_length != length || string[ length ] != '\0' ) return 0; // Invalid string + hash = *( ptr - 2 ); // Hash is stored before the length field + return hash; + } + } + + return 0; + } + + +static STRPOOL_EMBEDDED_U32 strpool_embedded_internal_calculate_hash( char const* string, int length, int ignore_case ) + { + STRPOOL_EMBEDDED_U32 hash = 5381U; + int i; + + if( ignore_case) + { + for( i = 0; i < length; ++i ) + { + char c = string[ i ]; + c = ( c <= 'z' && c >= 'a' ) ? c - ( 'a' - 'A' ) : c; + hash = ( ( hash << 5U ) + hash) ^ c; + } + } + else + { + for( i = 0; i < length; ++i ) + { + char c = string[ i ]; + hash = ( ( hash << 5U ) + hash) ^ c; + } + } + + hash = ( hash == 0 ) ? 1 : hash; // We can't allow 0-value hash keys, but dupes are ok + return hash; + } + + +static void strpool_embedded_internal_expand_hash_table( strpool_embedded_t* pool ) + { + int old_capacity = pool->hash_capacity; + strpool_embedded_internal_hash_slot_t* old_table = pool->hash_table; + int i; + + pool->hash_capacity *= 2; + + pool->hash_table = (strpool_embedded_internal_hash_slot_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->hash_capacity * sizeof( *pool->hash_table ) ); + STRPOOL_EMBEDDED_ASSERT( pool->hash_table ); + STRPOOL_EMBEDDED_MEMSET( pool->hash_table, 0, pool->hash_capacity * sizeof( *pool->hash_table ) ); + + for( i = 0; i < old_capacity; ++i ) + { + STRPOOL_EMBEDDED_U32 hash_key = old_table[ i ].hash_key; + if( hash_key ) + { + int base_slot = (int)( hash_key & (STRPOOL_EMBEDDED_U32)( pool->hash_capacity - 1 ) ); + int slot = base_slot; + while( pool->hash_table[ slot ].hash_key ) + slot = ( slot + 1 ) & ( pool->hash_capacity - 1 ); + STRPOOL_EMBEDDED_ASSERT( hash_key ); + pool->hash_table[ slot ].hash_key = hash_key; + pool->hash_table[ slot ].entry_index = old_table[ i ].entry_index; + pool->entries[ pool->hash_table[ slot ].entry_index ].hash_slot = slot; + ++pool->hash_table[ base_slot ].base_count; + } + } + + STRPOOL_EMBEDDED_FREE( pool->memctx, old_table ); + } + + +static void strpool_embedded_internal_expand_entries( strpool_embedded_t* pool ) + { + strpool_embedded_internal_entry_t* new_entries; + pool->entry_capacity *= 2; + new_entries = (strpool_embedded_internal_entry_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->entry_capacity * sizeof( *pool->entries ) ); + STRPOOL_EMBEDDED_ASSERT( new_entries ); + STRPOOL_EMBEDDED_MEMCPY( new_entries, pool->entries, pool->entry_count * sizeof( *pool->entries ) ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->entries ); + pool->entries = new_entries; + } + + +static void strpool_embedded_internal_expand_handles( strpool_embedded_t* pool ) + { + strpool_embedded_internal_handle_t* new_handles; + pool->handle_capacity *= 2; + new_handles = (strpool_embedded_internal_handle_t*) STRPOOL_EMBEDDED_MALLOC( pool->memctx, + pool->handle_capacity * sizeof( *pool->handles ) ); + STRPOOL_EMBEDDED_ASSERT( new_handles ); + STRPOOL_EMBEDDED_MEMCPY( new_handles, pool->handles, pool->handle_count * sizeof( *pool->handles ) ); + STRPOOL_EMBEDDED_FREE( pool->memctx, pool->handles ); + pool->handles = new_handles; + } + + +static char* strpool_embedded_internal_get_data_storage( strpool_embedded_t* pool, int size, int* alloc_size ) + { + char* data; + int i; + int offset; + if( size < (int)sizeof( strpool_embedded_internal_free_block_t ) ) size = sizeof( strpool_embedded_internal_free_block_t ); + if( size < pool->min_data_size ) size = pool->min_data_size; + size = (int)strpool_embedded_internal_pow2ceil( (STRPOOL_EMBEDDED_U32)size ); + + // Try to find a large enough free slot in existing blocks + for( i = 0; i < pool->block_count; ++i ) + { + int free_list = pool->blocks[ i ].free_list; + int prev_list = -1; + while( free_list >= 0 ) + { + strpool_embedded_internal_free_block_t* free_entry = + (strpool_embedded_internal_free_block_t*) ( pool->blocks[ i ].data + free_list ); + if( free_entry->size / 2 < size ) + { + // At this point, all remaining slots are too small, so bail out if the current slot is not large enough + if( free_entry->size < size ) break; + + if( prev_list < 0 ) + { + pool->blocks[ i ].free_list = free_entry->next; + } + else + { + strpool_embedded_internal_free_block_t* prev_entry = + (strpool_embedded_internal_free_block_t*) ( pool->blocks[ i ].data + prev_list ); + prev_entry->next = free_entry->next; + } + *alloc_size = free_entry->size; + return (char*) free_entry; + } + prev_list = free_list; + free_list = free_entry->next; + } + } + + // Use current block, if enough space left + offset = (int) ( pool->blocks[ pool->current_block ].tail - pool->blocks[ pool->current_block ].data ); + if( size <= pool->blocks[ pool->current_block ].capacity - offset ) + { + char* data = pool->blocks[ pool->current_block ].tail; + pool->blocks[ pool->current_block ].tail += size; + *alloc_size = size; + return data; + } + + // Allocate a new block + pool->current_block = strpool_embedded_internal_add_block( pool, size > pool->block_size ? size : pool->block_size ); + data = pool->blocks[ pool->current_block ].tail; + pool->blocks[ pool->current_block ].tail += size; + *alloc_size = size; + return data; + } + + +STRPOOL_EMBEDDED_U64 strpool_embedded_inject( strpool_embedded_t* pool, char const* string, int length ) + { + STRPOOL_EMBEDDED_U32 hash; + int base_slot; + int base_count; + int slot; + int first_free; + int handle_index; + strpool_embedded_internal_entry_t* entry; + int data_size; + char* data; + if( !string || length < 0 ) return 0; + + hash = strpool_embedded_internal_find_in_blocks( pool, string, length ); + // If no stored hash, calculate it from data + if( !hash ) hash = strpool_embedded_internal_calculate_hash( string, length, pool->ignore_case ); + + // Return handle to existing string, if it is already in pool + base_slot = (int)( hash & (STRPOOL_EMBEDDED_U32)( pool->hash_capacity - 1 ) ); + base_count = pool->hash_table[ base_slot ].base_count; + slot = base_slot; + first_free = slot; + while( base_count > 0 ) + { + STRPOOL_EMBEDDED_U32 slot_hash = pool->hash_table[ slot ].hash_key; + int slot_base; + if( slot_hash == 0 && pool->hash_table[ first_free ].hash_key != 0 ) first_free = slot; + slot_base = (int)( slot_hash & (STRPOOL_EMBEDDED_U32)( pool->hash_capacity - 1 ) ); + if( slot_base == base_slot ) + { + STRPOOL_EMBEDDED_ASSERT( base_count > 0 ); + --base_count; + if( slot_hash == hash ) + { + int index = pool->hash_table[ slot ].entry_index; + strpool_embedded_internal_entry_t* entry = &pool->entries[ index ]; + if( entry->length == length && + ( + ( !pool->ignore_case && STRPOOL_EMBEDDED_MEMCMP( entry->data + 2 * sizeof( STRPOOL_EMBEDDED_U32 ), string, (size_t)length ) == 0 ) + || ( pool->ignore_case && STRPOOL_EMBEDDED_STRNICMP( entry->data + 2 * sizeof( STRPOOL_EMBEDDED_U32 ), string, (size_t)length ) == 0 ) + ) + ) + { + int handle_index = entry->handle_index; + return strpool_embedded_internal_make_handle( handle_index, pool->handles[ handle_index ].counter, + pool->index_mask, pool->counter_shift, pool->counter_mask ); + } + } + } + slot = ( slot + 1 ) & ( pool->hash_capacity - 1 ); + } + + // This is a new string, so let's add it + + if( pool->entry_count >= ( pool->hash_capacity - pool->hash_capacity / 3 ) ) + { + strpool_embedded_internal_expand_hash_table( pool ); + + base_slot = (int)( hash & (STRPOOL_EMBEDDED_U32)( pool->hash_capacity - 1 ) ); + slot = base_slot; + first_free = slot; + while( base_count ) + { + STRPOOL_EMBEDDED_U32 slot_hash = pool->hash_table[ slot ].hash_key; + int slot_base; + if( slot_hash == 0 && pool->hash_table[ first_free ].hash_key != 0 ) first_free = slot; + slot_base = (int)( slot_hash & (STRPOOL_EMBEDDED_U32)( pool->hash_capacity - 1 ) ); + if( slot_base == base_slot ) --base_count; + slot = ( slot + 1 ) & ( pool->hash_capacity - 1 ); + } + } + + slot = first_free; + while( pool->hash_table[ slot ].hash_key ) + slot = ( slot + 1 ) & ( pool->hash_capacity - 1 ); + + if( pool->entry_count >= pool->entry_capacity ) + strpool_embedded_internal_expand_entries( pool ); + + STRPOOL_EMBEDDED_ASSERT( !pool->hash_table[ slot ].hash_key && ( hash & ( (STRPOOL_EMBEDDED_U32) pool->hash_capacity - 1 ) ) == (STRPOOL_EMBEDDED_U32) base_slot ); + STRPOOL_EMBEDDED_ASSERT( hash ); + pool->hash_table[ slot ].hash_key = hash; + pool->hash_table[ slot ].entry_index = pool->entry_count; + ++pool->hash_table[ base_slot ].base_count; + + if( pool->handle_count < pool->handle_capacity ) + { + handle_index = pool->handle_count; + pool->handles[ pool->handle_count ].counter = 1; + ++pool->handle_count; + } + else if( pool->handle_freelist_head >= 0 ) + { + handle_index = pool->handle_freelist_head; + if( pool->handle_freelist_tail == pool->handle_freelist_head ) + pool->handle_freelist_tail = pool->handles[ pool->handle_freelist_head ].entry_index; + pool->handle_freelist_head = pool->handles[ pool->handle_freelist_head ].entry_index; + } + else + { + strpool_embedded_internal_expand_handles( pool ); + handle_index = pool->handle_count; + pool->handles[ pool->handle_count ].counter = 1; + ++pool->handle_count; + } + + pool->handles[ handle_index ].entry_index = pool->entry_count; + + entry = &pool->entries[ pool->entry_count ]; + ++pool->entry_count; + + data_size = length + 1 + (int) ( 2 * sizeof( STRPOOL_EMBEDDED_U32 ) ); + data = strpool_embedded_internal_get_data_storage( pool, data_size, &data_size ); + entry->hash_slot = slot; + entry->handle_index = handle_index; + entry->data = data; + entry->size = data_size; + entry->length = length; + entry->refcount = 0; + + *(STRPOOL_EMBEDDED_U32*)(data) = hash; + data += sizeof( STRPOOL_EMBEDDED_U32 ); + *(STRPOOL_EMBEDDED_U32*)(data) = (STRPOOL_EMBEDDED_U32) length; + data += sizeof( STRPOOL_EMBEDDED_U32 ); + STRPOOL_EMBEDDED_MEMCPY( data, string, (size_t) length ); + data[ length ] = 0; // Ensure trailing zero + + return strpool_embedded_internal_make_handle( handle_index, pool->handles[ handle_index ].counter, pool->index_mask, + pool->counter_shift, pool->counter_mask ); + } + + +char const* strpool_embedded_cstr( strpool_embedded_t const* pool, STRPOOL_EMBEDDED_U64 handle ) + { + strpool_embedded_internal_entry_t const* entry = strpool_embedded_internal_get_entry( pool, handle ); + if( entry ) return entry->data + 2 * sizeof( STRPOOL_EMBEDDED_U32 ); // Skip leading hash value + return NULL; + } + +#endif // STRPOOL_EMBEDDED_IMPLEMENTATION_ONCE +#endif /* STRPOOL_EMBEDDED_IMPLEMENTATION */ + + +/* +revision history: + 1.4 fixed find_in_blocks substring bug, removed realloc, added docs + 1.3 fixed typo in mask bit shift + 1.2 made it possible to override standard library functions + 1.1 added is_valid function to query a handles validity + 1.0 first released version +*/ + + +/* +------------------------------------------------------------------------------ + +This software is available under 2 licenses - you may choose the one you like. + +------------------------------------------------------------------------------ + +ALTERNATIVE A - MIT License + +Copyright (c) 2015 Mattias Gustavsson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +ALTERNATIVE B - Public Domain (www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------ +*/ + +/* + end embedding strpool.h +*/ + +#if !defined(CUTE_TILED_WARNING) + #define CUTE_TILED_DEFAULT_WARNING + #define CUTE_TILED_WARNING(msg) cute_tiled_warning(msg, __LINE__) +#endif + +#if !defined(CUTE_TILED_MEMCPY) + #include // memcpy + #define CUTE_TILED_MEMCPY memcpy +#endif + +#if !defined(CUTE_TILED_MEMSET) + #include // memset + #define CUTE_TILED_MEMSET memset +#endif + +#if !defined(CUTE_TILED_UNUSED) + #if defined(_MSC_VER) + #define CUTE_TILED_UNUSED(x) (void)x + #else + #define CUTE_TILED_UNUSED(x) (void)(sizeof(x)) + #endif +#endif + +#if !defined(CUTE_TILED_SNPRINTF) + #include // snprintf + #define CUTE_TILED_SNPRINTF snprintf +#endif + +#if !defined(CUTE_TILED_SEEK_SET) + #include // SEEK_SET + #define CUTE_TILED_SEEK_SET SEEK_SET +#endif + +#if !defined(CUTE_TILED_SEEK_END) + #include // SEEK_END + #define CUTE_TILED_SEEK_END SEEK_END +#endif + +#if !defined(CUTE_TILED_FILE) + #include // FILE + #define CUTE_TILED_FILE FILE +#endif + +#if !defined(CUTE_TILED_FOPEN) + #include // fopen + #define CUTE_TILED_FOPEN fopen +#endif + +#if !defined(CUTE_TILED_FSEEK) + #include // fseek + #define CUTE_TILED_FSEEK fseek +#endif + +#if !defined(CUTE_TILED_FREAD) + #include // fread + #define CUTE_TILED_FREAD fread +#endif + +#if !defined(CUTE_TILED_FTELL) + #include // ftell + #define CUTE_TILED_FTELL ftell +#endif + +#if !defined(CUTE_TILED_FCLOSE) + #include // fclose + #define CUTE_TILED_FCLOSE fclose +#endif + +int cute_tiled_error_cline; // The line in cute_tiled.h where the error was triggered. +const char* cute_tiled_error_reason; // The error message. +int cute_tiled_error_line; // The line where the error happened in the json. +const char* cute_tiled_error_file = NULL; // The filepath of the file being parsed. NULL if from memory. + +#ifdef CUTE_TILED_DEFAULT_WARNING + #include + + void cute_tiled_warning(const char* warning, int line) + { + const char *error_file = cute_tiled_error_file ? cute_tiled_error_file : "MEMORY"; + cute_tiled_error_cline = line; + printf("WARNING (cute_tiled.h:%i): %s (%s:%i)\n", cute_tiled_error_cline, warning, error_file, cute_tiled_error_line); + } +#endif + +#include + +#if !defined(CUTE_TILED_STRTOLL) + #define CUTE_TILED_STRTOLL strtoll +#endif + +#if !defined(CUTE_TILED_STRTOULL) + #define CUTE_TILED_STRTOULL strtoull +#endif + +#if !defined(CUTE_TILED_STRTOD) + #define CUTE_TILED_STRTOD strtod +#endif + +typedef struct cute_tiled_page_t cute_tiled_page_t; +typedef struct cute_tiled_map_internal_t cute_tiled_map_internal_t; + +struct cute_tiled_page_t +{ + cute_tiled_page_t* next; + void* data; +}; + +#define CUTE_TILED_INTERNAL_BUFFER_MAX 1024 + +struct cute_tiled_map_internal_t +{ + char* in; + char* end; + cute_tiled_map_t map; + strpool_embedded_t strpool; + void* mem_ctx; + int page_size; + int bytes_left_on_page; + cute_tiled_page_t* pages; + int scratch_len; + char scratch[CUTE_TILED_INTERNAL_BUFFER_MAX]; +}; + +void* cute_tiled_alloc(cute_tiled_map_internal_t* m, int size) +{ + void* data; + if (size > m->page_size) return NULL; // Should never happen. + if (m->bytes_left_on_page < size) + { + cute_tiled_page_t* page = (cute_tiled_page_t*)CUTE_TILED_ALLOC(sizeof(cute_tiled_page_t) + m->page_size, m->mem_ctx); + if (!page) return 0; + page->next = m->pages; + page->data = page + 1; + m->pages = page; + m->bytes_left_on_page = m->page_size; + } + + data = ((char*)m->pages->data) + (m->page_size - m->bytes_left_on_page); + m->bytes_left_on_page -= size; + return data; +} + +CUTE_TILED_U64 cute_tiled_FNV1a(const void* buf, int len) +{ + CUTE_TILED_U64 h = (CUTE_TILED_U64)14695981039346656037U; + const char* str = (const char*)buf; + + while (len--) + { + char c = *str++; + h = h ^ (CUTE_TILED_U64)c; + h = h * (CUTE_TILED_U64)1099511628211; + } + + return h; +} + +static char* cute_tiled_read_file_to_memory_and_null_terminate(const char* path, int* size, void* mem_ctx) +{ + char* data = 0; + CUTE_TILED_FILE* fp = CUTE_TILED_FOPEN(path, "rb"); + int sz = 0; + CUTE_TILED_UNUSED(mem_ctx); + + if (fp) + { + CUTE_TILED_FSEEK(fp, 0, CUTE_TILED_SEEK_END); + sz = CUTE_TILED_FTELL(fp); + CUTE_TILED_FSEEK(fp, 0, CUTE_TILED_SEEK_SET); + data = (char*)CUTE_TILED_ALLOC(sz + 1, mem_ctx); + CUTE_TILED_FREAD(data, sz, 1, fp); + data[sz] = 0; + CUTE_TILED_FCLOSE(fp); + } + + if (size) *size = sz; + return data; +} + +cute_tiled_map_t* cute_tiled_load_map_from_file(const char* path, void* mem_ctx) +{ + int size; + void* file; + cute_tiled_map_t* map; + cute_tiled_error_file = path; + + file = cute_tiled_read_file_to_memory_and_null_terminate(path, &size, mem_ctx); + if (!file) CUTE_TILED_WARNING("Unable to find map file."); + map = cute_tiled_load_map_from_memory(file, size, mem_ctx); + CUTE_TILED_FREE(file, mem_ctx); + + cute_tiled_error_file = NULL; + + return map; +} + +#define CUTE_TILED_CHECK(X, Y) do { if (!(X)) { cute_tiled_error_reason = Y; cute_tiled_error_cline = __LINE__; goto cute_tiled_err; } } while (0) +#define CUTE_TILED_FAIL_IF(X) do { if (X) { goto cute_tiled_err; } } while (0) + +static int cute_tiled_isspace(char c) +{ + cute_tiled_error_line += c == '\n'; + + return (c == ' ') | + (c == '\t') | + (c == '\n') | + (c == '\v') | + (c == '\f') | + (c == '\r'); +} + +static char cute_tiled_peak(cute_tiled_map_internal_t* m) +{ + while (cute_tiled_isspace(*m->in)) m->in++; + return *m->in; +} + +#ifdef __clang__ + #define CUTE_TILED_CRASH() __builtin_trap() +#else + #define CUTE_TILED_CRASH() *(int*)0 = 0 +#endif + +static char cute_tiled_next(cute_tiled_map_internal_t* m) +{ + char c; + if (m->in == m->end) CUTE_TILED_CRASH(); + while (cute_tiled_isspace(c = *m->in++)); + return c; +} + +static char cute_tiled_string_next(cute_tiled_map_internal_t* m) +{ + char c; + if (m->in == m->end) CUTE_TILED_CRASH(); + c = *m->in++; + return c; +} + +static int cute_tiled_try(cute_tiled_map_internal_t* m, char expect) +{ + if (m->in == m->end) CUTE_TILED_CRASH(); + if (cute_tiled_peak(m) == expect) + { + m->in++; + return 1; + } + return 0; +} + +#define cute_tiled_expect(m, expect) \ + do { \ + static char error[128]; \ + CUTE_TILED_SNPRINTF(error, sizeof(error), "Found unexpected token '%c', expected '%c' (is this a valid JSON file?).", *m->in, expect); \ + CUTE_TILED_CHECK(cute_tiled_next(m) == (expect), error); \ + } while (0) + +char cute_tiled_parse_char(char c) +{ + switch (c) + { + case '\\': return '\\'; + case '\'': return '\''; + case '"': return '"'; + case 't': return '\t'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case '0': return '\0'; + default: return c; + } +} + +static int cute_tiled_skip_object_internal(cute_tiled_map_internal_t* m) +{ + int depth = 1; + cute_tiled_expect(m, '{'); + + while (depth) { + char c; + CUTE_TILED_CHECK(m->in <= m->end, "Attempted to read passed input buffer (is this a valid JSON file?)."); + + c = cute_tiled_next(m); + + switch(c) + { + case '{': + depth += 1; + break; + + case '}': + depth -= 1; + break; + + default: + break; + } + } + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_skip_object(m) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_skip_object_internal(m)); \ + } while (0) + +static int cute_tiled_skip_array_internal(cute_tiled_map_internal_t* m) +{ + int depth = 1; + cute_tiled_expect(m, '['); + + while (depth) + { + char c; + CUTE_TILED_CHECK(m->in <= m->end, "Attempted to read passed input buffer (is this a valid JSON file?)."); + + c = cute_tiled_next(m); + + switch(c) + { + case '[': + depth += 1; + break; + + case ']': + depth -= 1; + break; + + default: + break; + } + } + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_skip_array(m) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_skip_array_internal(m)); \ + } while (0) + +static int cute_tiled_read_string_internal(cute_tiled_map_internal_t* m) +{ + int count = 0; + int done = 0; + cute_tiled_expect(m, '"'); + + while (!done) + { + char c; + CUTE_TILED_CHECK(count < CUTE_TILED_INTERNAL_BUFFER_MAX, "String exceeded max length of CUTE_TILED_INTERNAL_BUFFER_MAX."); + c = cute_tiled_string_next(m); + + switch (c) + { + case '"': + m->scratch[count] = 0; + done = 1; + break; + + case '\\': + { + char the_char = cute_tiled_parse_char(cute_tiled_string_next(m)); + m->scratch[count++] = the_char; + } break; + + default: + m->scratch[count++] = c; + break; + } + } + + m->scratch_len = count; + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_string(m) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_string_internal(m)); \ + } while (0) + +static int cute_tiled_read_int_internal(cute_tiled_map_internal_t* m, int* out) +{ + char* end; + int val = (int)CUTE_TILED_STRTOLL(m->in, &end, 10); + if (*end == '.') CUTE_TILED_STRTOD(m->in, &end); // If we're reading a float as an int, then just skip the decimal part. + CUTE_TILED_CHECK(m->in != end, "Invalid integer found during parse."); + m->in = end; + *out = val; + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_int(m, num) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_int_internal(m, num)); \ + } while (0) + +static int cute_tiled_read_hex_int_internal(cute_tiled_map_internal_t* m, int* out) +{ + char* end; + unsigned long long int val; + switch (cute_tiled_peak(m)) + { + case '#': + cute_tiled_next(m); + break; + + case '0': + { + char c; + cute_tiled_next(m); + c = cute_tiled_next(m); + CUTE_TILED_CHECK((c == 'x') | (c == 'X'), "Expected 'x' or 'X' while parsing a hex number."); + } break; + } + + val = CUTE_TILED_STRTOULL(m->in, &end, 16); + CUTE_TILED_CHECK(m->in != end, "Invalid integer found during parse."); + m->in = end; + *out = (int)val; + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_hex_int(m, num) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_hex_int_internal(m, num)); \ + } while (0) + +static int cute_tiled_read_float_internal(cute_tiled_map_internal_t* m, float* out) +{ + char* end; + float val = (float)strtod(m->in, &end); + CUTE_TILED_CHECK(m->in != end, "Invalid integer found during parse."); + m->in = end; + *out = val; + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_float(m, num) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_float_internal(m, num)); \ + } while (0) + +static int cute_tiled_read_bool_internal(cute_tiled_map_internal_t* m, int* out) +{ + if ((cute_tiled_peak(m) == 't') | (cute_tiled_peak(m) == 'T')) + { + m->in += 4; + *out = 1; + } + + else if ((cute_tiled_peak(m) == 'f') | (cute_tiled_peak(m) == 'F')) + { + m->in += 5; + *out = 0; + } + + else goto cute_tiled_err; + + CUTE_TILED_CHECK(m->in <= m->end, "Attempted to read passed input buffer (is this a valid JSON file?)."); + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_bool(m, b) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_bool_internal(m, b)); \ + } while (0) + +int cute_tiled_read_csv_integers_internal(cute_tiled_map_internal_t* m, int* count_out, int** out) +{ + int count = 0; + int capacity = 1024; + int* integers = (int*)CUTE_TILED_ALLOC(capacity * sizeof(int), m->mem_ctx); + + char c; + do + { + int val; + cute_tiled_read_int(m, &val); + if (count == capacity) + { + int* new_integers; + capacity *= 2; + new_integers = (int*)CUTE_TILED_ALLOC(capacity * sizeof(int), m->mem_ctx); + CUTE_TILED_MEMCPY(new_integers, integers, sizeof(int) * count); + CUTE_TILED_FREE(integers, m->mem_ctx); + integers = new_integers; + } + integers[count++] = val; + c = cute_tiled_next(m); + } + while (c != ']'); + + *count_out = count; + *out = integers; + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_csv_integers(m, count_out, out) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_csv_integers_internal(m, count_out, out)); \ + } while (0) + +int cute_tiled_intern_string_internal(cute_tiled_map_internal_t* m, cute_tiled_string_t* out) +{ + STRPOOL_EMBEDDED_U64 id; + cute_tiled_read_string(m); + + // Store string id inside the memory of the pointer. This is important since + // the string pool can relocate strings while parsing the map file at any + // time. + + // Later there will be a second pass to patch all these string + // pointers by doing: *out = (const char*)strpool_embedded_cstr(&m->strpool, id); + + id = strpool_embedded_inject(&m->strpool, m->scratch, m->scratch_len); + // if (sizeof(const char*) < sizeof(STRPOOL_EMBEDDED_U64)) *(int*)0 = 0; // sanity check + out->hash_id = id; + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_intern_string(m, out) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_intern_string_internal(m, out)); \ + } while (0) + +int cute_tiled_read_vertex_array_internal(cute_tiled_map_internal_t* m, int* out_count, float** out_verts) +{ + int vert_count = 0; + int capacity = 32; + float *verts; + cute_tiled_expect(m, '['); + + verts = (float*)CUTE_TILED_ALLOC(sizeof(float) * capacity * 2, m->mem_ctx); + + while (cute_tiled_peak(m) != ']') + { + int swap; + float x = 0, y = 0; + cute_tiled_expect(m, '{'); + cute_tiled_expect(m, '"'); + + swap = cute_tiled_try(m, 'x') ? 0 : 1; + + cute_tiled_expect(m, '"'); + cute_tiled_expect(m, ':'); + cute_tiled_read_float(m, swap ? &y : &x); + cute_tiled_expect(m, ','); + cute_tiled_expect(m, '"'); + cute_tiled_expect(m, swap ? 'x' : 'y'); + cute_tiled_expect(m, '"'); + cute_tiled_expect(m, ':'); + cute_tiled_read_float(m, swap ? &x : &y); + cute_tiled_expect(m, '}'); + cute_tiled_try(m, ','); + + if (vert_count == capacity) + { + float* new_verts; + capacity *= 2; + new_verts = (float*)CUTE_TILED_ALLOC(sizeof(float) * capacity * 2, m->mem_ctx); + CUTE_TILED_MEMCPY(new_verts, verts, sizeof(float) * 2 * vert_count); + CUTE_TILED_FREE(verts, m->mem_ctx); + verts = new_verts; + } + + verts[vert_count * 2] = x; + verts[vert_count * 2 + 1] = y; + ++vert_count; + } + + cute_tiled_expect(m, ']'); + *out_count = vert_count; + *out_verts = verts; + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_vertex_array(m, out_count, out_verts) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_vertex_array_internal(m, out_count, out_verts)); \ + } while (0) + +int cute_tiled_skip_until_after_internal(cute_tiled_map_internal_t* m, char c) +{ + while (*m->in != c) { + cute_tiled_error_line += *m->in == '\n'; + m->in++; + } + cute_tiled_expect(m, c); + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_skip_until_after(m, c) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_skip_until_after_internal(m, c)); \ + } while (0) + +int cute_tiled_read_properties_internal(cute_tiled_map_internal_t* m, cute_tiled_property_t** out_properties, int* out_count) +{ + int count = 0; + int capacity = 32; + cute_tiled_property_t* props = (cute_tiled_property_t*)CUTE_TILED_ALLOC(capacity * sizeof(cute_tiled_property_t), m->mem_ctx); + + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_property_t prop; + char type_char; + char c; + cute_tiled_expect(m, '{'); + + prop.type = CUTE_TILED_PROPERTY_NONE; + + // Read in the property name. + cute_tiled_skip_until_after(m, ':'); + cute_tiled_intern_string(m, &prop.name); + + // Read in the property type. The value type is deduced while parsing, this is only used for float because the JSON format omits decimals on round floats. + cute_tiled_skip_until_after(m, ':'); + cute_tiled_expect(m, '"'); + type_char = cute_tiled_next(m); + + // Skip extraneous JSON information and go find the actual value data. + cute_tiled_skip_until_after(m, ':'); + + c = cute_tiled_peak(m); + + if (((c == 't') | (c == 'T')) | ((c == 'f') | (c == 'F'))) + { + cute_tiled_read_bool(m, &prop.data.boolean); + prop.type = CUTE_TILED_PROPERTY_BOOL; + } + + else if (c == '"') + { + char* s = m->in + 1; + int is_hex_color = 1; + + if (*s++ != '#') is_hex_color = 0; + else + { + while ((c = *s++) != '"') + { + switch (c) + { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': + case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + break; + + case '\\': + ++s; + break; + + default: + is_hex_color = 0; + break; + } + + if (!is_hex_color) break; + } + } + + if (is_hex_color) + { + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &prop.data.integer); + cute_tiled_expect(m, '"'); + prop.type = CUTE_TILED_PROPERTY_COLOR; + } + + else + { + cute_tiled_intern_string(m, &prop.data.string); + prop.type = CUTE_TILED_PROPERTY_STRING; + } + } + + else + { + char* s = m->in; + int is_float = 0; + while ((c = *s++) != ',') + { + if (c == '.') + { + is_float = 1; + break; + } + } + + if (is_float || type_char == 'f') + { + cute_tiled_read_float(m, &prop.data.floating); + prop.type = CUTE_TILED_PROPERTY_FLOAT; + } + + else + { + cute_tiled_read_int(m, &prop.data.integer); + prop.type = CUTE_TILED_PROPERTY_INT; + } + } + + if (count == capacity) + { + cute_tiled_property_t* new_props; + capacity *= 2; + new_props = (cute_tiled_property_t*)CUTE_TILED_ALLOC(capacity * sizeof(cute_tiled_property_t), m->mem_ctx); + CUTE_TILED_MEMCPY(new_props, props, sizeof(cute_tiled_property_t) * count); + CUTE_TILED_FREE(props, m->mem_ctx); + props = new_props; + } + props[count++] = prop; + + cute_tiled_expect(m, '}'); + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + cute_tiled_try(m, ','); + + *out_properties = count ? props : NULL; + if (!count) CUTE_TILED_FREE(props, m->mem_ctx); + *out_count = count; + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_properties(m, out_properties, out_count) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_properties_internal(m, out_properties, out_count)); \ + } while (0) + +cute_tiled_object_t* cute_tiled_read_object(cute_tiled_map_internal_t* m) +{ + cute_tiled_object_t* object = (cute_tiled_object_t*)cute_tiled_alloc(m, sizeof(cute_tiled_object_t)); + CUTE_TILED_MEMSET(object, 0, sizeof(cute_tiled_object_t)); + cute_tiled_expect(m, '{'); + + while (cute_tiled_peak(m) != '}') + { + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 14479365350473253539U: // ellipse + cute_tiled_read_bool(m, &object->ellipse); + break; + + case 14992147199312073281U: // gid + cute_tiled_read_int(m, &object->gid); + break; + + case 809651598226485190U: // height + cute_tiled_read_float(m, &object->height); + break; + + case 3133932603199444032U: // id + cute_tiled_read_int(m, &object->id); + break; + + case 12661511911333414066U: // name + cute_tiled_intern_string(m, &object->name); + break; + + case 15925463322410838979U: // point + cute_tiled_read_bool(m, &object->point); + break; + + case 11191351929714760271U: // polyline + cute_tiled_read_vertex_array(m, &object->vert_count, &object->vertices); + object->vert_type = 0; + break; + + case 6623316362411997547U: // polygon + cute_tiled_read_vertex_array(m, &object->vert_count, &object->vertices); + object->vert_type = 1; + break; + + case 8368542207491637236U: // properties + cute_tiled_read_properties(m, &object->properties, &object->property_count); + break; + + case 17386473859969670701U: // rotation + cute_tiled_read_float(m, &object->rotation); + break; + + case 7758770083360183834U: // text + CUTE_TILED_WARNING("Text field of Tiled objects is not yet supported. Ignoring field."); + while (cute_tiled_peak(m) != ',' && cute_tiled_peak(m) != '}') cute_tiled_next(m); + if (cute_tiled_peak(m) == '}') continue; + break; + + case 13509284784451838071U: // type + cute_tiled_intern_string(m, &object->type); + break; + + case 128234417907068947U: // visible + cute_tiled_read_bool(m, &object->visible); + break; + + case 7400839267610537869U: // width + cute_tiled_read_float(m, &object->width); + break; + + case 644252274336276709U: // x + cute_tiled_read_float(m, &object->x); + break; + + case 643295699219922364U: // y + cute_tiled_read_float(m, &object->y); + break; + + case 1485919047363370797U: // class + CUTE_TILED_WARNING("Class field of Tiled objects is not yet supported. Ignoring field."); + while (cute_tiled_peak(m) != ',' && cute_tiled_peak(m) != '}') cute_tiled_next(m); + if (cute_tiled_peak(m) == '}') continue; + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, '}'); + return object; + +cute_tiled_err: + return 0; +} + +cute_tiled_layer_t* cute_tiled_layers(cute_tiled_map_internal_t* m) +{ + cute_tiled_layer_t* layer = (cute_tiled_layer_t*)cute_tiled_alloc(m, sizeof(cute_tiled_layer_t)); + CUTE_TILED_MEMSET(layer, 0, sizeof(cute_tiled_layer_t)); + layer->parallaxx = 1.0f; + layer->parallaxy = 1.0f; + layer->repeatx = 0; + layer->repeaty = 0; + + cute_tiled_expect(m, '{'); + + while (cute_tiled_peak(m) != '}') + { + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 14868627273436340303U: // compression + CUTE_TILED_CHECK(0, "Compression is not yet supported. The expected tile format is CSV (uncompressed). Please see the docs if you are interested in compression."); + break; + + case 4430454992770877055U: // data + CUTE_TILED_CHECK(cute_tiled_peak(m) == '[', "The expected tile format is CSV (uncompressed). It looks like Base64 (uncompressed) was selected. Please see the docs if you are interested in compression."); + cute_tiled_expect(m, '['); + cute_tiled_read_csv_integers(m, &layer->data_count, &layer->data); + break; + + case 1888774307506158416U: // encoding + CUTE_TILED_CHECK(0, "Encoding is not yet supported. The expected tile format is CSV (uncompressed). Please see the docs if you are interested in compression."); + break; + + case 2841939415665718447U: // draworder + cute_tiled_intern_string(m, &layer->draworder); + break; + + case 809651598226485190U: // height + cute_tiled_read_int(m, &layer->height); + break; + + case 13522647194774232494U: // image + cute_tiled_intern_string(m, &layer->image); + break; + + case 4566956252693479661U: // layers + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_layer_t* child_layer = cute_tiled_layers(m); + CUTE_TILED_FAIL_IF(!child_layer); + child_layer->next = layer->layers; + layer->layers = child_layer; + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + break; + + case 12661511911333414066U: // name + cute_tiled_intern_string(m, &layer->name); + break; + + case 107323337999513585U: // objects + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_object_t* object = cute_tiled_read_object(m); + CUTE_TILED_FAIL_IF(!object); + object->next = layer->objects; + layer->objects = object; + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + break; + + case 5195853646368960386U: // offsetx + cute_tiled_read_float(m, &layer->offsetx); + break; + + case 5196810221485314731U: // offsety + cute_tiled_read_float(m, &layer->offsety); + break; + + case 11746902372727406098U: // opacity + cute_tiled_read_float(m, &layer->opacity); + break; + + case 8368542207491637236U: // properties + cute_tiled_read_properties(m, &layer->properties, &layer->property_count); + break; + + case 8489814081865549564U: // transparentcolor + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &layer->transparentcolor); + cute_tiled_expect(m, '"'); + break; + + case 1211175872446544425U: // tintcolor + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &layer->tintcolor); + cute_tiled_expect(m, '"'); + break; + + case 13509284784451838071U: // type + cute_tiled_intern_string(m, &layer->type); + break; + + case 128234417907068947U: // visible + cute_tiled_read_bool(m, &layer->visible); + break; + + case 7400839267610537869U: // width + cute_tiled_read_int(m, &layer->width); + break; + + case 644252274336276709U: // x + cute_tiled_read_int(m, &layer->x); + break; + + case 643295699219922364U: // y + cute_tiled_read_int(m, &layer->y); + break; + + case 18212633776084966362U: // parallaxx + cute_tiled_read_float(m, &layer->parallaxx); + break; + + case 18213590351201320707U: // parallaxy + cute_tiled_read_float(m, &layer->parallaxy); + break; + + case 222650047040294978U: // repeatx + cute_tiled_read_bool(m, &layer->repeatx); + break; + + case 223606622156649323U: // repeaty + cute_tiled_read_bool(m, &layer->repeaty); + break; + + case 3133932603199444032U: // id + cute_tiled_read_int(m, &layer->id); + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, '}'); + return layer; + +cute_tiled_err: + return 0; +} + +int cute_tiled_read_animation_frames_internal(cute_tiled_map_internal_t* m, cute_tiled_frame_t** out_frames, int* out_count) +{ + int count = 0; + int capacity = 32; + cute_tiled_frame_t* frames = (cute_tiled_frame_t*)CUTE_TILED_ALLOC(capacity * sizeof(cute_tiled_frame_t), m->mem_ctx); + + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_frame_t frame; + cute_tiled_expect(m, '{'); + + // Read in the duration and tileid. + cute_tiled_skip_until_after(m, ':'); + cute_tiled_read_int(m, &frame.duration); + cute_tiled_expect(m, ','); + cute_tiled_skip_until_after(m, ':'); + cute_tiled_read_int(m, &frame.tileid); + + if (count == capacity) + { + cute_tiled_frame_t* new_frames; + capacity *= 2; + new_frames = (cute_tiled_frame_t*)CUTE_TILED_ALLOC(capacity * sizeof(cute_tiled_frame_t), m->mem_ctx); + CUTE_TILED_MEMCPY(new_frames, frames, sizeof(cute_tiled_frame_t) * count); + CUTE_TILED_FREE(frames, m->mem_ctx); + frames = new_frames; + } + frames[count++] = frame; + + cute_tiled_expect(m, '}'); + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + cute_tiled_try(m, ','); + + *out_frames = frames; + *out_count = count; + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_animation_frames(m, out_frames, out_count) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_animation_frames_internal(m, out_frames, out_count)); \ + } while (0) + +cute_tiled_tile_descriptor_t* cute_tiled_read_tile_descriptor(cute_tiled_map_internal_t* m) +{ + cute_tiled_tile_descriptor_t* tile_descriptor = (cute_tiled_tile_descriptor_t*)cute_tiled_alloc(m, sizeof(cute_tiled_tile_descriptor_t)); + CUTE_TILED_MEMSET(tile_descriptor, 0, sizeof(cute_tiled_tile_descriptor_t)); + + cute_tiled_expect(m, '{'); + while (cute_tiled_peak(m) != '}') + { + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 3133932603199444032U: // id + cute_tiled_read_int(m, &tile_descriptor->tile_index); + break; + + case 13509284784451838071U: // type + cute_tiled_intern_string(m, &tile_descriptor->type); + break; + + case 13522647194774232494U: // image + cute_tiled_intern_string(m, &tile_descriptor->image); + break; + + case 7796197983149768626U: // imagewidth + cute_tiled_read_int(m, &tile_descriptor->imagewidth); + break; + + case 2114495263010514843U: // imageheight + cute_tiled_read_int(m, &tile_descriptor->imageheight); + break; + + case 8368542207491637236U: // properties + cute_tiled_read_properties(m, &tile_descriptor->properties, &tile_descriptor->property_count); + break; + + case 6659907350341014391U: // objectgroup + { + cute_tiled_layer_t* layer = cute_tiled_layers(m); + CUTE_TILED_FAIL_IF(!layer); + layer->next = tile_descriptor->objectgroup; + tile_descriptor->objectgroup = layer; + } break; + + case 6875414612738028948: // probability + cute_tiled_read_float(m, &tile_descriptor->probability); + break; + + case 2784044778313316778U: // terrain: used by tiled editor only + cute_tiled_skip_array(m); + break; + + case 3115399308714904519U: // animation + cute_tiled_read_animation_frames(m, &tile_descriptor->animation, &tile_descriptor->frame_count); + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, '}'); + return tile_descriptor; + +cute_tiled_err: + return 0; +} + +int cute_tiled_read_point_internal(cute_tiled_map_internal_t* m, int* point_x, int* point_y) +{ + *point_x = 0; + *point_y = 0; + + cute_tiled_expect(m, '{'); + while (cute_tiled_peak(m) != '}') + { + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 644252274336276709U: // x + cute_tiled_read_int(m, point_x); + break; + + case 643295699219922364U: // y + cute_tiled_read_int(m, point_y); + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, '}'); + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_read_point(m, x, y) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_read_point_internal(m, x, y)); \ + } while (0) + +static CUTE_TILED_INLINE int cute_tiled_skip_curly_braces_internal(cute_tiled_map_internal_t* m) +{ + int count = 1; + cute_tiled_expect(m, '{'); + while (count) + { + char c = cute_tiled_next(m); + if (c == '}') --count; + else if (c == '{') ++count; + } + + return 0; + +cute_tiled_err: + return 1; +} + +cute_tiled_tileset_t* cute_tiled_tileset(cute_tiled_map_internal_t* m) +{ + cute_tiled_tileset_t* tileset = (cute_tiled_tileset_t*)cute_tiled_alloc(m, sizeof(cute_tiled_tileset_t)); + CUTE_TILED_MEMSET(tileset, 0, sizeof(cute_tiled_tileset_t)); + cute_tiled_expect(m, '{'); + + while (cute_tiled_peak(m) != '}') + { + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 17465100621023921744U: // backgroundcolor + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &tileset->backgroundcolor); + cute_tiled_expect(m, '"'); + break; + + case 12570673734542705940U: // columns + cute_tiled_read_int(m, &tileset->columns); + break; + + case 13648382824248632287U: // editorsettings + cute_tiled_skip_object(m); + break; + + case 13956389100366699181U: // firstgid + cute_tiled_read_int(m, &tileset->firstgid); + break; + + case 16920059161811221315U: // grid: unsupported + cute_tiled_skip_object(m); + break; + + case 13522647194774232494U: // image + cute_tiled_intern_string(m, &tileset->image); + break; + + case 7796197983149768626U: // imagewidth + cute_tiled_read_int(m, &tileset->imagewidth); + break; + + case 2114495263010514843U: // imageheight + cute_tiled_read_int(m, &tileset->imageheight); + break; + + case 4864566659847942049U: // margin + cute_tiled_read_int(m, &tileset->margin); + break; + + case 12661511911333414066U: // name + cute_tiled_intern_string(m, &tileset->name); + break; + + case 1007832939408977147U: // tiledversion + cute_tiled_intern_string(m, &tileset->tiledversion); + break; + + case 8196820454517111669U: // version + if (*m->in == '"') + m->in++; + cute_tiled_read_float(m, &tileset->version); + if (*m->in == '"') + m->in++; + break; + + case 8368542207491637236U: // properties + cute_tiled_read_properties(m, &tileset->properties, &tileset->property_count); + break; + + case 6491372721122724890U: // spacing + cute_tiled_read_int(m, &tileset->spacing); + break; + + case 4065097716972592720U: // tilecount + cute_tiled_read_int(m, &tileset->tilecount); + break; + + case 13337683360624280154U: // tileheight + cute_tiled_read_int(m, &tileset->tileheight); + break; + + case 2769630600247906626U: // tileoffset + cute_tiled_read_point(m, &tileset->tileoffset_x, &tileset->tileoffset_y); + break; + + case 7277156227374254384U: // tileproperties + CUTE_TILED_WARNING("`tileproperties` is deprecated. Attempting to skip."); + CUTE_TILED_FAIL_IF(cute_tiled_skip_curly_braces_internal(m)); + break; + + case 15569462518706435895U: // tilepropertytypes + CUTE_TILED_WARNING("`tilepropertytypes` is deprecated. Attempting to skip."); + CUTE_TILED_FAIL_IF(cute_tiled_skip_curly_braces_internal(m)); + break; + + case 6504415465426505561U: // tilewidth + cute_tiled_read_int(m, &tileset->tilewidth); + break; + + case 8489814081865549564U: // transparentcolor + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &tileset->transparentcolor); + cute_tiled_expect(m, '"'); + break; + + case 13509284784451838071U: // type + cute_tiled_intern_string(m, &tileset->type); + break; + + case 8053780534892277672U: // source + cute_tiled_intern_string(m, &tileset->source); +#ifndef CUTE_TILED_NO_EXTERNAL_TILESET_WARNING + CUTE_TILED_WARNING("You might have forgotten to embed your tileset -- Most fields of `cute_tiled_tileset_t` will be zero'd out (unset)."); +#endif /* CUTE_TILED_NO_EXTERNAL_TILESET_WARNING */ + break; + + case 1819203229U: // objectalignment + cute_tiled_intern_string(m, &tileset->objectalignment); + break; + + case 104417158474046698U: // tiles + { + cute_tiled_expect(m, '['); + while (cute_tiled_peak(m) != ']') + { + cute_tiled_tile_descriptor_t* tile_descriptor = cute_tiled_read_tile_descriptor(m); + CUTE_TILED_FAIL_IF(!tile_descriptor); + tile_descriptor->next = tileset->tiles; + tileset->tiles = tile_descriptor; + cute_tiled_try(m, ','); + } + cute_tiled_expect(m, ']'); + } break; + + case 14766449174202642533U: // terrains: used by tiled editor only + cute_tiled_skip_array(m); + break; + + case 6029584663444593209U: // wangsets: used by tiled editor only + cute_tiled_skip_array(m); + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, '}'); + return tileset; + +cute_tiled_err: + return 0; +} + +static int cute_tiled_dispatch_map_internal(cute_tiled_map_internal_t* m) +{ + CUTE_TILED_U64 h; + cute_tiled_read_string(m); + cute_tiled_expect(m, ':'); + h = cute_tiled_FNV1a(m->scratch, m->scratch_len + 1); + + switch (h) + { + case 17465100621023921744U: // backgroundcolor + cute_tiled_expect(m, '"'); + cute_tiled_read_hex_int(m, &m->map.backgroundcolor); + cute_tiled_expect(m, '"'); + break; + + case 5549108793316760247U: // compressionlevel + { + int compressionlevel; + cute_tiled_read_int(m, &compressionlevel); + CUTE_TILED_CHECK(compressionlevel == -1 || compressionlevel == 0, "Compression is not yet supported."); + } break; + + case 13648382824248632287U: // editorsettings + cute_tiled_skip_object(m); + break; + + case 809651598226485190U: // height + cute_tiled_read_int(m, &m->map.height); + break; + + case 16529928297377797591U: // infinite + cute_tiled_read_bool(m, &m->map.infinite); + break; + + case 4566956252693479661U: // layers + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_layer_t* layer = cute_tiled_layers(m); + CUTE_TILED_FAIL_IF(!layer); + layer->next = m->map.layers; + m->map.layers = layer; + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + break; + + case 11291153769551921430U: // nextobjectid + cute_tiled_read_int(m, &m->map.nextobjectid); + break; + + case 563935667693078739U: // orientation + cute_tiled_intern_string(m, &m->map.orientation); + break; + + case 8368542207491637236U: // properties + cute_tiled_read_properties(m, &m->map.properties, &m->map.property_count); + break; + + case 16693886730115578029U: // renderorder + cute_tiled_intern_string(m, &m->map.renderorder); + break; + + case 1007832939408977147U: // tiledversion + cute_tiled_intern_string(m, &m->map.tiledversion); + break; + + case 13337683360624280154U: // tileheight + cute_tiled_read_int(m, &m->map.tileheight); + break; + + case 8310322674355535532U: // tilesets + { + cute_tiled_expect(m, '['); + + while (cute_tiled_peak(m) != ']') + { + cute_tiled_tileset_t* tileset = cute_tiled_tileset(m); + CUTE_TILED_FAIL_IF(!tileset); + tileset->next = m->map.tilesets; + m->map.tilesets = tileset; + cute_tiled_try(m, ','); + } + + cute_tiled_expect(m, ']'); + } break; + + case 6504415465426505561U: // tilewidth + cute_tiled_read_int(m, &m->map.tilewidth); + break; + + case 13509284784451838071U: // type + cute_tiled_intern_string(m, &m->map.type); + break; + + case 8196820454517111669U: // version + if (*m->in == '"') + m->in++; + cute_tiled_read_float(m, &m->map.version); + if (*m->in == '"') + m->in++; + break; + + case 7400839267610537869U: // width + cute_tiled_read_int(m, &m->map.width); + break; + + case 2498445529143042872U: // nextlayerid + cute_tiled_read_int(m, &m->map.nextlayerid); + break; + + default: + CUTE_TILED_CHECK(0, "Unknown identifier found."); + } + + return 1; + +cute_tiled_err: + return 0; +} + +#define cute_tiled_dispatch_map(m) \ + do { \ + CUTE_TILED_FAIL_IF(!cute_tiled_dispatch_map_internal(m)); \ + } while (0) + +static CUTE_TILED_INLINE void cute_tiled_deintern_string(cute_tiled_map_internal_t* m, cute_tiled_string_t* s) +{ + s->ptr = strpool_embedded_cstr(&m->strpool, s->hash_id); +} + +static void cute_tiled_deintern_properties(cute_tiled_map_internal_t* m, cute_tiled_property_t* properties, int property_count) +{ + int i; + for (i = 0; i < property_count; ++i) + { + cute_tiled_property_t* p = properties + i; + cute_tiled_deintern_string(m, &p->name); + if (p->type == CUTE_TILED_PROPERTY_STRING) cute_tiled_deintern_string(m, &p->data.string); + } +} + +static void cute_tiled_deintern_layer(cute_tiled_map_internal_t* m, cute_tiled_layer_t* layer) +{ + while (layer) + { + cute_tiled_object_t* object; + cute_tiled_deintern_string(m, &layer->draworder); + cute_tiled_deintern_string(m, &layer->name); + cute_tiled_deintern_string(m, &layer->type); + cute_tiled_deintern_string(m, &layer->image); + cute_tiled_deintern_properties(m, layer->properties, layer->property_count); + + object = layer->objects; + while (object) + { + cute_tiled_deintern_string(m, &object->name); + cute_tiled_deintern_string(m, &object->type); + cute_tiled_deintern_properties(m, object->properties, object->property_count); + object = object->next; + } + + cute_tiled_deintern_layer(m, layer->layers); + + layer = layer->next; + } +} + +static void cute_tiled_patch_tileset_strings(cute_tiled_map_internal_t* m, cute_tiled_tileset_t* tileset) +{ + cute_tiled_tile_descriptor_t* tile_descriptor; + cute_tiled_deintern_string(m, &tileset->image); + cute_tiled_deintern_string(m, &tileset->name); + cute_tiled_deintern_string(m, &tileset->type); + cute_tiled_deintern_string(m, &tileset->source); + cute_tiled_deintern_string(m, &tileset->tiledversion); + cute_tiled_deintern_string(m, &tileset->objectalignment); + cute_tiled_deintern_properties(m, tileset->properties, tileset->property_count); + tile_descriptor = tileset->tiles; + while (tile_descriptor) + { + cute_tiled_deintern_string(m, &tile_descriptor->image); + cute_tiled_deintern_string(m, &tile_descriptor->type); + cute_tiled_deintern_properties(m, tile_descriptor->properties, tile_descriptor->property_count); + tile_descriptor = tile_descriptor->next; + } +} + +static void cute_tiled_patch_interned_strings(cute_tiled_map_internal_t* m) +{ + cute_tiled_tileset_t* tileset; + cute_tiled_layer_t* layer; + cute_tiled_deintern_string(m, &m->map.orientation); + cute_tiled_deintern_string(m, &m->map.renderorder); + cute_tiled_deintern_string(m, &m->map.tiledversion); + cute_tiled_deintern_string(m, &m->map.type); + cute_tiled_deintern_properties(m, m->map.properties, m->map.property_count); + + tileset = m->map.tilesets; + while (tileset) + { + cute_tiled_patch_tileset_strings(m, tileset); + tileset = tileset->next; + } + + layer = m->map.layers; + cute_tiled_deintern_layer(m, layer); +} + +static void cute_tiled_free_objects(cute_tiled_object_t* objects, void* mem_ctx) +{ + cute_tiled_object_t* object = objects; + CUTE_TILED_UNUSED(mem_ctx); + while (object) + { + if (object->properties) CUTE_TILED_FREE(object->properties, mem_ctx); + if (object->vertices) CUTE_TILED_FREE(object->vertices, mem_ctx); + object = object->next; + } +} + +static void cute_tiled_free_layers(cute_tiled_layer_t* layers, void* mem_ctx) +{ + cute_tiled_layer_t* layer = layers; + CUTE_TILED_UNUSED(mem_ctx); + while (layer) + { + if (layer->data) CUTE_TILED_FREE(layer->data, mem_ctx); + if (layer->properties) CUTE_TILED_FREE(layer->properties, mem_ctx); + cute_tiled_free_layers(layer->layers, mem_ctx); + cute_tiled_free_objects(layer->objects, mem_ctx); + layer = layer->next; + } +} + +static void cute_tiled_free_map_internal(cute_tiled_map_internal_t* m) +{ + cute_tiled_tileset_t* tileset; + cute_tiled_page_t* page; + strpool_embedded_term(&m->strpool); + + cute_tiled_free_layers(m->map.layers, m->mem_ctx); + if (m->map.properties) CUTE_TILED_FREE(m->map.properties, m->mem_ctx); + + tileset = m->map.tilesets; + while (tileset) + { + cute_tiled_tile_descriptor_t* desc; + if (tileset->properties) CUTE_TILED_FREE(tileset->properties, m->mem_ctx); + desc = tileset->tiles; + while (desc) + { + if (desc->properties) CUTE_TILED_FREE(desc->properties, m->mem_ctx); + if (desc->animation) CUTE_TILED_FREE(desc->animation, mem_ctx); + cute_tiled_free_layers(desc->objectgroup, m->mem_ctx); + desc = desc->next; + } + tileset = tileset->next; + } + + // cute_tiled_read_csv_integers + // cute_tiled_read_vertex_array + // cute_tiled_read_properties + + page = m->pages; + while (page) + { + cute_tiled_page_t* next = page->next; + CUTE_TILED_FREE(page, m->mem_ctx); + page = next; + } + + CUTE_TILED_FREE(m, m->mem_ctx); +} + +#define CUTE_TILED_REVERSE_LIST(T, root) \ + do { \ + T* n = 0; \ + while (root) { \ + T* next = root->next; \ + root->next = n; \ + n = root; \ + root = next; \ + } \ + root = n; \ + } while (0) + +void cute_tiled_reverse_layers(cute_tiled_map_t* map) +{ + CUTE_TILED_REVERSE_LIST(cute_tiled_layer_t, map->layers); +} + +static cute_tiled_map_internal_t* cute_tiled_map_internal_alloc_internal(void* memory, int size_in_bytes, void* mem_ctx) +{ + strpool_embedded_config_t config; + cute_tiled_map_internal_t* m = (cute_tiled_map_internal_t*)CUTE_TILED_ALLOC(sizeof(cute_tiled_map_internal_t), mem_ctx); + CUTE_TILED_MEMSET(m, 0, sizeof(cute_tiled_map_internal_t)); + m->in = (char*)memory; + m->end = m->in + size_in_bytes; + m->mem_ctx = mem_ctx; + m->page_size = 1024 * 10; + m->bytes_left_on_page = m->page_size; + m->pages = (cute_tiled_page_t*)CUTE_TILED_ALLOC(sizeof(cute_tiled_page_t) + m->page_size, mem_ctx); + m->pages->next = 0; + m->pages->data = m->pages + 1; + config = strpool_embedded_default_config; + config.memctx = mem_ctx; + strpool_embedded_init(&m->strpool, &config); + return m; +} + +cute_tiled_map_t* cute_tiled_load_map_from_memory(const void* memory, int size_in_bytes, void* mem_ctx) +{ + cute_tiled_map_internal_t* m; + cute_tiled_layer_t* layer; + cute_tiled_tileset_t* tileset; + cute_tiled_error_line = 1; + + if (!memory) { + // Make sure your map file was correctly found and loaded. + // If you called `cute_tiled_load_map_from_file` make sure your path was correct, and check + // your printf warnings. + CUTE_TILED_CRASH(); + } + + m = cute_tiled_map_internal_alloc_internal((void*)memory, size_in_bytes, mem_ctx); + layer = m->map.layers; + tileset = m->map.tilesets; + cute_tiled_expect(m, '{'); + while (cute_tiled_peak(m) != '}') + { + cute_tiled_dispatch_map(m); + cute_tiled_try(m, ','); + } + cute_tiled_expect(m, '}'); + + // finalize output by patching strings and reversing singly linked lists + cute_tiled_patch_interned_strings(m); + CUTE_TILED_REVERSE_LIST(cute_tiled_layer_t, m->map.layers); + CUTE_TILED_REVERSE_LIST(cute_tiled_tileset_t, m->map.tilesets); + while (layer) + { + CUTE_TILED_REVERSE_LIST(cute_tiled_object_t, layer->objects); + layer = layer->next; + } + while (tileset) + { + CUTE_TILED_REVERSE_LIST(cute_tiled_tile_descriptor_t, tileset->tiles); + tileset = tileset->next; + } + + return &m->map; + +cute_tiled_err: + cute_tiled_free_map_internal(m); + CUTE_TILED_WARNING(cute_tiled_error_reason); + return 0; +} + +void cute_tiled_free_map(cute_tiled_map_t* map) +{ + cute_tiled_map_internal_t* m = (cute_tiled_map_internal_t*)(((char*)map) - (size_t)(&((cute_tiled_map_internal_t*)0)->map)); + cute_tiled_free_map_internal(m); +} + +cute_tiled_tileset_t* cute_tiled_load_external_tileset(const char* path, void* mem_ctx) +{ + int size; + void* file; + cute_tiled_tileset_t* tileset; + cute_tiled_error_file = path; + + file = cute_tiled_read_file_to_memory_and_null_terminate(path, &size, mem_ctx); + if (!file) CUTE_TILED_WARNING("Unable to find external tileset file."); + tileset = cute_tiled_load_external_tileset_from_memory(file, size, mem_ctx); + CUTE_TILED_FREE(file, mem_ctx); + + cute_tiled_error_file = NULL; + + return tileset; +} + +cute_tiled_tileset_t* cute_tiled_load_external_tileset_from_memory(const void* memory, int size_in_bytes, void* mem_ctx) +{ + cute_tiled_map_internal_t* m = cute_tiled_map_internal_alloc_internal((void*)memory, size_in_bytes, mem_ctx); + cute_tiled_tileset_t* tileset = cute_tiled_tileset(m); + cute_tiled_patch_tileset_strings(m, tileset); + CUTE_TILED_REVERSE_LIST(cute_tiled_tile_descriptor_t, tileset->tiles); + tileset->_internal = m; + return tileset; +} + +void cute_tiled_free_external_tileset(cute_tiled_tileset_t* tileset) +{ + cute_tiled_map_internal_t* m = (cute_tiled_map_internal_t*)tileset->_internal; + cute_tiled_free_map_internal(m); +} + +#endif // CUTE_TILED_IMPLEMENTATION_ONCE +#endif // CUTE_TILED_IMPLEMENTATION + +/* + ------------------------------------------------------------------------------ + This software is available under 2 licenses - you may choose the one you like. + ------------------------------------------------------------------------------ + ALTERNATIVE A - zlib license + Copyright (c) 2017 Randy Gaul http://www.randygaul.net + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from + the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + ------------------------------------------------------------------------------ + ALTERNATIVE B - Public Domain (www.unlicense.org) + This is free and unencumbered software released into the public domain. + Anyone is free to copy, modify, publish, use, compile, sell, or distribute this + software, either in source code form or as a compiled binary, for any purpose, + commercial or non-commercial, and by any means. + In jurisdictions that recognize copyright laws, the author or authors of this + software dedicate any and all copyright interest in the software to the public + domain. We make this dedication for the benefit of the public at large and to + the detriment of our heirs and successors. We intend this dedication to be an + overt act of relinquishment in perpetuity of all present and future rights to + this software under copyright law. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ------------------------------------------------------------------------------ +*/ diff --git a/main.c b/main.c deleted file mode 100644 index 523f7c8..0000000 --- a/main.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - - -int main(void) { - - const int screenWidth = 1280; - const int screenHeight = 720; - - InitWindow(screenWidth, screenHeight, "PixelDefense"); - SetTargetFPS(60); - - while (!WindowShouldClose()) { - if (IsKeyPressed(KEY_ESCAPE)) { - break; - } - - BeginDrawing(); - ClearBackground(RAYWHITE); - - EndDrawing(); - - } - - CloseWindow(); - - return 0; -} diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..f48fba6 --- /dev/null +++ b/src/common.h @@ -0,0 +1,7 @@ +#ifndef PIXELDEFENSE_COMMON_H +#define PIXELDEFENSE_COMMON_H + +#include +#include + +#endif //PIXELDEFENSE_COMMON_H diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ee8128e --- /dev/null +++ b/src/main.c @@ -0,0 +1,54 @@ +#include +#include +#include + +int main(void) { + + const int screenWidth = 1280; + const int screenHeight = 720; + + InitWindow(screenWidth, screenHeight, "PixelDefense"); + SetTargetFPS(60); + + cute_tiled_map_t* map = cute_tiled_load_map_from_file("assets/maps/test.tmj", NULL); + + + // get map width and height + int w = map->width; + int h = map->height; + printf("%d:%d\n", w, h); + + // loop over the map's layers + cute_tiled_layer_t* layer = map->layers; + while (layer) + { + int* data = layer->data; + int data_count = layer->data_count; + + // do something with the tile data + //UserFunction_HandleTiles(data, data_count); + printf("%s (%d): %d\n", layer->name.ptr, layer->id, layer->data_count); + + layer = layer->next; + } + + cute_tiled_free_map(map); + + + + while (!WindowShouldClose()) { + if (IsKeyPressed(KEY_ESCAPE)) { + break; + } + + BeginDrawing(); + ClearBackground(RAYWHITE); + + EndDrawing(); + + } + + CloseWindow(); + + return 0; +}