From 185c2c0b1e7d1462f8a7167f2bdf475f7a7650e2 Mon Sep 17 00:00:00 2001 From: kdx Date: Fri, 13 Jan 2023 20:26:21 +0100 Subject: wtf am i doing --- .gitignore | 4 + Makefile | 56 ++++ cfg.h | 20 ++ input.c | 39 +++ input.h | 17 ++ lzr.c | 916 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lzr.h | 129 +++++++++ main.c | 69 +++++ res/0.bmp | Bin 0 -> 752218 bytes res/1.bmp | Bin 0 -> 752218 bytes res/2.bmp | Bin 0 -> 752218 bytes res/3.bmp | Bin 0 -> 752218 bytes res/4.bmp | Bin 0 -> 752218 bytes res/5.bmp | Bin 0 -> 752218 bytes res/6.bmp | Bin 0 -> 752218 bytes res/7.bmp | Bin 0 -> 752218 bytes 16 files changed, 1250 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cfg.h create mode 100644 input.c create mode 100644 input.h create mode 100644 lzr.c create mode 100644 lzr.h create mode 100644 main.c create mode 100644 res/0.bmp create mode 100644 res/1.bmp create mode 100644 res/2.bmp create mode 100644 res/3.bmp create mode 100644 res/4.bmp create mode 100644 res/5.bmp create mode 100644 res/6.bmp create mode 100644 res/7.bmp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0eb2c8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +004 +004.* +*.o +*.d diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8788ff7 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +CCXX := g++ +CC := gcc +LD := gcc +SDL2-CFG := sdl2-config +SRC := $(wildcard *.c) $(wildcard *.cpp) +OBJ := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SRC))) +WINOBJ := $(patsubst %.c,%.win.o,$(patsubst %.cpp,%.win.o,$(SRC))) +DEP := $(patsubst %.c,%.d,$(patsubst %.cpp,%.d,$(SRC))) +NAME := 004 +ifeq ($(TARGET),windows) + SDL2-CFG := /usr/x86_64-w64-mingw32/bin/sdl2-config + CCXX := x86_64-w64-mingw32-$(CCXX) + CC := x86_64-w64-mingw32-$(CC) + LD := x86_64-w64-mingw32-$(LD) + NAME := $(NAME).exe + OBJ := $(WINOBJ) +endif +LZR_FLAGS := -DLZR_DISABLE_IMAGE -DLZR_DISABLE_GFX -DLZR_DISABLE_MIXER \ + -DLZR_DISABLE_DEVMODE +CFLAGS := -Wall -Wextra -std=c99 -pedantic -MMD \ + -D_POSIX_C_SOURCE=200809L $(LZR_FLAGS) $(shell $(SDL2-CFG) --cflags) +CXXFLAGS := -Wall -Wextra -std=c++03 -pedantic -MMD \ + -nostdlib -fno-rtti -fno-exceptions \ + $(LZR_FLAGS) $(shell $(SDL2-CFG) --cflags) +LDFLAGS := -lm $(shell $(SDL2-CFG) --libs) -lSDL2_mixer + +all: $(NAME) + +$(NAME): $(OBJ) + $(LD) -o $(NAME) $(OBJ) $(LDFLAGS) + +%.o: %.cpp + $(CCXX) $(CXXFLAGS) -c -o $@ $< + +%.win.o: %.cpp + $(CCXX) $(CXXFLAGS) -c -o $@ $< + +%.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.win.o: %.c + $(CC) $(CFLAGS) -c -o $@ $< + +clean: + rm -rf $(NAME) $(OBJ) $(WINOBJ) $(DEP) + +run: $(NAME) + ./$(NAME) + +re: + make clean + make + +.PHONY: all clean run re + +-include $(DEP) diff --git a/cfg.h b/cfg.h new file mode 100644 index 0000000..308c2c9 --- /dev/null +++ b/cfg.h @@ -0,0 +1,20 @@ +#pragma once +#include "lzr.h" + +enum { + CFG_DWIDTH = 421 * 2, + CFG_DHEIGHT = 595, + CFG_FPS = 30, + CFG_TSIZE = 16, +}; + +static const LZR_Config cfg = { + CFG_DWIDTH, + CFG_DHEIGHT, + CFG_FPS, + CFG_TSIZE, + "sunny day", + 0.0, + false, + true +}; diff --git a/input.c b/input.c new file mode 100644 index 0000000..03f0a0e --- /dev/null +++ b/input.c @@ -0,0 +1,39 @@ +#include "input.h" +#include "lzr.h" + +static const unsigned int keys[5] = {LZR_BUTTON_LEFT, LZR_BUTTON_RIGHT, + LZR_BUTTON_UP, LZR_BUTTON_DOWN, + LZR_BUTTON_O}; +static int states[5] = {0}; + +void input_update(void) +{ + int i = 5; + while (i-- > 0) + if (LZR_ButtonDown(keys[i])) + states[i] = + (states[i] == KS_UP) ? (KS_PRESSED) : (KS_DOWN); + else + states[i] = KS_UP; +} + +int input_up(unsigned int k) +{ + if (k >= 5) + return 0; + return states[k] == KS_UP; +} + +int input_down(unsigned int k) +{ + if (k >= 5) + return 0; + return states[k] == KS_DOWN || states[k] == KS_PRESSED; +} + +int input_pressed(unsigned int k) +{ + if (k >= 5) + return 0; + return states[k] == KS_PRESSED; +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..5aebd66 --- /dev/null +++ b/input.h @@ -0,0 +1,17 @@ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +enum Key { K_LEFT, K_RIGHT, K_UP, K_DOWN, K_O }; + +enum KeyState { KS_UP, KS_DOWN, KS_PRESSED }; + +void input_update(void); +int input_up(unsigned int); +int input_down(unsigned int); +int input_pressed(unsigned int); + +#ifdef __cplusplus +} +#endif diff --git a/lzr.c b/lzr.c new file mode 100644 index 0000000..edc6046 --- /dev/null +++ b/lzr.c @@ -0,0 +1,916 @@ +/* Licensing information can be found at the end of the file. */ +#include "lzr.h" +#include +#ifdef LZR_ENABLE_GFX +# include +#endif +#ifdef LZR_ENABLE_IMAGE +# include +#endif +#ifdef LZR_ENABLE_MIXER +# include +#endif +#ifdef LZR_ENABLE_DEVMODE +# include +#endif +#include +#include +#include +#include + +#define UNPACKED_COLOR color[0], color[1], color[2] +#define SCODE_BIND_MENU SDL_SCANCODE_F1 +#define SCODE_FULLSCREEN SDL_SCANCODE_F11 + +static LZR_Config config = {0}; +static char *basepath = NULL; +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDL_Texture *target = NULL; +static uint_least64_t next_time = 0; +static uint_least64_t min_dt = 0; +static bool should_quit = false; +static struct { + SDL_Texture *tex; + int width, height; + char *path; + long mtime; +} images[LZR_MAX_IMAGES] = {0}; +static unsigned int color[3] = {0}; +static unsigned int map[LZR_BUTTON_COUNT] = { + SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP, + SDL_SCANCODE_DOWN, SDL_SCANCODE_X, SDL_SCANCODE_C}; +static bool input[LZR_BUTTON_COUNT] = {false}; +static SDL_Point *points = NULL; +static uint64_t tick = 0; +static int off_x = 0; +static int off_y = 0; +static float scale = 1.0; +static int mouse_x = 0; +static int mouse_y = 0; + +#ifdef LZR_ENABLE_MIXER +static struct { + Mix_Chunk *ptr; +} sounds[LZR_MAX_SOUNDS] = {0}; +static Mix_Music *music = NULL; +#endif + +static char *_lzrstrdup(const char *str) +{ + char *const cpy = malloc(strlen(str) + 1); + if (cpy == NULL) + return NULL; + strcpy(cpy, str); + return cpy; +} + +static int _scode_to_button(unsigned int scode) +{ + for (int i = 0; i < LZR_BUTTON_MOUSE_L; i++) + if (map[i] == scode) + return i; + return -1; +} + +static void _draw_btn(SDL_Renderer *ren, int btn, int x, int y, + unsigned int size) +{ +#ifdef LZR_ENABLE_GFX + const unsigned int size_2thirds = size * 2 / 3; + switch (btn) { + case LZR_BUTTON_LEFT: + filledTrigonRGBA(ren, x - size / 2, y, x + size / 2, + y + size_2thirds, x + size / 2, + y - size_2thirds, 0, 0, 0, 255); + break; + case LZR_BUTTON_RIGHT: + filledTrigonRGBA(ren, x + size / 2, y, x - size / 2, + y + size_2thirds, x - size / 2, + y - size_2thirds, 0, 0, 0, 255); + break; + case LZR_BUTTON_UP: + filledTrigonRGBA(ren, x, y - size / 2, x - size_2thirds, + y + size / 2, x + size_2thirds, y + size / 2, + 0, 0, 0, 255); + break; + case LZR_BUTTON_DOWN: + filledTrigonRGBA(ren, x, y + size / 2, x - size_2thirds, + y - size / 2, x + size_2thirds, y - size / 2, + 0, 0, 0, 255); + break; + case LZR_BUTTON_O: + filledCircleRGBA(ren, x, y, size * 2 / 3, 0, 0, 0, 255); + break; + case LZR_BUTTON_X: + thickLineRGBA(ren, x - size / 2, y - size / 2, x + size / 2, + y + size / 2, size / 16 + 1, 0, 0, 0, 255); + thickLineRGBA(ren, x + size / 2, y - size / 2, x - size / 2, + y + size / 2, size / 16 + 1, 0, 0, 0, 255); + break; + default: + break; + } +#else + (void)ren, (void)btn, (void)x, (void)y, (void)size; +#endif +} + +static void _bind_menu(void) +{ + SDL_Log("entering bind menu"); + SDL_Window *win = NULL; + SDL_Renderer *ren = NULL; + if (SDL_CreateWindowAndRenderer(256, 256, 0, &win, &ren) < 0) { + SDL_Log("%s", SDL_GetError()); + return; + } + int btn = 0; + SDL_Event e; + while (btn < LZR_BUTTON_MOUSE_L) { + while (SDL_PollEvent(&e)) { + if (e.type != SDL_KEYDOWN || e.key.repeat || + e.key.keysym.scancode == SCODE_BIND_MENU || + e.key.keysym.scancode == SCODE_FULLSCREEN) + continue; + if (e.key.keysym.scancode == SDL_SCANCODE_ESCAPE || + e.type == SDL_QUIT) + goto exit_bind_menu; + LZR_ButtonBind(btn, e.key.keysym.scancode); + btn++; + } + SDL_SetRenderDrawColor(ren, 220, 220, 200, 255); + SDL_RenderClear(ren); + SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); + _draw_btn(ren, btn, 128, 128, 104); + SDL_RenderPresent(ren); + sleep(0); + } +exit_bind_menu: + SDL_DestroyRenderer(ren); + SDL_DestroyWindow(win); + SDL_Log("leaving bind menu"); +} + +int LZR_Init(LZR_Config cfg) +{ + memcpy(&config, &cfg, sizeof(config)); + if (config.display_width == 0) { + SDL_Log("display_width can't be 0"); + return -1; + } + if (config.display_height == 0) { + SDL_Log("display_height can't be 0"); + return -1; + } + if (config.title == NULL) { + SDL_Log("title is NULL, defaulting to 'LZR'"); + config.title = "LZR"; + } + if (config.tile_size == 0) + config.tile_size = 1; + if (config.ratio <= 0.0) + config.ratio = 1.0; + else { + const double ratio = + (float)config.display_width / (float)config.display_height; + config.ratio /= ratio; + } + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } +#ifdef LZR_ENABLE_IMAGE + if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG) { + SDL_Log("%s", IMG_GetError()); + return -1; + } +#endif +#ifdef LZR_ENABLE_MIXER + if (Mix_Init(MIX_INIT_OGG) != MIX_INIT_OGG) { + SDL_Log("%s", Mix_GetError()); + return -1; + } + if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 8, 1024) < 0) { + SDL_Log("%s", Mix_GetError()); + return -1; + } +#endif + basepath = SDL_GetBasePath(); + if (basepath == NULL) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + const int dwidth = config.display_width * config.ratio; + window = SDL_CreateWindow(config.title, SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, dwidth, + config.display_height, SDL_WINDOW_RESIZABLE); + if (window == NULL) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + if (renderer == NULL) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_TARGET, + config.display_width, config.display_height); + if (target == NULL) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + points = + calloc(cfg.display_width * cfg.display_height, sizeof(SDL_Point)); + if (points == NULL) { + SDL_Log("calloc failed"); + return -1; + } + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0); + if (config.target_fps) { + min_dt = 1000 / config.target_fps; + next_time = SDL_GetTicks64(); + } + if (config.hide_cursor && SDL_ShowCursor(SDL_DISABLE) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +void LZR_Quit(void) +{ + LZR_StopMusic(); +#ifdef LZR_ENABLE_MIXER + for (int i = 0; i < LZR_MAX_SOUNDS; i++) + if (sounds[i].ptr != NULL) { + Mix_FreeChunk(sounds[i].ptr); + sounds[i].ptr = NULL; + SDL_Log("destroyed sound %d", i); + } + Mix_CloseAudio(); + Mix_Quit(); +#endif + for (int i = 0; i < LZR_MAX_IMAGES; i++) { + if (images[i].tex != NULL) { + SDL_DestroyTexture(images[i].tex); + images[i].tex = NULL; + SDL_Log("destroyed image %d", i); + } + if (images[i].path != NULL) { + free(images[i].path); + images[i].path = NULL; + } + } + if (points != NULL) { + free(points); + points = NULL; + } + if (target != NULL) { + SDL_DestroyTexture(target); + target = NULL; + } + if (renderer != NULL) { + SDL_DestroyRenderer(renderer); + renderer = NULL; + } + if (window != NULL) { + SDL_DestroyWindow(window); + window = NULL; + } + if (basepath != NULL) { + SDL_free(basepath); + basepath = NULL; + } +#ifdef LZR_ENABLE_IMAGE + IMG_Quit(); +#endif + SDL_Quit(); +} + +bool LZR_ShouldQuit(void) +{ + return should_quit; +} + +char *LZR_PathPrefix(const char *path) +{ + if (path == NULL) { + SDL_Log("path is NULL"); + return NULL; + } + if (basepath == NULL) { + SDL_Log("basepath is NULL"); + return _lzrstrdup(path); + } + char *const buf = malloc(strlen(basepath) + strlen(path) + 1); + if (buf == NULL) { + SDL_Log("malloc failed"); + return NULL; + } + strcpy(buf, basepath); + strcat(buf, path); + return buf; +} + +int LZR_ImageLoad(const char *path) +{ + char *apath; + long mtime = 0; +#ifdef LZR_ENABLE_DEVMODE + apath = LZR_PathPrefix(path); + if (apath == NULL) + return -1; + struct stat st = {0}; + (void)stat(apath, &st); /* stat can fail safely */ + mtime = st.st_mtim.tv_nsec; +#endif + int i; + for (i = 0; i < LZR_MAX_IMAGES; i++) { + if (images[i].path != NULL && + strcmp(images[i].path, path) == 0) { + if (mtime != images[i].mtime) { + SDL_Log("reloading %d", i); + break; + } + return i; + } + if (images[i].tex == NULL) + break; + } + if (i >= LZR_MAX_IMAGES) { + SDL_Log("reached image limit (%d)", LZR_MAX_IMAGES); + return -1; + } + apath = LZR_PathPrefix(path); + if (apath == NULL) { + SDL_Log("LZR_PathPrefix failed"); + return -1; + } +#ifdef LZR_ENABLE_IMAGE + SDL_Surface *const surf = IMG_Load(apath); +#else + SDL_Surface *const surf = SDL_LoadBMP(apath); +#endif + free(apath); + if (surf == NULL) { + SDL_Log("%s: %s", path, SDL_GetError()); + return -1; + } + SDL_Texture *const tex = SDL_CreateTextureFromSurface(renderer, surf); + SDL_FreeSurface(surf); + if (tex == NULL) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + if (images[i].tex != NULL) + SDL_DestroyTexture(images[i].tex); + images[i].tex = tex; + images[i].mtime = mtime; + if (SDL_SetTextureBlendMode(images[i].tex, SDL_BLENDMODE_BLEND) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + if (SDL_QueryTexture(tex, NULL, NULL, &images[i].width, + &images[i].height)) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + if (images[i].path == NULL) + images[i].path = _lzrstrdup(path); + return i; +} + +int LZR_SoundLoad(const char *path, float volume) +{ +#ifdef LZR_ENABLE_MIXER + int i; + for (i = 0; i < LZR_MAX_SOUNDS; i++) + if (sounds[i].ptr == NULL) + break; + if (i >= LZR_MAX_SOUNDS) { + SDL_Log("reached sounds limit (%d)", LZR_MAX_SOUNDS); + return -1; + } + char *const apath = LZR_PathPrefix(path); + if (apath == NULL) { + SDL_Log("LZR_PathPrefix failed"); + return -1; + } + Mix_Chunk *const chunk = Mix_LoadWAV(apath); + free(apath); + if (chunk == NULL) { + SDL_Log("%s: %s", path, Mix_GetError()); + return -1; + } + Mix_VolumeChunk(chunk, volume * MIX_MAX_VOLUME); + sounds[i].ptr = chunk; + return i; +#else + (void)path, (void)volume; + return -1; +#endif +} + +bool LZR_PollEvent(LZR_Event *e) +{ + if (e == NULL) { + SDL_Log("e is NULL"); + return false; + } + SDL_Event se; + while (SDL_PollEvent(&se)) { + switch (se.type) { + case SDL_QUIT: + e->type = LZR_EVENT_QUIT; + should_quit = true; + return true; + case SDL_KEYDOWN: { + if (!config.disable_bind_menu && + se.key.keysym.scancode == SCODE_BIND_MENU) + _bind_menu(); + if (se.key.keysym.scancode == SCODE_FULLSCREEN) + LZR_ToggleFullscreen(); + const int b = _scode_to_button(se.key.keysym.scancode); + if (se.key.repeat || b < 0) + break; + e->type = LZR_EVENT_BUTTON_DOWN; + e->button = b; + input[b] = true; + return true; + } + case SDL_MOUSEBUTTONDOWN: { + e->type = LZR_EVENT_BUTTON_DOWN; + e->button = LZR_BUTTON_MOUSE_L + se.button.button - 1; + e->x = se.button.x, e->y = se.button.y; + LZR_ScreenTransform(&e->x, &e->y); + mouse_x = e->x, mouse_y = e->y; + if (e->button >= LZR_BUTTON_COUNT) + continue; + input[e->button] = true; + return true; + } + case SDL_KEYUP: { + const int b = _scode_to_button(se.key.keysym.scancode); + if (b < 0) + break; + e->type = LZR_EVENT_BUTTON_UP; + e->button = b; + input[b] = false; + return true; + } + case SDL_MOUSEBUTTONUP: { + e->type = LZR_EVENT_BUTTON_DOWN; + e->button = LZR_BUTTON_MOUSE_L + se.button.button - 1; + e->x = se.button.x, e->y = se.button.y; + LZR_ScreenTransform(&e->x, &e->y); + mouse_x = e->x, mouse_y = e->y; + if (e->button >= LZR_BUTTON_COUNT) + continue; + input[e->button] = false; + return true; + } + case SDL_MOUSEMOTION: { + e->type = LZR_EVENT_MOUSE_MOVE; + e->x = se.motion.x, e->y = se.motion.y; + LZR_ScreenTransform(&e->x, &e->y); + mouse_x = e->x, mouse_y = e->y; + return true; + } + default: + break; + } + } + return false; +} + +void LZR_CycleEvents(void) +{ + LZR_Event e; + while (LZR_PollEvent(&e)) + ; +} + +bool LZR_ButtonDown(LZR_Button btn) +{ + if (btn >= 0 && btn < LZR_BUTTON_COUNT) + return input[btn]; + else + SDL_Log("%d button doesn't exist", btn); + return false; +} + +void LZR_ButtonBind(LZR_Button btn, unsigned int code) +{ + if (btn < LZR_BUTTON_MOUSE_L && code != SCODE_BIND_MENU) { + map[btn] = code; + SDL_Log("bound key %s to button %u", + SDL_GetScancodeName(map[btn]), btn); + } else + SDL_Log("button %u can't be remapped to key %s", btn, + SDL_GetScancodeName(code)); +} + +int LZR_DrawBegin(void) +{ + if (config.target_fps > 0) + next_time += min_dt; + if (SDL_SetRenderTarget(renderer, target) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderClear(renderer); + return 0; +} + +int LZR_DrawEnd(void) +{ + if (SDL_SetRenderTarget(renderer, NULL)) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + if (LZR_DrawClear()) { + SDL_Log("LZY_DrawClear failed"); + return -1; + } + if (config.target_fps) { + const uint_least64_t cur_time = SDL_GetTicks64(); + if (next_time <= cur_time) + next_time = cur_time; + else + SDL_Delay(next_time - cur_time); + } + int win_w, win_h; + SDL_GetWindowSize(window, &win_w, &win_h); + const int width = config.display_width * config.ratio; + const int height = config.display_height; + const int ratio_w = win_w / width; + const int ratio_h = win_h / height; + scale = (ratio_w <= ratio_h) ? ratio_w : ratio_h; + off_x = (win_w - width * scale) / 2; + off_y = (win_h - height * scale) / 2; + const SDL_Rect dest = {off_x, off_y, width * scale, height * scale}; + if (SDL_RenderCopyEx(renderer, target, NULL, &dest, 0.0, NULL, 0) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + SDL_RenderPresent(renderer); + tick++; + return 0; +} + +int LZR_DrawSetColor(float r, float g, float b) +{ + const unsigned int ur = (unsigned int)(r * 255) & 255; + const unsigned int ug = (unsigned int)(g * 255) & 255; + const unsigned int ub = (unsigned int)(b * 255) & 255; + if (SDL_SetRenderDrawColor(renderer, ur, ug, ub, 255) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + color[0] = ur, color[1] = ug, color[2] = ub; + return 0; +} + +int LZR_DrawClear(void) +{ + if (SDL_RenderClear(renderer) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawPoint(int x, int y) +{ + if (SDL_RenderDrawPoint(renderer, x, y) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawPoints(int *x, int *y, int n) +{ + + if (n > (int)(config.display_width * config.display_height)) { + SDL_Log("%d > %u", n, + config.display_width * config.display_height); + return -1; + } + for (int i = 0; i < n; i++) { + points[i].x = x[i]; + points[i].y = y[i]; + } + if (SDL_RenderDrawPoints(renderer, points, n) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawLine(int x0, int y0, int x1, int y1) +{ + if (SDL_RenderDrawLine(renderer, x0, y0, x1, y1) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawRectangle(bool fill, int x, int y, int w, int h) +{ + SDL_Rect rect = {x, y, w, h}; + if ((fill ? SDL_RenderFillRect : SDL_RenderDrawRect)(renderer, &rect)) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawCircle(bool fill, int x, int y, int radius) +{ +#ifdef LZR_ENABLE_GFX + if ((fill ? filledCircleRGBA : circleRGBA)(renderer, x, y, radius, + UNPACKED_COLOR, 255) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +#else + (void)fill, (void)x, (void)y, (void)radius; + SDL_Log("LZR GFX module is disabled"); + return -1; +#endif +} + +int LZR_DrawPolygon(bool fill, int *vx, int *vy, int n) +{ +#ifdef LZR_ENABLE_GFX + if (n > 32) { + SDL_Log("%d > 32", n); + return -1; + } + Sint16 x[32], y[32]; + for (int i = 0; i < n; i++) + x[i] = vx[i], y[i] = vy[i]; + if ((fill ? filledPolygonRGBA : polygonRGBA)(renderer, x, y, n, + UNPACKED_COLOR, 255) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +#else + (void)fill, (void)vx, (void)vy, (void)n; + SDL_Log("LZR GFX module is disabled"); + return -1; +#endif +} + +int LZR_DrawImage(int id, int x, int y) +{ + if (id < 0) { + SDL_Log("id is negative"); + return -1; + } + if (id >= LZR_MAX_IMAGES || images[id].tex == NULL) { + SDL_Log("no image with id %d", id); + return -1; + } + if (SDL_SetTextureColorMod(images[id].tex, UNPACKED_COLOR) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + const SDL_Rect dest = {x, y, images[id].width, images[id].height}; + if (SDL_RenderCopy(renderer, images[id].tex, NULL, &dest) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawImageEx(int id, int x, int y, LZR_ImageDrawSettings stg) +{ + if (id < 0) { + SDL_Log("id is negative"); + return -1; + } + if (id >= LZR_MAX_IMAGES || images[id].tex == NULL) { + SDL_Log("no image with id %d", id); + return -1; + } + const int width = (stg.width > 0) ? stg.width : images[id].width; + const int height = (stg.height > 0) ? stg.height : images[id].height; + if (stg.center) { + x -= stg.scale_x * width / 2; + y -= stg.scale_y * height / 2; + } + SDL_Rect src = {stg.ix, stg.iy, width, height}; + SDL_Rect dst = {x, y, width * stg.scale_x, height * stg.scale_y}; + if (stg.ix < 0) { + src.w += stg.ix; + dst.x = 0 - stg.ix; + dst.w += stg.ix; + } + if (stg.iy < 0) { + src.y = 0 - stg.iy; + src.h += stg.iy; + dst.y -= stg.iy; + dst.h += stg.iy; + } + if (stg.ix + width > images[id].width) { + src.w = images[id].width - stg.ix; + dst.w = images[id].width - stg.ix; + } + if (stg.iy + height > images[id].height) { + src.h = images[id].height - stg.iy; + dst.h = images[id].height - stg.iy; + } + if (SDL_SetTextureColorMod(images[id].tex, UNPACKED_COLOR) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + const int flip = (stg.flip_v ? SDL_FLIP_VERTICAL : 0) | + (stg.flip_h ? SDL_FLIP_HORIZONTAL : 0); + if (SDL_RenderCopyEx(renderer, images[id].tex, &src, &dst, + stg.angle * 360.0, NULL, flip)) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_DrawTile(int id, int tile, int x, int y, double rot, int flip) +{ + if (id < 0) { + SDL_Log("id is negative"); + return -1; + } + if (id >= LZR_MAX_IMAGES || images[id].tex == NULL) { + SDL_Log("no image with id %d", id); + return -1; + } + if (tile < 0) { + SDL_Log("tile is negative"); + return -1; + } + const int img_width = images[id].width / config.tile_size; + const int img_height = images[id].height / config.tile_size; + if (tile >= img_width * img_height) { + SDL_Log("tile exceeds boundaries"); + return -1; + } + if (SDL_SetTextureColorMod(images[id].tex, UNPACKED_COLOR) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + SDL_Rect src; + src.x = (tile % img_width) * config.tile_size; + src.y = (tile / img_width) * config.tile_size; + src.w = config.tile_size, src.h = config.tile_size; + const SDL_Rect dst = {x, y, config.tile_size, config.tile_size}; + if (SDL_RenderCopyEx(renderer, images[id].tex, &src, &dst, rot, NULL, + flip) < 0) { + SDL_Log("%s", SDL_GetError()); + return -1; + } + return 0; +} + +int LZR_PlaySound(int id) +{ +#ifdef LZR_ENABLE_MIXER + if (id < 0) { + SDL_Log("id is negative"); + return -1; + } + if (id >= LZR_MAX_SOUNDS || sounds[id].ptr == NULL) { + SDL_Log("no sound with id %d", id); + return -1; + } + if (Mix_PlayChannel(-1, sounds[id].ptr, 0) < 0) { + SDL_Log("%s", Mix_GetError()); + return -1; + } + return 0; +#else + (void)id; + SDL_Log("LZR MIXER module is disabled"); + return -1; +#endif +} + +int LZR_SetMusicVolume(float volume) +{ +#ifdef LZR_ENABLE_MIXER + if (Mix_VolumeMusic(volume * MIX_MAX_VOLUME) < 0) { + SDL_Log("%s", Mix_GetError()); + return -1; + } + return 0; +#else + (void)volume; + SDL_Log("LZR MIXER module is disabled"); + return -1; +#endif +} + +int LZR_PlayMusic(const char *path, int loops) +{ +#ifdef LZR_ENABLE_MIXER + LZR_StopMusic(); + char *const apath = LZR_PathPrefix(path); + if (apath == NULL) { + SDL_Log("LZR_PathPrefix failed"); + return -1; + } + music = Mix_LoadMUS(apath); + free(apath); + if (music == NULL) { + SDL_Log("%s: %s", path, Mix_GetError()); + return -1; + } + if (Mix_PlayMusic(music, loops) < 0) { + SDL_Log("%s", Mix_GetError()); + return -1; + } + Mix_RewindMusic(); + return 0; +#else + (void)path, (void)loops; + SDL_Log("LZR MIXER module is disabled"); + return -1; +#endif +} + +void LZR_StopMusic(void) +{ +#ifdef LZR_ENABLE_MIXER + if (Mix_PlayingMusic()) + Mix_HaltMusic(); + if (music != NULL) { + Mix_FreeMusic(music); + music = NULL; + } +#endif +} + +void LZR_ToggleFullscreen(void) +{ + static int fullscreen = 0; + fullscreen = !fullscreen; + SDL_SetWindowFullscreen(window, + fullscreen * SDL_WINDOW_FULLSCREEN_DESKTOP); +} + +uint64_t LZR_GetTick(void) +{ + return tick; +} + +void LZR_ScreenTransform(int *x, int *y) +{ + if (scale == 0.0) + return; + if (x != NULL) { + *x -= off_x; + *x /= scale; + } + if (y != NULL) { + *y -= off_y; + *y /= scale; + } +} + +void LZR_MousePosition(int *x, int *y) +{ + if (x != NULL) + *x = mouse_x; + if (y != NULL) + *y = mouse_y; +} + +/* +** Copyright (c) 2022, 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. +*/ diff --git a/lzr.h b/lzr.h new file mode 100644 index 0000000..220d13e --- /dev/null +++ b/lzr.h @@ -0,0 +1,129 @@ +/* Licensing informations can be found at the end of the file. */ +#pragma once +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifndef LZR_DISABLE_IMAGE +# define LZR_ENABLE_IMAGE +#endif +#ifndef LZR_DISABLE_MIXER +# define LZR_ENABLE_MIXER +#endif +#ifndef LZR_DISABLE_GFX +# define LZR_ENABLE_GFX +#endif + +/* devmode tracks and autoreloads ressources on change */ +#ifndef LZR_DISABLE_DEVMODE +# define LZR_ENABLE_DEVMODE +#endif + +#define LZR_MAX_IMAGES 32 +#define LZR_MAX_SOUNDS 64 +#define LZR_BUTTON(btn) LZR_ButtonDown(LZR_BUTTON_##btn) +#define LZR_IMAGE(img) LZR_ImageLoad(img) + +typedef struct LZR_Config { + unsigned int display_width; + unsigned int display_height; + unsigned int target_fps; + unsigned int tile_size; + const char *title; + double ratio; + bool disable_bind_menu; + bool hide_cursor; +} LZR_Config; + +typedef enum LZR_EventType { + LZR_EVENT_QUIT, + LZR_EVENT_BUTTON_DOWN, + LZR_EVENT_BUTTON_UP, + LZR_EVENT_MOUSE_MOVE +} LZR_EventType; + +typedef enum LZR_Button { + LZR_BUTTON_LEFT, + LZR_BUTTON_RIGHT, + LZR_BUTTON_UP, + LZR_BUTTON_DOWN, + LZR_BUTTON_O, + LZR_BUTTON_X, + LZR_BUTTON_MOUSE_L, + LZR_BUTTON_MOUSE_M, + LZR_BUTTON_MOUSE_R, + LZR_BUTTON_COUNT +} LZR_Button; + +typedef struct LZR_Event { + LZR_EventType type; + LZR_Button button; + int x, y; +} LZR_Event; + +typedef struct LZR_ImageDrawSettings { + int ix, iy, width, height; + double scale_x, scale_y, angle; + bool center, flip_h, flip_v; +} LZR_ImageDrawSettings; + +int LZR_Init(LZR_Config cfg); +void LZR_Quit(void); +bool LZR_ShouldQuit(void); +bool LZR_PollEvent(LZR_Event *e); +void LZR_CycleEvents(void); +bool LZR_ButtonDown(LZR_Button btn); +void LZR_ButtonBind(LZR_Button btn, unsigned int code); +char *LZR_PathPrefix(const char *path); +int LZR_ImageLoad(const char *path); +int LZR_SoundLoad(const char *path, float volume); +int LZR_DrawBegin(void); +int LZR_DrawEnd(void); +int LZR_DrawSetColor(float r, float g, float b); +int LZR_DrawClear(void); +int LZR_DrawPoint(int x, int y); +int LZR_DrawPoints(int *x, int *y, int n); +int LZR_DrawLine(int x0, int y0, int x1, int y1); +int LZR_DrawRectangle(bool fill, int x, int y, int w, int h); +int LZR_DrawCircle(bool fill, int x, int y, int radius); +int LZR_DrawPolygon(bool fill, int *vx, int *vy, int n); +int LZR_DrawImage(int id, int x, int y); +int LZR_DrawImageEx(int id, int x, int y, LZR_ImageDrawSettings stg); +int LZR_DrawTile(int id, int tile, int x, int y, double rot, int flip); +int LZR_PlaySound(int id); +int LZR_SetMusicVolume(float volume); +int LZR_PlayMusic(const char *path, int loops); +void LZR_StopMusic(void); +void LZR_ToggleFullscreen(void); +uint64_t LZR_GetTick(void); +void LZR_ScreenTransform(int *x, int *y); +void LZR_MousePosition(int *x, int *y); + +#ifdef __cplusplus +} +#endif + +/* +** Copyright (c) 2022, 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. +*/ diff --git a/main.c b/main.c new file mode 100644 index 0000000..a8cd2d0 --- /dev/null +++ b/main.c @@ -0,0 +1,69 @@ +#include "lzr.h" +#include "cfg.h" +#include "input.h" +#include +#include + +#define PAGES 8 + +int main(int argc, char **argv) +{ + (void)argc, (void)argv; + if (LZR_Init(cfg)) + return LZR_Quit(), 1; + int pages[PAGES] = { + LZR_IMAGE("res/0.bmp"), + LZR_IMAGE("res/1.bmp"), + LZR_IMAGE("res/2.bmp"), + LZR_IMAGE("res/3.bmp"), + LZR_IMAGE("res/4.bmp"), + LZR_IMAGE("res/5.bmp"), + LZR_IMAGE("res/6.bmp"), + LZR_IMAGE("res/7.bmp"), + }; + for (int i = 0; i < PAGES; i++) + if (pages[i] < 0) + return LZR_Quit(), 1; + LZR_ImageDrawSettings stg = { + .ix = 0, + .iy = 0, + .width = -1, + .height = -1, + .scale_x = 1.0, + .scale_y = 1.0, + .angle = 0.0, + .center = false, + .flip_h = false, + .flip_v = false + }; + int page = 0; + while (!LZR_ShouldQuit()) { + const float color = 1.0 - fabs(sin((float)LZR_GetTick() / 16)); + LZR_CycleEvents(); + input_update(); + if (input_pressed(K_LEFT) && page > 0) + page -= 1; + if (input_pressed(K_RIGHT) && page < 4) + page += 1; + LZR_DrawBegin(); + //LZR_DrawSetColor(color, 1.0 - color, color); + LZR_DrawSetColor(0, 0, 0); + LZR_DrawClear(); + LZR_DrawSetColor(1.0 - color, color, 1.0 - color); + if (page > 0) + LZR_DrawImageEx(pages[page * 2 - 1], + 0, + (CFG_DHEIGHT - 595) / 2, + stg); + if (page < 4) + LZR_DrawImageEx(pages[page * 2], + CFG_DWIDTH - 421, + (CFG_DHEIGHT - 595) / 2, + stg); + //LZR_DrawSetColor(color, 1.0 - color, color); + LZR_DrawSetColor(0, 0, 0); + LZR_DrawEnd(); + } + LZR_Quit(); + return 0; +} diff --git a/res/0.bmp b/res/0.bmp new file mode 100644 index 0000000..f6021f7 Binary files /dev/null and b/res/0.bmp differ diff --git a/res/1.bmp b/res/1.bmp new file mode 100644 index 0000000..5c45822 Binary files /dev/null and b/res/1.bmp differ diff --git a/res/2.bmp b/res/2.bmp new file mode 100644 index 0000000..85312c1 Binary files /dev/null and b/res/2.bmp differ diff --git a/res/3.bmp b/res/3.bmp new file mode 100644 index 0000000..2d70053 Binary files /dev/null and b/res/3.bmp differ diff --git a/res/4.bmp b/res/4.bmp new file mode 100644 index 0000000..c1f03cd Binary files /dev/null and b/res/4.bmp differ diff --git a/res/5.bmp b/res/5.bmp new file mode 100644 index 0000000..463e990 Binary files /dev/null and b/res/5.bmp differ diff --git a/res/6.bmp b/res/6.bmp new file mode 100644 index 0000000..6f051a0 Binary files /dev/null and b/res/6.bmp differ diff --git a/res/7.bmp b/res/7.bmp new file mode 100644 index 0000000..92b88f5 Binary files /dev/null and b/res/7.bmp differ -- cgit v1.2.3