diff options
author | kdx <kikoodx@paranoici.org> | 2023-12-27 15:08:42 +0100 |
---|---|---|
committer | kdx <kikoodx@paranoici.org> | 2023-12-27 15:08:42 +0100 |
commit | 9a053d369978d0eaab704b7dd3d80cfef236ccbd (patch) | |
tree | f32045609bfac1755136eb6814df09a3643629fe | |
download | 007-9a053d369978d0eaab704b7dd3d80cfef236ccbd.tar.gz |
initial commit
-rw-r--r-- | Tupfile | 12 | ||||
-rw-r--r-- | Tupfile.ini | 0 | ||||
-rw-r--r-- | compile_flags.txt | 6 | ||||
-rw-r--r-- | res/death.bmp | bin | 0 -> 1638538 bytes | |||
-rw-r--r-- | res/rain.wav | bin | 0 -> 7614 bytes | |||
-rw-r--r-- | res/title.bmp | bin | 0 -> 265242 bytes | |||
-rwxr-xr-x | run.sh | 2 | ||||
-rw-r--r-- | src/main.c | 67 | ||||
-rw-r--r-- | vendors/TZR.c | 1419 | ||||
-rw-r--r-- | vendors/TZR.h | 502 | ||||
-rw-r--r-- | vendors/_.c | 79 | ||||
-rw-r--r-- | vendors/_.h | 55 |
12 files changed, 2142 insertions, 0 deletions
@@ -0,0 +1,12 @@ +CC = gcc +LD = gcc +SRC = src/*.c vendors/*.c +CFLAGS = -std=c2x -Wall -Wextra -Wno-override-init -iquoteinc -includevendors/_.h -O3 +LDFLAGS = -O3 -s +LIBS = -lSDL2 -lSDL2_mixer +NAME = 007 + +: foreach $(SRC) |> $(CC) -c -o %o $(CFLAGS) %f |> build/%B.o +: build/*.o |> $(LD) $(LDFLAGS) -o %o %f $(LIBS) |> build/$(NAME) + +.gitignore 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..2225c3e --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,6 @@ +-std=c2x +-Wall +-Wextra +-Wno-initializer-overrides +-iquoteinc +-includevendors/_.h diff --git a/res/death.bmp b/res/death.bmp Binary files differnew file mode 100644 index 0000000..259aa95 --- /dev/null +++ b/res/death.bmp diff --git a/res/rain.wav b/res/rain.wav Binary files differnew file mode 100644 index 0000000..cb4a31b --- /dev/null +++ b/res/rain.wav diff --git a/res/title.bmp b/res/title.bmp Binary files differnew file mode 100644 index 0000000..c245624 --- /dev/null +++ b/res/title.bmp @@ -0,0 +1,2 @@ +#!/bin/sh +tup -q && build/007 . diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3298b03 --- /dev/null +++ b/src/main.c @@ -0,0 +1,67 @@ +#include <time.h> + +static int _main_loop(void *udata); + +static float +randf(void) +{ + return 10000. / (float)(rand() % 10000); +} + +int +main([[maybe_unused]] int argc, [[maybe_unused]] char **argv) +{ + srand(time(nullptr)); + defer(wdeinit); + assert(TZR_Init(.interlace=true, + .width=640, + .height=640, + .ratio=4/3., + .basepath=argv[1], + .pixel_perfect=true, + .target_fps=60) == 0); + defer(TZR_Quit); + TZR_MainLoop(_main_loop, nullptr); + return 0; +} + +static int +_main_loop([[maybe_unused]] void *udata) +{ + assert(TZR_DrawBegin() == 0); + if (rand() % 10 == 0) { + int r; do r = rand(); while ((r & 7) == 0); + TZR_DrawSetColor(r & 1, (r & 2) != 0, (r & 4) != 0); + TZR_DrawClear(); + TZR_PlaySound(TZR_RES("res/rain.wav")); + + TZR_DrawSetColor(0, 0, 0); + + if (rand() % 128) { + TZR_DrawImage(TZR_RES("res/title.bmp"), + ___tzr_config.width / 2 + 255 - rand() % 512, + ___tzr_config.height / 2 + 255 - rand() % 512, + .center=true, + .sx=randf() * 2, + .sy=randf() * 2, + .r=randf(), + .flip_x=(rand()%3)==0, + .flip_y=(rand()%3)==0); + } else { + TZR_DrawImage(TZR_RES("res/death.bmp"), + ___tzr_config.width / 2, + ___tzr_config.height / 2, + .center=true, + .sx=1.0+randf()*.1, + .sy=1.0+randf()*.1); + } + //map_draw(); + //player_draw(); + } else { + TZR_DrawSetColor(0, 0, 0); + TZR_DrawClear(); + } + + assert(TZR_DrawEnd() == 0); + return 0; +} diff --git a/vendors/TZR.c b/vendors/TZR.c new file mode 100644 index 0000000..e28348f --- /dev/null +++ b/vendors/TZR.c @@ -0,0 +1,1419 @@ +/* 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 <errno.h> +#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 <SDL2/SDL_ttf.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; +SDL_Texture *___tzr_target_pre = 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; +int ___tzr_camera_x = 0; +int ___tzr_camera_y = 0; + +#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: + TZR_Log("unknown resource type %u", 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) { + TZR_Log("stbi: %s", stbi_failure_reason()); + return NULL; + } + + if (chans != 4) { + TZR_Log("TZR only supports RGBA images"); + 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) { + TZR_Log("%s", strerror(errno)); + 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) { + TZR_Log("audio mixer disabled, skip loading"); + return -1; + } +#ifdef TZR_SOLOUD + Wav *wav = Wav_create(); + if (wav == NULL) { + TZR_Log("Wav_create failed"); + return -1; + } + if (Wav_loadMemEx(wav, data, size, true, false)) { + TZR_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: + TZR_Log("invalid type"); + 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 (___tzr_config.interlace) { + SDL_Texture *const tmp = ___tzr_target; + ___tzr_target = ___tzr_target_pre; + ___tzr_target_pre = tmp; + } + 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 (___tzr_config.interlace) { + static int odd = 0; + odd ^= 1; + for (int i = odd; i < ___tzr_config.height; i += 2) { + const SDL_Rect rect = { + 0, + i, + ___tzr_config.width, + 1 + }; + if (SDL_RenderCopy(___tzr_renderer, + ___tzr_target_pre, + &rect, + &rect) < 0) + return sdl_error(-1); + } + } + + 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 int width = ___tzr_config.width * ___tzr_config.ratio; + const float ratio_w = win_w / (float)(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 - 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, + 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) { + TZR_Log("args->id is 0"); + return -1; + } + + if (TZR_GetResourceType(args->id) != TZR_RES_IMAGE) { + TZR_Log("%u isn't an image", 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) + TZR_GetCameraX(), + y - (args->center ? (simg_height * scale_y / 2) : 0) + TZR_GetCameraY(), + 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) +{ + x0 += TZR_GetCameraX(); + y0 += TZR_GetCameraY(); + x1 += TZR_GetCameraX(); + y1 += TZR_GetCameraY(); + 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) +{ + x += TZR_GetCameraX(); + y += TZR_GetCameraY(); + 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) + TZR_GetCameraX(), + args->y - args->center * (args->h / 2) + TZR_GetCameraY(), + 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_GetCameraX.c */ + +int +TZR_GetCameraX(void) +{ + return ___tzr_camera_x; +} +/* sources/TZR_GetCameraY.c */ + +int +TZR_GetCameraY(void) +{ + return ___tzr_camera_y; +} +/* 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) + { + TZR_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 TZR_Log("%s", strerror(errno)), TZR_Quit(), -1; + + ___tzr_window = + SDL_CreateWindow(config->title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + ___tzr_config.width * ___tzr_config.ratio + * ___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_SetViewportSize(___tzr_config.width, ___tzr_config.height)) { + TZR_Quit(); + return -1; + } + + 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) { + TZR_Log("Soloud_create failed"); + Mix_Quit(); + ___tzr_config.mixer = false; + break; + } + + if (Soloud_init(___tzr_soloud)) { + TZR_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))) + { + TZR_Log("failed to reserve for new ressource"); + 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) { + TZR_Log("%s: %s", path, strerror(errno)); + 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) + TZR_Log("%s", strerror(errno)); + 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) { + TZR_Log("mixer is disabled"); + 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) { + TZR_Log("args->id is 0"); + return -1; + } + + if (TZR_GetResourceType(args->id) != TZR_RES_SOUND) { + TZR_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 *))) + { + TZR_Log("failed to reserve for new joystick"); + 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; + TZR_Log("opened joystick"); + 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; + } + } + TZR_Log("didn't remove no joystick"); + 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_pre != NULL) { + SDL_DestroyTexture(___tzr_target_pre); + ___tzr_target = NULL; + } + 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) { + TZR_Log("resource path of %u is NULL", id); + return -1; + } + FILE *const fp = fopen(res->path, "rb"); + if (fp == NULL) { + TZR_Log("%s: %s", res->path, strerror(errno)); + 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_SetCamera.c */ + +void +TZR_SetCamera(float x, float y) +{ + ___tzr_camera_x = -(int)(x + 0.5f); + ___tzr_camera_y = -(int)(y + 0.5f); +} +/* sources/TZR_SetViewportSize.c */ + +int +TZR_SetViewportSize(int width, int height) +{ + const int pw = ___tzr_config.width; + const int ph = ___tzr_config.height; + ___tzr_config.width = width; + ___tzr_config.height = height; + if (___tzr_config.hd_render) { + if (SDL_RenderSetLogicalSize(___tzr_renderer, + ___tzr_config.width, + ___tzr_config.height) < 0) + return sdl_error(-1); + } else { + if (___tzr_target != NULL && pw == width && ph == height) + return 0; + if (___tzr_target != NULL) + SDL_DestroyTexture(___tzr_target); + ___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(-1); + if (___tzr_config.interlace) { + ___tzr_target_pre = SDL_CreateTexture( + ___tzr_renderer, + SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_TARGET, + ___tzr_config.width, + ___tzr_config.height); + } + } + return 0; +} + +/* 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: 221a7d0618b4f8b6af19b089f49818806fe1e1b3 */ diff --git a/vendors/TZR.h b/vendors/TZR.h new file mode 100644 index 0000000..a06ea3b --- /dev/null +++ b/vendors/TZR.h @@ -0,0 +1,502 @@ +/* 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_Font TZR_Font; +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; + float scale; + int target_fps; + float ratio; + bool pixel_perfect; + bool interlace; + 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_Font { + void *ptr; + int ptsize; +}; + +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; + TZR_Font ttf; + }; +}; + +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 SDL_Texture *___tzr_target_pre; +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; +extern int ___tzr_camera_x; +extern int ___tzr_camera_y; + +#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_camera.h */ + +void TZR_SetCamera(float x, float y); +int TZR_GetCameraX(void); +int TZR_GetCameraY(void); +/* headers/TZR.h */ + +#ifndef TZR_Log +#define TZR_Log SDL_Log +#endif + +/* 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=1.0f, \ + .target_fps=60, \ + .ratio=1.0f, \ + .pixel_perfect=true, \ + .interlace=false, \ + .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); + +/* NEVER call this during draw */ +int TZR_SetViewportSize(int width, int height); + +#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: 221a7d0618b4f8b6af19b089f49818806fe1e1b3 */ diff --git a/vendors/_.c b/vendors/_.c new file mode 100644 index 0000000..9092bad --- /dev/null +++ b/vendors/_.c @@ -0,0 +1,79 @@ +#include "_.h" +#undef free +#undef malloc +#undef calloc +#undef realloc + +static void **_alloced = nullptr; +static size_t _alloced_size = 0; +static size_t _alloced_capacity = 0; + +void +wdeinit(void) +{ + if (_alloced != nullptr) { + rfor (i, 0u, _alloced_size) + free(_alloced[i]); + free(_alloced); + } + _alloced = nullptr; + _alloced_size = 0; + _alloced_capacity = 0; +} + +void * +alloc(size_t size) +{ + _alloced_size += 1; + if (_alloced == nullptr) { + _alloced = calloc(sizeof(void *), 16); + assert(_alloced != nullptr); + _alloced_capacity = 16; + } + if (_alloced_size > _alloced_capacity) { + size_t new_alloced_capacity = 16; + while (new_alloced_capacity <= _alloced_size) + new_alloced_capacity *= 2; + void *new_alloced = + realloc(_alloced, new_alloced_capacity * sizeof(void *)); + _alloced_size -= (new_alloced == nullptr); + assert(new_alloced != nullptr); + _alloced = new_alloced; + _alloced_capacity = new_alloced_capacity; + } + + _alloced[_alloced_size - 1] = calloc(1, size); + assert(_alloced[_alloced_size - 1] != nullptr); + return _alloced[_alloced_size - 1]; +} + +void +_free(void *ptr) +{ + rfor (i, 0u, _alloced_size) { + if (_alloced[i] == ptr) { + free(_alloced[i]); + _alloced[i] = nullptr; + rfor (k, i, _alloced_size - 1) + _alloced[k] = _alloced[k + 1]; + _alloced_size -= 1; + return; + } + } + pwrn("double free of %p", ptr); +} + +void * +_realloc(void *ptr, size_t size) +{ + rfor (i, 0u, _alloced_size) { + if (_alloced[i] == ptr) { + void *new_ptr = realloc(ptr, size); + assert(new_ptr != nullptr); + _alloced[i] = new_ptr; + return new_ptr; + } + } + panic("fucked up realloc, have fun debugging"); + __builtin_unreachable(); +} diff --git a/vendors/_.h b/vendors/_.h new file mode 100644 index 0000000..ef10927 --- /dev/null +++ b/vendors/_.h @@ -0,0 +1,55 @@ +#pragma once +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "TZR.h" + +void wdeinit(void); +[[nodiscard]] void *alloc(size_t size); +void _free(void *ptr); +void *_realloc(void *ptr, size_t size); + +#define free _free +#define malloc alloc +#define calloc(X, Y) alloc((X) * (Y)) +#define realloc _realloc + +// math +#define clamp(X, MIN, MAX) ({ \ + const auto ___x = (X); \ + const auto ___min = (MIN); \ + const auto ___max = (MAX); \ + (___x > ___max) ? ___max : ((___x < ___min) ? ___min : ___x); \ +}) +#define max(X, Y) ({ \ + const auto ___x = (X); \ + const auto ___y = (Y); \ + (___x > ___y) ? ___x : ___y; \ +}) +#define min(X, Y) ({ \ + const auto ___x = (X); \ + const auto ___y = (Y); \ + (___x < ___y) ? ___x : ___y; \ +}) + +#define STR(X) #X +#define defer(X) if (atexit(X)) { X(); panic("defer"); } +#define auto __auto_type +#define foreach(E, L) for (__auto_type E = (L); E != NULL; E = E->next) +#define rfor(I, F, T) for (__auto_type I = F; I < T; I++) +#define repeat(I, T) for (__auto_type I = 0u; I < T; I++) +#define with(I, T) auto I = (T); if (I) + +// logging +#define pgeneric(PREFIX, ...) fprintf(stderr, \ + "\x1b["PREFIX" %s:%s:%d\x1b[0m \t", \ + __FILE_NAME__, __FUNCTION__, __LINE__), \ + fprintf(stderr, __VA_ARGS__), fputc('\n', stderr) +#define plog(...) pgeneric("94mLOG", __VA_ARGS__) +#define perr(...) pgeneric("91mERR", __VA_ARGS__) +#define pwrn(...) pgeneric("93mWRN", __VA_ARGS__) +#define panic(...) perr(__VA_ARGS__), exit(EXIT_FAILURE) +#define assert(X) if (!(X)) panic("assert failed: "STR(X)) |