aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkdx <kikoodx@paranoici.org>2023-09-26 11:46:23 +0200
committerkdx <kikoodx@paranoici.org>2023-09-26 11:46:23 +0200
commit87b12b319573ba73a6f221af28e5f869da0f8178 (patch)
treed1d3520573826f338c08b4735111a9a3cd1722d3
downloadmort-87b12b319573ba73a6f221af28e5f869da0f8178.tar.gz
initial commit
-rw-r--r--.gitignore2
-rw-r--r--Tupfile8
-rw-r--r--Tupfile.ini0
-rw-r--r--compile_flags.txt6
-rw-r--r--inc/TZR.h476
-rw-r--r--inc/_.h6
-rw-r--r--src/TZR.c1329
-rw-r--r--src/main.c7
8 files changed, 1834 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3e888de
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/.tup
+/build
diff --git a/Tupfile b/Tupfile
new file mode 100644
index 0000000..6c82446
--- /dev/null
+++ b/Tupfile
@@ -0,0 +1,8 @@
+CC = clang
+LD = clang
+CFLAGS = -Wall -Wextra -Wno-unused-parameter -O3 -std=c99 -iquoteinc -include_.h
+LDFLAGS = -O3 -s
+LIBS = -lm -lSDL2 -lSDL2_mixer
+
+: foreach src/*.c |> $(CC) $(CFLAGS) -c -o %o %f |> build/%B.o
+: build/*.o |> $(LD) $(LDFLAGS) -o %o %f $(LIBS) |> build/game
diff --git a/Tupfile.ini b/Tupfile.ini
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tupfile.ini
diff --git a/compile_flags.txt b/compile_flags.txt
new file mode 100644
index 0000000..cf34aab
--- /dev/null
+++ b/compile_flags.txt
@@ -0,0 +1,6 @@
+-Wall
+-Wextra
+-Wno-unused-parameter
+-std=c99
+-iquoteinc
+-include_.h
diff --git a/inc/TZR.h b/inc/TZR.h
new file mode 100644
index 0000000..20e81cb
--- /dev/null
+++ b/inc/TZR.h
@@ -0,0 +1,476 @@
+/* Licensing information can be found at the end of the file. */
+#pragma once
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef TZR_SOLOUD
+#include "soloud_c.h"
+#endif
+#include <limits.h>
+#include <SDL2/SDL_gamecontroller.h>
+#include <SDL2/SDL_main.h>
+#include <SDL2/SDL_mixer.h>
+#include <SDL2/SDL_mouse.h>
+#include <SDL2/SDL_render.h>
+#include <SDL2/SDL_scancode.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+/* headers/TZR_types.h */
+
+enum TZR_ResourceType {
+ TZR_RES_RAW,
+ TZR_RES_IMAGE,
+ TZR_RES_SOUND,
+ ___TZR_RES_COUNT
+};
+
+enum TZR_EventType {
+ TZR_EV_QUIT,
+ TZR_EV_KEYDOWN,
+ TZR_EV_KEYUP
+};
+
+enum TZR_KeyState {
+ TZR_KEYSTATE_UP,
+ TZR_KEYSTATE_DOWN,
+ TZR_KEYSTATE_RELEASE,
+ TZR_KEYSTATE_PRESS
+};
+
+enum TZR_MixerFlags {
+ TZR_MIXER_OFF,
+ TZR_MIXER_ON,
+ TZR_MIXER_FLAC
+};
+
+typedef unsigned int TZR_Uint;
+typedef struct TZR_Config TZR_Config;
+typedef struct TZR_Point TZR_Point;
+typedef struct TZR_Color TZR_Color;
+typedef struct TZR_Rect TZR_Rect;
+typedef enum TZR_ResourceType TZR_ResourceType;
+typedef struct TZR_Raw TZR_Raw;
+typedef struct TZR_Image TZR_Image;
+typedef struct TZR_Sound TZR_Sound;
+typedef struct TZR_Resource TZR_Resource;
+typedef enum TZR_EventType TZR_EventType;
+typedef struct TZR_Event TZR_Event;
+typedef struct TZR_Music TZR_Music;
+typedef struct TZR_DrawImageArgs TZR_DrawImageArgs;
+typedef struct TZR_DrawRectangleArgs TZR_DrawRectangleArgs;
+typedef struct TZR_PlaySoundArgs TZR_PlaySoundArgs;
+typedef enum TZR_KeyState TZR_KeyState;
+typedef struct TZR_Joystick TZR_Joystick;
+
+struct TZR_Config {
+ int _;
+ int width;
+ int height;
+ int scale;
+ int target_fps;
+ bool pixel_perfect;
+ bool hd_render;
+ bool scale_linear;
+ bool show_cursor;
+ unsigned int mixer;
+ const char *basepath;
+ const char *title;
+};
+
+struct TZR_Point {
+ int x;
+ int y;
+};
+
+struct TZR_Color {
+ int _;
+ float r;
+ float g;
+ float b;
+ float a;
+};
+
+struct TZR_Rect {
+ TZR_Point from;
+ TZR_Point to;
+};
+
+struct TZR_Raw {
+ void *data;
+ size_t size;
+};
+
+struct TZR_Image {
+ int width;
+ int height;
+ SDL_Texture *ptr;
+};
+
+#ifdef TZR_SOLOUD
+struct TZR_Sound {
+ Wav *ptr;
+};
+#else
+struct TZR_Sound {
+ Mix_Chunk *ptr;
+};
+#endif
+
+struct TZR_Resource {
+ TZR_ResourceType type;
+ char *path; /* allocated and freed by TZR; can be NULL */
+ long mtime;
+ union {
+ TZR_Raw raw; /* raw file data */
+ TZR_Image image;
+ TZR_Sound sound;
+ };
+};
+
+struct TZR_Event {
+ TZR_EventType type;
+ int button;
+};
+
+struct TZR_DrawImageArgs {
+ int _;
+ TZR_Uint id;
+ int x;
+ int y;
+ int ix;
+ int iy;
+ int w;
+ int h;
+ float r;
+ float sx;
+ float sy;
+ bool center;
+ bool flip_x;
+ bool flip_y;
+};
+
+struct TZR_DrawRectangleArgs {
+ int _;
+ int x;
+ int y;
+ int w;
+ int h;
+ bool fill;
+ bool center;
+};
+
+struct TZR_PlaySoundArgs {
+ int _;
+ TZR_Uint id;
+ int loop;
+ float volume;
+};
+
+struct TZR_Joystick
+{
+ SDL_GameController *ptr;
+ float leftx;
+ float lefty;
+ float rightx;
+ float righty;
+};
+/* headers/TZR_globals.h */
+
+extern TZR_Config ___tzr_config;
+extern TZR_Color ___tzr_color;
+extern TZR_Resource *___tzr_resources;
+extern size_t ___tzr_resources_capacity;
+extern size_t ___tzr_resources_size;
+extern SDL_Window *___tzr_window;
+extern SDL_Renderer *___tzr_renderer;
+extern SDL_Texture *___tzr_target;
+extern unsigned long ___tzr_tick;
+extern unsigned long ___tzr_next_time;
+extern unsigned long ___tzr_min_dt;
+extern int ___tzr_should_quit;
+extern int ___tzr_mouse_x;
+extern int ___tzr_mouse_y;
+extern TZR_KeyState ___tzr_keystates[SDL_NUM_SCANCODES];
+extern TZR_KeyState ___tzr_mousestates[256];
+extern TZR_KeyState ___tzr_joystates[SDL_CONTROLLER_BUTTON_MAX];
+extern float ___tzr_scale;
+extern int ___tzr_off_x;
+extern int ___tzr_off_y;
+extern Mix_Music *___tzr_music;
+extern TZR_Joystick *___tzr_joysticks;
+extern size_t ___tzr_joysticks_capacity;
+extern size_t ___tzr_joysticks_size;
+extern SDL_BlendMode ___tzr_blendmode;
+
+#ifdef TZR_SOLOUD
+extern Soloud ___tzr_soloud;
+#endif
+/* headers/TZR_resource.h */
+
+#define TZR_RES TZR_LoadResource
+
+/* Return 0 on error. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+TZR_Uint TZR_LoadResourceFromMemory(TZR_ResourceType type, const void *data, int size);
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+TZR_Uint TZR_LoadResourceTyped(TZR_ResourceType type, const char *path);
+
+/* Return 0 on error; try to autodetect resource type from file extension. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+TZR_Uint TZR_LoadResource(const char *path);
+
+/* Doesn't handle invalid ID. Pointer might get invalidated by resource
+ * creation calls, use with caution. */
+TZR_Resource *TZR_GetResourcePointer(TZR_Uint id);
+
+/* Doesn't handle invalid ID. Pointer might get invalidated by resource
+ * creation calls, use with caution. */
+TZR_Raw *TZR_GetRawResource(TZR_Uint id);
+
+/* Doesn't handle invalid ID. */
+TZR_ResourceType TZR_GetResourceType(TZR_Uint id);
+
+/* Doesn't handle invalid ID. */
+const char *TZR_GetResourcePath(TZR_Uint id);
+
+/* Doesn't handle invalid ID. */
+int TZR_GetImageWidth(TZR_Uint id);
+
+/* Doesn't handle invalid ID. */
+int TZR_GetImageHeight(TZR_Uint id);
+
+/* Reload ressource. Returns -1 on error and doesn't apply changes. */
+int TZR_ReloadResource(TZR_Uint id);
+
+/* Watch for changes on managed resources and hotreload if appropriate. */
+void TZR_ResourcesWatch(void);
+
+/* Used internaly. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+int TZR_DirectResourceLoad(TZR_Resource *res, const void *data, int size);
+void TZR_DestroyResource(TZR_Resource *res, int free_path);
+/* headers/TZR_sound.h */
+
+#define TZR_PlaySound(...) ({ const TZR_PlaySoundArgs ____arg = { \
+ .id=0, .loop=0, .volume=1.0f, ._=0, __VA_ARGS__ }; \
+ _TZR_PlaySound(&____arg); })
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int _TZR_PlaySound(const TZR_PlaySoundArgs *args);
+
+/* Return -1 on error. */
+int TZR_PlayMusic(const char *path, int loop);
+
+void TZR_StopMusic(void);
+/* headers/TZR_events.h */
+
+/* Write event data to `e`. Return 0 when event queue is empty. */
+int TZR_PollEvent(TZR_Event *e);
+
+/* Drain queued events with TZR_PollEvent and call TZR_ResourcesWatch. */
+void TZR_CycleEvents(void);
+/* headers/TZR_render.h */
+
+/* All draw calls should happen between TZR_DrawBegin and TZR_DrawEnd.
+ * Return -1 on error. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+int TZR_DrawBegin(void);
+
+/* Return -1 on error. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+int TZR_DrawEnd(void);
+
+/* Return -1 on error. */
+#define TZR_DrawSetColor(...) ({ const TZR_Color ____arg = { \
+ .r=-1.0f, .g=-1.0f, .b=-1.0f, .a=-1.0f, ._=0, __VA_ARGS__ }; \
+ _TZR_DrawSetColor(&____arg); })
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int _TZR_DrawSetColor(const TZR_Color *color);
+
+/* Return -1 on error. */
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int TZR_DrawSetColor8(uint8_t r, uint8_t g, uint8_t b, uint8_t a);
+
+/* Return -1 on error. */
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int TZR_DrawClear(void);
+
+/* Return -1 on error. Draw point on `x`;`y` in the framebuffer. */
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int TZR_DrawPoint(int x, int y);
+
+/* Return -1 on error. Draw line between `x0`;`y0` and `x1`;`y1` in the
+ * framebuffer. */
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int TZR_DrawLine(int x0, int y0, int x1, int y1);
+
+/* Return -1 on error. Draw rectangle at `x`;`y` position of size `w`x`h` in the
+ * framebuffer. */
+#define TZR_DrawRectangle(...) ({ const TZR_DrawRectangleArgs ____arg = { \
+ .x=0, .y=0, .w=0, .h=0, .fill=false, .center=false, ._=0, __VA_ARGS__ }; \
+ _TZR_DrawRectangle(&____arg); })
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int _TZR_DrawRectangle(const TZR_DrawRectangleArgs *args);
+
+/* Return -1 on error. Draw texture ressource `id` at `x`;`y` position of
+ * the framebuffer. */
+#define TZR_DrawImage(...) ({ const TZR_DrawImageArgs ____arg = { \
+ .x=0, .y=0, .ix=0, .iy=0, .w=INT_MIN, .h=INT_MIN, .r=0.0f, .sx=1.0f, \
+ .sy=1.0f, .center=false, .flip_x=false, .flip_y=false, ._=0, \
+ __VA_ARGS__ }; \
+ _TZR_DrawImage(&____arg); })
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int _TZR_DrawImage(const TZR_DrawImageArgs *args);
+
+void TZR_ScreenTransform(int *x, int *y);
+
+#ifdef TZR_PARANOID
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+#endif
+int TZR_BlendMode(SDL_BlendMode mode);
+/* headers/TZR_keystate.h */
+
+TZR_KeyState TZR_GetKeyState(int scancode);
+bool TZR_IsKeyDown(int scancode);
+bool TZR_IsKeyUp(int scancode);
+bool TZR_IsKeyReleased(int scancode);
+bool TZR_IsKeyPressed(int scancode);
+/* headers/TZR_mouse.h */
+
+/* `x` and `y` are out values and nullable. */
+void TZR_MouseGetPosition(int *x, int *y);
+
+TZR_KeyState TZR_MouseGetState(uint8_t button);
+bool TZR_MouseDown(uint8_t button);
+bool TZR_MouseUp(uint8_t button);
+bool TZR_MouseReleased(uint8_t button);
+bool TZR_MousePressed(uint8_t button);
+/* headers/TZR_joystick.h */
+
+TZR_KeyState TZR_JoystickGetState(uint8_t button);
+bool TZR_JoystickDown(uint8_t button);
+bool TZR_JoystickUp(uint8_t button);
+bool TZR_JoystickReleased(uint8_t button);
+bool TZR_JoystickPressed(uint8_t button);
+float TZR_JoystickLeftX(void);
+float TZR_JoystickLeftY(void);
+float TZR_JoystickRightX(void);
+float TZR_JoystickRightY(void);
+/* headers/TZR.h */
+
+/* TZR manages all loaded resources internally and tries to avoid duplicate.
+ * A resource can be loaded multiple times if asked, identification doesn't rely
+ * on filepath. In doubt, use TZR_LoadResource ONCE per asset and store the ID.
+ * Resources are exposed as ID instead of pointer, as a future proof measure in
+ * case TZR needs to rearrange memory at runtime in the future. */
+
+#define TZR_Init(...) ({ const TZR_Config ____arg = { \
+ .width=256, \
+ .height=224, \
+ .scale=2, \
+ .target_fps=60, \
+ .pixel_perfect=true, \
+ .hd_render=false, \
+ .scale_linear=false, \
+ .show_cursor=false, \
+ .mixer=TZR_MIXER_ON, \
+ .basepath=NULL, \
+ .title="TZR", \
+ ._=0, __VA_ARGS__ }; \
+ _TZR_Init(&____arg); })
+
+/* Should be called only once before usage of any other function unless
+ * otherwise specified. On error this calls TZR_Quit and returns -1.
+ * `config` will be duplicated internally and can be safely discarded. */
+#if __STDC_VERSION__ > 201710L
+[[nodiscard]]
+#endif
+int _TZR_Init(const TZR_Config *config);
+
+/* Destroy and free everything created by TZR. */
+void TZR_Quit(void);
+
+/* Return non-zero value when a quit event has been received. */
+int TZR_ShouldQuit(void);
+
+int TZR_MainLoop(int (*main_loop)(void *udata), void *udata);
+
+unsigned long TZR_GetTick(void);
+
+void TZR_ToggleFullscreen(void);
+
+#ifdef __cplusplus
+}
+#endif
+/*
+** Copyright (c) 2023 kdx
+**
+** 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.
+*/
+/* commit hash: ae4af8c1f825d904eaea3be5cdf02e097e30a30f */
diff --git a/inc/_.h b/inc/_.h
new file mode 100644
index 0000000..20b2649
--- /dev/null
+++ b/inc/_.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#define auto __auto_type
+#define foreach(E, L) for (auto E = (L); E != NULL; E = E->next)
+#define forloop(I, F, T) for (auto I = F; I < T; I++)
+#define with(I, T) auto I = (T); if (I)
diff --git a/src/TZR.c b/src/TZR.c
new file mode 100644
index 0000000..627d510
--- /dev/null
+++ b/src/TZR.c
@@ -0,0 +1,1329 @@
+/* Licensing information can be found at the end of the file. */
+
+#ifdef TZR_STB_IMAGE
+#define STB_IMAGE_IMPLEMENTATION
+#include "stb_image.h"
+#undef STB_IMAGE_IMPLEMENTATION
+#endif
+
+#ifdef TZR_SOLOUD
+#include "soloud_c.h"
+#endif
+#include <limits.h>
+#include <math.h>
+#include <SDL2/SDL_error.h>
+#include <SDL2/SDL_events.h>
+#include <SDL2/SDL_gamecontroller.h>
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_log.h>
+#include <SDL2/SDL_mixer.h>
+#include <SDL2/SDL_mouse.h>
+#include <SDL2/SDL_render.h>
+#include <SDL2/SDL_rwops.h>
+#include <SDL2/SDL_scancode.h>
+#include <SDL2/SDL_timer.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include "TZR.h"
+#include <unistd.h>
+
+/* sources/drain.h */
+
+static void *drain_error(void)
+{
+ perror("TZR_LoadResourceTyped:drain");
+ return NULL;
+}
+
+static char *drain(FILE *fp, size_t *size)
+{
+ if (fseek(fp, 0, SEEK_END) < 0)
+ return drain_error();
+ *size = ftell(fp);
+ if (fseek(fp, 0, SEEK_SET) < 0)
+ return drain_error();
+ char *data = malloc(*size);
+ if (data == NULL)
+ return drain_error();
+ if (fread(data, 1, *size, fp) != *size)
+ return free(data), drain_error();
+ return data;
+}
+
+/* sources/reserve.h */
+
+static int
+reserve(void **vec, size_t size, size_t *capacity, size_t elem)
+{
+ if (size <= *capacity)
+ return 0;
+ size_t target = 16;
+ while (target < size * elem)
+ target *= 2;
+ void *new_resources;
+ if (*vec == NULL)
+ new_resources = malloc(target);
+ else
+ new_resources = realloc(*vec, target);
+ if (new_resources == NULL) {
+ perror("TZR_LoadResourceFromMemory:reserve_ressources");
+ return 1;
+ }
+ *vec = new_resources;
+ *capacity = target / elem;
+ return 0;
+}
+
+/* sources/sdl_error.h */
+
+static int sdl_error(int rv)
+{
+ SDL_Log("%s\n", SDL_GetError());
+ return rv;
+}
+/* sources/sdl_rect_to_rect.h */
+
+static void TZR_RectFromSDLRect(TZR_Rect *dest, const SDL_Rect *src)
+{
+ dest->from.x = src->x;
+ dest->from.y = src->y;
+ dest->to.x = src->x + src->w - 1;
+ dest->to.y = src->y + src->h - 1;
+}
+/* sources/globals.c */
+
+TZR_Config ___tzr_config = {0};
+TZR_Color ___tzr_color = {0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
+TZR_Resource *___tzr_resources = NULL;
+size_t ___tzr_resources_capacity = 0;
+size_t ___tzr_resources_size = 0;
+SDL_Window *___tzr_window = NULL;
+SDL_Renderer *___tzr_renderer = NULL;
+SDL_Texture *___tzr_target = NULL;
+unsigned long ___tzr_tick = 0;
+unsigned long ___tzr_next_time = 0;
+unsigned long ___tzr_min_dt = 0;
+int ___tzr_should_quit = 0;
+int ___tzr_mouse_x = 0;
+int ___tzr_mouse_y = 0;
+TZR_KeyState ___tzr_keystates[SDL_NUM_SCANCODES] = {0};
+TZR_KeyState ___tzr_mousestates[256] = {0}; //doc says than mouse button is u8
+TZR_KeyState ___tzr_joystates[SDL_CONTROLLER_BUTTON_MAX] = {0};
+float ___tzr_scale = 1.0;
+int ___tzr_off_x = 1.0;
+int ___tzr_off_y = 1.0;
+Mix_Music *___tzr_music = NULL;
+TZR_Joystick *___tzr_joysticks = NULL;
+size_t ___tzr_joysticks_capacity = 0;
+size_t ___tzr_joysticks_size = 0;
+SDL_BlendMode ___tzr_blendmode = SDL_BLENDMODE_BLEND;
+
+#ifdef TZR_SOLOUD
+Soloud ___tzr_soloud = NULL;
+#endif
+/* sources/TZR_BlendMode.c */
+
+int
+TZR_BlendMode(SDL_BlendMode mode)
+{
+ ___tzr_blendmode = mode;
+ if (SDL_SetRenderDrawBlendMode(___tzr_renderer, mode))
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_CycleEvents.c */
+
+void
+next_state(TZR_KeyState *keystate)
+{
+ switch (*keystate) {
+ case TZR_KEYSTATE_RELEASE:
+ case TZR_KEYSTATE_UP:
+ *keystate = TZR_KEYSTATE_UP;
+ break;
+ case TZR_KEYSTATE_PRESS:
+ case TZR_KEYSTATE_DOWN:
+ *keystate = TZR_KEYSTATE_DOWN;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+get_axis(SDL_GameController *gc, float *out, SDL_GameControllerAxis axis)
+{
+ const Sint16 v = SDL_GameControllerGetAxis(gc, axis);
+ if (v > -16384 && v < 16384)
+ *out = 0.0f;
+ else
+ *out = (float)v / INT16_MAX;
+}
+
+void
+TZR_CycleEvents(void)
+{
+ TZR_ResourcesWatch();
+ for (int i = 0; i < SDL_NUM_SCANCODES; i++)
+ next_state(&___tzr_keystates[i]);
+ for (int i = 0; i < 256; i++)
+ next_state(&___tzr_mousestates[i]);
+ for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++)
+ next_state(&___tzr_joystates[i]);
+ TZR_Event e;
+ while (TZR_PollEvent(&e))
+ ;
+
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ TZR_Joystick *const jstick = &___tzr_joysticks[i];
+ if (jstick->ptr == NULL)
+ continue;
+ for (int k = 0; k < SDL_CONTROLLER_BUTTON_MAX; k++) {
+ if (SDL_GameControllerGetButton(jstick->ptr, k)) {
+ if (___tzr_joystates[k] != TZR_KEYSTATE_DOWN)
+ ___tzr_joystates[k] = TZR_KEYSTATE_PRESS;
+ } else {
+ if (___tzr_joystates[k] != TZR_KEYSTATE_UP)
+ ___tzr_joystates[k] = TZR_KEYSTATE_RELEASE;
+ }
+ }
+ get_axis(jstick->ptr, &jstick->leftx, SDL_CONTROLLER_AXIS_LEFTX);
+ get_axis(jstick->ptr, &jstick->lefty, SDL_CONTROLLER_AXIS_LEFTY);
+ get_axis(jstick->ptr, &jstick->rightx, SDL_CONTROLLER_AXIS_RIGHTX);
+ get_axis(jstick->ptr, &jstick->righty, SDL_CONTROLLER_AXIS_RIGHTY);
+ }
+}
+/* sources/TZR_DestroyResource.c */
+
+void
+TZR_DestroyResource(TZR_Resource *res, int free_path)
+{
+ if (res->path != NULL && free_path)
+ free(res->path);
+ switch (res->type) {
+ case TZR_RES_RAW:
+ if (res->raw.data != NULL)
+ free(res->raw.data);
+ break;
+ case TZR_RES_IMAGE:
+ if (res->image.ptr != NULL)
+ SDL_DestroyTexture(res->image.ptr);
+ break;
+ case TZR_RES_SOUND:
+#ifdef TZR_SOLOUD
+ if (res->sound.ptr != NULL)
+ Wav_destroy(res->sound.ptr);
+#else
+ if (res->sound.ptr != NULL)
+ Mix_FreeChunk(res->sound.ptr);
+#endif
+ break;
+ default:
+ fprintf(stderr, "unknown resource type %u\n", res->type);
+ break;
+ }
+}
+
+/* sources/TZR_DirectResourceLoad.c */
+
+#ifdef TZR_STB_IMAGE
+#define STB_IMAGE_IMPLEMENTATION
+
+SDL_Surface *
+stbi_load_surface(void *src, int size)
+{
+ int width, height, chans;
+ void *const data = stbi_load_from_memory(src, size, &width, &height,
+ &chans, 4);
+ const int pitch = (width * 4 + 3) & ~3;
+
+ if (data == NULL) {
+ fprintf(stderr, "stbi: %s\n", stbi_failure_reason());
+ return NULL;
+ }
+
+ if (chans != 4) {
+ fprintf(stderr, "TZR only supports RGBA images\n");
+ free(data);
+ return NULL;
+ }
+
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+ const int rmask = 0x000000ff;
+ const int gmask = 0x0000ff00;
+ const int bmask = 0x00ff0000;
+ const int amask = 0xff000000 * (chans == 4);
+#else
+ const int s = 8 * (chans != 4);
+ const int rmask = 0xff000000 >> s;
+ const int gmask = 0x00ff0000 >> s;
+ const int bmask = 0x0000ff00 >> s;
+ const int amask = 0x000000ff >> s;
+#endif
+
+ SDL_Surface *const surf = SDL_CreateRGBSurfaceFrom(data, width, height, chans * 8, pitch, rmask, gmask, bmask, amask);
+ if (surf == NULL) {
+ sdl_error(0);
+ free(data);
+ return NULL;
+ }
+
+ return surf;
+}
+#endif
+
+int
+TZR_DirectResourceLoad(TZR_Resource *res, const void *data, int size)
+{
+ switch (res->type) {
+ case TZR_RES_RAW:
+ res->raw.size = size;
+ res->raw.data = malloc(size);
+ if (res->raw.data == NULL) {
+ perror("TZR_DirectResourceLoad");
+ return -1;
+ }
+ memcpy(res->raw.data, data, size);
+ break;
+ case TZR_RES_IMAGE: {
+#ifdef TZR_STB_IMAGE
+ SDL_Surface *const surf = stbi_load_surface((void*)data, size);
+ if (surf == NULL)
+ return -1;
+#else
+ SDL_RWops *const rw = SDL_RWFromConstMem(data, size);
+ if (rw == NULL)
+ return sdl_error(-1);
+ SDL_Surface *const surf = SDL_LoadBMP_RW(rw, 1);
+ if (surf == NULL)
+ return sdl_error(-1);
+#endif
+ SDL_Texture *const tex =
+ SDL_CreateTextureFromSurface(___tzr_renderer, surf);
+ SDL_FreeSurface(surf);
+ if (tex == NULL)
+ return sdl_error(-1);
+ if (SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND) < 0) {
+ SDL_DestroyTexture(tex);
+ return sdl_error(-1);
+ }
+ if (SDL_QueryTexture(tex, NULL, NULL,
+ &res->image.width, &res->image.height)) {
+ SDL_DestroyTexture(tex);
+ return sdl_error(-1);
+ }
+ res->image.ptr = tex;
+ } break;
+ case TZR_RES_SOUND: {
+ if (!___tzr_config.mixer) {
+ SDL_Log("audio mixer disabled, skip loading");
+ return -1;
+ }
+#ifdef TZR_SOLOUD
+ Wav *wav = Wav_create();
+ if (wav == NULL) {
+ SDL_Log("Wav_create failed");
+ return -1;
+ }
+ if (Wav_loadMemEx(wav, data, size, true, false)) {
+ SDL_Log("Wav_loadMem failed");
+ Wav_destroy(wav);
+ return -1;
+ }
+ res->sound.ptr = wav;
+#else
+ SDL_RWops *const rw = SDL_RWFromConstMem(data, size);
+ if (rw == NULL)
+ return sdl_error(-1);
+ Mix_Chunk *const chunk = Mix_LoadWAV_RW(rw, 0);
+ SDL_RWclose(rw);
+ if (chunk == NULL)
+ return sdl_error(-1);
+ res->sound.ptr = chunk;
+#endif
+ } break;
+ default:
+ fprintf(stderr, "invalid type\n");
+ return -1;
+ }
+ return 0;
+}
+/* sources/TZR_DrawBegin.c */
+
+int
+TZR_DrawBegin(void)
+{
+ if (TZR_BlendMode(SDL_BLENDMODE_BLEND))
+ return sdl_error(-1);
+ if (___tzr_config.hd_render)
+ return 0;
+ if (SDL_SetRenderTarget(___tzr_renderer, ___tzr_target) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_DrawClear.c */
+
+int
+TZR_DrawClear(void)
+{
+ if (SDL_RenderClear(___tzr_renderer) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_DrawEnd.c */
+
+static int
+pixel_draw_end(void)
+{
+ if (SDL_SetRenderTarget(___tzr_renderer, NULL) < 0)
+ return sdl_error(-1);
+ if (SDL_SetRenderDrawColor(___tzr_renderer, 0, 0, 0, 255) < 0)
+ return sdl_error(-1);
+ if (SDL_RenderClear(___tzr_renderer) < 0)
+ return sdl_error(-1);
+
+ int win_w, win_h;
+ SDL_GetWindowSize(___tzr_window, &win_w, &win_h);
+ win_w = win_w ? win_w : 1;
+ win_h = win_h ? win_h : 1;
+
+ const float ratio_w = win_w / (float)___tzr_config.width;
+ const float ratio_h = win_h / (float)___tzr_config.height;
+ ___tzr_scale = (ratio_w < ratio_h) ? ratio_w : ratio_h;
+ if (___tzr_scale > 1.0f && ___tzr_config.pixel_perfect)
+ ___tzr_scale = (int)___tzr_scale;
+ ___tzr_off_x = (win_w - ___tzr_config.width * ___tzr_scale) / 2;
+ ___tzr_off_y = (win_h - ___tzr_config.height * ___tzr_scale) / 2;
+ const SDL_Rect dest = {
+ ___tzr_off_x,
+ ___tzr_off_y,
+ ___tzr_config.width * ___tzr_scale,
+ ___tzr_config.height * ___tzr_scale
+ };
+
+ if (SDL_RenderCopy(___tzr_renderer, ___tzr_target, NULL, &dest) < 0)
+ return sdl_error(-1);
+
+ return 0;
+}
+
+int
+TZR_DrawEnd(void)
+{
+ if (!___tzr_config.hd_render && pixel_draw_end())
+ return -1;
+
+ if (___tzr_config.target_fps > 0) {
+ ___tzr_next_time += ___tzr_min_dt;
+ const unsigned long cur_time = SDL_GetTicks64();
+ if (___tzr_next_time <= cur_time)
+ ___tzr_next_time = cur_time;
+ else
+ SDL_Delay(___tzr_next_time - cur_time);
+ }
+ SDL_RenderPresent(___tzr_renderer);
+
+ ___tzr_tick += 1;
+ return 0;
+}
+/* sources/TZR_DrawImage.c */
+
+int
+_TZR_DrawImage(const TZR_DrawImageArgs *args)
+{
+ if (args->id == 0) {
+ fprintf(stderr, "args->id is 0\n");
+ return -1;
+ }
+
+ if (TZR_GetResourceType(args->id) != TZR_RES_IMAGE) {
+ fprintf(stderr, "%u isn't an image\n", args->id);
+ return -1;
+ }
+ TZR_Resource *const res = TZR_GetResourcePointer(args->id);
+ const TZR_Image *const img = &res->image;
+
+ if (SDL_SetTextureColorMod(img->ptr, ___tzr_color.r * 255,
+ ___tzr_color.g * 255, ___tzr_color.b * 255))
+ return sdl_error(-1);
+ if (SDL_SetTextureAlphaMod(img->ptr, ___tzr_color.a * 255))
+ return sdl_error(-1);
+ if (SDL_SetTextureBlendMode(img->ptr, ___tzr_blendmode) < 0)
+ return sdl_error(-1);
+
+ float scale_x = args->sx;
+ float scale_y = args->sy;
+
+ int simg_width = (args->w == INT_MIN) ? img->width : args->w;
+ if (simg_width < 0) {
+ simg_width *= -1;
+ scale_x *= -1;
+ }
+ if (simg_width + args->ix > img->width)
+ simg_width = img->width - args->ix;
+
+ int simg_height = (args->h == INT_MIN) ? img->height : args->h;
+ if (simg_height < 0) {
+ simg_height *= -1;
+ scale_y *= -1;
+ }
+ if (simg_height + args->iy > img->height)
+ simg_height = img->height - args->iy;
+
+ int flip = 0;
+ flip |= SDL_FLIP_HORIZONTAL * ((scale_x < 0.0f) ^ args->flip_x);
+ flip |= SDL_FLIP_VERTICAL * ((scale_y < 0.0f) ^ args->flip_y);
+ const SDL_Rect src = {
+ args->ix,
+ args->iy,
+ simg_width,
+ simg_height
+ };
+
+ const int width = simg_width * fabs(scale_x);
+ const int height = simg_height * fabs(scale_y);
+ const int x = args->x - (scale_x < 0.0f ? width : 0);
+ const int y = args->y - (scale_y < 0.0f ? height : 0);
+
+ const SDL_Rect dest = {
+ x - (args->center ? (simg_width * scale_x / 2) : 0),
+ y - (args->center ? (simg_height * scale_y / 2) : 0),
+ width,
+ height
+ };
+ if (SDL_RenderCopyEx(___tzr_renderer, img->ptr, &src, &dest,
+ args->r * 360, NULL, flip) < 0)
+ return sdl_error(-1);
+ TZR_Rect area;
+ TZR_RectFromSDLRect(&area, &dest);
+ return 0;
+}
+/* sources/TZR_DrawLine.c */
+
+int
+TZR_DrawLine(int x0, int y0, int x1, int y1)
+{
+ if (SDL_RenderDrawLine(___tzr_renderer, x0, y0, x1, y1) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_DrawPoint.c */
+
+int
+TZR_DrawPoint(int x, int y)
+{
+ if (SDL_RenderDrawPoint(___tzr_renderer, x, y) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_DrawRectangle.c */
+
+int
+_TZR_DrawRectangle(const TZR_DrawRectangleArgs *args)
+{
+ const SDL_Rect rect = {
+ args->x - args->center * (args->w / 2),
+ args->y - args->center * (args->h / 2),
+ args->w,
+ args->h
+ };
+ if (args->fill) {
+ if (SDL_RenderFillRect(___tzr_renderer, &rect) < 0)
+ return sdl_error(-1);
+ } else {
+ if (SDL_RenderDrawRect(___tzr_renderer, &rect) < 0)
+ return sdl_error(-1);
+ }
+ return 0;
+}
+/* sources/TZR_DrawSetColor8.c */
+
+int
+TZR_DrawSetColor8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+ ___tzr_color.r = (float)r / 255.0f;
+ ___tzr_color.g = (float)g / 255.0f;
+ ___tzr_color.b = (float)b / 255.0f;
+ ___tzr_color.a = (float)a / 255.0f;
+ if (SDL_SetRenderDrawColor(___tzr_renderer, r, g, b, a) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_DrawSetColor.c */
+
+int
+_TZR_DrawSetColor(const TZR_Color *color)
+{
+ ___tzr_color.r = (color->r < 0.0f) ? ___tzr_color.r : color->r;
+ ___tzr_color.g = (color->g < 0.0f) ? ___tzr_color.g : color->g;
+ ___tzr_color.b = (color->b < 0.0f) ? ___tzr_color.b : color->b;
+ ___tzr_color.a = (color->a < 0.0f) ? ___tzr_color.a : color->a;
+ const uint8_t r = ___tzr_color.r * 255;
+ const uint8_t g = ___tzr_color.g * 255;
+ const uint8_t b = ___tzr_color.b * 255;
+ const uint8_t a = ___tzr_color.a * 255;
+ if (SDL_SetRenderDrawColor(___tzr_renderer, r, g, b, a) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_GetImageHeight.c */
+
+int
+TZR_GetImageHeight(TZR_Uint id)
+{
+ return TZR_GetResourcePointer(id)->image.height;
+}
+/* sources/TZR_GetImageWidth.c */
+
+int
+TZR_GetImageWidth(TZR_Uint id)
+{
+ return TZR_GetResourcePointer(id)->image.width;
+}
+/* sources/TZR_GetKeyState.c */
+
+TZR_KeyState
+TZR_GetKeyState(int scancode)
+{
+ return ___tzr_keystates[scancode];
+}
+/* sources/TZR_GetRawResource.c */
+
+TZR_Raw *
+TZR_GetRawResource(TZR_Uint id)
+{
+ return &TZR_GetResourcePointer(id)->raw;
+}
+/* sources/TZR_GetResourcePath.c */
+
+const char *
+TZR_GetResourcePath(TZR_Uint id)
+{
+ return TZR_GetResourcePointer(id)->path;
+}
+/* sources/TZR_GetResourcePointer.c */
+
+TZR_Resource *
+TZR_GetResourcePointer(TZR_Uint id)
+{
+ return &___tzr_resources[id - 1];
+}
+/* sources/TZR_GetResourceType.c */
+
+TZR_ResourceType
+TZR_GetResourceType(TZR_Uint id)
+{
+ return TZR_GetResourcePointer(id)->type;
+}
+/* sources/TZR_GetTick.c */
+
+unsigned long
+TZR_GetTick(void)
+{
+ return ___tzr_tick;
+}
+/* sources/TZR_Init.c */
+
+static int
+_sdl_error(void)
+{
+ sdl_error(-1);
+ TZR_Quit();
+ return -1;
+}
+
+int
+_TZR_Init(const TZR_Config *config)
+{
+ memcpy(&___tzr_config, config, sizeof(TZR_Config));
+
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0)
+ return _sdl_error();
+ if (___tzr_config.mixer == TZR_MIXER_FLAC &&
+ Mix_Init(MIX_INIT_FLAC) != MIX_INIT_FLAC)
+ {
+ SDL_Log("%s", Mix_GetError());
+ ___tzr_config.mixer = TZR_MIXER_OFF;
+ }
+
+ int chdir_rv;
+ if (___tzr_config.basepath == NULL) {
+ char *const basepath = SDL_GetBasePath();
+ if (basepath == NULL)
+ return _sdl_error();
+ chdir_rv = chdir(basepath);
+ SDL_free(basepath);
+ } else
+ chdir_rv = chdir(___tzr_config.basepath);
+ if (chdir_rv < 0)
+ return perror("TZR_Init"), -1;
+
+ ___tzr_window =
+ SDL_CreateWindow(config->title,
+ SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED,
+ ___tzr_config.width * ___tzr_config.scale,
+ ___tzr_config.height * ___tzr_config.scale,
+ SDL_WINDOW_RESIZABLE);
+ if (___tzr_window == NULL)
+ return _sdl_error();
+
+#ifdef __EMSCRIPTEN__
+ ___tzr_renderer = SDL_CreateRenderer(___tzr_window, -1,
+ SDL_RENDERER_SOFTWARE);
+#else
+ ___tzr_renderer = SDL_CreateRenderer(___tzr_window, -1,
+ SDL_RENDERER_ACCELERATED);
+#endif
+ if (___tzr_renderer == NULL)
+ return _sdl_error();
+
+ if (___tzr_config.hd_render) {
+ if (SDL_RenderSetLogicalSize(___tzr_renderer,
+ ___tzr_config.width,
+ ___tzr_config.height) < 0)
+ return _sdl_error();
+ } else {
+ ___tzr_target = SDL_CreateTexture(___tzr_renderer,
+ SDL_PIXELFORMAT_RGB888,
+ SDL_TEXTUREACCESS_TARGET,
+ ___tzr_config.width,
+ ___tzr_config.height);
+ if (___tzr_target == NULL)
+ return _sdl_error();
+ }
+
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
+ ___tzr_config.scale_linear ? "1" : "0");
+ ___tzr_tick = 0;
+
+ if (___tzr_config.target_fps > 0) {
+ ___tzr_min_dt = 1000 / ___tzr_config.target_fps;
+ ___tzr_next_time = SDL_GetTicks64();
+ }
+
+ if (!___tzr_config.show_cursor)
+ SDL_ShowCursor(SDL_FALSE);
+ ___tzr_mouse_x = ___tzr_config.width / 2;
+ ___tzr_mouse_y = ___tzr_config.height / 2;
+
+ /* Setup audio. */
+ do if (___tzr_config.mixer) {
+ if (Mix_OpenAudio(48000, MIX_DEFAULT_FORMAT, 8, 1024) < 0) {
+ sdl_error(0);
+ Mix_Quit();
+ ___tzr_config.mixer = false;
+ break;
+ }
+
+#ifdef TZR_SOLOUD
+ ___tzr_soloud = Soloud_create();
+ if (___tzr_soloud == NULL) {
+ SDL_Log("Soloud_create failed");
+ Mix_Quit();
+ ___tzr_config.mixer = false;
+ break;
+ }
+
+ if (Soloud_init(___tzr_soloud)) {
+ SDL_Log("Soloud_init failed");
+ Soloud_destroy(___tzr_soloud);
+ ___tzr_soloud = NULL;
+ Mix_Quit();
+ ___tzr_config.mixer = false;
+ break;
+ }
+#endif
+ } while (0);
+ return 0;
+}
+/* sources/TZR_IsKeyDown.c */
+
+bool
+TZR_IsKeyDown(int scancode)
+{
+ const TZR_KeyState state = TZR_GetKeyState(scancode);
+ return (state == TZR_KEYSTATE_DOWN || state == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_IsKeyPressed.c */
+
+bool
+TZR_IsKeyPressed(int scancode)
+{
+ return (TZR_GetKeyState(scancode) == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_IsKeyReleased.c */
+
+bool
+TZR_IsKeyReleased(int scancode)
+{
+ if (TZR_GetKeyState(scancode) == TZR_KEYSTATE_RELEASE)
+ return true;
+ return false;
+}
+/* sources/TZR_IsKeyUp.c */
+
+bool
+TZR_IsKeyUp(int scancode)
+{
+ const TZR_KeyState state = TZR_GetKeyState(scancode);
+ return (state == TZR_KEYSTATE_UP || state == TZR_KEYSTATE_RELEASE);
+}
+/* sources/TZR_JoystickDown.c */
+
+bool
+TZR_JoystickDown(uint8_t button)
+{
+ const TZR_KeyState state = TZR_JoystickGetState(button);
+ return (state == TZR_KEYSTATE_DOWN || state == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_JoystickGetState.c */
+
+TZR_KeyState
+TZR_JoystickGetState(uint8_t button)
+{
+ return ___tzr_joystates[button];
+}
+/* sources/TZR_JoystickLeftX.c */
+
+float
+TZR_JoystickLeftX(void)
+{
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr == NULL)
+ continue;
+ if (___tzr_joysticks[i].leftx)
+ return ___tzr_joysticks[i].leftx;
+ }
+ return 0.0f;
+}
+/* sources/TZR_JoystickLeftY.c */
+
+float
+TZR_JoystickLeftY(void)
+{
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr == NULL)
+ continue;
+ if (___tzr_joysticks[i].lefty)
+ return ___tzr_joysticks[i].lefty;
+ }
+ return 0.0f;
+}
+/* sources/TZR_JoystickPressed.c */
+
+bool
+TZR_JoystickPressed(uint8_t button)
+{
+ return (TZR_JoystickGetState(button) == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_JoystickReleased.c */
+
+bool
+TZR_JoystickReleased(uint8_t button)
+{
+ return (TZR_JoystickGetState(button) == TZR_KEYSTATE_RELEASE);
+}
+/* sources/TZR_JoystickRightX.c */
+
+float
+TZR_JoystickRightX(void)
+{
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr == NULL)
+ continue;
+ if (___tzr_joysticks[i].rightx)
+ return ___tzr_joysticks[i].rightx;
+ }
+ return 0.0f;
+}
+/* sources/TZR_JoystickRightY.c */
+
+float
+TZR_JoystickRightY(void)
+{
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr == NULL)
+ continue;
+ if (___tzr_joysticks[i].righty)
+ return ___tzr_joysticks[i].righty;
+ }
+ return 0.0f;
+}
+/* sources/TZR_JoystickUp.c */
+
+bool
+TZR_JoystickUp(uint8_t button)
+{
+ const TZR_KeyState state = TZR_JoystickGetState(button);
+ return (state == TZR_KEYSTATE_UP || state == TZR_KEYSTATE_RELEASE);
+}
+/* sources/TZR_LoadResource.c */
+
+static TZR_ResourceType
+deduce_type(const char *path)
+{
+ const char *const path_extension = strrchr(path, '.');
+ if (path_extension == NULL)
+ return TZR_RES_RAW;
+ if (strcasecmp(path_extension, ".bmp") == 0 ||
+ strcasecmp(path_extension, ".png") == 0 ||
+ strcasecmp(path_extension, ".jpg") == 0 ||
+ strcasecmp(path_extension, ".qoi") == 0)
+ return TZR_RES_IMAGE;
+ if (strcasecmp(path_extension, ".wav") == 0)
+ return TZR_RES_SOUND;
+ return TZR_RES_RAW;
+}
+
+TZR_Uint
+TZR_LoadResource(const char *path)
+{
+ return TZR_LoadResourceTyped(deduce_type(path), path);
+}
+/* sources/TZR_LoadResourceFromMemory.c */
+
+TZR_Uint
+TZR_LoadResourceFromMemory(TZR_ResourceType type, const void *data, int size)
+{
+ void *vec = ___tzr_resources;
+ if (reserve(&vec,
+ ___tzr_resources_size + 1,
+ &___tzr_resources_capacity,
+ sizeof(TZR_Resource)))
+ {
+ fprintf(stderr, "failed to reserve for new ressource\n");
+ return 0;
+ }
+ ___tzr_resources = vec;
+
+ TZR_Resource *const res = &___tzr_resources[___tzr_resources_size];
+ res->type = type;
+ res->path = NULL;
+ if (TZR_DirectResourceLoad(res, data, size))
+ return 0;
+ ___tzr_resources_size += 1;
+ return ___tzr_resources_size;
+}
+/* sources/TZR_LoadResourceTyped.c */
+
+TZR_Uint
+TZR_LoadResourceTyped(TZR_ResourceType type, const char *path)
+{
+ for (TZR_Uint i = 0; i < ___tzr_resources_size; i++)
+ if (___tzr_resources[i].type == type &&
+ strcmp(___tzr_resources[i].path, path) == 0)
+ return i + 1;
+ FILE *const fp = fopen(path, "rb");
+ if (fp == NULL) {
+ perror(path);
+ return 0;
+ }
+ size_t size;
+ char *const data = drain(fp, &size);
+ fclose(fp);
+ if (data == NULL)
+ return 0;
+ const TZR_Uint id = TZR_LoadResourceFromMemory(type, data, size);
+ free(data);
+ if (id == 0)
+ return 0;
+ TZR_Resource *const res = TZR_GetResourcePointer(id);
+
+ const size_t path_len = strlen(path);
+ struct stat st = {0};
+ (void)stat(path, &st);
+ res->path = malloc(path_len + 1);
+ res->mtime = 0;
+ if (res->path == NULL)
+ perror("TZR_LoadResourceTyped");
+ else
+ strcpy(res->path, path);
+ return id;
+}
+/* sources/TZR_MainLoop.c */
+
+#ifdef __EMSCRIPTEN__
+
+static int (*_main_loop)(void *udata);
+static void *_udata;
+static void
+_em_loop(void)
+{
+ TZR_CycleEvents();
+ _main_loop(_udata);
+}
+
+int
+TZR_MainLoop(int (*main_loop)(void *udata), void *udata)
+{
+ _main_loop = main_loop;
+ _udata = udata;
+ emscripten_set_main_loop(_em_loop, 0, 1);
+ return 0;
+}
+
+#else
+
+int
+TZR_MainLoop(int (*main_loop)(void *udata), void *udata)
+{
+ while (!TZR_ShouldQuit()) {
+ TZR_CycleEvents();
+ if (main_loop(udata))
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+/* sources/TZR_MouseDown.c */
+
+bool
+TZR_MouseDown(uint8_t button)
+{
+ const TZR_KeyState state = TZR_MouseGetState(button);
+ return (state == TZR_KEYSTATE_DOWN || state == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_MouseGetPosition.c */
+
+void
+TZR_MouseGetPosition(int *x, int *y)
+{
+ if (x != NULL)
+ *x = ___tzr_mouse_x;
+ if (y != NULL)
+ *y = ___tzr_mouse_y;
+ TZR_ScreenTransform(x, y);
+}
+/* sources/TZR_MouseGetState.c */
+
+TZR_KeyState
+TZR_MouseGetState(uint8_t button)
+{
+ return ___tzr_mousestates[button];
+}
+/* sources/TZR_MousePressed.c */
+
+bool
+TZR_MousePressed(uint8_t button)
+{
+ return (TZR_MouseGetState(button) == TZR_KEYSTATE_PRESS);
+}
+/* sources/TZR_MouseReleased.c */
+
+bool
+TZR_MouseReleased(uint8_t button)
+{
+ return (TZR_MouseGetState(button) == TZR_KEYSTATE_RELEASE);
+}
+/* sources/TZR_MouseUp.c */
+
+bool
+TZR_MouseUp(uint8_t button)
+{
+ const TZR_KeyState state = TZR_MouseGetState(button);
+ return (state == TZR_KEYSTATE_UP || state == TZR_KEYSTATE_RELEASE);
+}
+/* sources/TZR_PlayMusic.c */
+
+int
+TZR_PlayMusic(const char *path, int loop)
+{
+ if (!___tzr_config.mixer) {
+ fprintf(stderr, "mixer is disabled\n");
+ return -1;
+ }
+ TZR_StopMusic();
+ ___tzr_music = Mix_LoadMUS(path);
+ if (___tzr_music == NULL)
+ return sdl_error(-1);
+ if (Mix_PlayMusic(___tzr_music, loop) < 0)
+ return sdl_error(-1);
+ return 0;
+}
+/* sources/TZR_PlaySound.c */
+
+int
+_TZR_PlaySound(const TZR_PlaySoundArgs *args)
+{
+ if (args->id == 0) {
+ SDL_Log("args->id is 0");
+ return -1;
+ }
+
+ if (TZR_GetResourceType(args->id) != TZR_RES_SOUND) {
+ SDL_Log("%u isn't a sound", args->id);
+ return -1;
+ }
+
+ TZR_Sound *const sound = &TZR_GetResourcePointer(args->id)->sound;
+
+#ifdef TZR_SOLOUD
+ const unsigned handle = Soloud_play(___tzr_soloud, sound->ptr);
+ Soloud_setVolume(___tzr_soloud, handle, args->volume);
+ Soloud_setLooping(___tzr_soloud, handle, args->loop);
+#else
+ Mix_VolumeChunk(sound->ptr, args->volume * MIX_MAX_VOLUME);
+ if (Mix_PlayChannel(-1, sound->ptr, args->loop) < 0)
+ return sdl_error(-1);
+#endif
+
+ return 0;
+}
+/* sources/TZR_PollEvent.c */
+
+int
+TZR_PollEvent(TZR_Event *e)
+{
+ TZR_Event discard;
+ if (e == NULL)
+ e = &discard;
+
+ SDL_Event se;
+ while (SDL_PollEvent(&se)) switch (se.type) {
+ case SDL_QUIT:
+ e->type = TZR_EV_QUIT;
+ ___tzr_should_quit = 1;
+ return 1;
+ case SDL_KEYDOWN:
+ if (se.key.repeat)
+ break;
+ e->type = TZR_EV_KEYDOWN;
+ e->button = se.key.keysym.scancode;
+ ___tzr_keystates[e->button] = TZR_KEYSTATE_PRESS;
+ return 1;
+ case SDL_KEYUP:
+ e->type = TZR_EV_KEYUP;
+ e->button = se.key.keysym.scancode;
+ ___tzr_keystates[e->button] = TZR_KEYSTATE_RELEASE;
+ return 1;
+ case SDL_MOUSEBUTTONDOWN:
+ ___tzr_mouse_x = se.button.x;
+ ___tzr_mouse_y = se.button.y;
+ ___tzr_mousestates[se.button.button] = TZR_KEYSTATE_PRESS;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ ___tzr_mouse_x = se.button.x;
+ ___tzr_mouse_y = se.button.y;
+ ___tzr_mousestates[se.button.button] = TZR_KEYSTATE_RELEASE;
+ break;
+ case SDL_MOUSEMOTION:
+ ___tzr_mouse_x = se.motion.x;
+ ___tzr_mouse_y = se.motion.y;
+ break;
+ case SDL_JOYDEVICEADDED: {
+ size_t i;
+ for (i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr == NULL)
+ break;
+ }
+ if (i == ___tzr_joysticks_size) {
+ void *vec = ___tzr_joysticks;
+ if (reserve(&vec,
+ ___tzr_joysticks_size + 1,
+ &___tzr_joysticks_capacity,
+ sizeof(SDL_Joystick *)))
+ {
+ fprintf(stderr, "failed to reserve"
+ "for new joystick\n");
+ break;
+ }
+ ___tzr_joysticks = vec;
+ ___tzr_joysticks_size += 1;
+ i = ___tzr_joysticks_size - 1;
+ }
+
+ SDL_GameController *jstick = SDL_GameControllerOpen(se.jdevice.which);
+ if (jstick == NULL) {
+ sdl_error(0);
+ break;
+ }
+ memset(&___tzr_joysticks[i], 0, sizeof(___tzr_joysticks[i]));
+ ___tzr_joysticks[i].ptr = jstick;
+ fprintf(stderr, "opened joystick\n");
+ break;
+ }
+ case SDL_JOYDEVICEREMOVED:
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ SDL_Joystick *const jstick =
+ SDL_GameControllerGetJoystick(___tzr_joysticks[i].ptr);
+ if (se.jdevice.which == SDL_JoystickInstanceID(jstick)) {
+ SDL_GameControllerClose(___tzr_joysticks[i].ptr);
+ ___tzr_joysticks[i].ptr = NULL;
+ break;
+ }
+ }
+ fprintf(stderr, "didn't remove no joystick\n");
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+/* sources/TZR_Quit.c */
+
+void
+TZR_Quit(void)
+{
+ for (size_t i = 0; i < ___tzr_resources_size; i++)
+ TZR_DestroyResource(&___tzr_resources[i], 1);
+ if (___tzr_resources != NULL)
+ free(___tzr_resources);
+ ___tzr_resources = NULL;
+ ___tzr_resources_size = 0;
+ ___tzr_resources_capacity = 0;
+ for (size_t i = 0; i < ___tzr_joysticks_size; i++) {
+ if (___tzr_joysticks[i].ptr != NULL)
+ SDL_GameControllerClose(___tzr_joysticks[i].ptr);
+ }
+ if (___tzr_joysticks != NULL)
+ free(___tzr_joysticks);
+ ___tzr_joysticks = NULL;
+ ___tzr_joysticks_size = 0;
+ ___tzr_joysticks_capacity = 0;
+ if (___tzr_target != NULL) {
+ SDL_DestroyTexture(___tzr_target);
+ ___tzr_target = NULL;
+ }
+ if (___tzr_renderer != NULL) {
+ SDL_DestroyRenderer(___tzr_renderer);
+ ___tzr_renderer = NULL;
+ }
+ if (___tzr_window != NULL) {
+ SDL_DestroyWindow(___tzr_window);
+ ___tzr_window = NULL;
+ }
+ if (___tzr_config.mixer) {
+#ifdef TZR_SOLOUD
+ Soloud_deinit(___tzr_soloud);
+ Soloud_destroy(___tzr_soloud);
+ ___tzr_soloud = NULL;
+#endif
+ Mix_CloseAudio();
+ Mix_Quit();
+ }
+ SDL_Quit();
+}
+/* sources/TZR_ReloadResource.c */
+
+int
+TZR_ReloadResource(TZR_Uint id)
+{
+ TZR_Resource *const res = TZR_GetResourcePointer(id);
+ if (res->path == NULL) {
+ fprintf(stderr, "resource path of %u is NULL\n", id);
+ return -1;
+ }
+ FILE *const fp = fopen(res->path, "rb");
+ if (fp == NULL) {
+ perror(res->path);
+ return -1;
+ }
+ size_t size;
+ char *const data = drain(fp, &size);
+ fclose(fp);
+ if (data == NULL)
+ return -1;
+ TZR_Resource new_res;
+ memcpy(&new_res, res, sizeof(TZR_Resource));
+ const int load_rv = TZR_DirectResourceLoad(&new_res, data, size);
+ free(data);
+ if (load_rv)
+ return -1;
+ TZR_DestroyResource(res, 0);
+ memcpy(res, &new_res, sizeof(TZR_Resource));
+ return 0;
+}
+/* sources/TZR_ResourcesWatch.c */
+
+void
+TZR_ResourcesWatch(void)
+{
+ for (TZR_Uint i = 0; i < ___tzr_resources_size; i++) {
+ TZR_Resource *const res = TZR_GetResourcePointer(i + 1);
+ if (res->path == NULL)
+ continue;
+ struct stat st = {0};
+ (void)stat(res->path, &st);
+ if (st.st_mtime != res->mtime) {
+ if (res->mtime != 0)
+ TZR_ReloadResource(i + 1);
+ res->mtime = st.st_mtime;
+ }
+ }
+}
+/* sources/TZR_ScreenTransform.c */
+
+void
+TZR_ScreenTransform(int *x, int *y)
+{
+ if (___tzr_scale == 0.0)
+ return;
+ if (x != NULL) {
+ *x -= ___tzr_off_x;
+ *x /= ___tzr_scale;
+ }
+ if (y != NULL) {
+ *y -= ___tzr_off_y;
+ *y /= ___tzr_scale;
+ }
+}
+/* sources/TZR_ShouldQuit.c */
+
+int
+TZR_ShouldQuit(void)
+{
+ return ___tzr_should_quit;
+}
+/* sources/TZR_StopMusic.c */
+
+void
+TZR_StopMusic(void)
+{
+ if (!___tzr_config.mixer)
+ return;
+ if (Mix_PlayingMusic())
+ Mix_HaltMusic();
+ if (___tzr_music != NULL)
+ Mix_FreeMusic(___tzr_music);
+ ___tzr_music = NULL;
+}
+/* sources/TZR_ToggleFullscreen.c */
+
+void
+TZR_ToggleFullscreen(void)
+{
+ static bool fullscreen = false;
+ fullscreen = !fullscreen;
+ SDL_SetWindowFullscreen(___tzr_window, fullscreen
+ ? SDL_WINDOW_FULLSCREEN_DESKTOP
+ : 0);
+}
+
+/*
+** Copyright (c) 2023 kdx
+**
+** 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.
+*/
+/* commit hash: ae4af8c1f825d904eaea3be5cdf02e097e30a30f */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..dfd967f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int
+main(int argc, char **argv)
+{
+ return 0;
+}