summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKikooDX <kikoodx@paranoici.org>2022-04-01 17:37:59 +0200
committerKikooDX <kikoodx@paranoici.org>2022-04-01 17:37:59 +0200
commit9ea7bf73bbe283c9fe97ee68740b8c0c8cffd155 (patch)
tree101235b89264945125a264f583583b55bd741b44
downloadwehfou-9ea7bf73bbe283c9fe97ee68740b8c0c8cffd155.tar.gz
art
-rw-r--r--.clang-format9
-rw-r--r--CMakeLists.txt27
-rw-r--r--Makefile34
-rw-r--r--inc/conf.h6
-rw-r--r--inc/lzy.h1119
-rw-r--r--inc/player.h7
-rw-r--r--res/font.pngbin0 -> 1836 bytes
-rw-r--r--res/fxconv-metadata.txt7
-rw-r--r--res/icon-sel.pngbin0 -> 2783 bytes
-rw-r--r--res/icon-uns.pngbin0 -> 1010 bytes
-rw-r--r--res/tset.pngbin0 -> 2560 bytes
-rw-r--r--src/lzy.c10
-rw-r--r--src/main.c50
-rw-r--r--src/player.c18
14 files changed, 1287 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..edb6674
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,9 @@
+BasedOnStyle: LLVM
+IndentWidth: 8
+UseTab: AlignWithSpaces
+BreakBeforeBraces: Linux
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortFunctionsOnASingleLine: Empty
+IndentCaseLabels: false
+ColumnLimit: 80
+AlignConsecutiveMacros: true
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..fce70ab
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,27 @@
+cmake_minimum_required(VERSION 3.22)
+project(wehfou)
+
+include(GenerateG3A)
+include(Fxconv)
+find_package(Gint 2.7.0 REQUIRED)
+
+include_directories(inc)
+
+set(SOURCES
+ src/main.c
+ src/lzy.c
+)
+
+set(ASSETS
+ res/tset.png
+ res/font.png
+)
+
+fxconv_declare_assets(${ASSETS} WITH_METADATA)
+
+add_executable(thyaddin ${SOURCES} ${ASSETS})
+target_compile_options(thyaddin PRIVATE -Wall -Wextra -Os)
+target_link_libraries(thyaddin Gint::Gint)
+
+generate_g3a(TARGET thyaddin OUTPUT "wehfou.g3a"
+ NAME "wehfou" ICONS res/icon-uns.png res/icon-sel.png)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..e371044
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+CC ?= gcc
+CFLAGS = -std=c99 -Wall -Wextra -O3 -I./inc -MMD $(shell sdl2-config --cflags)
+LDFLAGS = -lSDL2 -lSDL2_image -lSDL2_mixer $(shell sdl2-config --libs)
+
+OBJ_NAME = wehfou
+OBJS := $(patsubst %.c,%.o,$(wildcard src/*.c))
+
+all: $(OBJ_NAME)
+
+$(OBJ_NAME): $(OBJS)
+ $(CC) $(LDFLAGS) $(LIBRARIES) -o $(OBJ_NAME) $(OBJS)
+ strip $(OBJ_NAME)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+cg:
+ fxsdk build-cg
+
+run: $(OBJ_NAME)
+ ./$(OBJ_NAME)
+
+format:
+ @clang-format -style=file -verbose -i src/*.c
+ @clang-format -style=file -verbose -i inc/*.h
+
+clean:
+ rm -f $(OBJ_NAME).g3a $(OBJ_NAME)
+ rm -f $(OBJS) src/*.d
+ rm -Rf build-cg
+
+.PHONY: cg run run-txt format clean
+
+-include src/*.d
diff --git a/inc/conf.h b/inc/conf.h
new file mode 100644
index 0000000..30fceb0
--- /dev/null
+++ b/inc/conf.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#define CHR_WIDTH 8
+#define CHR_HEIGHT 16
+#define DISPLAY_WIDTH 400
+#define DISPLAY_HEIGHT 224
diff --git a/inc/lzy.h b/inc/lzy.h
new file mode 100644
index 0000000..e8805bb
--- /dev/null
+++ b/inc/lzy.h
@@ -0,0 +1,1119 @@
+/*
+** Copyright (c) 2022 KikooDX
+**
+** Permission is hereby granted, free of charge, to any person obtaining a copy
+** of this software and associated documentation files (the "Software"), to
+** deal in the Software without restriction, including without limitation the
+** rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+** sell copies of the Software, and to permit persons to whom the Software is
+** furnished to do so, subject to the following conditions:
+**
+** The above copyright notice and this permission notice shall be included in
+** all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+** IN THE SOFTWARE.
+*/
+
+#ifndef LZY_H_
+#define LZY_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define LZY_UNUSED(x) (void)(x)
+
+#ifdef FXCG50
+#include <gint/keycodes.h>
+typedef void LZY_Music;
+typedef void LZY_Sound;
+enum LZY_ScanCode {
+ LZYK_LEFT = KEY_LEFT,
+ LZYK_RIGHT = KEY_RIGHT,
+ LZYK_UP = KEY_UP,
+ LZYK_DOWN = KEY_DOWN,
+ LZYK_O = KEY_SHIFT,
+ LZYK_X = KEY_OPTN,
+};
+#else /* end FXCG50, begin SDL2 */
+#ifndef LZY_SDL_MIXER_INCLUDE
+#define LZY_SDL_MIXER_INCLUDE <SDL_mixer.h>
+#endif
+#include LZY_SDL_MIXER_INCLUDE
+enum LZY_ScanCode {
+ LZYK_LEFT,
+ LZYK_RIGHT,
+ LZYK_UP,
+ LZYK_DOWN,
+ LZYK_O,
+ LZYK_X,
+ LZYK_COUNT,
+};
+typedef Mix_Music LZY_Music;
+typedef Mix_Chunk LZY_Sound;
+#endif /* SDL2 */
+
+enum LZY_EventType {
+ LZY_QUIT,
+ LZY_KEYDOWN,
+ LZY_KEYUP,
+};
+
+typedef struct LZY_KeyEvent {
+ unsigned int scancode;
+ int down;
+} LZY_KeyEvent;
+
+typedef union LZY_EventUnion {
+ LZY_KeyEvent key;
+} LZY_EventUnion;
+
+typedef struct LZY_Event {
+ int type;
+ LZY_EventUnion u;
+} LZY_Event;
+
+int LZY_Init(int argc, const char **argv, const char *title, int target_fps,
+ const char *tset_path, const char *font_path);
+void LZY_Quit(void);
+int LZY_DrawBegin(void);
+int LZY_DrawEnd(void);
+void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b);
+void LZY_DrawSetColorNone(void);
+int LZY_DrawClear(void);
+int LZY_DrawPoint(int x, int y);
+int LZY_DrawLine(int x0, int y0, int x1, int y1);
+int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h);
+int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h);
+int LZY_DrawTile(unsigned int id, int x, int y);
+int LZY_DrawTileEx(unsigned int id, int x, int y, unsigned int w,
+ unsigned int h);
+int LZY_DrawChar(unsigned char chr, int x, int y);
+int LZY_DrawText(const char *text, int x, int y);
+LZY_Music *LZY_MusicLoad(const char *path);
+int LZY_MusicDestroy(LZY_Music *music);
+int LZY_MusicPlay(LZY_Music *music, int loops);
+LZY_Sound *LZY_SoundLoad(const char *path);
+int LZY_SoundDestroy(LZY_Sound *sound);
+void LZY_SoundSetVolume(LZY_Sound *sound, float volume);
+int LZY_SoundPlay(LZY_Sound *sound, int loops);
+int LZY_PollEvent(LZY_Event *);
+void LZY_CycleEvents(void);
+int LZY_KeyDown(unsigned int key);
+int LZY_ShouldQuit(void);
+void LZY_Log(const char *msg);
+const char *LZY_GetError(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LZY_H_ */
+
+/* implementation */
+#ifdef LZY_IMPLEMENTATION
+#undef LZY_IMPLEMENTATION
+#ifndef LZY_SDL_INCLUDE
+#define LZY_SDL_INCLUDE <SDL.h>
+#endif
+#ifndef LZY_SDL_IMAGE_INCLUDE
+#define LZY_SDL_IMAGE_INCLUDE <SDL_image.h>
+#endif
+#ifndef LZY_SDL_MIXER_INCLUDE
+#define LZY_SDL_MIXER_INCLUDE <SDL_mixer.h>
+#endif
+#ifndef LZY_DISPLAY_WIDTH
+#define LZY_DISPLAY_WIDTH 396
+#endif
+#ifndef LZY_DISPLAY_HEIGHT
+#define LZY_DISPLAY_HEIGHT 224
+#endif
+#ifndef LZY_TILE_SIZE
+#define LZY_TILE_SIZE 16
+#endif
+#ifndef LZY_CHR_WIDTH
+#define LZY_CHR_WIDTH LZY_TILE_SIZE
+#endif
+#ifndef LZY_CHR_HEIGHT
+#define LZY_CHR_HEIGHT LZY_TILE_SIZE
+#endif
+#ifndef LZY_FIRST_CHR
+#define LZY_FIRST_CHR ' '
+#endif
+
+int LZY_DrawLine(int x0, int y0, int x1, int y1)
+{
+ int dx, dy, sx, sy, err, e2;
+ int rc = 0;
+
+ dx = x1 - x0;
+ dx = (dx < 0) ? (-dx) : (dx);
+ dy = y1 - y0;
+ dy = (dy < 0) ? (dy) : (-dy);
+ sx = (x0 < x1) ? (1) : (-1);
+ sy = (y0 < y1) ? (1) : (-1);
+ err = dx + dy;
+
+ for (;;) {
+ if (LZY_DrawPoint(x0, y0))
+ rc = -1;
+
+ if (x0 == x1 && y0 == y1)
+ break;
+
+ e2 = 2 * err;
+ if (e2 >= dy) {
+ err += dy;
+ x0 += sx;
+ }
+ if (e2 <= dx) {
+ err += dx;
+ y0 += sy;
+ }
+ }
+
+ return rc;
+}
+
+#ifdef FXCG50
+#include <gint/cpu.h>
+#include <gint/display.h>
+#include <gint/keyboard.h>
+#include <gint/timer.h>
+#include <stdint.h>
+
+static const int draw_off_x = (DWIDTH - LZY_DISPLAY_WIDTH) / 2;
+static const int draw_off_y = (DHEIGHT - LZY_DISPLAY_HEIGHT) / 2;
+static color_t draw_color = C_BLACK;
+static int should_quit = 0;
+static int timer = 0;
+static volatile int has_ticked = 0;
+#ifdef LZY_GINT_TILESET
+static unsigned int tset_width, tset_height;
+#endif
+#ifdef LZY_GINT_FONT
+static unsigned int font_width, font_height;
+#endif
+static int timer_callback(volatile int *);
+
+int LZY_Init(int argc, const char **argv, const char *title, int target_fps,
+ const char *tset_path, const char *font_path)
+{
+#ifdef LZY_GINT_TILESET
+ extern bopti_image_t LZY_GINT_TILESET;
+ tset_width = LZY_GINT_TILESET.width / LZY_TILE_SIZE;
+ tset_height = LZY_GINT_TILESET.height / LZY_TILE_SIZE;
+#endif
+#ifdef LZY_GINT_FONT
+ extern bopti_image_t LZY_GINT_FONT;
+ font_width = LZY_GINT_FONT.width / LZY_CHR_WIDTH;
+ font_height = LZY_GINT_FONT.height / LZY_CHR_HEIGHT;
+#endif
+ LZY_UNUSED(argc);
+ LZY_UNUSED(argv);
+ LZY_UNUSED(title);
+ LZY_UNUSED(tset_path);
+ LZY_UNUSED(font_path);
+
+ if (target_fps > 0) {
+ timer = timer_configure(TIMER_ANY, 1000000 / target_fps,
+ GINT_CALL(timer_callback, &has_ticked));
+ timer_start(timer);
+ }
+ return 0;
+}
+
+static int timer_callback(volatile int *arg)
+{
+ *arg += 1;
+ return 0;
+}
+
+void LZY_Quit(void)
+{
+ if (timer) {
+ timer_stop(timer);
+ timer = 0;
+ }
+}
+
+int LZY_DrawBegin(void)
+{
+ return 0;
+}
+
+int LZY_DrawEnd(void)
+{
+ dupdate();
+ while (timer && !has_ticked)
+ sleep();
+ has_ticked = 0;
+ return 0;
+}
+
+void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b)
+{
+ draw_color = C_RGB(r / 8, g / 8, b / 8);
+}
+
+void LZY_DrawSetColorNone(void)
+{
+ draw_color = C_NONE;
+}
+
+int LZY_DrawClear(void)
+{
+ dclear(draw_color);
+ return 0;
+}
+
+int LZY_DrawPoint(int x, int y)
+{
+ dpixel(x + draw_off_x, y + draw_off_y, draw_color);
+ return 0;
+}
+
+int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h)
+{
+ if (w < 1 || h < 1)
+ return -1;
+ x += draw_off_x;
+ y += draw_off_y;
+
+ drect_border(x, y, x + w - 1, y + h - 1, C_NONE, 1, draw_color);
+ return 0;
+}
+
+int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h)
+{
+ if (w < 1 || h < 1)
+ return -1;
+ x += draw_off_x;
+ y += draw_off_y;
+
+ drect(x, y, x + w - 1, y + h - 1, draw_color);
+ return 0;
+}
+
+int LZY_DrawTile(unsigned int id, int x, int y)
+{
+#ifndef LZY_GINT_TILESET
+ LZY_UNUSED(id);
+ LZY_UNUSED(x);
+ LZY_UNUSED(y);
+ return -1;
+#else
+ extern bopti_image_t LZY_GINT_TILESET;
+ int ix, iy;
+
+ if (id >= tset_width * tset_height)
+ return -1;
+ x += draw_off_x;
+ y += draw_off_y;
+
+ ix = (id % tset_width) * LZY_TILE_SIZE;
+ iy = (id / tset_width) * LZY_TILE_SIZE;
+ dsubimage(x, y, &LZY_GINT_TILESET, ix, iy, LZY_TILE_SIZE, LZY_TILE_SIZE,
+ DIMAGE_NONE);
+
+ return 0;
+#endif
+}
+
+int LZY_DrawTileEx(unsigned int id, int x, int y, unsigned int w,
+ unsigned int h)
+{
+#ifndef LZY_GINT_TILESET
+ LZY_UNUSED(id);
+ LZY_UNUSED(x);
+ LZY_UNUSED(y);
+ LZY_UNUSED(w);
+ LZY_UNUSED(h);
+ return -1;
+#else
+ extern bopti_image_t LZY_GINT_TILESET;
+ int ix, iy;
+
+ if (w * h == 0)
+ return -1;
+
+ x += draw_off_x;
+ y += draw_off_y;
+
+ ix = id % tset_width;
+ iy = id / tset_width;
+
+ if (ix >= (int)tset_width || ix + w - 1 >= tset_width ||
+ iy >= (int)tset_height || iy + h - 1 >= tset_height)
+ return -1;
+
+ ix *= LZY_TILE_SIZE;
+ iy *= LZY_TILE_SIZE;
+ dsubimage(x, y, &LZY_GINT_TILESET, ix, iy, w * LZY_TILE_SIZE,
+ h * LZY_TILE_SIZE, DIMAGE_NONE);
+
+ return 0;
+#endif
+}
+
+int LZY_DrawChar(unsigned char chr, int x, int y)
+{
+#ifndef LZY_GINT_FONT
+ LZY_UNUSED(chr);
+ LZY_UNUSED(x);
+ LZY_UNUSED(y);
+ return -1;
+#else
+ extern bopti_image_t LZY_GINT_FONT;
+ const unsigned int id = (unsigned int)chr - LZY_FIRST_CHR;
+ int ix, iy;
+
+ if (id >= font_width * font_height)
+ return -1;
+ x += draw_off_x;
+ y += draw_off_y;
+
+ ix = (id % font_width) * LZY_CHR_WIDTH;
+ iy = (id / font_width) * LZY_CHR_HEIGHT;
+ dsubimage(x, y, &LZY_GINT_FONT, ix, iy, LZY_CHR_WIDTH, LZY_CHR_HEIGHT,
+ DIMAGE_NONE);
+
+ return 0;
+#endif
+}
+
+int LZY_DrawText(const char *text, int x, int y)
+{
+#ifndef LZY_GINT_FONT
+ LZY_UNUSED(text);
+ LZY_UNUSED(x);
+ LZY_UNUSED(y);
+ return -1;
+#else
+ int err = 0;
+
+ if (text == NULL)
+ return -1;
+
+ for (; *text != '\0'; text++) {
+ const int rc = LZY_DrawChar(*text, x, y);
+ if (rc)
+ err = rc - 1;
+ x += LZY_CHR_WIDTH;
+ }
+
+ return err;
+#endif
+}
+
+LZY_Music *LZY_MusicLoad(const char *path)
+{
+ LZY_UNUSED(path);
+ return NULL;
+}
+
+int LZY_MusicDestroy(LZY_Music *music)
+{
+ LZY_UNUSED(music);
+ return -1;
+}
+
+int LZY_MusicPlay(LZY_Music *music, int loops)
+{
+ LZY_UNUSED(music);
+ LZY_UNUSED(loops);
+ return -1;
+}
+
+LZY_Sound *LZY_SoundLoad(const char *path)
+{
+ LZY_UNUSED(path);
+ return NULL;
+}
+
+int LZY_SoundDestroy(LZY_Sound *sound)
+{
+ LZY_UNUSED(sound);
+ return -1;
+}
+
+void LZY_SoundSetVolume(LZY_Sound *sound, float volume)
+{
+ LZY_UNUSED(sound);
+ LZY_UNUSED(volume);
+}
+
+int LZY_SoundPlay(LZY_Sound *sound, int loops)
+{
+ LZY_UNUSED(sound);
+ LZY_UNUSED(loops);
+ return -1;
+}
+
+int LZY_PollEvent(LZY_Event *e)
+{
+ for (;;) {
+ const key_event_t gk_e = pollevent();
+
+ switch (gk_e.type) {
+ case KEYEV_NONE:
+ return 0;
+ case KEYEV_DOWN:
+ e->type = LZY_KEYDOWN;
+ e->u.key.scancode = gk_e.key;
+ if (gk_e.key == KEY_EXIT)
+ should_quit = 1;
+ return 1;
+ case KEYEV_UP:
+ e->type = LZY_KEYUP;
+ e->u.key.scancode = gk_e.key;
+ return 1;
+ default:
+ break;
+ }
+ }
+}
+
+void LZY_CycleEvents(void)
+{
+ clearevents();
+ should_quit = should_quit || keydown(KEY_EXIT);
+}
+
+int LZY_KeyDown(unsigned int key)
+{
+ return keydown(key);
+}
+
+int LZY_ShouldQuit(void)
+{
+ return should_quit;
+}
+
+void LZY_Log(const char *msg)
+{
+ LZY_UNUSED(msg);
+}
+
+const char *LZY_GetError(void)
+{
+ return NULL;
+}
+
+#else /* end FXCG50, begin SDL2 */
+#include LZY_SDL_INCLUDE
+#include LZY_SDL_IMAGE_INCLUDE
+#include LZY_SDL_MIXER_INCLUDE
+#include <libgen.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const SDL_Scancode sc[LZYK_COUNT] = {
+ SDL_SCANCODE_LEFT, SDL_SCANCODE_RIGHT, SDL_SCANCODE_UP,
+ SDL_SCANCODE_DOWN, SDL_SCANCODE_Z, SDL_SCANCODE_X,
+};
+static const SDL_Scancode fullscreen_sc = SDL_SCANCODE_F11;
+static const char *error = NULL;
+static SDL_Window *window = NULL;
+static SDL_Renderer *renderer = NULL;
+static SDL_Texture *target = NULL;
+static SDL_Texture *tset = NULL;
+static SDL_Texture *font = NULL;
+static int tset_width, tset_height, font_width, font_height;
+static int should_quit = 0;
+static int input[LZYK_COUNT] = {0};
+static int fps_limiter = 0;
+static uint64_t min_dt;
+static uint64_t next_time;
+static char *datadir;
+
+static char *str_dup(const char *src)
+{
+ char *copy;
+
+ if (src == NULL) {
+ error = "src is NULL";
+ return NULL;
+ }
+
+ copy = calloc(strlen(src) + 1, sizeof(char));
+ if (copy == NULL) {
+ error = "calloc failed";
+ return NULL;
+ }
+
+ strcpy(copy, src);
+ return copy;
+}
+
+static char *path_relative(const char *path)
+{
+ char *buf;
+
+ if (datadir == NULL) {
+ error = "datadir is NULL";
+ return NULL;
+ }
+
+ if (path == NULL) {
+ error = "path is NULL";
+ return NULL;
+ }
+
+ buf = calloc(strlen(datadir) + strlen(path) + 2, sizeof(char));
+ if (buf == NULL) {
+ error = "calloc failed";
+ return NULL;
+ }
+
+ strcpy(buf, datadir);
+ strcat(buf, "/");
+ strcat(buf, path);
+
+ return buf;
+}
+
+int LZY_Init(int argc, const char **argv, const char *title, int target_fps,
+ const char *tset_path, const char *font_path)
+{
+ const int img_flags = IMG_INIT_PNG;
+ char *buf;
+
+ datadir = str_dup((argc < 1) ? ("./rl") : (argv[0]));
+ if (datadir == NULL)
+ return -1;
+ dirname(datadir);
+
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ if ((IMG_Init(img_flags) & img_flags) != img_flags) {
+ error = IMG_GetError();
+ return -1;
+ }
+
+ if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
+ error = Mix_GetError();
+ return -1;
+ }
+
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, 0);
+
+ window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED,
+ SDL_WINDOWPOS_UNDEFINED, LZY_DISPLAY_WIDTH,
+ LZY_DISPLAY_HEIGHT, SDL_WINDOW_RESIZABLE);
+ if (window == NULL) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ SDL_SetWindowMinimumSize(window, LZY_DISPLAY_WIDTH, LZY_DISPLAY_HEIGHT);
+
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+ if (renderer == NULL) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB888,
+ SDL_TEXTUREACCESS_TARGET, LZY_DISPLAY_WIDTH,
+ LZY_DISPLAY_HEIGHT);
+ if (target == NULL) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ buf = path_relative(tset_path);
+ if (buf == NULL)
+ return -1;
+ tset = IMG_LoadTexture(renderer, buf);
+ if (tset == NULL) {
+ error = IMG_GetError();
+ return -1;
+ }
+ free(buf);
+
+ if (SDL_QueryTexture(tset, NULL, NULL, &tset_width, &tset_height) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+ tset_width /= LZY_TILE_SIZE;
+ tset_height /= LZY_TILE_SIZE;
+
+ buf = path_relative(font_path);
+ if (buf == NULL)
+ return -1;
+ font = IMG_LoadTexture(renderer, buf);
+ if (font == NULL) {
+ error = IMG_GetError();
+ return -1;
+ }
+ free(buf);
+
+ if (SDL_QueryTexture(font, NULL, NULL, &font_width, &font_height) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+ font_width /= LZY_CHR_WIDTH;
+ font_height /= LZY_CHR_HEIGHT;
+
+ fps_limiter = target_fps > 0;
+ if (fps_limiter) {
+ min_dt = 1000 / target_fps;
+ next_time = SDL_GetTicks64();
+ }
+
+ return 0;
+}
+
+void LZY_Quit(void)
+{
+ if (datadir != NULL) {
+ free(datadir);
+ datadir = NULL;
+ }
+
+ if (tset != NULL) {
+ SDL_DestroyTexture(tset);
+ tset = 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;
+ }
+
+ Mix_Quit();
+ IMG_Quit();
+ SDL_Quit();
+}
+
+int LZY_DrawBegin(void)
+{
+ if (fps_limiter)
+ next_time += min_dt;
+
+ if (SDL_SetRenderTarget(renderer, target) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+ return 0;
+}
+
+int LZY_DrawEnd(void)
+{
+ int win_w, win_h, off_x, off_y;
+ float ratio_w, ratio_h, scale;
+ SDL_Rect src, dst;
+
+ if (SDL_SetRenderTarget(renderer, NULL)) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ LZY_DrawSetColor(0x00, 0x00, 0x00);
+ if (LZY_DrawClear())
+ return -2;
+
+ SDL_GetWindowSize(window, &win_w, &win_h);
+ ratio_w = (float)win_w / LZY_DISPLAY_WIDTH;
+ ratio_h = (float)win_h / LZY_DISPLAY_HEIGHT;
+ scale = (ratio_w < ratio_h) ? (ratio_w) : (ratio_h);
+ off_x = (win_w - LZY_DISPLAY_WIDTH * scale) / 2;
+ off_y = (win_h - LZY_DISPLAY_HEIGHT * scale) / 2;
+
+ src.x = 0;
+ src.y = 0;
+ src.w = LZY_DISPLAY_WIDTH;
+ src.h = LZY_DISPLAY_HEIGHT;
+
+ dst.x = off_x;
+ dst.y = off_y;
+ dst.w = LZY_DISPLAY_WIDTH * scale;
+ dst.h = LZY_DISPLAY_HEIGHT * scale;
+
+ if (SDL_RenderCopy(renderer, target, &src, &dst) < 0) {
+ error = SDL_GetError();
+ return -3;
+ }
+
+ SDL_RenderPresent(renderer);
+
+ if (fps_limiter) {
+ const uint64_t cur_time = SDL_GetTicks64();
+ if (next_time <= cur_time)
+ next_time = cur_time;
+ else
+ SDL_Delay(next_time - cur_time);
+ }
+ return 0;
+}
+
+void LZY_DrawSetColor(uint8_t r, uint8_t g, uint8_t b)
+{
+ SDL_SetRenderDrawColor(renderer, r, g, b, 0xff);
+}
+
+void LZY_DrawSetColorNone(void)
+{
+ SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
+}
+
+int LZY_DrawClear(void)
+{
+ if (SDL_RenderClear(renderer) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+ return 0;
+}
+
+int LZY_DrawPoint(int x, int y)
+{
+ if (SDL_RenderDrawPoint(renderer, x, y) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+ return 0;
+}
+
+int LZY_DrawRect(int x, int y, unsigned int w, unsigned int h)
+{
+ const SDL_Rect rect = {x, y, w, h};
+
+ if (w == 0 || h == 0) {
+ error = "rectangle dimensions cannot be 0";
+ return -1;
+ }
+
+ if (SDL_RenderDrawRect(renderer, &rect) < 0) {
+ error = SDL_GetError();
+ return -2;
+ }
+ return 0;
+}
+
+int LZY_DrawFillRect(int x, int y, unsigned int w, unsigned int h)
+{
+ const SDL_Rect rect = {x, y, w, h};
+
+ if (w == 0 || h == 0) {
+ error = "rectangle dimensions cannot be 0";
+ return -1;
+ }
+
+ if (SDL_RenderFillRect(renderer, &rect) < 0) {
+ error = SDL_GetError();
+ return -2;
+ }
+ return 0;
+}
+
+int LZY_DrawTile(unsigned int id, int x, int y)
+{
+ SDL_Rect src, dst;
+
+ if (id >= (unsigned int)(tset_width * tset_height)) {
+ error = "id exceeds boundaries";
+ return -1;
+ }
+
+ src.x = (id % tset_width) * LZY_TILE_SIZE;
+ src.y = (id / tset_width) * LZY_TILE_SIZE;
+ src.w = LZY_TILE_SIZE;
+ src.h = LZY_TILE_SIZE;
+
+ dst.x = x;
+ dst.y = y;
+ dst.w = LZY_TILE_SIZE;
+ dst.h = LZY_TILE_SIZE;
+
+ if (SDL_RenderCopy(renderer, tset, &src, &dst) < 0) {
+ error = SDL_GetError();
+ return -2;
+ }
+
+ return 0;
+}
+
+int LZY_DrawTileEx(unsigned int id, int x, int y, unsigned int w,
+ unsigned int h)
+{
+ SDL_Rect src, dst;
+
+ if (w * h == 0) {
+ error = "tile size can't be 0";
+ return -1;
+ }
+
+ if (id >= (unsigned int)(tset_width * tset_height)) {
+ error = "id exceeds boundaries";
+ return -1;
+ }
+
+ src.x = id % tset_width;
+ src.y = id / tset_width;
+
+ if (src.x >= tset_width || src.x + w - 1 >= (unsigned int)tset_width ||
+ src.y >= tset_height ||
+ src.y + h - 1 >= (unsigned int)tset_height) {
+ error = "tile is not in tileset";
+ return -1;
+ }
+
+ src.x *= LZY_TILE_SIZE;
+ src.y *= LZY_TILE_SIZE;
+ src.w = w * LZY_TILE_SIZE;
+ src.h = h * LZY_TILE_SIZE;
+
+ dst.x = x;
+ dst.y = y;
+ dst.w = w * LZY_TILE_SIZE;
+ dst.h = h * LZY_TILE_SIZE;
+
+ if (SDL_RenderCopy(renderer, tset, &src, &dst) < 0) {
+ error = SDL_GetError();
+ return -1;
+ }
+
+ return 0;
+}
+
+int LZY_DrawChar(unsigned char chr, int x, int y)
+{
+ const unsigned int id = (unsigned int)chr - LZY_FIRST_CHR;
+ SDL_Rect src, dst;
+
+ if (id >= (unsigned int)(font_width * font_height)) {
+ error = "chr exceeds boundaries";
+ return -1;
+ }
+
+ src.x = (id % font_width) * LZY_CHR_WIDTH;
+ src.y = (id / font_width) * LZY_CHR_HEIGHT;
+ src.w = LZY_CHR_WIDTH;
+ src.h = LZY_CHR_HEIGHT;
+
+ dst.x = x;
+ dst.y = y;
+ dst.w = LZY_CHR_WIDTH;
+ dst.h = LZY_CHR_HEIGHT;
+
+ if (SDL_RenderCopy(renderer, font, &src, &dst) < 0) {
+ error = SDL_GetError();
+ return -2;
+ }
+
+ return 0;
+}
+
+int LZY_DrawText(const char *text, int x, int y)
+{
+ int err = 0;
+
+ if (text == NULL) {
+ error = "text is NULL";
+ return -1;
+ }
+
+ for (; *text != '\0'; text++) {
+ const int rc = LZY_DrawChar(*text, x, y);
+ if (rc)
+ err = rc - 1;
+ x += LZY_CHR_WIDTH;
+ }
+
+ return err;
+}
+
+LZY_Music *LZY_MusicLoad(const char *path)
+{
+ char *buf;
+ LZY_Music *music;
+
+ buf = path_relative(path);
+ if (buf == NULL)
+ return NULL;
+ music = Mix_LoadMUS(buf);
+ if (music == NULL)
+ error = Mix_GetError();
+ free(buf);
+
+ return music;
+}
+
+int LZY_MusicDestroy(LZY_Music *music)
+{
+ if (music == NULL) {
+ error = "music is NULL";
+ return -1;
+ }
+
+ Mix_FreeMusic(music);
+ return 0;
+}
+
+int LZY_MusicPlay(LZY_Music *music, int loops)
+{
+ if (music == NULL) {
+ error = "music is NULL";
+ return -1;
+ }
+
+ if (Mix_PlayMusic(music, loops) < 0) {
+ error = Mix_GetError();
+ return -2;
+ }
+ return 0;
+}
+
+LZY_Sound *LZY_SoundLoad(const char *path)
+{
+ char *buf;
+ LZY_Sound *sound;
+
+ buf = path_relative(path);
+ if (buf == NULL)
+ return NULL;
+ sound = Mix_LoadWAV(buf);
+ if (sound == NULL)
+ error = Mix_GetError();
+ free(buf);
+
+ return sound;
+}
+
+int LZY_SoundDestroy(LZY_Sound *sound)
+{
+ if (sound == NULL) {
+ error = "sound is NULL";
+ return -1;
+ }
+
+ Mix_FreeChunk(sound);
+ return 0;
+}
+
+void LZY_SoundSetVolume(LZY_Sound *sound, float volume)
+{
+ if (sound != NULL)
+ Mix_VolumeChunk(sound, MIX_MAX_VOLUME * volume);
+}
+
+int LZY_SoundPlay(LZY_Sound *sound, int loops)
+{
+ if (sound == NULL) {
+ error = "sound is NULL";
+ return -1;
+ }
+
+ if (Mix_PlayChannel(-1, sound, loops) < 0) {
+ error = Mix_GetError();
+ return -2;
+ }
+
+ return 0;
+}
+
+int LZY_PollEvent(LZY_Event *e)
+{
+ SDL_Event sdl_e;
+
+ while (SDL_PollEvent(&sdl_e) != 0) {
+ switch (sdl_e.type) {
+ case SDL_QUIT:
+ e->type = LZY_QUIT;
+ should_quit = 1;
+ return 1;
+ case SDL_KEYDOWN: {
+ if (!sdl_e.key.repeat) {
+ int i = LZYK_COUNT;
+ while (i-- > 0) {
+ if (sc[i] ==
+ sdl_e.key.keysym.scancode) {
+ e->type = LZY_KEYDOWN;
+ e->u.key.scancode = i;
+ input[i] = 1;
+ return 1;
+ }
+ }
+ if (sdl_e.key.keysym.scancode ==
+ fullscreen_sc) {
+ const unsigned int flag =
+ SDL_WINDOW_FULLSCREEN;
+ const unsigned int fullscreen =
+ SDL_GetWindowFlags(window) & flag;
+ SDL_SetWindowFullscreen(window,
+ !fullscreen);
+ }
+ }
+ } break;
+ case SDL_KEYUP: {
+ int i = LZYK_COUNT;
+ while (i-- > 0) {
+ if (sc[i] == sdl_e.key.keysym.scancode) {
+ e->type = LZY_KEYUP;
+ e->u.key.scancode = i;
+ input[i] = 0;
+ return 1;
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void LZY_CycleEvents(void)
+{
+ LZY_Event e;
+
+ while (LZY_PollEvent(&e) != 0)
+ ;
+}
+
+int LZY_KeyDown(unsigned int key)
+{
+ if (key >= LZYK_COUNT)
+ return 0;
+ return input[key];
+}
+
+int LZY_ShouldQuit(void)
+{
+ return should_quit;
+}
+
+void LZY_Log(const char *msg)
+{
+ SDL_Log("%s", msg);
+}
+
+const char *LZY_GetError(void)
+{
+ return error;
+}
+#endif /* SDL2 */
+#endif /* LZY_IMPLEMENTATION */
diff --git a/inc/player.h b/inc/player.h
new file mode 100644
index 0000000..c1d6d7a
--- /dev/null
+++ b/inc/player.h
@@ -0,0 +1,7 @@
+#pragma once
+
+void player_init(float x, float y);
+void player_draw(void);
+
+#define PLAYER_WIDTH 12
+#define PLAYER_HEIGHT 12
diff --git a/res/font.png b/res/font.png
new file mode 100644
index 0000000..c6d3af0
--- /dev/null
+++ b/res/font.png
Binary files differ
diff --git a/res/fxconv-metadata.txt b/res/fxconv-metadata.txt
new file mode 100644
index 0000000..3e64f5d
--- /dev/null
+++ b/res/fxconv-metadata.txt
@@ -0,0 +1,7 @@
+tset.png:
+ type: bopti-image
+ name: bimg_tset
+font.png:
+ type: bopti-image
+ name: bimg_font
+ profile: p4
diff --git a/res/icon-sel.png b/res/icon-sel.png
new file mode 100644
index 0000000..3fefb8a
--- /dev/null
+++ b/res/icon-sel.png
Binary files differ
diff --git a/res/icon-uns.png b/res/icon-uns.png
new file mode 100644
index 0000000..6381b23
--- /dev/null
+++ b/res/icon-uns.png
Binary files differ
diff --git a/res/tset.png b/res/tset.png
new file mode 100644
index 0000000..f963f41
--- /dev/null
+++ b/res/tset.png
Binary files differ
diff --git a/src/lzy.c b/src/lzy.c
new file mode 100644
index 0000000..c4e903b
--- /dev/null
+++ b/src/lzy.c
@@ -0,0 +1,10 @@
+#include "conf.h"
+#define LZY_IMPLEMENTATION
+#define LZY_GINT_TILESET bimg_tset
+#define LZY_GINT_FONT bimg_font
+#define LZY_CHR_WIDTH CHR_WIDTH
+#define LZY_CHR_HEIGHT CHR_HEIGHT
+#define LZY_DISPLAY_WIDTH DISPLAY_WIDTH
+#define LZY_DISPLAY_HEIGHT DISPLAY_HEIGHT
+#define LZY_FIRST_CHR ' '
+#include "lzy.h"
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..acd2aba
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,50 @@
+#include "conf.h"
+#include "lzy.h"
+
+int main(int argc, const char **argv)
+{
+ int x = 0;
+ int y = 0;
+
+ if (LZY_Init(argc, argv, "wehfou official goty", 30, "res/tset.png",
+ "res/font.png")) {
+ LZY_Log(LZY_GetError());
+ LZY_Quit();
+ return 1;
+ }
+
+ while (!LZY_ShouldQuit()) {
+ /* update */
+ LZY_CycleEvents();
+
+ if (LZY_KeyDown(LZYK_LEFT))
+ x -= 2;
+ if (LZY_KeyDown(LZYK_RIGHT))
+ x += 2;
+ if (LZY_KeyDown(LZYK_UP))
+ y -= 2;
+ if (LZY_KeyDown(LZYK_DOWN))
+ y += 2;
+
+ /* draw */
+ LZY_DrawBegin();
+ {
+ /* draw background */
+ LZY_DrawTileEx(0, 0, 0, 13, 7);
+ LZY_DrawTileEx(0, DISPLAY_WIDTH / 2, 0, 13, 7);
+ LZY_DrawTileEx(0, 0, DISPLAY_HEIGHT / 2, 13, 7);
+ LZY_DrawTileEx(0, DISPLAY_WIDTH / 2, DISPLAY_HEIGHT / 2,
+ 13, 7);
+
+ /* draw player */
+ if (LZY_DrawChar('s', x, y))
+ LZY_Log(LZY_GetError());
+ }
+ LZY_DrawEnd();
+ }
+
+ LZY_Log("cya");
+ LZY_Quit();
+
+ return 0;
+}
diff --git a/src/player.c b/src/player.c
new file mode 100644
index 0000000..85bf2a7
--- /dev/null
+++ b/src/player.c
@@ -0,0 +1,18 @@
+#include "player.h"
+#include "lzy.h"
+
+static float x, y, spd_x, spd_y;
+
+void player_init(float nx, float ny)
+{
+ x = nx;
+ y = ny;
+ spd_x = 0.0f;
+ spd_y = 0.0f;
+}
+
+void player_draw(void)
+{
+ LZY_DrawSetColor(255, 0, 255);
+ LZY_DrawFillRect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT);
+}