From 086dd28512c9da289bd8c8bc21c95b10c169e8c8 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Fri, 10 Feb 2023 19:54:46 -0800 Subject: [PATCH] Tiled map importing with metadesk, quad view cull --- assets.mdesk | 16 +- assets/level0.json | 125 ++++++++++ assets/mystery_tile.png | Bin 0 -> 652 bytes assets/testsmalllevel.json | 39 +++ codegen.c | 101 +++++++- main.c | 149 ++++++++++-- run_codegen.bat | 2 + thirdparty/jsmn.h | 471 +++++++++++++++++++++++++++++++++++++ 8 files changed, 871 insertions(+), 32 deletions(-) create mode 100644 assets/level0.json create mode 100644 assets/mystery_tile.png create mode 100644 assets/testsmalllevel.json create mode 100644 thirdparty/jsmn.h diff --git a/assets.mdesk b/assets.mdesk index d6b6ad9..694a1c5 100644 --- a/assets.mdesk +++ b/assets.mdesk @@ -1,12 +1,20 @@ @image merchant: { - filepath = "copyrighted/merchant.png", + filepath: "copyrighted/merchant.png", } -@image bg_tilesheet: +@image animated_terrain: { - filepath = "copyrighted/wall-1 - 3 tiles tall.png", + filepath: "copyrighted/animated_terrain.png", } @image white_square: { - filepath = "white square.png", + filepath: "white square.png", +} +@image mystery_tile: +{ + filepath: "mystery_tile.png", +} +@level level0: +{ + filepath: "testsmalllevel.json", } diff --git a/assets/level0.json b/assets/level0.json new file mode 100644 index 0000000..da132cb --- /dev/null +++ b/assets/level0.json @@ -0,0 +1,125 @@ +{ "compressionlevel":-1, + "height":60, + "infinite":false, + "layers":[ + { + "data":[53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 158, 213, 53, 53, 209, 213, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 158, 159, 159, 159, 159, 159, 160, 53, 53, 53, 53, 261, 265, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 261, 316, 367, 367, 367, 367, 368, 53, 53, 53, 53, 261, 265, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 313, 368, 53, 53, 313, 317, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 158, 160, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 261, 212, 213, 53, 53, 53, 53, 53, 53, 209, 159, 160, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 261, 265, 53, 53, 53, 53, 53, 53, 366, 314, 212, 160, 53, 53, 53, 53, 158, 210, 316, 368, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 313, 317, 53, 53, 53, 53, 53, 53, 53, 366, 314, 212, 159, 159, 159, 159, 210, 316, 317, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 313, 367, 367, 367, 367, 367, 367, 368, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 209, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 160, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 212, 213, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 158, 210, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 366, 314, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 366, 314, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 261, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 265, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 313, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 367, 317, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53], + "height":60, + "id":4, + "name":"Animated Tiles1", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":60, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":5, + "name":"objects", + "objects":[ + { + "class":"", + "height":32, + "id":1, + "name":"spawn", + "rotation":0, + "visible":true, + "width":32, + "x":960, + "y":928 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":6, + "nextobjectid":2, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.9.2", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Animated Terrains-16 frames.tsx" + }, + { + "firstgid":2341, + "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Tileset-wall-1.tsx" + }, + { + "firstgid":2469, + "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Ancient Ruins-Tileset-wall-2.tsx" + }, + { + "firstgid":2597, + "source":"..\/EPIC RPG World Pack - Ancient Ruins V 1.7\/EPIC RPG World Pack - Ancient Ruins V 1.7\/TiledMap Editor\/Terrain - Ancient Ruins.tsx" + }], + "tilewidth":32, + "type":"map", + "version":"1.9", + "width":60 +} \ No newline at end of file diff --git a/assets/mystery_tile.png b/assets/mystery_tile.png new file mode 100644 index 0000000000000000000000000000000000000000..837e4658d75f81cc1c11907adc5016eedb04fa6a GIT binary patch literal 652 zcmV;70(1R|P)EX>4Tx04R}tkv&MmKpe$iQ^gM|4ptCx$WWauh)NM$q>4qbP}&NuI+$Gg1x*@~ z6cs^>M{K$3L zaarNK#aS&^S@WL!g@K&5lHxke5yY^BI1&&cqlyyBun?wIBgI6L_M;yDVaJ~!mrSk_ z7&#VDfeOj-ga5(rZq5AEq?;6o0-Z0m{V@u3?E;OOZGRuzcH;!_KLb}<%U`JjGoPf_ zT3YA`=-&n|u3MVC2VCv|Lr=P7NRH&EDdh9O`x$*x78tk%de_|En)^6?0MgXe@(pls z2#ggdd)?#R-R-^od#2gn5Bs8W-Zen4m;e9(32;bRa{vGf6951U69E94oEQKA00(qQ zO+^Ri0t*8i8gmCvg#Z8mnn^@KR9M69mOBo>AP7K}n7FkE@cu{h0FF-WWa3aG1dybG z(I7ne8n0ZQU?>ob#R1F#67@ps_4EABMGs(hx^gshs339IhOJkUQK~*j8XFNogr-q)i%UHfX3_pJ%}*HI--ZEfh3jd83;4DL;*OpBm$gH@GfuwbAT6^UN~nLes{nC0000 +#include -#define assert(cond, explanation) { if(!cond) { printf("Codegen assertion %s failed: %.*s\n", #cond, MD_S8VArg(explanation)); exit(1); } } +#define assert(cond, explanation) { if(!cond) { printf("Codegen assertion line %d %s failed: %.*s\n", __LINE__, #cond, MD_S8VArg(explanation)); __debugbreak(); exit(1); } } #pragma warning(disable : 4996) // nonsense about fopen being insecure @@ -11,6 +12,8 @@ #include "md.c" #pragma warning(pop) +#include "jsmn.h" + MD_String8 OUTPUT_FOLDER = MD_S8LitComp("gen"); // no trailing slash MD_String8 ASSETS_FOLDER = MD_S8LitComp("assets"); @@ -37,8 +40,51 @@ void dump(MD_ParseResult parse) { } } +MD_Arena *cg_arena = NULL; + +static bool jsoneq(const char *json, jsmntok_t *tok, const char *s) { + if (tok->type == JSMN_STRING && (int)strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) { + return true; + } + return false; +} + +static int getint(const char *json, jsmntok_t *tok) { + assert(tok->type == JSMN_PRIMITIVE, MD_S8Lit("not primitive")); + char tmp[256] = {0}; + strncpy(tmp, json + tok->start, tok->end - tok->start); + return atoi(tmp); +} + +// will look over into random memory if can't find... +static int getnextofname(const char *json, jsmntok_t *starting_at, const char *name) { + jsmntok_t *cur = starting_at; + // max lookahead, bad + for(int i = 0; i < 1024*10; i++) { + if(jsoneq(json, cur, name)) { + return getint(json, cur+1); + } + cur++; + } + assert(false, MD_S8Fmt(cg_arena, "Could not find integer for %s\n", name)); + return -1; +} + + +MD_String8 ChildValue(MD_Node *n, MD_String8 name) { + MD_Node *child_with_value = MD_ChildFromString(n, name, 0); + assert(child_with_value, MD_S8Fmt(cg_arena, "Could not find child named '%.*s' of node '%.*s'", MD_S8VArg(name), MD_S8VArg(n->string))); + assert(child_with_value->first_child, MD_S8Lit("Must have child")); + return child_with_value->first_child->string; +} + +MD_String8 asset_file_path(MD_String8 filename) { + return MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(ASSETS_FOLDER), MD_S8VArg(filename)); +} + int main(int argc, char **argv) { - MD_Arena *cg_arena = MD_ArenaAlloc(); + cg_arena = MD_ArenaAlloc(); assert(cg_arena, MD_S8Lit("Memory")); // I hope to God MD_String8's are null terminated... @@ -52,17 +98,13 @@ int main(int argc, char **argv) { MD_String8List declarations_list = {0}; MD_String8List load_list = {0}; + MD_String8List level_decl_list = {0}; for(MD_EachNode(node, parse.node->first_child)) { if(MD_S8Match(node->first_tag->string, MD_S8Lit("image"), 0)) { MD_String8 variable_name = MD_S8Fmt(cg_arena, "image_%.*s", MD_S8VArg(node->string)); log("New image variable %.*s\n", MD_S8VArg(variable_name)); - MD_String8 filepath = {0}; - for(MD_EachNode(child, node->first_child)) { - if(MD_S8Match(child->string, MD_S8Lit("filepath"), 0)) { - filepath = child->next->next->string; // segfault here but to check for it is ugly - } - } - filepath = MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(ASSETS_FOLDER), MD_S8VArg(filepath)); + MD_String8 filepath = ChildValue(node, MD_S8Lit("filepath")); + filepath = asset_file_path(filepath); assert(filepath.str != 0, MD_S8Fmt(cg_arena, "No filepath specified for image '%.*s'", MD_S8VArg(node->string))); FILE *asset_file = fopen(filepath.str, "r"); assert(asset_file, MD_S8Fmt(cg_arena, "Could not open filepath %.*s for asset '%.*s'", MD_S8VArg(filepath), MD_S8VArg(node->string))); @@ -71,6 +113,47 @@ int main(int argc, char **argv) { MD_S8ListPush(cg_arena, &declarations_list, MD_S8Fmt(cg_arena, "sg_image %.*s = {0};\n", MD_S8VArg(variable_name))); MD_S8ListPush(cg_arena, &load_list, MD_S8Fmt(cg_arena, "%.*s = load_image(\"%.*s\");\n", MD_S8VArg(variable_name), MD_S8VArg(filepath))); } + if(MD_S8Match(node->first_tag->string, MD_S8Lit("level"), 0)) { + MD_String8 variable_name = MD_S8Fmt(cg_arena, "level_%.*s", MD_S8VArg(node->string)); + MD_String8 filepath = asset_file_path(ChildValue(node, MD_S8Lit("filepath"))); + MD_String8 level_file_contents = MD_LoadEntireFile(cg_arena, filepath); + assert(level_file_contents.str != 0 && level_file_contents.size > 1, MD_S8Lit("Failed to load level file")); + jsmn_parser p; + jsmntok_t t[1024*5]; + jsmn_init(&p); + const char *json_string = level_file_contents.str; + int r = jsmn_parse(&p, json_string, level_file_contents.size, t, sizeof(t) / sizeof(t[0])); + assert(r > 0, MD_S8Lit("Failed to parse json")); + + int layer_obj_token = 0; + fprintf(output, "Level %.*s = {\n.tiles = {\n", MD_S8VArg(variable_name)); + // this code is absolutely disgusting but I think it's fine because this is meta code, I guess time will tell if this needs to be cleaner or not... sorry future self! + for(int i = 0; i < r; i++) { + if(jsoneq(json_string, &t[i], "data")) { + jsmntok_t *arr = &t[i+1]; + assert(arr->type == JSMN_ARRAY, MD_S8Lit("Expected array after data")); + int width = getnextofname(json_string, &t[i+1], "width"); + int height = getnextofname(json_string, &t[i+1], "height"); + + fprintf(output, "{ "); + int last_token_index = i + 2 + arr->size; + for(int ii = i + 2; ii < last_token_index; ii++) { + jsmntok_t *num_to_add = &t[ii]; + assert(num_to_add->type == JSMN_PRIMITIVE, MD_S8Lit("not a number")); + int num_index = ii - (i + 2); + fprintf(output, "%.*s, ", num_to_add->end - num_to_add->start, json_string + num_to_add->start); + if(num_index % width == width - 1) { + if(ii == last_token_index - 1) { + fprintf(output, "},\n}\n}; // %.*s\n", MD_S8VArg(variable_name)); + } else { + fprintf(output, "},\n{ "); + } + } + } + } + } + + } } MD_StringJoin join = MD_ZERO_STRUCT; diff --git a/main.c b/main.c index 7f1571c..836b421 100644 --- a/main.c +++ b/main.c @@ -12,6 +12,32 @@ #include +typedef struct AABB { + HMM_Vec2 upper_left; + HMM_Vec2 lower_right; +} AABB; + +typedef struct TileInstance { + uint16_t kind; +} TileInstance; + +typedef struct TileInfo { + uint16_t kind; + int num_frames; + AABB regions[32]; + sg_image *img; +} TileInfo; + +#define LEVEL_TILES 60 +#define TILE_SIZE 32 // in pixels +typedef struct Level { + TileInstance tiles[LEVEL_TILES][LEVEL_TILES]; +} Level; + +HMM_Vec2 tilecoord_to_world(int x, int y) { + return HMM_V2( (float)x * (float)TILE_SIZE * 1.0f, -(float)y * (float)TILE_SIZE * 1.0f ); +} + sg_image load_image(const char *path) { sg_image to_return = {0}; @@ -22,7 +48,7 @@ sg_image load_image(const char *path) { &png_width, &png_height, &num_channels, 0); assert(pixels); - to_return = sg_make_image(&(sg_image_desc){ + to_return = sg_make_image(&(sg_image_desc) { .width = png_width, .height = png_height, .pixel_format = SG_PIXELFORMAT_RGBA8, @@ -40,6 +66,20 @@ sg_image load_image(const char *path) { #include "quad-sapp.glsl.h" #include "assets.gen.c" +TileInfo tiles[] = { + { + .kind = 53, + .num_frames = 1, + .regions[0] = { 0, 32, 32, 64 }, + .img = &image_animated_terrain, + }, +}; +TileInfo mystery_tile = { + .img = &image_mystery_tile, + .num_frames = 1, + .regions[0] = { 0, 0, 32, 32 }, +}; + // so can be grep'd and removed #define dbgprint(...) { printf("Debug | %s:%d | ", __FILE__, __LINE__); printf(__VA_ARGS__); } @@ -60,7 +100,7 @@ void init(void) { state.bind.vertex_buffers[0] = sg_make_buffer(&(sg_buffer_desc){ .usage = SG_USAGE_STREAM, //.data = SG_RANGE(vertices), - .size = 1024, + .size = 1024*500, .label = "quad-vertices" }); @@ -97,21 +137,14 @@ void init(void) { }); state.pass_action = (sg_pass_action) { - .colors[0] = { .action=SG_ACTION_CLEAR, .value={0.0f, 0.0f, 0.0f, 1.0f } } + .colors[0] = { .action=SG_ACTION_CLEAR, .value={1.0f, 1.0f, 1.0f, 1.0f } } }; } -typedef struct AABB { - HMM_Vec2 upper_left; - HMM_Vec2 lower_right; -} AABB; typedef HMM_Vec4 Color; -Color col(float r, float g, float b, float a) { - return HMM_V4(r, g, b, a); -} -#define WHITE col(1.0f, 1.0f, 1.0f, 1.0f) +#define WHITE (Color){1.0f, 1.0f, 1.0f, 1.0f} HMM_Vec2 screen_size() { return HMM_V2((float)sapp_width(), (float)sapp_height()); @@ -132,7 +165,7 @@ HMM_Vec2 cam_offset() { return HMM_AddV2(cam.pos, HMM_MulV2F(screen_size(), 0.5f)); } -// screen coords are in bottom right, and in pixels +// screen coords are in pixels counting from bottom left as (0,0), Y+ is up HMM_Vec2 world_to_screen(HMM_Vec2 world) { HMM_Vec2 to_return = world; to_return = HMM_MulV2F(to_return, cam.scale); @@ -148,21 +181,80 @@ HMM_Vec2 screen_to_world(HMM_Vec2 screen) { } // out must be of at least length 4 -void quad_points_centered_size(HMM_Vec2 *out, HMM_Vec2 at, HMM_Vec2 size) { +void quad_points_corner_size(HMM_Vec2 *out, HMM_Vec2 at, HMM_Vec2 size) { out[0] = HMM_V2(0.0, 0.0); out[1] = HMM_V2(size.X, 0.0); out[2] = HMM_V2(size.X, -size.Y); out[3] = HMM_V2(0.0, -size.Y); for(int i = 0; i < 4; i++) { - out[i] = HMM_AddV2(out[i], HMM_V2(-size.X*0.5f, size.Y*0.5f)); out[i] = HMM_AddV2(out[i], at); } } +// out must be of at least length 4 +void quad_points_centered_size(HMM_Vec2 *out, HMM_Vec2 at, HMM_Vec2 size) { + quad_points_corner_size(out, at, size); + for(int i = 0; i < 4; i++) { + out[i] = HMM_AddV2(out[i], HMM_V2(-size.X*0.5f, size.Y*0.5f)); + } +} + +// both segment_a and segment_b must be arrays of length 2 +bool segments_overlapping(float *a_segment, float *b_segment) { + assert(a_segment[1] >= a_segment[0]); + assert(b_segment[1] >= b_segment[0]); + float total_length = (a_segment[1] - a_segment[0]) + (b_segment[1] - b_segment[0]); + float farthest_to_left = min(a_segment[0], b_segment[0]); + float farthest_to_right = max(a_segment[1], b_segment[1]); + if (farthest_to_right - farthest_to_left < total_length) { + return true; + } else { + return false; + } +} + +bool overlapping(AABB a, AABB b) { + // x axis + { + float a_segment[2] = { a.upper_left.X, a.lower_right.X }; + float b_segment[2] = { b.upper_left.X, b.lower_right.X }; + if(segments_overlapping(a_segment, b_segment)) { + } else { + return false; + } + } + + // y axis + { + float a_segment[2] = { a.lower_right.Y, a.upper_left.Y }; + float b_segment[2] = { b.lower_right.Y, b.upper_left.Y }; + if(segments_overlapping(a_segment, b_segment)) { + } else { + return false; + } + } + + return true; // both segments overlapping +} + // points must be of length 4, and be in the order: upper left, upper right, lower right, lower left // the points are in pixels in screen space. The image region is in pixel space of the image void draw_quad_all_parameters(HMM_Vec2 *points, sg_image image, AABB image_region, Color tint) { + AABB cam_aabb = { .upper_left = HMM_V2(0.0, screen_size().Y), .lower_right = HMM_V2(screen_size().X, 0.0) }; + AABB points_bounding_box = { .upper_left = HMM_V2(INFINITY, -INFINITY), .lower_right = HMM_V2(-INFINITY, INFINITY) }; + + for(int i = 0; i < 4; i++) { + points_bounding_box.upper_left.X = min(points_bounding_box.upper_left.X, points[i].X); + points_bounding_box.upper_left.Y = max(points_bounding_box.upper_left.Y, points[i].Y); + + points_bounding_box.lower_right.X = max(points_bounding_box.lower_right.X, points[i].X); + points_bounding_box.lower_right.Y = min(points_bounding_box.lower_right.Y, points[i].Y); + } + if(!overlapping(cam_aabb, points_bounding_box)) { + return; // cull out of screen quads + } + float new_vertices[ (2 + 2)*4 ]; HMM_Vec2 region_size = HMM_SubV2(image_region.lower_right, image_region.upper_left); assert(region_size.X > 0.0); @@ -258,10 +350,29 @@ void frame(void) { sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height()); sg_apply_pipeline(state.pip); - // background - HMM_Vec2 bg_points[4] = {0}; - quad_points_centered_size(bg_points, HMM_V2(0.0, 0.0), img_size(image_bg_tilesheet)); - draw_quad_world_all(bg_points, image_bg_tilesheet, full_region(image_bg_tilesheet), WHITE); + // tilemap +#if 1 + Level * cur_level = &level_level0; + for(int row = 0; row < LEVEL_TILES; row++) { + for(int col = 0; col < LEVEL_TILES; col++) + { + TileInstance cur = cur_level->tiles[row][col]; + if(cur.kind != 0){ + HMM_Vec2 points[4] = {0}; + quad_points_corner_size(points, tilecoord_to_world(col, row), HMM_V2(TILE_SIZE, TILE_SIZE)); + TileInfo *info = NULL; + for(int i = 0; i < sizeof(tiles)/sizeof(*tiles); i++) { + if(tiles[i].kind == cur.kind) { + info = &tiles[i]; + break; + } + } + if(info == NULL) info = &mystery_tile; + draw_quad_world_all(points, *info->img, info->regions[0], WHITE); + } + } + } +#endif // merchant int index = (int)floor(time/0.3); @@ -319,7 +430,7 @@ sapp_desc sokol_main(int argc, char* argv[]) { .event_cb = event, .width = 800, .height = 600, - .gl_force_gles2 = true, + //.gl_force_gles2 = true, not sure why this was here in example, look into .window_title = "RPGPT", .win32_console_attach = true, diff --git a/run_codegen.bat b/run_codegen.bat index 14211bd..f3349b1 100644 --- a/run_codegen.bat +++ b/run_codegen.bat @@ -7,6 +7,7 @@ rmdir /S /q assets\copyrighted mkdir assets\copyrighted copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Characters\NPC Merchant-idle.png" "assets\copyrighted\merchant.png" || goto :error copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\wall-1 - 3 tiles tall.png" "assets\copyrighted\wall-1 - 3 tiles tall.png" || goto :error +copy "EPIC RPG World Pack - Ancient Ruins V 1.7\EPIC RPG World Pack - Ancient Ruins V 1.7\Tilesets\Tileset-Animated Terrains-16 frames.png" "assets\copyrighted\animated_terrain.png" || goto :error rmdir /S /q gen mkdir gen @@ -21,4 +22,5 @@ codegen || goto :error goto :EOF :error +echo Codegen failed exit /B %ERRORLEVEL% diff --git a/thirdparty/jsmn.h b/thirdparty/jsmn.h new file mode 100644 index 0000000..8ac14c1 --- /dev/null +++ b/thirdparty/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * 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. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */