changeset 59:52792b863ff7

misc: separate core from game
author David Demelier <markand@malikania.fr>
date Tue, 21 Jan 2020 12:42:33 +0100
parents d7d88ac30611
children 7266c750b649
files .hgignore Makefile src/action.h src/adventure/main.c src/adventure/splashscreen_state.c src/adventure/splashscreen_state.h src/animation.c src/animation.h src/clock.c src/clock.h src/color.h src/core/action.h src/core/animation.c src/core/animation.h src/core/clock.c src/core/clock.h src/core/color.h src/core/error.c src/core/error.h src/core/error_p.h src/core/event.c src/core/event.h src/core/font.c src/core/font.h src/core/game.c src/core/game.h src/core/image.c src/core/image.h src/core/key.h src/core/map.c src/core/map.h src/core/map_state.c src/core/map_state.h src/core/message.c src/core/message.h src/core/mouse.h src/core/painter.c src/core/painter.h src/core/sprite.c src/core/sprite.h src/core/state.h src/core/sys.c src/core/sys.h src/core/texture.c src/core/texture.h src/core/texture_p.h src/core/util.c src/core/util.h src/core/walksprite.c src/core/walksprite.h src/core/window.c src/core/window.h src/core/window_p.h src/error.c src/error.h src/error_p.h src/event.c src/event.h src/font.c src/font.h src/game.c src/game.h src/image.c src/image.h src/key.h src/main.c src/map.c src/map.h src/map_state.c src/map_state.h src/message.c src/message.h src/mouse.h src/painter.c src/painter.h src/splashscreen.c src/splashscreen.h src/sprite.c src/sprite.h src/state.h src/sys.c src/sys.h src/texture.c src/texture.h src/texture_p.h src/util.c src/util.h src/walksprite.c src/walksprite.h src/window.c src/window.h src/window_p.h
diffstat 92 files changed, 4416 insertions(+), 4409 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Jan 21 12:31:56 2020 +0100
+++ b/.hgignore	Tue Jan 21 12:42:33 2020 +0100
@@ -16,7 +16,7 @@
 ^tests/test-color(\.exe)?$
 ^tests/test-error(\.exe)?$
 ^tests/test-map(\.exe)?$
-^tools/molko-map$
+^tools/molko-map(\.exe)?$
 
 # doxygen stuff.
 ^doxygen/html$
--- a/Makefile	Tue Jan 21 12:31:56 2020 +0100
+++ b/Makefile	Tue Jan 21 12:42:33 2020 +0100
@@ -24,26 +24,31 @@
 # CFLAGS=         -MMD -O3 -DNDEBUG -std=c18 -Wall -Wextra
 PROG=           molko
 LIB=            libmolko.a
-SRCS=           src/animation.c \
-                src/clock.c \
-                src/error.c \
-                src/event.c \
-                src/font.c \
-                src/game.c \
-                src/image.c \
-                src/map.c \
-                src/map_state.c \
-                src/message.c \
-                src/painter.c \
-                src/sprite.c \
-                src/sys.c \
-                src/texture.c \
-                src/util.c \
-                src/splashscreen.c \
-                src/walksprite.c \
-                src/window.c
-OBJS=           ${SRCS:.c=.o}
-DEPS=           ${SRCS:.c=.d}
+
+CORE_SRCS=      src/core/animation.c \
+                src/core/clock.c \
+                src/core/error.c \
+                src/core/event.c \
+                src/core/font.c \
+                src/core/game.c \
+                src/core/image.c \
+                src/core/map.c \
+                src/core/map_state.c \
+                src/core/message.c \
+                src/core/painter.c \
+                src/core/sprite.c \
+                src/core/sys.c \
+                src/core/texture.c \
+                src/core/util.c \
+                src/core/walksprite.c \
+                src/core/window.c
+CORE_OBJS=      ${CORE_SRCS:.c=.o}
+CORE_DEPS=      ${CORE_SRCS:.c=.d}
+
+ADV_SRCS=       src/adventure/main.c \
+                src/adventure/splashscreen_state.c
+ADV_OBJS=       ${ADV_SRCS:.c=.o}
+ADV_DEPS=       ${ADV_SRCS:.c=.d}
 
 PREFIX=         /usr/local
 BINDIR=         ${PREFIX}/bin
@@ -58,7 +63,7 @@
 TESTS=          tests/test-color.c \
                 tests/test-error.c \
                 tests/test-map.c
-TESTS_INCS=     -I extern/libgreatest -I src ${SDL_CFLAGS}
+TESTS_INCS=     -I extern/libgreatest -I src/core ${SDL_CFLAGS}
 TESTS_LIBS=     ${LIB} ${SDL_LDFLAGS} ${LDFLAGS}
 TESTS_OBJS=     ${TESTS:.c=}
 TESTS_DEPS=     ${TESTS:.c=.d}
@@ -70,25 +75,26 @@
 DEFINES=        -DPREFIX=\""${PREFIX}"\" \
                 -DBINDIR=\""${BINDIR}"\" \
                 -DSHAREDIR=\""${SHAREDIR}"\"
+INCLUDES=       -I src/core -I src/adventure
 
 .SUFFIXES:
 .SUFFIXES: .c .o
 
 all: ${PROG}
 
--include ${DEPS} ${TESTS_DEPS} ${TOOLS_DEPS}
+-include ${CORE_DEPS} ${ADV_DEPS} ${TESTS_DEPS} ${TOOLS_DEPS}
 
 .c.o:
-	${CC} ${DEFINES} ${SDL_CFLAGS} ${CFLAGS} -c $< -o $@
+	${CC} ${DEFINES} ${INCLUDES} ${SDL_CFLAGS} ${CFLAGS} -c $< -o $@
 
 .c:
 	${CC} ${TESTS_INCS} -o $@ ${CFLAGS} $< ${TESTS_LIBS}
 
-${LIB}: ${OBJS}
-	${AR} -rcs $@ ${OBJS}
+${LIB}: ${CORE_OBJS}
+	${AR} -rcs $@ ${CORE_OBJS}
 
-${PROG}: ${LIB} src/main.o
-	${CC} -o $@ src/main.o ${LIB} ${SDL_LDFLAGS} ${LDFLAGS}
+${PROG}: ${LIB} ${ADV_OBJS}
+	${CC} -o $@ ${ADV_OBJS} ${LIB} ${SDL_LDFLAGS} ${LDFLAGS}
 
 ${TESTS_OBJS}: ${LIB}
 
@@ -111,9 +117,10 @@
 	cp -R assets/* ${DESTDIR}${SHAREDIR}/molko
 
 clean:
-	rm -f ${PROG} src/main.o src/main.d
 	rm -rf doxygen/html doxygen/man
-	rm -f ${LIB} ${OBJS} ${DEPS}
+	rm -f ${LIB} ${PROG}
+	rm -f ${CORE_OBJS} ${CORE_DEPS}
+	rm -f ${ADV_OBJS} ${ADV_DEPS}
 	rm -f ${TESTS_OBJS} ${TESTS_DEPS}
 	rm -f ${TOOLS_OBJS} ${TOOLS_DEPS}
 
--- a/src/action.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * action.h -- action states
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef ACTION_H
-#define ACTION_H
-
-/**
- * \file action.h
- * \brief Action states.
- */
-
-#include <stdbool.h>
-
-union event;
-
-/**
- * \brief Action flags.
- */
-enum action_flags {
-	ACTION_NONE,                            /*!< No flags */
-	ACTION_AUTO_LEAVE       = (1 << 0)      /*!< Action is removed on state change */
-};
-
-/**
- * \brief Action structure.
- */
-struct action {
-	/**
-	 * (RW)
-	 *
-	 * Optional flags.
-	 */
-	enum action_flags flags;
-
-	/**
-	 * (RW)
-	 *
-	 * Arbitrary user data.
-	 */
-	void *data;
-
-	/**
-	 * (RW)
-	 *
-	 * Handle event.
-	 */
-	void (*handle)(struct action *, const union event *event);
-
-	/**
-	 * (RW)
-	 *
-	 * Update the action.
-	 *
-	 * If returns true, the action is removed.
-	 */
-	bool (*update)(struct action *, unsigned int);
-
-	/**
-	 * (RW)
-	 *
-	 * Draw the aciton.
-	 */
-	void (*draw)(struct action *);
-
-	/**
-	 * (RW)
-	 *
-	 * Close the action before removal.
-	 */
-	void (*finish)(struct action *);
-};
-
-#endif /* !ACTION_H*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/adventure/main.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,96 @@
+/*
+ * main.c -- Molko's Adventure
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "clock.h"
+#include "error.h"
+#include "event.h"
+#include "game.h"
+#include "image.h"
+#include "map.h"
+#include "map_state.h"
+#include "splashscreen_state.h"
+#include "sprite.h"
+#include "sys.h"
+#include "util.h"
+#include "window.h"
+
+#define WINDOW_WIDTH 1280
+#define WINDOW_HEIGHT 720
+
+static void
+init(void)
+{
+	if (!sys_init())
+		error_fatal();
+	if (!window_init("Molko's Adventure", WINDOW_WIDTH, WINDOW_HEIGHT))
+		error_fatal();
+
+	/* Default state is splash screen */
+	game_switch(&splashscreen_state, true);
+}
+
+static void
+run(void)
+{
+	union event ev;
+	struct clock clock;
+
+	clock_start(&clock);
+
+	for (;;) {
+		unsigned int elapsed = clock_elapsed(&clock);
+
+		clock_start(&clock);
+
+		while (event_poll(&ev)) {
+			/* TODO: this must be handled by states. */
+			if (ev.type == EVENT_QUIT)
+				return;
+
+			game_handle(&ev);
+		}
+
+		game_update(elapsed);
+		game_draw();
+
+		if ((elapsed = clock_elapsed(&clock)) < 20)
+			delay(20 - elapsed);
+	}
+}
+
+static void
+close(void)
+{
+	window_close();
+	sys_close();
+}
+
+int
+main(int argc, char **argv)
+{
+	(void)argc;
+	(void)argv;
+
+	init();
+	run();
+	close();
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/adventure/splashscreen_state.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,103 @@
+/*
+ * splashscreen_state.c -- splash screen state
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "error.h"
+#include "font.h"
+#include "game.h"
+#include "image.h"
+#include "map.h"
+#include "map_state.h"
+#include "painter.h"
+#include "splashscreen_state.h"
+#include "state.h"
+#include "sys.h"
+#include "texture.h"
+#include "window.h"
+
+#define DELAY 3000
+
+static unsigned int elapsed;
+static struct font *font;
+static struct texture *text;
+static int x;
+static int y;
+
+static void
+enter(void)
+{
+	if (!(font = font_openf(sys_datapath("fonts/knights-quest.ttf"), 160)))
+		error_fatal();
+	if (!(text = font_render(font, "Molko's Adventure", 0x000000ff)))
+		error_fatal();
+
+	/* Compute position. */
+	const unsigned int w = texture_width(text);
+	const unsigned int h = texture_height(text);
+
+	x = (window_width() / 2) - (w / 2);
+	y = (window_height() / 2) - (h / 2) - 100;
+}
+
+static void
+leave(void)
+{
+	font_close(font);
+}
+
+static void
+handle(const union event *event)
+{
+	(void)event;
+}
+
+static void
+update(unsigned int ticks)
+{
+	elapsed += ticks;
+
+	/* TODO: change this once map is done. */
+	if (elapsed >= DELAY) {
+		/* TODO: this will be removed too. */
+		static struct texture *image;
+
+		if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
+			error_fatal();
+		if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
+			error_fatal();
+
+		sprite_init(&map_state_data.player.sprite, image, 48, 48);
+		game_switch(&map_state, false);
+	}
+}
+
+static void
+draw(void)
+{
+	painter_set_color(0xffffffff);
+	painter_clear();
+	texture_draw(text, x, y);
+	painter_present();
+}
+
+struct state splashscreen_state = {
+	.enter = enter,
+	.leave = leave,
+	.handle = handle,
+	.update = update,
+	.draw = draw
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/adventure/splashscreen_state.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,32 @@
+/*
+ * splashscreen_state.h -- splash screen state
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_SPLASHSCREEN_ADVENTURE_H
+#define MOLKO_SPLASHSCREEN_ADVENTURE_H
+
+/**
+ * \file splashscreen.h
+ * \brief Splash screen state.
+ */
+
+/**
+ * \brief Splash screen state.
+ */
+extern struct state splashscreen_state;
+
+#endif /* !MOLKO_SPLASHSCREEN_ADVENTURE_H */
--- a/src/animation.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,84 +0,0 @@
-/*
- * animation.c -- basic animations
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "animation.h"
-#include "sprite.h"
-
-void
-animation_init(struct animation *an, struct sprite *sprite, unsigned int delay)
-{
-	assert(an);
-	assert(sprite);
-
-	an->sprite = sprite;
-	an->row = 0;
-	an->column = 0;
-	an->delay = delay;
-	an->elapsed = 0;
-}
-
-bool
-animation_is_complete(const struct animation *an)
-{
-	assert(an);
-
-	return an->row == an->sprite->nrows &&
-	       an->column == an->sprite->ncols &&
-	       an->elapsed >= an->delay;
-}
-
-void
-animation_start(struct animation *an)
-{
-	assert(an);
-
-	an->row = 0;
-	an->column = 0;
-	an->elapsed = 0;
-}
-
-void
-animation_update(struct animation *an, unsigned int ticks)
-{
-	assert(an);
-
-	an->elapsed += ticks;
-
-	if (an->elapsed < an->delay)
-		return;
-
-	/* Increment column first */
-	if (++an->column >= an->sprite->ncols) {
-		/*
-		 * Increment row, if we reach the last row it means we are
-		 * at the last frame.
-		 */
-		if (++an->row >= an->sprite->nrows)
-			an->row = an->sprite->nrows;
-		else
-			an->column = 0;
-	}
-}
-
-void
-animation_draw(struct animation *an, int x, int y)
-{
-	sprite_draw(an->sprite, an->row, an->column, x, y);
-}
--- a/src/animation.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-/*
- * animation.h -- basic animations
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_ANIMATION_H
-#define MOLKO_ANIMATION_H
-
-/**
- * \file animation.h
- * \brief Basic animations.
- */
-
-#include <stdbool.h>
-
-struct sprite;
-
-/**
- * \brief Animation object
- */
-struct animation {
-	struct sprite *sprite;  /*!< Sprite to use (RW) */
-	unsigned int row;       /*!< current row (RO) */
-	unsigned int column;    /*!< current column (RO) */
-	unsigned int delay;     /*!< delay between frames (RW) */
-	unsigned int elapsed;   /*!< elapsed time since last frame (RO) */
-};
-
-/**
- * Initialize the animation.
- *
- * The animation does not take sprite ownership, it must be valid until
- * animation is no longer used.
- *
- * \pre an != NULL
- * \pre sprite != NULL
- * \param an the animation
- * \param sprite the sprite to use
- * \param delay the delay between frames in milliseconds
- */
-void
-animation_init(struct animation *an, struct sprite *sprite, unsigned int delay);
-
-/**
- * Tells if the animation is complete.
- *
- * \pre an != NULL
- * \param an the animation
- * \return true if the animation has completed
- */
-bool
-animation_is_complete(const struct animation *an);
-
-/**
- * Start an animation.
- *
- * \pre an != NULL
- * \param an the animation
- */
-void
-animation_start(struct animation *an);
-
-/**
- * Update the animation.
- *
- * You must call this function at each loop iteration to update the animation
- * frame depending on its delay.
- *
- * \pre an != NULL
- * \param an the animation
- * \param ticks the elapsed ticks since the last call
- */
-void
-animation_update(struct animation *an, unsigned int ticks);
-
-/**
- * Draw the animation.
- *
- * \pre an != NULL
- * \param an the animation
- * \param x the X coordinate
- * \param y the Y coordinate
- */
-void
-animation_draw(struct animation *an, int x, int y);
-
-#endif /* !MOLKO_ANIMATION_H */
--- a/src/clock.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * clock.c -- track elapsed time
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <SDL.h>
-
-#include "clock.h"
-
-void
-clock_start(struct clock *clock)
-{
-	clock->ticks = SDL_GetTicks();
-}
-
-unsigned int
-clock_elapsed(const struct clock *clock)
-{
-	return SDL_GetTicks() - clock->ticks;
-}
--- a/src/clock.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * clock.h -- track elapsed time
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_CLOCK_H
-#define MOLKO_CLOCK_H
-
-/**
- * \file clock.h
- * \brief Track elapsed time.
- */
-
-/**
- * \brief Clock structure.
- */
-struct clock {
-	unsigned int ticks;     /*!< time point on initialization */
-};
-
-/**
- * Start the clock and track elapsed time.
- *
- * \pre clock != NULL
- * \param clock the clock
- */
-void
-clock_start(struct clock *clock);
-
-/**
- * Tell the measured time.
- *
- * \pre clock != NULL
- * \param clock the clock
- * \return the elapsed time in milliseconds
- */
-unsigned int
-clock_elapsed(const struct clock *clock);
-
-#endif /* !MOLKO_CLOCK_H */
--- a/src/color.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * color.h -- basic color routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_COLOR_H
-#define MOLKO_COLOR_H
-
-/**
- * \file color.h
- * \brief Basic color routines.
- */
-
-/**
- * Get red component of hexadecimal color.
- *
- * \param c the hexadecimal color
- * \return the red component
- */
-#define COLOR_R(c)              (c >> 24 & 0xff)
-
-/**
- * Get green component of hexadecimal color.
- *
- * \param c the hexadecimal color
- * \return the green component
- */
-#define COLOR_G(c)              (c >> 16 & 0xff)
-
-/**
- * Get blue component of hexadecimal color.
- *
- * \param c the hexadecimal color
- * \return the blue component
- */
-#define COLOR_B(c)              (c >> 8  & 0xff)
-
-/**
- * Get alpha component of hexadecimal color.
- *
- * \param c the hexadecimal color
- * \return the alpha component
- */
-#define COLOR_A(c)              (c       & 0xff)
-
-/**
- * Convert individual RGBA components into a hexadecimal color.
- *
- * \param r the red component
- * \param g the green component
- * \param b the blue component
- * \param a the alpha component
- * \return the hexadecimal color
- */
-#define COLOR_HEX(r, g, b, a)   \
-        ((r << 24 & 0xff000000) | \
-         (g << 16 & 0x00ff0000) | \
-         (b << 8  & 0x0000ff00) | \
-         (a       & 0x000000ff))
-
-#endif /* !MOLKO_COLOR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/action.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,88 @@
+/*
+ * action.h -- action states
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef ACTION_H
+#define ACTION_H
+
+/**
+ * \file action.h
+ * \brief Action states.
+ */
+
+#include <stdbool.h>
+
+union event;
+
+/**
+ * \brief Action flags.
+ */
+enum action_flags {
+	ACTION_NONE,                            /*!< No flags */
+	ACTION_AUTO_LEAVE       = (1 << 0)      /*!< Action is removed on state change */
+};
+
+/**
+ * \brief Action structure.
+ */
+struct action {
+	/**
+	 * (RW)
+	 *
+	 * Optional flags.
+	 */
+	enum action_flags flags;
+
+	/**
+	 * (RW)
+	 *
+	 * Arbitrary user data.
+	 */
+	void *data;
+
+	/**
+	 * (RW)
+	 *
+	 * Handle event.
+	 */
+	void (*handle)(struct action *, const union event *event);
+
+	/**
+	 * (RW)
+	 *
+	 * Update the action.
+	 *
+	 * If returns true, the action is removed.
+	 */
+	bool (*update)(struct action *, unsigned int);
+
+	/**
+	 * (RW)
+	 *
+	 * Draw the aciton.
+	 */
+	void (*draw)(struct action *);
+
+	/**
+	 * (RW)
+	 *
+	 * Close the action before removal.
+	 */
+	void (*finish)(struct action *);
+};
+
+#endif /* !ACTION_H*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/animation.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,84 @@
+/*
+ * animation.c -- basic animations
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "animation.h"
+#include "sprite.h"
+
+void
+animation_init(struct animation *an, struct sprite *sprite, unsigned int delay)
+{
+	assert(an);
+	assert(sprite);
+
+	an->sprite = sprite;
+	an->row = 0;
+	an->column = 0;
+	an->delay = delay;
+	an->elapsed = 0;
+}
+
+bool
+animation_is_complete(const struct animation *an)
+{
+	assert(an);
+
+	return an->row == an->sprite->nrows &&
+	       an->column == an->sprite->ncols &&
+	       an->elapsed >= an->delay;
+}
+
+void
+animation_start(struct animation *an)
+{
+	assert(an);
+
+	an->row = 0;
+	an->column = 0;
+	an->elapsed = 0;
+}
+
+void
+animation_update(struct animation *an, unsigned int ticks)
+{
+	assert(an);
+
+	an->elapsed += ticks;
+
+	if (an->elapsed < an->delay)
+		return;
+
+	/* Increment column first */
+	if (++an->column >= an->sprite->ncols) {
+		/*
+		 * Increment row, if we reach the last row it means we are
+		 * at the last frame.
+		 */
+		if (++an->row >= an->sprite->nrows)
+			an->row = an->sprite->nrows;
+		else
+			an->column = 0;
+	}
+}
+
+void
+animation_draw(struct animation *an, int x, int y)
+{
+	sprite_draw(an->sprite, an->row, an->column, x, y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/animation.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,100 @@
+/*
+ * animation.h -- basic animations
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_ANIMATION_H
+#define MOLKO_ANIMATION_H
+
+/**
+ * \file animation.h
+ * \brief Basic animations.
+ */
+
+#include <stdbool.h>
+
+struct sprite;
+
+/**
+ * \brief Animation object
+ */
+struct animation {
+	struct sprite *sprite;  /*!< Sprite to use (RW) */
+	unsigned int row;       /*!< current row (RO) */
+	unsigned int column;    /*!< current column (RO) */
+	unsigned int delay;     /*!< delay between frames (RW) */
+	unsigned int elapsed;   /*!< elapsed time since last frame (RO) */
+};
+
+/**
+ * Initialize the animation.
+ *
+ * The animation does not take sprite ownership, it must be valid until
+ * animation is no longer used.
+ *
+ * \pre an != NULL
+ * \pre sprite != NULL
+ * \param an the animation
+ * \param sprite the sprite to use
+ * \param delay the delay between frames in milliseconds
+ */
+void
+animation_init(struct animation *an, struct sprite *sprite, unsigned int delay);
+
+/**
+ * Tells if the animation is complete.
+ *
+ * \pre an != NULL
+ * \param an the animation
+ * \return true if the animation has completed
+ */
+bool
+animation_is_complete(const struct animation *an);
+
+/**
+ * Start an animation.
+ *
+ * \pre an != NULL
+ * \param an the animation
+ */
+void
+animation_start(struct animation *an);
+
+/**
+ * Update the animation.
+ *
+ * You must call this function at each loop iteration to update the animation
+ * frame depending on its delay.
+ *
+ * \pre an != NULL
+ * \param an the animation
+ * \param ticks the elapsed ticks since the last call
+ */
+void
+animation_update(struct animation *an, unsigned int ticks);
+
+/**
+ * Draw the animation.
+ *
+ * \pre an != NULL
+ * \param an the animation
+ * \param x the X coordinate
+ * \param y the Y coordinate
+ */
+void
+animation_draw(struct animation *an, int x, int y);
+
+#endif /* !MOLKO_ANIMATION_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/clock.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,33 @@
+/*
+ * clock.c -- track elapsed time
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <SDL.h>
+
+#include "clock.h"
+
+void
+clock_start(struct clock *clock)
+{
+	clock->ticks = SDL_GetTicks();
+}
+
+unsigned int
+clock_elapsed(const struct clock *clock)
+{
+	return SDL_GetTicks() - clock->ticks;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/clock.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,53 @@
+/*
+ * clock.h -- track elapsed time
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_CLOCK_H
+#define MOLKO_CLOCK_H
+
+/**
+ * \file clock.h
+ * \brief Track elapsed time.
+ */
+
+/**
+ * \brief Clock structure.
+ */
+struct clock {
+	unsigned int ticks;     /*!< time point on initialization */
+};
+
+/**
+ * Start the clock and track elapsed time.
+ *
+ * \pre clock != NULL
+ * \param clock the clock
+ */
+void
+clock_start(struct clock *clock);
+
+/**
+ * Tell the measured time.
+ *
+ * \pre clock != NULL
+ * \param clock the clock
+ * \return the elapsed time in milliseconds
+ */
+unsigned int
+clock_elapsed(const struct clock *clock);
+
+#endif /* !MOLKO_CLOCK_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/color.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,74 @@
+/*
+ * color.h -- basic color routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_COLOR_H
+#define MOLKO_COLOR_H
+
+/**
+ * \file color.h
+ * \brief Basic color routines.
+ */
+
+/**
+ * Get red component of hexadecimal color.
+ *
+ * \param c the hexadecimal color
+ * \return the red component
+ */
+#define COLOR_R(c)              (c >> 24 & 0xff)
+
+/**
+ * Get green component of hexadecimal color.
+ *
+ * \param c the hexadecimal color
+ * \return the green component
+ */
+#define COLOR_G(c)              (c >> 16 & 0xff)
+
+/**
+ * Get blue component of hexadecimal color.
+ *
+ * \param c the hexadecimal color
+ * \return the blue component
+ */
+#define COLOR_B(c)              (c >> 8  & 0xff)
+
+/**
+ * Get alpha component of hexadecimal color.
+ *
+ * \param c the hexadecimal color
+ * \return the alpha component
+ */
+#define COLOR_A(c)              (c       & 0xff)
+
+/**
+ * Convert individual RGBA components into a hexadecimal color.
+ *
+ * \param r the red component
+ * \param g the green component
+ * \param b the blue component
+ * \param a the alpha component
+ * \return the hexadecimal color
+ */
+#define COLOR_HEX(r, g, b, a)   \
+        ((r << 24 & 0xff000000) | \
+         (g << 16 & 0x00ff0000) | \
+         (b << 8  & 0x0000ff00) | \
+         (a       & 0x000000ff))
+
+#endif /* !MOLKO_COLOR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/error.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,97 @@
+/*
+ * error.c -- error routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error_p.h"
+
+#include <SDL.h>
+
+static char buffer[2048];
+
+const char *
+error(void)
+{
+	return buffer;
+}
+
+bool
+error_errno(void)
+{
+	error_printf("%s", strerror(errno));
+
+	return false;
+}
+
+bool
+error_printf(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	error_vprintf(fmt, ap);
+	va_end(ap);
+
+	return false;
+}
+
+bool
+error_vprintf(const char *fmt, va_list ap)
+{
+	vsnprintf(buffer, sizeof (buffer), fmt, ap);
+
+	return false;
+}
+
+noreturn void
+error_fatal(void)
+{
+	fprintf(stderr, "%s\n", buffer);
+	exit(1);
+}
+
+noreturn void
+error_fatalf(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	error_vfatalf(fmt, ap);
+	va_end(ap);
+}
+
+noreturn void
+error_vfatalf(const char *fmt, va_list ap)
+{
+	fprintf(stderr, fmt, ap);
+	exit(1);
+}
+
+/* private: error_p.h */
+
+bool
+error_sdl(void)
+{
+	error_printf("%s", SDL_GetError());
+
+	return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/error.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,90 @@
+/*
+ * error.h -- error routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_ERROR_H
+#define MOLKO_ERROR_H
+
+/**
+ * \file error.h
+ * \brief Error routines.
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdnoreturn.h>
+
+/**
+ * Get the last error returned.
+ *
+ * \return the error string
+ */
+const char *
+error(void);
+
+/**
+ * Convenient handler that sets last error from global C errno and then return
+ * false.
+ *
+ * \return false
+ */
+bool
+error_errno(void);
+
+/**
+ * Set the game error with a printf-like format.
+ *
+ * \param fmt the format string
+ * \return false
+ */
+bool
+error_printf(const char *fmt, ...);
+
+/**
+ * Similar to \a error_printf.
+ *
+ * \param fmt the format stinrg
+ * \param ap the variadic arguments pointer
+ * \return false
+ */
+bool
+error_vprintf(const char *fmt, va_list ap);
+
+/**
+ * Print last registered error and exit with code 1.
+ */
+noreturn void
+error_fatal(void);
+
+/**
+ * Prints an error to stderr and exit.
+ *
+ * \param fmt the format string
+ */
+noreturn void
+error_fatalf(const char *fmt, ...);
+
+/**
+ * Similar to \a error_fatalf
+ *
+ * \param fmt the format string
+ * \param ap the variadic arguments pointer
+ */
+noreturn void
+error_vfatalf(const char *fmt, va_list ap);
+
+#endif /* !MOLKO_ERROR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/error_p.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,33 @@
+/*
+ * error.h -- (PRIVATE) error routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_ERROR_P_H
+#define MOLKO_ERROR_P_H
+
+#include <stdbool.h>
+
+/**
+ * Convenient handler that sets the game error to the last SDL error and then
+ * return false.
+ *
+ * \return false
+ */
+bool
+error_sdl(void);
+
+#endif /* !MOLKO_ERROR_P_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/event.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,255 @@
+/*
+ * event.c -- event management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <SDL.h>
+
+#include "event.h"
+
+/* Maintain with enum key constants in key.h */
+static const struct {
+	SDL_Keycode key;
+	enum key value;
+} keymap[] = {
+	{ SDLK_RETURN,          KEY_ENTER               },
+	{ SDLK_ESCAPE,          KEY_ESCAPE              },
+	{ SDLK_BACKSPACE,       KEY_BACKSPACE           },
+	{ SDLK_TAB,             KEY_TAB                 },
+	{ SDLK_SPACE,           KEY_SPACE               },
+	{ SDLK_EXCLAIM,         KEY_EXCLAIM             },
+	{ SDLK_QUOTEDBL,        KEY_DOUBLE_QUOTE        },
+	{ SDLK_HASH,            KEY_HASH                },
+	{ SDLK_PERCENT,         KEY_PERCENT             },
+	{ SDLK_DOLLAR,          KEY_DOLLAR              },
+	{ SDLK_AMPERSAND,       KEY_AMPERSAND           },
+	{ SDLK_QUOTE,           KEY_QUOTE               },
+	{ SDLK_LEFTPAREN,       KEY_LEFT_PAREN          },
+	{ SDLK_RIGHTPAREN,      KEY_RIGHT_PAREN         },
+	{ SDLK_ASTERISK,        KEY_ASTERISK            },
+	{ SDLK_PLUS,            KEY_PLUS                },
+	{ SDLK_COMMA,           KEY_COMMA               },
+	{ SDLK_MINUS,           KEY_MINUS               },
+	{ SDLK_PERIOD,          KEY_PERIOD              },
+	{ SDLK_SLASH,           KEY_SLASH               },
+	{ SDLK_0,               KEY_0                   },
+	{ SDLK_1,               KEY_1                   },
+	{ SDLK_2,               KEY_2                   },
+	{ SDLK_3,               KEY_3                   },
+	{ SDLK_4,               KEY_4                   },
+	{ SDLK_5,               KEY_5                   },
+	{ SDLK_6,               KEY_6                   },
+	{ SDLK_7,               KEY_7                   },
+	{ SDLK_8,               KEY_8                   },
+	{ SDLK_9,               KEY_9                   },
+	{ SDLK_COLON,           KEY_COLON               },
+	{ SDLK_SEMICOLON,       KEY_SEMICOLON           },
+	{ SDLK_LESS,            KEY_LESS                },
+	{ SDLK_EQUALS,          KEY_EQUALS              },
+	{ SDLK_GREATER,         KEY_GREATER             },
+	{ SDLK_QUESTION,        KEY_QUESTION            },
+	{ SDLK_AT,              KEY_AT                  },
+	{ SDLK_LEFTBRACKET,     KEY_LEFT_BRACKET        },
+	{ SDLK_BACKSLASH,       KEY_BACKSLASH           },
+	{ SDLK_RIGHTBRACKET,    KEY_RIGHT_BRACKET       },
+	{ SDLK_CARET,           KEY_CARET               },
+	{ SDLK_UNDERSCORE,      KEY_UNDERSCORE          },
+	{ SDLK_BACKQUOTE,       KEY_BACKQUOTE           },
+	{ SDLK_a,               KEY_a                   },
+	{ SDLK_b,               KEY_b                   },
+	{ SDLK_c,               KEY_c                   },
+	{ SDLK_d,               KEY_d                   },
+	{ SDLK_e,               KEY_e                   },
+	{ SDLK_f,               KEY_f                   },
+	{ SDLK_g,               KEY_g                   },
+	{ SDLK_h,               KEY_h                   },
+	{ SDLK_i,               KEY_i                   },
+	{ SDLK_j,               KEY_j                   },
+	{ SDLK_k,               KEY_k                   },
+	{ SDLK_l,               KEY_l                   },
+	{ SDLK_m,               KEY_m                   },
+	{ SDLK_n,               KEY_n                   },
+	{ SDLK_o,               KEY_o                   },
+	{ SDLK_p,               KEY_p                   },
+	{ SDLK_q,               KEY_q                   },
+	{ SDLK_r,               KEY_r                   },
+	{ SDLK_s,               KEY_s                   },
+	{ SDLK_t,               KEY_t                   },
+	{ SDLK_u,               KEY_u                   },
+	{ SDLK_v,               KEY_v                   },
+	{ SDLK_w,               KEY_w                   },
+	{ SDLK_x,               KEY_x                   },
+	{ SDLK_y,               KEY_y                   },
+	{ SDLK_z,               KEY_z                   },
+	{ SDLK_CAPSLOCK,        KEY_CAPSLOCK            },
+	{ SDLK_F1,              KEY_F1                  },
+	{ SDLK_F2,              KEY_F2                  },
+	{ SDLK_F3,              KEY_F3                  },
+	{ SDLK_F4,              KEY_F4                  },
+	{ SDLK_F5,              KEY_F5                  },
+	{ SDLK_F6,              KEY_F6                  },
+	{ SDLK_F7,              KEY_F7                  },
+	{ SDLK_F8,              KEY_F8                  },
+	{ SDLK_F9,              KEY_F9                  },
+	{ SDLK_F10,             KEY_F10                 },
+	{ SDLK_F11,             KEY_F11                 },
+	{ SDLK_F12,             KEY_F12                 },
+	{ SDLK_F13,             KEY_F13                 },
+	{ SDLK_F14,             KEY_F14                 },
+	{ SDLK_F15,             KEY_F15                 },
+	{ SDLK_F16,             KEY_F16                 },
+	{ SDLK_F17,             KEY_F17                 },
+	{ SDLK_F18,             KEY_F18                 },
+	{ SDLK_F19,             KEY_F19                 },
+	{ SDLK_F20,             KEY_F20                 },
+	{ SDLK_F21,             KEY_F21                 },
+	{ SDLK_F22,             KEY_F22                 },
+	{ SDLK_F23,             KEY_F23                 },
+	{ SDLK_F24,             KEY_F24                 },
+	{ SDLK_PRINTSCREEN,     KEY_PRINTSCREEN         },
+	{ SDLK_SCROLLLOCK,      KEY_SCROLLLOCK          },
+	{ SDLK_PAUSE,           KEY_PAUSE               },
+	{ SDLK_INSERT,          KEY_INSERT              },
+	{ SDLK_HOME,            KEY_HOME                },
+	{ SDLK_PAGEUP,          KEY_PAGEUP              },
+	{ SDLK_DELETE,          KEY_DELETE              },
+	{ SDLK_END,             KEY_END                 },
+	{ SDLK_PAGEDOWN,        KEY_PAGEDOWN            },
+	{ SDLK_RIGHT,           KEY_RIGHT               },
+	{ SDLK_LEFT,            KEY_LEFT                },
+	{ SDLK_DOWN,            KEY_DOWN                },
+	{ SDLK_UP,              KEY_UP                  },
+	{ SDLK_KP_DIVIDE,       KEY_KP_DIVIDE           },
+	{ SDLK_KP_MULTIPLY,     KEY_KP_MULTIPLY         },
+	{ SDLK_KP_MINUS,        KEY_KP_MINUS            },
+	{ SDLK_KP_PLUS,         KEY_KP_PLUS             },
+	{ SDLK_KP_ENTER,        KEY_KP_ENTER            },
+	{ SDLK_KP_1,            KEY_KP_1                },
+	{ SDLK_KP_2,            KEY_KP_2                },
+	{ SDLK_KP_3,            KEY_KP_3                },
+	{ SDLK_KP_4,            KEY_KP_4                },
+	{ SDLK_KP_5,            KEY_KP_5                },
+	{ SDLK_KP_6,            KEY_KP_6                },
+	{ SDLK_KP_7,            KEY_KP_7                },
+	{ SDLK_KP_8,            KEY_KP_8                },
+	{ SDLK_KP_9,            KEY_KP_9                },
+	{ SDLK_KP_0,            KEY_KP_0                },
+	{ SDLK_KP_PERIOD,       KEY_KP_PERIOD           },
+	{ SDLK_KP_COMMA,        KEY_KP_COMMA            },
+	{ SDLK_MENU,            KEY_MENU                },
+	{ SDLK_MUTE,            KEY_MUTE                },
+	{ SDLK_VOLUMEUP,        KEY_VOLUME_UP           },
+	{ SDLK_VOLUMEDOWN,      KEY_VOLUME_DOWN         },
+	{ SDLK_LCTRL,           KEY_LCTRL               },
+	{ SDLK_LSHIFT,          KEY_LSHIFT              },
+	{ SDLK_LALT,            KEY_LALT                },
+	{ SDLK_LGUI,            KEY_LSUPER              },
+	{ SDLK_RCTRL,           KEY_RCTRL               },
+	{ SDLK_RSHIFT,          KEY_RSHIFT              },
+	{ SDLK_RALT,            KEY_RALT                },
+	{ SDLK_RGUI,            KEY_RSUPER              },
+	{ 0,                    -1                      }
+};
+
+/* Maintain with enum mouse_button constants in mouse.h */
+static const struct {
+	int key;
+	enum mouse_button value;
+} buttons[] = {
+	{ SDL_BUTTON_LEFT,      MOUSE_BUTTON_LEFT       },
+	{ SDL_BUTTON_MIDDLE,    MOUSE_BUTTON_MIDDLE     },
+	{ SDL_BUTTON_RIGHT,     MOUSE_BUTTON_RIGHT      },
+	{ -1,                   MOUSE_BUTTON_UNKNOWN    }
+};
+
+static void
+convert_key(const SDL_Event *event, union event *ev)
+{
+	ev->type = event->type == SDL_KEYDOWN ? EVENT_KEYDOWN : EVENT_KEYUP;
+	ev->key.key = KEY_UNKNOWN;
+
+	for (size_t i = 0; keymap[i].key != 0; ++i) {
+		if (keymap[i].key == event->key.keysym.sym) {
+			ev->key.key = keymap[i].value;
+			break;
+		}
+	}
+}
+
+static void
+convert_mouse(const SDL_Event *event, union event *ev)
+{
+	ev->type = EVENT_MOUSE;
+	ev->mouse.buttons = 0;
+	ev->mouse.x = event->motion.x;
+	ev->mouse.y = event->motion.y;
+
+	if (event->motion.state & SDL_BUTTON_LMASK)
+		ev->mouse.buttons |= MOUSE_BUTTON_LEFT;
+	if (event->motion.state & SDL_BUTTON_MMASK)
+		ev->mouse.buttons |= MOUSE_BUTTON_MIDDLE;
+	if (event->motion.state & SDL_BUTTON_RMASK)
+		ev->mouse.buttons |= MOUSE_BUTTON_RIGHT;
+}
+
+static void
+convert_click(const SDL_Event *event, union event *ev)
+{
+	ev->type = event->type == SDL_MOUSEBUTTONDOWN ? EVENT_CLICKDOWN : EVENT_CLICKUP;
+	ev->click.button = MOUSE_BUTTON_UNKNOWN;
+	ev->click.x = event->button.x;
+	ev->click.y = event->button.y;
+
+	for (size_t i = 0; buttons[i].value != MOUSE_BUTTON_UNKNOWN; ++i) {
+		if (buttons[i].key == event->button.button) {
+			ev->click.button = buttons[i].value;
+			break;
+		}
+	}
+}
+
+bool
+event_poll(union event *ev)
+{
+	SDL_Event event;
+
+	/*
+	 * Loop until we find an event we want to report, we skip unneeded
+	 * ones.
+	 */
+	while (SDL_PollEvent(&event)) {
+		switch (event.type) {
+		case SDL_QUIT:
+			ev->type = EVENT_QUIT;
+			return true;
+		case SDL_KEYDOWN:
+		case SDL_KEYUP:
+			convert_key(&event, ev);
+			return true;
+		case SDL_MOUSEMOTION:
+			convert_mouse(&event, ev);
+			return true;
+		case SDL_MOUSEBUTTONDOWN:
+		case SDL_MOUSEBUTTONUP:
+			convert_click(&event, ev);
+			return true;
+		default:
+			continue;
+		}
+	}
+
+	return false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/event.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,88 @@
+/*
+ * event.h -- event management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_EVENT_H
+#define MOLKO_EVENT_H
+
+/**
+ * \file event.h
+ * \brief Event management.
+ */
+
+#include <stdbool.h>
+
+#include "key.h"
+#include "mouse.h"
+
+/**
+ * \brief Kind of event.
+ */
+enum event_type {
+	EVENT_CLICKDOWN,        /*!< Mouse click down */
+	EVENT_CLICKUP,          /*!< Mouse click released */
+	EVENT_KEYDOWN,          /*!< Single key down */
+	EVENT_KEYUP,            /*!< Single key released */
+	EVENT_MOUSE,            /*!< Mouse moved */
+	EVENT_QUIT,             /*!< Quit request */
+};
+
+/**
+ * \brief Store events.
+ */
+union event {
+	enum event_type type;                   /*!< Which kind of event */
+
+	/**
+	 * Store key down/up event.
+	 */
+	struct {
+		enum event_type type;           /*!< EVENT_KEYDOWN or EVENT_KEYUP */
+		enum key key;                   /*!< Which key */
+	} key;
+
+	/**
+	 * Store mouse motion event.
+	 */
+	struct {
+		enum event_type type;           /*!< EVENT_MOUSE */
+		enum mouse_button buttons;      /*!< OR'ed buttons that are pressed */
+		int x;                          /*!< Mouse position in x */
+		int y;                          /*!< Mouse position in y */
+	} mouse;
+
+	/**
+	 * Store mouse click event.
+	 */
+	struct {
+		enum event_type type;           /*!< EVENT_CLICKDOWN or EVENT_CLICKUP */
+		enum mouse_button button;       /*!< Unique button that was pressed */
+		int x;                          /*!< Mouse position in x */
+		int y;                          /*!< Mouse position in y */
+	} click;
+};
+
+/**
+ * Fetch the next event or return false if there are not.
+ *
+ * \param ev the event
+ * \return true if the event was filled or false otherwise
+ */
+bool
+event_poll(union event *ev);
+
+#endif /* !MOLKO_EVENT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/font.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,104 @@
+/*
+ * font.c -- basic font management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include <SDL_ttf.h>
+
+#include "color.h"
+#include "error.h"
+#include "error_p.h"
+#include "font.h"
+#include "texture_p.h"
+#include "util.h"
+
+struct font {
+	TTF_Font *handle;
+};
+
+struct font *
+font_openf(const char *path, unsigned int size)
+{
+	assert(path);
+
+	struct font *f;
+
+	f = ecalloc(1, sizeof (struct font));
+
+	if (!(f->handle = TTF_OpenFont(path, size))) {
+		error_sdl();
+		free(f);
+		return NULL;
+	}
+
+	return f;
+}
+
+struct font *
+font_openb(const void *buffer, size_t buflen, unsigned int size)
+{
+	assert(buffer);
+
+	struct font *f;
+	SDL_RWops *ops;
+
+	f = ecalloc(1, sizeof (struct font));
+
+	if (!(ops = SDL_RWFromConstMem(buffer, buflen)) ||
+	   (!(f->handle = TTF_OpenFontRW(ops, true, size)))) {
+		error_sdl();
+		free(f);
+		return NULL;
+	}
+
+	return f;
+}
+
+struct texture *
+font_render(struct font *font, const char *text, unsigned long color)
+{
+	assert(font);
+	assert(text);
+
+	SDL_Color fg = {
+		.r = COLOR_R(color),
+		.g = COLOR_G(color),
+		.b = COLOR_B(color),
+		.a = COLOR_A(color)
+	};
+
+	SDL_Surface *surface;
+
+	if (!(surface = TTF_RenderUTF8_Blended(font->handle, text, fg))) {
+		error_sdl();
+		return NULL;
+	}
+
+	return texture_from_surface(surface);
+}
+
+void
+font_close(struct font *font)
+{
+	assert(font);
+
+	TTF_CloseFont(font->handle);
+	free(font);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/font.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,83 @@
+/*
+ * font.h -- basic font management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_FONT_H
+#define MOLKO_FONT_H
+
+/**
+ * \file font.h
+ * \brief Basic font management.
+ */
+
+#include <stddef.h>
+
+/**
+ * \brief Font object.
+ *
+ * This object is not publicly defined because it contains
+ * implementation-defined data.
+ */
+struct font;
+struct texture;
+
+/**
+ * Open font from path file.
+ *
+ * \pre path != NULL
+ * \param path the path to the font
+ * \param size the desired size
+ * \return the font or NULL on error
+ */
+struct font *
+font_openf(const char *path, unsigned int size);
+
+/**
+ * Open font from memory buffer.
+ *
+ * \pre buffer != NULL
+ * \param buffer the memory buffer
+ * \param buflen the memory buffer length
+ * \param size the desired size
+ * \warning The buffer must remain valid until font is closed
+ * \return the font or NULL on error
+ */
+struct font *
+font_openb(const void *buffer, size_t buflen, unsigned int size);
+
+/**
+ * Render a text.
+ *
+ * \pre font != NULL
+ * \pre text != NULL
+ * \param font the font handle
+ * \param text the text in UTF-8
+ * \param color the color
+ */
+struct texture *
+font_render(struct font *font, const char *text, unsigned long color);
+
+/**
+ * Close the font.
+ *
+ * \pre font != NULL
+ * \param font the font handle
+ */
+void
+font_close(struct font *font);
+
+#endif /* !MOLKO_FONT_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/game.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,158 @@
+/*
+ * game.c -- main game object
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "game.h"
+#include "state.h"
+
+struct game game = {
+	.state = NULL,
+	.state_next = NULL
+};
+
+static struct action *
+find_empty_action(void)
+{
+	static struct action null;
+
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
+		if (memcmp(&game.actions[i], &null, sizeof (struct action)) == 0)
+			return &game.actions[i];
+
+	return NULL;
+}
+
+static void
+clear_actions(void)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
+		struct action *a = &game.actions[i];
+
+		/* These actions are removed on state change. */
+		if (a->flags & ACTION_AUTO_LEAVE) {
+			if (a->finish)
+				a->finish(a);
+
+			memset(a, 0, sizeof (struct action));
+		}
+	}
+}
+
+static void
+handle_actions(const union event *event)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
+		if (game.actions[i].handle)
+			game.actions[i].handle(&game.actions[i], event);
+}
+
+static void
+update_actions(unsigned int ticks)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
+		struct action *a = &game.actions[i];
+
+		if (!a->update)
+			continue;
+
+		if (a->update(a, ticks)) {
+			if (a->finish)
+				a->finish(a);
+
+			memset(&game.actions[i], 0, sizeof (struct action));
+		}
+	}
+}
+
+static void
+draw_actions(void)
+{
+	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
+		if (game.actions[i].draw)
+			game.actions[i].draw(&game.actions[i]);
+}
+
+
+void
+game_switch(struct state *state, bool quick)
+{
+	assert(state);
+
+	if (quick) {
+		game.state = state;
+		game.state->enter();
+	} else
+		game.state_next = state;
+}
+
+void
+game_handle(const union event *event)
+{
+	assert(event);
+
+	if (game.state)
+		game.state->handle(event);
+
+	handle_actions(event);
+}
+
+void
+game_update(unsigned int ticks)
+{
+	/* Change state if any. */
+	if (game.state_next) {
+		/* Inform the current state we're gonna leave it. */
+		if (game.state)
+			game.state->leave();
+
+		game.state = game.state_next;
+		game.state->enter();
+		game.state_next = NULL;
+
+		/* Remove any actions that must be deleted. */
+		clear_actions();
+	}
+
+	if (game.state)
+		game.state->update(ticks);
+
+	update_actions(ticks);
+}
+
+void
+game_draw(void)
+{
+	if (game.state)
+		game.state->draw();
+
+	draw_actions();
+}
+
+void
+game_add_action(const struct action *action)
+{
+	assert(action);
+
+	struct action *pos;
+
+	if ((pos = find_empty_action()))
+		memcpy(pos, action, sizeof (struct action));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/game.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,105 @@
+/*
+ * game.h -- main game object
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_GAME_H
+#define MOLKO_GAME_H
+
+/**
+ * \file game.h
+ * \brief Main game object.
+ */
+
+#include <stdbool.h>
+
+#include "action.h"
+
+/**
+ * \brief Max number of actions allowed at the same time.
+ */
+#define GAME_ACTIONS_MAX 32
+
+struct state;
+
+union event;
+
+/**
+ * \brief Main game object.
+ */
+struct game {
+	/* Game states. */
+	struct state *state;            /*!< (RO) Current state  */
+	struct state *state_next;       /*!< (RO) Next state */
+
+	/** Array of actions. */
+	struct action actions[GAME_ACTIONS_MAX];
+};
+
+/**
+ * Global game object.
+ */
+extern struct game game;
+
+/**
+ * Request to change state.
+ *
+ * This function will only update state after the next \a game_update call.
+ *
+ * If quick is true, change state immediately.
+ *
+ * \pre state != NULL
+ * \param state the new state
+ * \param quick quickly change the state
+ */
+void
+game_switch(struct state *state, bool quick);
+
+/**
+ * Handle input event.
+ *
+ * \param event the event
+ */
+void
+game_handle(const union event *event);
+
+/**
+ * Update the game state.
+ *
+ * \param ticks the number of milliseconds between last frame
+ */
+void
+game_update(unsigned int ticks);
+
+/**
+ * Draw the game using the current state.
+ */
+void
+game_draw(void);
+
+/**
+ * Add an action to the game.
+ *
+ * If there are no room for the action, action is discarded. Make sure to not
+ * exceed the limit GAME_ACTIONS_MAX.
+ *
+ * \pre action != NULL
+ * \param action the action to copy
+ */
+void
+game_add_action(const struct action *action);
+
+#endif /* !MOLKO_GAME_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/image.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,56 @@
+/*
+ * image.c -- basic image management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+
+#include <SDL_image.h>
+
+#include "error_p.h"
+#include "texture_p.h"
+
+struct texture *
+image_openf(const char *path)
+{
+	assert(path);
+
+	SDL_Surface *surface = IMG_Load(path);
+
+	if (!surface) {
+		error_sdl();
+		return NULL;
+	}
+
+	return texture_from_surface(surface);
+}
+
+struct texture *
+image_openb(const void *buffer, size_t size)
+{
+	assert(buffer);
+
+	SDL_RWops *ops = SDL_RWFromConstMem(buffer, size);
+	SDL_Surface *surface;
+
+	if (!ops || !(surface = IMG_Load_RW(ops, true))) {
+		error_sdl();
+		return NULL;
+	}
+
+	return texture_from_surface(surface);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/image.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,52 @@
+/*
+ * image.h -- basic image management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_IMAGE_H
+#define MOLKO_IMAGE_H
+
+/**
+ * \file image.h
+ * \brief Basic image management.
+ */
+
+#include <stddef.h>
+
+struct texture;
+
+/**
+ * Open a file from a path.
+ *
+ * \pre path != NULL
+ * \param path the path to the file
+ * \return the texture or NULL on error
+ */
+struct texture *
+image_openf(const char *path);
+
+/**
+ * Open a file from a memory buffer.
+ *
+ * \pre buffer != NULL
+ * \param buffer the memory buffer
+ * \param size the memory size
+ * \return the texture or NULL on error
+ */
+struct texture *
+image_openb(const void *buffer, size_t size);
+
+#endif /* !MOLKO_IMAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/key.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,190 @@
+/*
+ * key.h -- keyboard definitions
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_KEY_H
+#define MOLKO_KEY_H
+
+/**
+ * \file key.h
+ * \brief Keyboard definitions.
+ */
+
+/**
+ * \brief Key codes.
+ */
+enum key {
+	KEY_UNKNOWN,
+	KEY_ENTER,
+	KEY_ESCAPE,
+	KEY_BACKSPACE,
+	KEY_TAB,
+	KEY_SPACE,
+	KEY_EXCLAIM,
+	KEY_DOUBLE_QUOTE,
+	KEY_HASH,
+	KEY_PERCENT,
+	KEY_DOLLAR,
+	KEY_AMPERSAND,
+	KEY_QUOTE,
+	KEY_LEFT_PAREN,
+	KEY_RIGHT_PAREN,
+	KEY_ASTERISK,
+	KEY_PLUS,
+	KEY_COMMA,
+	KEY_MINUS,
+	KEY_PERIOD,
+	KEY_SLASH,
+	KEY_0,
+	KEY_1,
+	KEY_2,
+	KEY_3,
+	KEY_4,
+	KEY_5,
+	KEY_6,
+	KEY_7,
+	KEY_8,
+	KEY_9,
+	KEY_COLON,
+	KEY_SEMICOLON,
+	KEY_LESS,
+	KEY_EQUALS,
+	KEY_GREATER,
+	KEY_QUESTION,
+	KEY_AT,
+	KEY_LEFT_BRACKET,
+	KEY_BACKSLASH,
+	KEY_RIGHT_BRACKET,
+	KEY_CARET,
+	KEY_UNDERSCORE,
+	KEY_BACKQUOTE,
+	KEY_a,
+	KEY_b,
+	KEY_c,
+	KEY_d,
+	KEY_e,
+	KEY_f,
+	KEY_g,
+	KEY_h,
+	KEY_i,
+	KEY_j,
+	KEY_k,
+	KEY_l,
+	KEY_m,
+	KEY_n,
+	KEY_o,
+	KEY_p,
+	KEY_q,
+	KEY_r,
+	KEY_s,
+	KEY_t,
+	KEY_u,
+	KEY_v,
+	KEY_w,
+	KEY_x,
+	KEY_y,
+	KEY_z,
+	KEY_CAPSLOCK,
+	KEY_F1,
+	KEY_F2,
+	KEY_F3,
+	KEY_F4,
+	KEY_F5,
+	KEY_F6,
+	KEY_F7,
+	KEY_F8,
+	KEY_F9,
+	KEY_F10,
+	KEY_F11,
+	KEY_F12,
+	KEY_F13,
+	KEY_F14,
+	KEY_F15,
+	KEY_F16,
+	KEY_F17,
+	KEY_F18,
+	KEY_F19,
+	KEY_F20,
+	KEY_F21,
+	KEY_F22,
+	KEY_F23,
+	KEY_F24,
+	KEY_PRINTSCREEN,
+	KEY_SCROLLLOCK,
+	KEY_PAUSE,
+	KEY_INSERT,
+	KEY_HOME,
+	KEY_PAGEUP,
+	KEY_DELETE,
+	KEY_END,
+	KEY_PAGEDOWN,
+	KEY_RIGHT,
+	KEY_LEFT,
+	KEY_DOWN,
+	KEY_UP,
+	KEY_NUMLOCKCLEAR,
+	KEY_KP_DIVIDE,
+	KEY_KP_MULTIPLY,
+	KEY_KP_MINUS,
+	KEY_KP_PLUS,
+	KEY_KP_ENTER,
+	KEY_KP_00,
+	KEY_KP_000,
+	KEY_KP_1,
+	KEY_KP_2,
+	KEY_KP_3,
+	KEY_KP_4,
+	KEY_KP_5,
+	KEY_KP_6,
+	KEY_KP_7,
+	KEY_KP_8,
+	KEY_KP_9,
+	KEY_KP_0,
+	KEY_KP_PERIOD,
+	KEY_KP_COMMA,
+	KEY_MENU,
+	KEY_MUTE,
+	KEY_VOLUME_UP,
+	KEY_VOLUME_DOWN,
+	KEY_LCTRL,
+	KEY_LSHIFT,
+	KEY_LALT,
+	KEY_LSUPER,
+	KEY_RCTRL,
+	KEY_RSHIFT,
+	KEY_RALT,
+	KEY_RSUPER,
+};
+
+/**
+ * \brief Keybord modifiers.
+ *
+ * This enumeration is usually stored as OR'ed flags as several modifiers can
+ * be pressed at a time.
+ */
+enum keymod {
+	KEYMOD_LSHIFT   = 1 << 0,       /*!< Left shift */
+	KEYMOD_LCTRL    = 1 << 1,       /*!< Left control */
+	KEYMOD_LALT     = 1 << 2,       /*!< Left alt */
+	KEYMOD_LSUPER   = 1 << 3,       /*!< Left super (logo) */
+	KEYMOD_RSHIFT   = 1 << 4,       /*!< Right shift */
+	KEYMOD_RCTRL    = 1 << 5,       /*!< Right control */
+	KEYMOD_RALT     = 1 << 6,       /*!< Right alt */
+	KEYMOD_RSUPER   = 1 << 7        /*!< Right super (logo) */
+};
+
+#endif /* !MOLKO_KEY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/map.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,225 @@
+/*
+ * map.c -- game map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "error.h"
+#include "error_p.h"
+#include "image.h"
+#include "map.h"
+#include "painter.h"
+#include "sprite.h"
+#include "sys.h"
+#include "texture.h"
+#include "window.h"
+
+#include <SDL.h>
+
+/* Create %<v>c string literal for scanf */
+#define MAX_F(v) MAX_F_(v)
+#define MAX_F_(v) "%" #v "c"
+
+static void
+parse_layer(struct map *map, const char *line, FILE *fp)
+{
+	char layer_name[32 + 1] = { 0 };
+	struct map_layer *layer;
+	size_t amount, current;
+
+	/* Determine layer. */
+	if (sscanf(line, "layer|%32s", layer_name) <= 0)
+		return;
+	if (strcmp(layer_name, "background") == 0)
+		layer = &map->layers[0];
+	else if (strcmp(layer_name, "foreground") == 0)
+		layer = &map->layers[1];
+	else
+		return;
+
+	/* Check if weight/height has been specified. */
+	if (map->width == 0 || map->height == 0)
+		return;
+
+	amount = map->width * map->height;
+	current = 0;
+
+	if (!(layer->tiles = calloc(amount, sizeof (unsigned short))))
+		return;
+
+	for (int tile; fscanf(fp, "%d", &tile) && current < amount; ++current)
+		layer->tiles[current] = tile;
+}
+
+static void
+parse_tileset(struct map *map, const char *line)
+{
+	char filename[128 + 1] = { 0 };
+
+	sscanf(line, "tileset|%128s", filename);
+
+	if (map->tilewidth == 0 || map->tileheight == 0)
+		return;
+	if (!(map->tileset = image_openf(sys_datapath("tilesets/%s", filename))))
+		return;
+
+	sprite_init(&map->sprite, map->tileset, map->tilewidth, map->tileheight);
+}
+
+static void
+parse(struct map *map, const char *line, FILE *fp)
+{
+	if (strncmp(line, "title", 5) == 0)
+		sscanf(line, "title|" MAX_F(MAP_TITLE_MAX), map->title);
+	else if (strncmp(line, "width", 5) == 0)
+		sscanf(line, "width|%u", &map->width);
+	else if (strncmp(line, "height", 6) == 0)
+		sscanf(line, "height|%u", &map->height);
+	else if (strncmp(line, "tilewidth", 9) == 0)
+		sscanf(line, "tilewidth|%hu", &map->tilewidth);
+	else if (strncmp(line, "tileheight", 10) == 0)
+		sscanf(line, "tileheight|%hu", &map->tileheight);
+	else if (strncmp(line, "origin", 6) == 0)
+		sscanf(line, "origin|%d|%d", &map->origin_x, &map->origin_y);
+	else if (strncmp(line, "tileset", 7) == 0)
+		parse_tileset(map, line);
+	else if (strncmp(line, "layer", 5) == 0)
+		parse_layer(map, line, fp);
+}
+
+static bool
+check(struct map *map)
+{
+	if (strlen(map->title) == 0)
+		return error_printf("map has no title");
+	if (!map->tileset)
+		return error_printf("unable to open tileset");
+	if (map->width == 0 || map->height == 0)
+		return error_printf("map has null sizes");
+	if (map->tilewidth == 0 || map->tileheight == 0)
+		return error_printf("map has null tile sizes");
+	if (!map->layers[0].tiles || !map->layers[1].tiles)
+		return error_printf("could not allocate data");
+
+	return true;
+}
+
+static void
+draw_layer(struct map *map, const struct map_layer *layer)
+{
+	assert(map);
+	assert(layer);
+
+	int x = 0, y = 0;
+
+	for (unsigned int r = 0; r < map->width; ++r) {
+		for (unsigned int c = 0; c < map->height; ++c) {
+			unsigned int si = r * map->width + c;
+			unsigned int sr = (layer->tiles[si] - 1) / map->sprite.ncols;
+			unsigned int sc = (layer->tiles[si] - 1) % map->sprite.nrows;
+
+			if (layer->tiles[si] != 0)
+				sprite_draw(&map->sprite, sr, sc, x, y);
+
+			x += map->tilewidth;
+		}
+
+		x = 0;
+		y += map->tileheight;
+	}
+}
+
+bool
+map_open(struct map *map, const char *path)
+{
+	assert(map);
+	assert(path);
+
+	memset(map, 0, sizeof (struct map));
+
+	FILE *fp = fopen(path, "r");
+	char line[BUFSIZ];
+
+	if (!fp)
+		return false;
+
+	while (fgets(line, sizeof (line), fp)) {
+		/* Remove \n if any */
+		line[strcspn(line, "\n")] = '\0';
+		parse(map, line, fp);
+	}
+
+	fclose(fp);
+
+	if (!check(map)) {
+		map_close(map);
+		return false;
+	}
+
+	size_t pw = map->width * map->tilewidth;
+	size_t ph = map->height * map->tileheight;
+
+	if (!(map->picture = texture_new(pw, ph)))
+		return error_sdl();
+
+	return true;
+}
+
+void
+map_draw(struct map *map, int srcx, int srcy)
+{
+	texture_draw_ex(
+		map->picture,
+		srcx,
+		srcy,
+		window_width(),
+		window_height(),
+		0,
+		0,
+		window_width(),
+		window_height(),
+		0
+	);
+}
+
+void
+map_repaint(struct map *map)
+{
+	PAINTER_BEGIN(map->picture);
+	draw_layer(map, &map->layers[0]);
+	draw_layer(map, &map->layers[1]);
+	PAINTER_END();
+}
+
+void
+map_close(struct map *map)
+{
+	assert(map);
+
+	if (map->tileset)
+		texture_close(map->tileset);
+	if (map->picture)
+		texture_close(map->picture);
+
+	free(map->layers[0].tiles);
+	free(map->layers[1].tiles);
+
+	memset(map, 0, sizeof (struct map));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/map.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,107 @@
+/*
+ * map.h -- game map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MAP_H
+#define MOLKO_MAP_H
+
+/**
+ * \file map.h
+ * \brief Game map.
+ */
+
+#include <stdbool.h>
+
+#include "sprite.h"
+
+/**
+ * \brief Max title length for a map.
+ */
+#define MAP_TITLE_MAX   32
+
+struct texture;
+
+/**
+ * \brief Map layer.
+ */
+struct map_layer {
+	unsigned short *tiles;          /*!< (RO) Array of tiles, depending on the map size. */
+};
+
+/**
+ * \brief Map object.
+ *
+ * This structure only defines the map characteristics. It does not have any
+ * logic and is left for game state.
+ */
+struct map {
+	char title[MAP_TITLE_MAX];      /*!< (RW) The map title */
+	struct texture *tileset;        /*!< (RW) Tileset to use */
+	struct texture *picture;        /*!< (RO) Map drawn into a picture */
+	struct sprite sprite;           /*!< (RO) Sprite to render */
+	int origin_x;                   /*!< (RO) Where the player starts in X */
+	int origin_y;                   /*!< (RO) Where the player starts in Y */
+	unsigned int width;             /*!< (RO) Map width in cells */
+	unsigned int height;            /*!< (RO) Map height in cells */
+	unsigned short tilewidth;       /*!< (RO) Pixels per cell (width) */
+	unsigned short tileheight;      /*!< (RO) Pixels per cell (height) */
+	struct map_layer layers[2];     /*!< (RO) Layers (background, foreground) */
+};
+
+/**
+ * Open a map.
+ *
+ * \pre map != NULL
+ * \pre path != NULL
+ * \param map the map to fill
+ * \param path the path to the map
+ * \return true if successfully loaded
+ */
+bool
+map_open(struct map *map, const char *path);
+
+/**
+ * Render a map.
+ *
+ * \pre map != NULL
+ * \param map the map to render
+ * \param srcx the x coordinate region
+ * \param srcy the y coordinate region
+ */
+void
+map_draw(struct map *map, int srcx, int srcy);
+
+/**
+ * Force map repaint on its texture.
+ *
+ * \pre map != NULL
+ * \param map the map to repaint
+ * \warning This function does not render anything on the screen
+ */
+void
+map_repaint(struct map *map);
+
+/**
+ * Close the map and its resources.
+ *
+ * \pre map != NULL
+ * \param map the map to render
+ */
+void
+map_close(struct map *map);
+
+#endif /* !MOLKO_MAP_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/map_state.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,341 @@
+/*
+ * map_state.c -- state when player is on a map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+
+#include "event.h"
+#include "game.h"
+#include "map.h"
+#include "map_state.h"
+#include "painter.h"
+#include "state.h"
+#include "texture.h"
+#include "walksprite.h"
+#include "window.h"
+
+/*
+ * This is the speed the player moves on the map.
+ *
+ * SPEED represents the number of pixels it must move per SEC.
+ * SEC simply represends the number of milliseconds in one second.
+ */
+#define SPEED 100
+#define SEC   1000
+
+/*
+ * Those are margins within the edge of the screen. The camera always try to
+ * keep those padding between the player and the screen.
+ */
+#define MARGIN_WIDTH    80
+#define MARGIN_HEIGHT   80
+
+/*
+ * This structure defines the possible movement of the player as flags since
+ * it's possible to make diagonal movements.
+ */
+enum movement {
+	MOVING_UP       = 1 << 0,
+	MOVING_RIGHT    = 1 << 1,
+	MOVING_DOWN     = 1 << 2,
+	MOVING_LEFT     = 1 << 3
+};
+
+/*
+ * A bit of explanation within this array. The structure walksprite requires
+ * an orientation between 0-7 depending on the user direction.
+ *
+ * Since keys for moving the character may be pressed at the same time, we need
+ * a conversion table from "key pressed" to "orientation".
+ *
+ * When an orientation is impossible, it is set to -1. Example, when both left
+ * and right are pressed.
+ *
+ * MOVING_UP    = 0001 = 0x1
+ * MOVING_RIGHT = 0010 = 0x2
+ * MOVING_DOWN  = 0100 = 0x3
+ * MOVING_LEFT  = 1000 = 0x4
+ */
+static unsigned int orientations[16] = {
+	[0x1] = 0,
+	[0x2] = 2,
+	[0x3] = 1,
+	[0x4] = 4,
+	[0x6] = 3,
+	[0x8] = 6,
+	[0x9] = 7,
+	[0xC] = 5
+};
+
+/*
+ * Additional data that is not necessary to expose in map_state_data.
+ */
+static struct {
+	struct {
+		enum movement moving;
+		struct walksprite ws;
+	} player;
+
+	struct {
+		int x;
+		int y;
+		unsigned int w;
+		unsigned int h;
+	} margin;
+} cache;
+
+static void
+center(void)
+{
+	map_state_data.view.x = map_state_data.player.x - (map_state_data.view.w / 2);
+	map_state_data.view.y = map_state_data.player.y - (map_state_data.view.h / 2);
+
+	if (map_state_data.view.x < 0)
+		map_state_data.view.x = 0;
+	else if ((unsigned int)map_state_data.view.x > map_state_data.map.w - map_state_data.view.w)
+		map_state_data.view.x = map_state_data.map.w - map_state_data.view.w;
+
+	if (map_state_data.view.y < 0)
+		map_state_data.view.y = 0;
+	else if ((unsigned int)map_state_data.view.y > map_state_data.map.h - map_state_data.view.h)
+		map_state_data.view.y = map_state_data.map.h - map_state_data.view.h;
+}
+
+static void
+enter(void)
+{
+	/* Adjust map properties. */
+	struct map *m = &map_state_data.map.map;
+
+	if (!m->picture)
+		map_repaint(m);
+
+	map_repaint(m);
+	map_state_data.map.w = texture_width(m->picture);
+	map_state_data.map.h = texture_height(m->picture);
+
+	/* Adjust view. */
+	map_state_data.view.w = window_width();
+	map_state_data.view.h = window_height();
+
+	/* Adjust margin. */
+	cache.margin.w = map_state_data.view.w - (MARGIN_WIDTH * 2);
+	cache.margin.h = map_state_data.view.h - (MARGIN_HEIGHT * 2);
+
+	/* Center the view by default. */
+	center();
+
+	/* Final bits. */
+	walksprite_init(&cache.player.ws, &map_state_data.player.sprite, 300);
+}
+
+static void
+leave(void)
+{
+}
+
+static void
+handle_keydown(const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_UP:
+		cache.player.moving |= MOVING_UP;
+		break;
+	case KEY_RIGHT:
+		cache.player.moving |= MOVING_RIGHT;
+		break;
+	case KEY_DOWN:
+		cache.player.moving |= MOVING_DOWN;
+		break;
+	case KEY_LEFT:
+		cache.player.moving |= MOVING_LEFT;
+		break;
+	default:
+		break;
+	}
+
+	map_state_data.player.angle = orientations[cache.player.moving];
+}
+
+static void
+handle_keyup(const union event *event)
+{
+	switch (event->key.key) {
+	case KEY_UP:
+		cache.player.moving &= ~(MOVING_UP);
+		break;
+	case KEY_RIGHT:
+		cache.player.moving &= ~(MOVING_RIGHT);
+		break;
+	case KEY_DOWN:
+		cache.player.moving &= ~(MOVING_DOWN);
+		break;
+	case KEY_LEFT:
+		cache.player.moving &= ~(MOVING_LEFT);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+move_right(unsigned int delta)
+{
+	map_state_data.player.x += delta;
+
+	if (map_state_data.player.x > (int)(cache.margin.x + cache.margin.w)) {
+		map_state_data.view.x = (map_state_data.player.x - map_state_data.view.w) + MARGIN_WIDTH;
+
+		if (map_state_data.view.x >= (int)(map_state_data.map.w - map_state_data.view.w))
+			map_state_data.view.x = map_state_data.map.w - map_state_data.view.w;
+	}
+
+	if (map_state_data.player.x > (int)map_state_data.map.w - 48)
+		map_state_data.player.x = map_state_data.map.w - 48;
+}
+
+static void
+move_left(unsigned int delta)
+{
+	map_state_data.player.x -= delta;
+
+	if (map_state_data.player.x < cache.margin.x) {
+		map_state_data.view.x = map_state_data.player.x - MARGIN_WIDTH;
+
+		if (map_state_data.view.x < 0)
+			map_state_data.view.x = 0;
+	}
+
+	if (map_state_data.player.x < 0)
+		map_state_data.player.x = 0;
+}
+
+static void
+move_down(unsigned int delta)
+{
+	map_state_data.player.y += delta;
+
+	if (map_state_data.player.y > (int)(cache.margin.y + cache.margin.h)) {
+		map_state_data.view.y = (map_state_data.player.y - map_state_data.view.h) + MARGIN_HEIGHT;
+
+		if (map_state_data.view.y >= (int)(map_state_data.map.h - map_state_data.view.h))
+			map_state_data.view.y = map_state_data.map.h - map_state_data.view.h;
+	}
+
+	if (map_state_data.player.y > (int)map_state_data.map.h - 48)
+		map_state_data.player.y = map_state_data.map.h - 48;
+}
+
+static void
+move_up(unsigned int delta)
+{
+	map_state_data.player.y -= delta;
+
+	if (map_state_data.player.y < cache.margin.y) {
+		map_state_data.view.y = map_state_data.player.y - MARGIN_HEIGHT;
+
+		if (map_state_data.view.y < 0)
+			map_state_data.view.y = 0;
+	}
+
+	if (map_state_data.player.y < 0)
+		map_state_data.player.y = 0;
+}
+
+static void
+move(unsigned int ticks)
+{
+	/* This is the amount of pixels the player must move. */
+	const int delta = SPEED * ticks / SEC;
+
+	/* This is the rectangle within the view where users must be. */
+	cache.margin.x = map_state_data.view.x + MARGIN_WIDTH;
+	cache.margin.y = map_state_data.view.y + MARGIN_HEIGHT;
+
+	int dx = 0;
+	int dy = 0;
+
+	if (cache.player.moving == 0)
+		return;
+
+	if (cache.player.moving & MOVING_UP)
+		dy = -1;
+	if (cache.player.moving & MOVING_DOWN)
+		dy = 1;
+	if (cache.player.moving & MOVING_LEFT)
+		dx = -1;
+	if (cache.player.moving & MOVING_RIGHT)
+		dx = 1;
+
+	/* Move the player and adjust view if needed. */
+	if (dx > 0)
+		move_right(delta);
+	else if (dx < 0)
+		move_left(delta);
+
+	if (dy > 0)
+		move_down(delta);
+	else if (dy < 0)
+		move_up(delta);
+
+	walksprite_update(&cache.player.ws, ticks);
+}
+
+static void
+handle(const union event *event)
+{
+	switch (event->type) {
+	case EVENT_KEYDOWN:
+		handle_keydown(event);
+		break;
+	case EVENT_KEYUP:
+		handle_keyup(event);
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+update(unsigned int ticks)
+{
+	move(ticks);
+}
+
+static void
+draw(void)
+{
+	painter_set_color(0x000000ff);
+	painter_clear();
+	map_draw(&map_state_data.map.map, map_state_data.view.x, map_state_data.view.y);
+	walksprite_draw(
+		&cache.player.ws,
+		map_state_data.player.angle,
+		map_state_data.player.x - map_state_data.view.x,
+		map_state_data.player.y - map_state_data.view.y);
+	painter_present();
+}
+
+struct map_state_data map_state_data;
+
+struct state map_state = {
+	.enter = enter,
+	.leave = leave,
+	.update = update,
+	.handle = handle,
+	.draw = draw
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/map_state.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,74 @@
+/*
+ * map_state.h -- state when player is on a map
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MAP_STATE_H
+#define MOLKO_MAP_STATE_H
+
+/**
+ * \file map_state.h
+ * \brief State when player is on a map.
+ */
+
+#include "map.h"
+#include "sprite.h"
+
+/**
+ * \brief Data for the state.
+ *
+ * Update this structure before switching to this state.
+ */
+extern struct map_state_data {
+	/**
+	 * Map properties.
+	 */
+	struct {
+		struct map map;         /*!< (RO) The map definition */
+		unsigned int w;         /*!< (RO) Map width */
+		unsigned int h;         /*!< (RO) Map height */
+	} map;
+
+	/**
+	 * Player position.
+	 *
+	 * If you adjust this structure, it is strictly encouraged to update
+	 * the view as well.
+	 */
+	struct {
+		struct sprite sprite;   /*!< (RW) The sprite to use */
+		int x;                  /*!< (RO) Player position in x */
+		int y;                  /*!< (RO) Player position in y */
+		int angle;              /*!< (RO) Player angle (see walksprite) */
+	} player;
+
+	/**
+	 * Position and size of the view.
+	 */
+	struct {
+		int x;                  /*!< (RW) Position in x */
+		int y;                  /*!< (RW) Position in y */
+		unsigned int w;         /*!< (RO) View width */
+		unsigned int h;         /*!< (RO) View height */
+	} view;
+} map_state_data; /*!< Access to data. */
+
+/**
+ * \brief State when player is on a map.
+ */
+extern struct state map_state;
+
+#endif /* !MOLKO_MAP_STATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/message.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,101 @@
+/*
+ * message.c -- message dialog
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "font.h"
+#include "message.h"
+#include "painter.h"
+#include "sprite.h"
+#include "texture.h"
+
+#define MESSAGE_SPEED   1000    /* Time delay for animations */
+#define MESSAGE_TIMEOUT 5000    /* Time for auto-closing */
+
+void
+message_start(struct message *msg)
+{
+	assert(msg);
+
+	msg->elapsed = 0;
+	msg->state = MESSAGE_OPENING;
+}
+
+void
+message_update(struct message *msg, unsigned int ticks)
+{
+	assert(msg);
+
+	msg->elapsed += ticks;
+
+	switch (msg->state) {
+	case MESSAGE_OPENING:
+		if (msg->elapsed >= MESSAGE_SPEED)
+			msg->state = MESSAGE_SHOWING;
+		break;
+	case MESSAGE_SHOWING:
+		/* Do automatically switch state if requested by the user. */
+		if (msg->flags & MESSAGE_AUTOMATIC && msg->elapsed >= MESSAGE_TIMEOUT)
+			msg->state = MESSAGE_HIDING;
+
+		break;
+	default:
+		break;
+	}
+}
+
+void
+message_draw(struct message *msg)
+{
+	assert(msg);
+
+	/* TODO: more constant variables. */
+	struct texture *lines[3];
+	int x = 160;
+	int y = 80;
+
+	painter_set_color(0xff0000ff);
+	painter_draw_rectangle(true, x, y, 960, 160);
+
+	for (int i = 0; msg->text[i]; ++i) {
+		lines[i] = font_render(msg->font, msg->text[i], 0xffffffff);
+
+		if (!lines[i])
+			continue;
+
+		texture_draw(lines[i], x, y);
+		texture_close(lines[i]);
+		y += 53;
+	}
+}
+
+void
+message_hide(struct message *msg)
+{
+	assert(msg);
+
+	msg->elapsed = 0;
+}
+
+bool
+message_is_complete(struct message *msg)
+{
+	assert(msg);
+
+	return msg->state == MESSAGE_HIDING && msg->elapsed >= MESSAGE_SPEED;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/message.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,110 @@
+/*
+ * message.h -- message dialog
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MESSAGE_H
+#define MOLKO_MESSAGE_H
+
+/**
+ * \file message.h
+ * \brief Message dialog.
+ */
+
+#include <stdbool.h>
+
+struct sprite;
+struct font;
+
+/**
+ * \brief Message flags.
+ */
+enum message_flags {
+	MESSAGE_AUTOMATIC = (1 << 0)    /*!< Message will automatically close */
+};
+
+/**
+ * \brief Message state.
+ */
+enum message_state {
+	MESSAGE_OPENING,                /*!< Message animation is opening */
+	MESSAGE_SHOWING,                /*!< Message is displaying */
+	MESSAGE_HIDING                  /*!< Message animation for hiding */
+};
+
+/**
+ * \brief Message object.
+ */
+struct message {
+	const char *text[3];            /*!< (RW) lines of text to show */
+	struct sprite *theme;           /*!< (RW) sprite to use for the frame */
+	struct texture *avatar;         /*!< (RW) optional avatar */
+	struct font *font;              /*!< (RW) font to use */
+	enum message_flags flags;       /*!< (RW) message flags */
+	enum message_state state;       /*!< (RO) current state */
+	unsigned int elapsed;           /*!< (RW) elapsed time while displaying */
+};
+
+/**
+ * Start opening the message. This function will reset the message state and
+ * elapsed time.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ */
+void
+message_start(struct message *msg);
+
+/**
+ * Update the message state and elapsed time..
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ * \param ticks the elapsed delay since last frame
+ */
+void
+message_update(struct message *msg, unsigned int ticks);
+
+/**
+ * Draw the message into the screen.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ */
+void
+message_draw(struct message *msg);
+
+/**
+ * Start hiding the message.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ * \note You should still continue to draw the message as the animation is not
+ *       finished!
+ */
+void
+message_hide(struct message *msg);
+
+/**
+ * Tells if the message is complete.
+ *
+ * \pre msg != NULL
+ * \param msg the message
+ */
+bool
+message_is_complete(struct message *msg);
+
+#endif /* !MOLKO_MESSAGE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/mouse.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,42 @@
+/*
+ * mouse.h -- mouse definitions
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_MOUSE_H
+#define MOLKO_MOUSE_H
+
+/**
+ * \file mouse.h
+ * \brief Mouse definitions.
+ */
+
+/**
+ * \brief Buttons from mouse.
+ *
+ * This enumeration is used as both flags or constants. For example when the
+ * user press one button on the mouse it generates one constant event. On the
+ * other hand, while moving the mouse the user may have one or more buttons
+ * pressed, thus the OR'ed combination.
+ */
+enum mouse_button {
+	MOUSE_BUTTON_UNKNOWN    = 0,            /*!< No buttons pressed */
+	MOUSE_BUTTON_LEFT       = (1 << 0),     /*!< Left button pressed */
+	MOUSE_BUTTON_MIDDLE     = (1 << 1),     /*!< Middle button pressed */
+	MOUSE_BUTTON_RIGHT      = (1 << 2)      /*!< Right button pressed */
+};
+
+#endif /* !MOLKO_MOUSE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/painter.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,99 @@
+/*
+ * painter.c -- basic drawing routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "color.h"
+#include "painter.h"
+#include "texture_p.h"
+#include "window_p.h"
+
+static struct texture *renderer;
+
+struct texture *
+painter_get_target(void)
+{
+	return renderer;
+}
+
+void
+painter_set_target(struct texture *tex)
+{
+	renderer = tex;
+	SDL_SetRenderTarget(win.renderer, tex ? tex->handle : NULL);
+}
+
+unsigned long
+painter_get_color(void)
+{
+	Uint8 r = 0, g = 0, b = 0, a = 0;
+
+	SDL_GetRenderDrawColor(win.renderer, &r, &g, &b, &a);
+
+	return COLOR_HEX(r, g, b, a);
+}
+
+void
+painter_set_color(unsigned long color)
+{
+	SDL_SetRenderDrawColor(
+		win.renderer,
+		COLOR_R(color),
+		COLOR_G(color),
+		COLOR_B(color),
+		COLOR_A(color)
+	);
+}
+
+void
+painter_draw_line(int x1, int y1, int x2, int y2)
+{
+	SDL_RenderDrawLine(win.renderer, x1, y1, x2, y2);
+}
+
+void
+painter_draw_point(int x1, int y1)
+{
+	SDL_RenderDrawPoint(win.renderer, x1, y1);
+}
+
+void
+painter_draw_rectangle(bool fill, int x, int y, unsigned int width, unsigned int height)
+{
+	const SDL_Rect rect = {
+		.w = width,
+		.h = height,
+		.x = x,
+		.y = y
+	};
+
+	if (fill)
+		SDL_RenderFillRect(win.renderer, &rect);
+	else
+		SDL_RenderDrawRect(win.renderer, &rect);
+}
+
+void
+painter_clear(void)
+{
+	SDL_RenderClear(win.renderer);
+}
+
+void
+painter_present(void)
+{
+	SDL_RenderPresent(win.renderer);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/painter.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,140 @@
+/*
+ * painter.h -- basic drawing routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_PAINTER_H
+#define MOLKO_PAINTER_H
+
+/**
+ * \file painter.h
+ * \brief Basic drawing routines.
+ */
+
+#include <stdbool.h>
+
+struct texture;
+
+/**
+ * Give the current texture being used for rendering, maybe NULL if the
+ * rendering is on root.
+ *
+ * \return texture or NULL
+ */
+struct texture *
+painter_get_target(void);
+
+/**
+ * Set the rendering context to the given texture.
+ *
+ * Since this function change an internal global variable it is strongly
+ * encouraged to store a local backup of the current texture using \a
+ * painter_get_target and call this function with it afterwards.
+ *
+ * If texture is NULL, use default context aka the window.
+ *
+ * \param tex the texture
+ * \see PAINTER_BEGIN
+ * \see PAINTER_END
+ */
+void
+painter_set_target(struct texture *tex);
+
+/**
+ * Get the current drawing color.
+ *
+ * \return the color in RRGGBBAA format
+ */
+unsigned long
+painter_get_color(void);
+
+/**
+ * Set the rendering drawing color.
+ *
+ * \param color in RRGGBBAA format
+ */
+void
+painter_set_color(unsigned long color);
+
+/**
+ * Draw a line.
+ *
+ * \param x1 first X coordinate
+ * \param y1 first Y coordinate
+ * \param x2 second X coordinate
+ * \param y2 second Y coordinate
+ */
+void
+painter_draw_line(int x1, int y1, int x2, int y2);
+
+/**
+ * Draw a pixel point.
+ *
+ * \param x the X coordinate
+ * \param y the Y coordinate
+ */
+void
+painter_draw_point(int x, int y);
+
+/**
+ * Draw a rectangle
+ *
+ * \param fill set to true to fill the rectangle
+ * \param x the X coordinate
+ * \param y the Y coordinate
+ * \param w the rectangle width
+ * \param h the rectangle height
+ */
+void
+painter_draw_rectangle(bool fill, int x, int y, unsigned int w, unsigned int h);
+
+/**
+ * Clear the window.
+ */
+void
+painter_clear(void);
+
+/**
+ * Present the window, only call this function one time in the main loop.
+ */
+void
+painter_present(void);
+
+/**
+ * Use this macro to start painting on the texture to store the current
+ * rendering context and put it back afterwards.
+ *
+ * \pre tex != NULL
+ * \param tex the texture to use
+ * \see PAINTER_END
+ */
+#define PAINTER_BEGIN(tex    )                                          \
+do {                                                                    \
+        struct texture *__current_texture__;                            \
+                                                                        \
+        __current_texture__ = painter_get_target();                     \
+        painter_set_target((tex))
+
+/**
+ * Use this macro at the end of rendering into a given texture.
+ *
+ * \see PAINTER_BEGIN
+ */
+#define PAINTER_END()                                                   \
+        painter_set_target(__current_texture__);                        \
+} while (0)
+
+#endif /* !MOLKO_PAINTER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/sprite.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,64 @@
+/*
+ * sprite.c -- image sprites
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "sprite.h"
+#include "texture_p.h"
+#include "texture.h"
+
+void
+sprite_init(struct sprite *sprite,
+            struct texture *tex,
+            unsigned int cellw,
+            unsigned int cellh)
+{
+	int w = 0;
+	int h = 0;
+
+	assert(sprite);
+	assert(tex);
+
+	/* TODO: use texture_get_size */
+	SDL_QueryTexture(tex->handle, NULL, NULL, &w, &h);
+
+	sprite->texture = tex;
+	sprite->cellw = cellw;
+	sprite->cellh = cellh;
+	sprite->nrows = h / cellh;
+	sprite->ncols = w / cellw;
+}
+
+void
+sprite_draw(struct sprite *sprite, unsigned int r, unsigned int c, int x, int y)
+{
+	assert(sprite);
+
+	texture_draw_ex(
+		sprite->texture,
+		c * sprite->cellw,      /* src y */
+		r * sprite->cellh,      /* src x */
+		sprite->cellw,          /* src width */
+		sprite->cellh,          /* src height */
+		x,                      /* dst x */
+		y,                      /* dst y */
+		sprite->cellw,          /* dst width */
+		sprite->cellh,          /* dst height */
+		0.0                     /* angle */
+	);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/sprite.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,79 @@
+/*
+ * sprite.h -- image sprites
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_SPRITE_H
+#define MOLKO_SPRITE_H
+
+/**
+ * \file sprite.h
+ * \brief Image sprites.
+ */
+
+struct texture;
+
+/**
+ * \brief Sprite structure.
+ */
+struct sprite {
+	struct texture *texture;        /*!< Texture to access (RO) */
+	unsigned int cellw;             /*!< Width per cell (RW) */
+	unsigned int cellh;             /*!< Height per cell (RW) */
+	unsigned int nrows;             /*!< Number of rows (RW) */
+	unsigned int ncols;             /*!< Number of columns (RW) */
+};
+
+/**
+ * Initialize a sprite.
+ *
+ * The sprite does not take ownership of texture and must be valid until the
+ * sprite is no longer used.
+ *
+ * This function is only provided as convenience, user may initialize the
+ * sprite by itself if wanted.
+ *
+ * The fields `nrows' and `ncols' will be determined automatically from the
+ * texture size.
+ *
+ * \pre sprite != NULL
+ * \pre tex != NULL
+ * \param sprite the sprite to initialize
+ * \param tex the texture
+ * \param cellw the width per cell in pixels
+ * \param cellh the height per cell in pixels
+ */
+void
+sprite_init(struct sprite *sprite,
+            struct texture *tex,
+            unsigned int cellw,
+            unsigned int cellh);
+
+/**
+ * Draw the sprite component from row `r' and column `c'.
+ *
+ * \pre sprite != NULL
+ * \param sprite the sprite to draw
+ * \param r the row number
+ * \param c the column number
+ * \param x the X destination
+ * \param y the Y destination
+ * \warning No bounds checking
+ */
+void
+sprite_draw(struct sprite *sprite, unsigned int r, unsigned int c, int x, int y);
+
+#endif /* !MOLKO_SPRITE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/state.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,60 @@
+/*
+ * state.h -- abstract state
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_STATE_H
+#define MOLKO_STATE_H
+
+/**
+ * \file state.h
+ * \brief Abstract state.
+ */
+
+union event;
+
+/**
+ * \brief Abstract state.
+ */
+struct state {
+	/**
+	 * This function is called when the state is entered.
+	 */
+	void (*enter)(void);
+
+	/**
+	 * This function is called when the state is about to be left.
+	 */
+	void (*leave)(void);
+
+	/**
+	 * This function is called for each event that happened.
+	 */
+	void (*handle)(const union event *);
+
+	/**
+	 * This function is called to update the game, with the number of
+	 * milliseconds since the last frame.
+	 */
+	void (*update)(unsigned int ticks);
+
+	/**
+	 * This function is supposed to draw the game.
+	 */
+	void (*draw)(void);
+};
+
+#endif /* !MOLKO_STATE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/sys.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,162 @@
+/*
+ * sys.c -- system routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_mixer.h>
+#include <SDL_ttf.h>
+
+#if !defined(_WIN32)            /* Assuming POSIX */
+#	include <sys/types.h>
+#	include <dirent.h>
+#endif
+
+#include "error.h"
+#include "error_p.h"
+#include "sys.h"
+
+#if defined(_WIN32)
+
+static void
+determine(char path[], size_t pathlen)
+{
+	char *base = SDL_GetBasePath();
+
+	/* On Windows, the data hierarchy is the same as the project. */
+	snprintf(path, pathlen, "%sassets", base);
+	SDL_free(base);
+}
+
+#else                           /* Assuming POSIX */
+
+static bool
+is_absolute(const char *path)
+{
+	assert(path);
+
+	return path[0] == '/';
+}
+
+static void
+determine(char path[], size_t pathlen)
+{
+	char localassets[FILENAME_MAX];
+	char *base = SDL_GetBasePath();
+	DIR *dp;
+
+	/* Try assets directory where executable lives. */
+	snprintf(localassets, sizeof (localassets), "%sassets", base);
+
+	if ((dp = opendir(localassets))) {
+		snprintf(path, pathlen, "%sassets", base);
+		closedir(dp);
+	} else {
+		/* We are not in the project source directory. */
+		if (is_absolute(SHAREDIR)) {
+			/* SHAREDIR is absolute */
+			snprintf(path, pathlen, "%s/molko", SHAREDIR);
+		} else if (is_absolute(BINDIR)) {
+			/* SHAREDIR is relative but BINDIR is absolute */
+			snprintf(path, pathlen, "%s/%s/molko", PREFIX, SHAREDIR);
+		} else {
+			/* SHAREDIR, BINDIR are both relative */
+			char *ptr = strstr(base, BINDIR);
+
+			if (ptr) {
+				*ptr = '\0';
+				snprintf(path, pathlen, "%s%s/molko", base, SHAREDIR);
+			} else {
+				/* Unable to determine. */
+				snprintf(path, pathlen, ".");
+			}
+		}
+	}
+
+	SDL_free(base);
+}
+
+#endif
+
+bool
+sys_init(void)
+{
+#if defined(__MINGW64__)
+	/* On MinGW buffering leads to painful debugging. */
+	setvbuf(stdout, NULL, _IONBF, 0);
+#endif
+
+	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+		return error_sdl();
+	if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
+		return error_sdl();
+	if (TTF_Init() < 0)
+		return error_sdl();
+	if (Mix_Init(MIX_INIT_OGG) != MIX_INIT_OGG)
+		return error_sdl();
+
+	return true;
+}
+
+const char *
+sys_datadir(void)
+{
+	static char path[1024];
+
+	if (path[0] == '\0')
+		determine(path, sizeof (path));
+
+	return path;
+}
+
+const char *
+sys_datapath(const char *fmt, ...)
+{
+	const char *ret;
+	va_list ap;
+
+	va_start(ap, fmt);
+	ret = sys_datapathv(fmt, ap);
+	va_end(ap);
+
+	return ret;
+}
+
+const char *
+sys_datapathv(const char *fmt, va_list ap)
+{
+	static char path[2048 + FILENAME_MAX];
+	char filename[FILENAME_MAX];
+
+	vsnprintf(filename, sizeof (filename), fmt, ap);
+	snprintf(path, sizeof (path), "%s/%s", sys_datadir(), filename);
+
+	return path;
+}
+
+void
+sys_close(void)
+{
+	Mix_Quit();
+	TTF_Quit();
+	IMG_Quit();
+	SDL_Quit();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/sys.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,71 @@
+/*
+ * sys.h -- system routines
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_SYS_H
+#define MOLKO_SYS_H
+
+/**
+ * \file sys.h
+ * \brief System routines.
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+/**
+ * Initialize the system, should be called in the beginning of the main.
+ */
+bool
+sys_init(void);
+
+/**
+ * Get the base system directory path.
+ *
+ * \return the path where the executable lives
+ */
+const char *
+sys_datadir(void);
+
+/**
+ * Construct path to assets directory using printf-like format.
+ *
+ * \param fmt the format string
+ * \return the path to the file
+ * \note This function returns pointer to static string.
+ */
+const char *
+sys_datapath(const char *fmt, ...);
+
+/**
+ * Similar to \a sys_datapath.
+ *
+ * \param fmt the format string
+ * \param ap the variadic arguments pointer
+ * \return the path to the file
+ * \note This function returns pointer to static string.
+ */
+const char *
+sys_datapathv(const char *fmt, va_list ap);
+
+/**
+ * Close the system.
+ */
+void
+sys_close(void);
+
+#endif /* !MOLKO_SYS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/texture.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,139 @@
+/*
+ * texture.c -- basic texture management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include "error.h"
+#include "error_p.h"
+#include "texture.h"
+#include "texture_p.h"
+#include "util.h"
+#include "window_p.h"
+
+struct texture *
+texture_new(unsigned int w, unsigned int h)
+{
+	struct texture *tex = emalloc(sizeof (struct texture));
+
+	if (!(tex->handle = SDL_CreateTexture(win.renderer,
+	    SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h))) {
+		error_sdl();
+		free(tex);
+		return NULL;
+	}
+
+	return tex;
+}
+
+unsigned int
+texture_width(struct texture *tex)
+{
+	assert(tex);
+
+	int width;
+
+	if (SDL_QueryTexture(tex->handle, NULL, NULL, &width, NULL) < 0)
+		return 0;
+
+	return width;
+}
+
+unsigned int
+texture_height(struct texture *tex)
+{
+	assert(tex);
+
+	int height;
+
+	if (SDL_QueryTexture(tex->handle, NULL, NULL, NULL, &height) < 0)
+		return 0;
+
+	return height;
+}
+
+void
+texture_draw(struct texture *tex, int x, int y)
+{
+	SDL_Rect dst = {
+		.x = x,
+		.y = y,
+		.w = 0,
+		.h = 0
+	};
+
+	assert(tex);
+
+	/* We need to determine size */
+	SDL_QueryTexture(tex->handle, NULL, NULL, &dst.w, &dst.h);
+	SDL_RenderCopy(win.renderer, tex->handle, NULL, &dst);
+}
+
+void
+texture_draw_ex(struct texture *tex,
+                int src_x,
+                int src_y,
+                unsigned src_w,
+                unsigned src_h,
+                int dst_x,
+                int dst_y,
+                unsigned dst_w,
+                unsigned dst_h,
+                double angle)
+{
+	const SDL_Rect src = {
+		.x = src_x,
+		.y = src_y,
+		.w = src_w,
+		.h = src_h
+	};
+	const SDL_Rect dst = {
+		.x = dst_x,
+		.y = dst_y,
+		.w = dst_w,
+		.h = dst_h
+	};
+
+	SDL_RenderCopyEx(win.renderer, tex->handle, &src, &dst, angle, NULL, SDL_FLIP_NONE);
+}
+
+void
+texture_close(struct texture *tex)
+{
+	assert(tex);
+
+	SDL_DestroyTexture(tex->handle);
+	free(tex);
+}
+
+/* private */
+
+struct texture *
+texture_from_surface(SDL_Surface *surface)
+{
+	assert(surface);
+
+	struct texture *texture = ecalloc(1, sizeof (struct texture));
+
+	if (!(texture->handle = SDL_CreateTextureFromSurface(win.renderer, surface))) {
+		error_sdl();
+		free(texture);
+		return NULL;
+	}
+
+	return texture;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/texture.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,116 @@
+/*
+ * texture.h -- basic texture management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_TEXTURE_H
+#define MOLKO_TEXTURE_H
+
+/**
+ * \file texture.h
+ * \brief Basic texture management.
+ *
+ * See also \a image.h for usage of textures.
+ */
+
+#include <stdbool.h>
+
+/**
+ * \brief Texture object.
+ *
+ * This object is not publicly defined because it contains
+ * implementation-defined data.
+ */
+struct texture;
+
+/**
+ * Create a new texture.
+ *
+ * \param w the width
+ * \param h the height
+ * \return the texture or NULL on error
+ */
+struct texture *
+texture_new(unsigned int w, unsigned int h);
+
+/**
+ * Get texture width.
+ *
+ * \pre tex != NULL
+ * \param tex the texture
+ * \return the width
+ */
+unsigned int
+texture_width(struct texture *tex);
+
+/**
+ * Get texture height.
+ *
+ * \pre tex != NULL
+ * \param tex the texture
+ * \return the height
+ */
+unsigned int
+texture_height(struct texture *tex);
+
+/**
+ * Simple texture drawing.
+ *
+ * \pre tex != NULL
+ * \param tex the texture
+ * \param x the X coordinate
+ * \param y the Y coordinate
+ */
+void
+texture_draw(struct texture *tex, int x, int y);
+
+/**
+ * Advanced texture drawing.
+ *
+ * \pre tex != NULL
+ * \param tex the texture
+ * \param src_x the source rectangle X coordinate
+ * \param src_y the source rectangle Y coordinate
+ * \param src_w the source rectangle width
+ * \param src_h the source rectangle height
+ * \param dst_x the destination rectangle X coordinate
+ * \param dst_y the destination rectangle Y coordinate
+ * \param dst_w the destination rectangle width
+ * \param dst_h the destination rectangle height
+ * \param angle the angle
+ */
+void
+texture_draw_ex(struct texture *tex,
+                int src_x,
+                int src_y,
+                unsigned src_w,
+                unsigned src_h,
+                int dst_x,
+                int dst_y,
+                unsigned dst_w,
+                unsigned dst_h,
+                double angle);
+
+/**
+ * Close the texture, do not use afterwards.
+ *
+ * \pre tex != NULL
+ * \param tex the texture
+ */
+void
+texture_close(struct texture *tex);
+
+#endif /* !MOLKO_TEXTURE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/texture_p.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,31 @@
+/*
+ * texture_p.h -- (PRIVATE) basic texture management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_TEXTURE_P_H
+#define MOLKO_TEXTURE_P_H
+
+#include <SDL.h>
+
+struct texture {
+	SDL_Texture *handle;
+};
+
+struct texture *
+texture_from_surface(SDL_Surface *surface);
+
+#endif /* !MOLKO_TEXTURE_P_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/util.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,54 @@
+/*
+ * util.c -- utilities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <SDL.h>
+
+#include "util.h"
+#include "error.h"
+
+void *
+emalloc(size_t size)
+{
+	void *mem;
+
+	if (!(mem = malloc(size)))
+		error_fatalf("%s\n", strerror(errno));
+
+	return mem;
+}
+
+void *
+ecalloc(size_t n, size_t size)
+{
+	void *mem;
+
+	if (!(mem = calloc(n, size)))
+		error_fatalf("%s\n", strerror(errno));
+
+	return mem;
+}
+
+void
+delay(unsigned int ms)
+{
+	SDL_Delay(ms);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/util.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,61 @@
+/*
+ * util.h -- utilities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_UTIL_H
+#define MOLKO_UTIL_H
+
+#include <stddef.h>
+
+/**
+ * Get the number of elements in a static array.
+ *
+ * \param x the array
+ * \return the number of elements
+ */
+#define nelem(x) sizeof ((x)) / sizeof ((x)[0])
+
+/**
+ * Wrapper around malloc(3) that exits on allocation failure.
+ *
+ * \param size the size
+ * \return a pointer
+ * \post returned pointer will never be NULL
+ */
+void *
+emalloc(size_t size);
+
+/**
+ * Wrapper around calloc(3) that exits on allocation failure.
+ *
+ * \param n the number of objects to allocate
+ * \param size the size per n
+ * \return a pointer
+ * \post returned pointer will never be NULL
+ */
+void *
+ecalloc(size_t n, size_t size);
+
+/**
+ * Put the thread to sleep for a given amount of milliseconds.
+ *
+ * \param ms the number of milliseconds to wait
+ */
+void
+delay(unsigned int ms);
+
+#endif /* !MOLKO_UTIL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/walksprite.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,60 @@
+/*
+ * walksprite.c -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "walksprite.h"
+#include "sprite.h"
+
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay)
+{
+	assert(ws);
+	assert(sprite);
+
+	memset(ws, 0, sizeof (struct walksprite));
+	ws->sprite = sprite;
+	ws->delay = delay;
+}
+
+void
+walksprite_update(struct walksprite *ws, unsigned int ticks)
+{
+	assert(ws);
+
+	ws->elapsed += ticks;
+
+	if (ws->elapsed >= ws->delay) {
+		ws->index += 1;
+
+		if (ws->index >= ws->sprite->ncols)
+			ws->index = 0;
+
+		ws->elapsed = 0;
+	}
+}
+
+void
+walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y)
+{
+	assert(ws);
+	assert(orientation < 8);
+
+	sprite_draw(ws->sprite, orientation, ws->index, x, y);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/walksprite.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,102 @@
+/*
+ * walksprite.h -- sprite designed for walking entities
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_WALKSPRITE_H
+#define MOLKO_WALKSPRITE_H
+
+/**
+ * \file walksprite.h
+ * \brief Sprite designed for walking entities.
+ */
+
+struct sprite;
+
+/**
+ * \brief Sprite designed for walking entities.
+ *
+ * This structure works with sprite images that are defined as using the
+ * following conventions:
+ *
+ * ```
+ * 7   0   1
+ *   ↖ ↑ ↗
+ * 6 ←   → 2
+ *   ↙ ↓ ↘
+ * 5   4   3
+ * ```
+ *
+ * Where numbers define row in the sprite according to the character
+ * orientation. In other terms, your image sprite should look like this:
+ *
+ * ```
+ * row columns in your image
+ * ---|---------------------
+ *  0 | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
+ *  1 | ↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗
+ *  2 | →→→→→→→→→→→→→→→→→→→→
+ *  3 | ↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘
+ *  4 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
+ *  5 | ↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙
+ *  6 | ←←←←←←←←←←←←←←←←←←←←
+ *  7 | ↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖
+ * ```
+ */
+struct walksprite {
+	struct sprite *sprite;  /*!< (RW) The sprite to use */
+	unsigned int delay;     /*!< (RW) The delay between frames */
+	unsigned int index;     /*!< (RO) Current column index */
+	unsigned int elapsed;   /*!< (RO) Elapsed time since last frame */
+};
+
+/**
+ * Initialize the walking sprite.
+ *
+ * \pre ws != NULL
+ * \pre sprite != NULL
+ * \param ws the walking sprite
+ * \param sprite the sprite to use
+ * \param delay the delay between sprites
+ */
+void
+walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay);
+
+/**
+ * Update the walking sprite
+ *
+ * \pre ws != NULL
+ * \param ws the walking sprite
+ * \param ticks the number of milliseconds between last frame
+ */
+void
+walksprite_update(struct walksprite *ws, unsigned int ticks);
+
+/**
+ * Draw the appropriate sprite cell to the screen according to the orientation
+ * given.
+ *
+ * \pre ws != NULL
+ * \pre orientation < 8
+ * \param ws the walking sprite
+ * \param orientation the orientation (or the row between 0 and 7)
+ * \param x the x coordinate
+ * \param y the y coordinate
+ */
+void
+walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y);
+
+#endif /* !MOLKO_WALKSPRITE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/window.c	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,72 @@
+/*
+ * window.c -- basic window management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+
+#include <SDL.h>
+
+#include "error_p.h"
+#include "window.h"
+#include "window_p.h"
+
+/* global object, used by textures */
+struct window win = {
+	.win = NULL,
+	.renderer = NULL
+};
+
+bool
+window_init(const char *title, unsigned int width, unsigned int height)
+{
+	assert(title);
+
+	if (SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL,
+	    &win.win, &win.renderer) < 0)
+		return error_sdl();
+
+	SDL_SetWindowTitle(win.win, title);
+
+	return true;
+}
+
+unsigned int
+window_width(void)
+{
+	int width;
+
+	SDL_GetWindowSize(win.win, &width, NULL);
+
+	return width;
+}
+
+unsigned int
+window_height(void)
+{
+	int height;
+
+	SDL_GetWindowSize(win.win, NULL, &height);
+
+	return height;
+}
+
+void
+window_close(void)
+{
+	SDL_DestroyRenderer(win.renderer);
+	SDL_DestroyWindow(win.win);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/window.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,63 @@
+/*
+ * window.h -- basic window management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_WINDOW_H
+#define MOLKO_WINDOW_H
+
+/**
+ * \file window.h
+ * \brief Basic window management.
+ */
+
+#include <stdbool.h>
+
+/**
+ * Initialize window.
+ *
+ * \pre title != NULL
+ * \param title the window title
+ * \param width the desired width
+ * \param height the desired height
+ * \return true on success
+ */
+bool
+window_init(const char *title, unsigned int width, unsigned int height);
+
+/**
+ * Get the current window's width.
+ *
+ * \return the width
+ */
+unsigned int
+window_width(void);
+
+/**
+ * Get the current window's height.
+ *
+ * \return the height
+ */
+unsigned int
+window_height(void);
+
+/**
+ * Close the window and destroy associated resources.
+ */
+void
+window_close(void);
+
+#endif /* !MOLKO_WINDOW_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core/window_p.h	Tue Jan 21 12:42:33 2020 +0100
@@ -0,0 +1,32 @@
+/*
+ * window.c -- (PRIVATE) basic window management
+ *
+ * Copyright (c) 2020 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MOLKO_WINDOW_P_H
+#define MOLKO_WINDOW_P_H
+
+#include <SDL.h>
+
+struct window {
+	SDL_Window *win;
+	SDL_Renderer *renderer;
+};
+
+/* Global window object */
+extern struct window win;
+
+#endif /* !MOLKO_WINDOW_P_H */
--- a/src/error.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-/*
- * error.c -- error routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "error.h"
-#include "error_p.h"
-
-#include <SDL.h>
-
-static char buffer[2048];
-
-const char *
-error(void)
-{
-	return buffer;
-}
-
-bool
-error_errno(void)
-{
-	error_printf("%s", strerror(errno));
-
-	return false;
-}
-
-bool
-error_printf(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	error_vprintf(fmt, ap);
-	va_end(ap);
-
-	return false;
-}
-
-bool
-error_vprintf(const char *fmt, va_list ap)
-{
-	vsnprintf(buffer, sizeof (buffer), fmt, ap);
-
-	return false;
-}
-
-noreturn void
-error_fatal(void)
-{
-	fprintf(stderr, "%s\n", buffer);
-	exit(1);
-}
-
-noreturn void
-error_fatalf(const char *fmt, ...)
-{
-	va_list ap;
-
-	va_start(ap, fmt);
-	error_vfatalf(fmt, ap);
-	va_end(ap);
-}
-
-noreturn void
-error_vfatalf(const char *fmt, va_list ap)
-{
-	fprintf(stderr, fmt, ap);
-	exit(1);
-}
-
-/* private: error_p.h */
-
-bool
-error_sdl(void)
-{
-	error_printf("%s", SDL_GetError());
-
-	return false;
-}
--- a/src/error.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,90 +0,0 @@
-/*
- * error.h -- error routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_ERROR_H
-#define MOLKO_ERROR_H
-
-/**
- * \file error.h
- * \brief Error routines.
- */
-
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdnoreturn.h>
-
-/**
- * Get the last error returned.
- *
- * \return the error string
- */
-const char *
-error(void);
-
-/**
- * Convenient handler that sets last error from global C errno and then return
- * false.
- *
- * \return false
- */
-bool
-error_errno(void);
-
-/**
- * Set the game error with a printf-like format.
- *
- * \param fmt the format string
- * \return false
- */
-bool
-error_printf(const char *fmt, ...);
-
-/**
- * Similar to \a error_printf.
- *
- * \param fmt the format stinrg
- * \param ap the variadic arguments pointer
- * \return false
- */
-bool
-error_vprintf(const char *fmt, va_list ap);
-
-/**
- * Print last registered error and exit with code 1.
- */
-noreturn void
-error_fatal(void);
-
-/**
- * Prints an error to stderr and exit.
- *
- * \param fmt the format string
- */
-noreturn void
-error_fatalf(const char *fmt, ...);
-
-/**
- * Similar to \a error_fatalf
- *
- * \param fmt the format string
- * \param ap the variadic arguments pointer
- */
-noreturn void
-error_vfatalf(const char *fmt, va_list ap);
-
-#endif /* !MOLKO_ERROR_H */
--- a/src/error_p.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-/*
- * error.h -- (PRIVATE) error routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_ERROR_P_H
-#define MOLKO_ERROR_P_H
-
-#include <stdbool.h>
-
-/**
- * Convenient handler that sets the game error to the last SDL error and then
- * return false.
- *
- * \return false
- */
-bool
-error_sdl(void);
-
-#endif /* !MOLKO_ERROR_P_H */
--- a/src/event.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,255 +0,0 @@
-/*
- * event.c -- event management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <SDL.h>
-
-#include "event.h"
-
-/* Maintain with enum key constants in key.h */
-static const struct {
-	SDL_Keycode key;
-	enum key value;
-} keymap[] = {
-	{ SDLK_RETURN,          KEY_ENTER               },
-	{ SDLK_ESCAPE,          KEY_ESCAPE              },
-	{ SDLK_BACKSPACE,       KEY_BACKSPACE           },
-	{ SDLK_TAB,             KEY_TAB                 },
-	{ SDLK_SPACE,           KEY_SPACE               },
-	{ SDLK_EXCLAIM,         KEY_EXCLAIM             },
-	{ SDLK_QUOTEDBL,        KEY_DOUBLE_QUOTE        },
-	{ SDLK_HASH,            KEY_HASH                },
-	{ SDLK_PERCENT,         KEY_PERCENT             },
-	{ SDLK_DOLLAR,          KEY_DOLLAR              },
-	{ SDLK_AMPERSAND,       KEY_AMPERSAND           },
-	{ SDLK_QUOTE,           KEY_QUOTE               },
-	{ SDLK_LEFTPAREN,       KEY_LEFT_PAREN          },
-	{ SDLK_RIGHTPAREN,      KEY_RIGHT_PAREN         },
-	{ SDLK_ASTERISK,        KEY_ASTERISK            },
-	{ SDLK_PLUS,            KEY_PLUS                },
-	{ SDLK_COMMA,           KEY_COMMA               },
-	{ SDLK_MINUS,           KEY_MINUS               },
-	{ SDLK_PERIOD,          KEY_PERIOD              },
-	{ SDLK_SLASH,           KEY_SLASH               },
-	{ SDLK_0,               KEY_0                   },
-	{ SDLK_1,               KEY_1                   },
-	{ SDLK_2,               KEY_2                   },
-	{ SDLK_3,               KEY_3                   },
-	{ SDLK_4,               KEY_4                   },
-	{ SDLK_5,               KEY_5                   },
-	{ SDLK_6,               KEY_6                   },
-	{ SDLK_7,               KEY_7                   },
-	{ SDLK_8,               KEY_8                   },
-	{ SDLK_9,               KEY_9                   },
-	{ SDLK_COLON,           KEY_COLON               },
-	{ SDLK_SEMICOLON,       KEY_SEMICOLON           },
-	{ SDLK_LESS,            KEY_LESS                },
-	{ SDLK_EQUALS,          KEY_EQUALS              },
-	{ SDLK_GREATER,         KEY_GREATER             },
-	{ SDLK_QUESTION,        KEY_QUESTION            },
-	{ SDLK_AT,              KEY_AT                  },
-	{ SDLK_LEFTBRACKET,     KEY_LEFT_BRACKET        },
-	{ SDLK_BACKSLASH,       KEY_BACKSLASH           },
-	{ SDLK_RIGHTBRACKET,    KEY_RIGHT_BRACKET       },
-	{ SDLK_CARET,           KEY_CARET               },
-	{ SDLK_UNDERSCORE,      KEY_UNDERSCORE          },
-	{ SDLK_BACKQUOTE,       KEY_BACKQUOTE           },
-	{ SDLK_a,               KEY_a                   },
-	{ SDLK_b,               KEY_b                   },
-	{ SDLK_c,               KEY_c                   },
-	{ SDLK_d,               KEY_d                   },
-	{ SDLK_e,               KEY_e                   },
-	{ SDLK_f,               KEY_f                   },
-	{ SDLK_g,               KEY_g                   },
-	{ SDLK_h,               KEY_h                   },
-	{ SDLK_i,               KEY_i                   },
-	{ SDLK_j,               KEY_j                   },
-	{ SDLK_k,               KEY_k                   },
-	{ SDLK_l,               KEY_l                   },
-	{ SDLK_m,               KEY_m                   },
-	{ SDLK_n,               KEY_n                   },
-	{ SDLK_o,               KEY_o                   },
-	{ SDLK_p,               KEY_p                   },
-	{ SDLK_q,               KEY_q                   },
-	{ SDLK_r,               KEY_r                   },
-	{ SDLK_s,               KEY_s                   },
-	{ SDLK_t,               KEY_t                   },
-	{ SDLK_u,               KEY_u                   },
-	{ SDLK_v,               KEY_v                   },
-	{ SDLK_w,               KEY_w                   },
-	{ SDLK_x,               KEY_x                   },
-	{ SDLK_y,               KEY_y                   },
-	{ SDLK_z,               KEY_z                   },
-	{ SDLK_CAPSLOCK,        KEY_CAPSLOCK            },
-	{ SDLK_F1,              KEY_F1                  },
-	{ SDLK_F2,              KEY_F2                  },
-	{ SDLK_F3,              KEY_F3                  },
-	{ SDLK_F4,              KEY_F4                  },
-	{ SDLK_F5,              KEY_F5                  },
-	{ SDLK_F6,              KEY_F6                  },
-	{ SDLK_F7,              KEY_F7                  },
-	{ SDLK_F8,              KEY_F8                  },
-	{ SDLK_F9,              KEY_F9                  },
-	{ SDLK_F10,             KEY_F10                 },
-	{ SDLK_F11,             KEY_F11                 },
-	{ SDLK_F12,             KEY_F12                 },
-	{ SDLK_F13,             KEY_F13                 },
-	{ SDLK_F14,             KEY_F14                 },
-	{ SDLK_F15,             KEY_F15                 },
-	{ SDLK_F16,             KEY_F16                 },
-	{ SDLK_F17,             KEY_F17                 },
-	{ SDLK_F18,             KEY_F18                 },
-	{ SDLK_F19,             KEY_F19                 },
-	{ SDLK_F20,             KEY_F20                 },
-	{ SDLK_F21,             KEY_F21                 },
-	{ SDLK_F22,             KEY_F22                 },
-	{ SDLK_F23,             KEY_F23                 },
-	{ SDLK_F24,             KEY_F24                 },
-	{ SDLK_PRINTSCREEN,     KEY_PRINTSCREEN         },
-	{ SDLK_SCROLLLOCK,      KEY_SCROLLLOCK          },
-	{ SDLK_PAUSE,           KEY_PAUSE               },
-	{ SDLK_INSERT,          KEY_INSERT              },
-	{ SDLK_HOME,            KEY_HOME                },
-	{ SDLK_PAGEUP,          KEY_PAGEUP              },
-	{ SDLK_DELETE,          KEY_DELETE              },
-	{ SDLK_END,             KEY_END                 },
-	{ SDLK_PAGEDOWN,        KEY_PAGEDOWN            },
-	{ SDLK_RIGHT,           KEY_RIGHT               },
-	{ SDLK_LEFT,            KEY_LEFT                },
-	{ SDLK_DOWN,            KEY_DOWN                },
-	{ SDLK_UP,              KEY_UP                  },
-	{ SDLK_KP_DIVIDE,       KEY_KP_DIVIDE           },
-	{ SDLK_KP_MULTIPLY,     KEY_KP_MULTIPLY         },
-	{ SDLK_KP_MINUS,        KEY_KP_MINUS            },
-	{ SDLK_KP_PLUS,         KEY_KP_PLUS             },
-	{ SDLK_KP_ENTER,        KEY_KP_ENTER            },
-	{ SDLK_KP_1,            KEY_KP_1                },
-	{ SDLK_KP_2,            KEY_KP_2                },
-	{ SDLK_KP_3,            KEY_KP_3                },
-	{ SDLK_KP_4,            KEY_KP_4                },
-	{ SDLK_KP_5,            KEY_KP_5                },
-	{ SDLK_KP_6,            KEY_KP_6                },
-	{ SDLK_KP_7,            KEY_KP_7                },
-	{ SDLK_KP_8,            KEY_KP_8                },
-	{ SDLK_KP_9,            KEY_KP_9                },
-	{ SDLK_KP_0,            KEY_KP_0                },
-	{ SDLK_KP_PERIOD,       KEY_KP_PERIOD           },
-	{ SDLK_KP_COMMA,        KEY_KP_COMMA            },
-	{ SDLK_MENU,            KEY_MENU                },
-	{ SDLK_MUTE,            KEY_MUTE                },
-	{ SDLK_VOLUMEUP,        KEY_VOLUME_UP           },
-	{ SDLK_VOLUMEDOWN,      KEY_VOLUME_DOWN         },
-	{ SDLK_LCTRL,           KEY_LCTRL               },
-	{ SDLK_LSHIFT,          KEY_LSHIFT              },
-	{ SDLK_LALT,            KEY_LALT                },
-	{ SDLK_LGUI,            KEY_LSUPER              },
-	{ SDLK_RCTRL,           KEY_RCTRL               },
-	{ SDLK_RSHIFT,          KEY_RSHIFT              },
-	{ SDLK_RALT,            KEY_RALT                },
-	{ SDLK_RGUI,            KEY_RSUPER              },
-	{ 0,                    -1                      }
-};
-
-/* Maintain with enum mouse_button constants in mouse.h */
-static const struct {
-	int key;
-	enum mouse_button value;
-} buttons[] = {
-	{ SDL_BUTTON_LEFT,      MOUSE_BUTTON_LEFT       },
-	{ SDL_BUTTON_MIDDLE,    MOUSE_BUTTON_MIDDLE     },
-	{ SDL_BUTTON_RIGHT,     MOUSE_BUTTON_RIGHT      },
-	{ -1,                   MOUSE_BUTTON_UNKNOWN    }
-};
-
-static void
-convert_key(const SDL_Event *event, union event *ev)
-{
-	ev->type = event->type == SDL_KEYDOWN ? EVENT_KEYDOWN : EVENT_KEYUP;
-	ev->key.key = KEY_UNKNOWN;
-
-	for (size_t i = 0; keymap[i].key != 0; ++i) {
-		if (keymap[i].key == event->key.keysym.sym) {
-			ev->key.key = keymap[i].value;
-			break;
-		}
-	}
-}
-
-static void
-convert_mouse(const SDL_Event *event, union event *ev)
-{
-	ev->type = EVENT_MOUSE;
-	ev->mouse.buttons = 0;
-	ev->mouse.x = event->motion.x;
-	ev->mouse.y = event->motion.y;
-
-	if (event->motion.state & SDL_BUTTON_LMASK)
-		ev->mouse.buttons |= MOUSE_BUTTON_LEFT;
-	if (event->motion.state & SDL_BUTTON_MMASK)
-		ev->mouse.buttons |= MOUSE_BUTTON_MIDDLE;
-	if (event->motion.state & SDL_BUTTON_RMASK)
-		ev->mouse.buttons |= MOUSE_BUTTON_RIGHT;
-}
-
-static void
-convert_click(const SDL_Event *event, union event *ev)
-{
-	ev->type = event->type == SDL_MOUSEBUTTONDOWN ? EVENT_CLICKDOWN : EVENT_CLICKUP;
-	ev->click.button = MOUSE_BUTTON_UNKNOWN;
-	ev->click.x = event->button.x;
-	ev->click.y = event->button.y;
-
-	for (size_t i = 0; buttons[i].value != MOUSE_BUTTON_UNKNOWN; ++i) {
-		if (buttons[i].key == event->button.button) {
-			ev->click.button = buttons[i].value;
-			break;
-		}
-	}
-}
-
-bool
-event_poll(union event *ev)
-{
-	SDL_Event event;
-
-	/*
-	 * Loop until we find an event we want to report, we skip unneeded
-	 * ones.
-	 */
-	while (SDL_PollEvent(&event)) {
-		switch (event.type) {
-		case SDL_QUIT:
-			ev->type = EVENT_QUIT;
-			return true;
-		case SDL_KEYDOWN:
-		case SDL_KEYUP:
-			convert_key(&event, ev);
-			return true;
-		case SDL_MOUSEMOTION:
-			convert_mouse(&event, ev);
-			return true;
-		case SDL_MOUSEBUTTONDOWN:
-		case SDL_MOUSEBUTTONUP:
-			convert_click(&event, ev);
-			return true;
-		default:
-			continue;
-		}
-	}
-
-	return false;
-}
--- a/src/event.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * event.h -- event management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_EVENT_H
-#define MOLKO_EVENT_H
-
-/**
- * \file event.h
- * \brief Event management.
- */
-
-#include <stdbool.h>
-
-#include "key.h"
-#include "mouse.h"
-
-/**
- * \brief Kind of event.
- */
-enum event_type {
-	EVENT_CLICKDOWN,        /*!< Mouse click down */
-	EVENT_CLICKUP,          /*!< Mouse click released */
-	EVENT_KEYDOWN,          /*!< Single key down */
-	EVENT_KEYUP,            /*!< Single key released */
-	EVENT_MOUSE,            /*!< Mouse moved */
-	EVENT_QUIT,             /*!< Quit request */
-};
-
-/**
- * \brief Store events.
- */
-union event {
-	enum event_type type;                   /*!< Which kind of event */
-
-	/**
-	 * Store key down/up event.
-	 */
-	struct {
-		enum event_type type;           /*!< EVENT_KEYDOWN or EVENT_KEYUP */
-		enum key key;                   /*!< Which key */
-	} key;
-
-	/**
-	 * Store mouse motion event.
-	 */
-	struct {
-		enum event_type type;           /*!< EVENT_MOUSE */
-		enum mouse_button buttons;      /*!< OR'ed buttons that are pressed */
-		int x;                          /*!< Mouse position in x */
-		int y;                          /*!< Mouse position in y */
-	} mouse;
-
-	/**
-	 * Store mouse click event.
-	 */
-	struct {
-		enum event_type type;           /*!< EVENT_CLICKDOWN or EVENT_CLICKUP */
-		enum mouse_button button;       /*!< Unique button that was pressed */
-		int x;                          /*!< Mouse position in x */
-		int y;                          /*!< Mouse position in y */
-	} click;
-};
-
-/**
- * Fetch the next event or return false if there are not.
- *
- * \param ev the event
- * \return true if the event was filled or false otherwise
- */
-bool
-event_poll(union event *ev);
-
-#endif /* !MOLKO_EVENT_H */
--- a/src/font.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * font.c -- basic font management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdbool.h>
-#include <stdio.h>
-
-#include <SDL_ttf.h>
-
-#include "color.h"
-#include "error.h"
-#include "error_p.h"
-#include "font.h"
-#include "texture_p.h"
-#include "util.h"
-
-struct font {
-	TTF_Font *handle;
-};
-
-struct font *
-font_openf(const char *path, unsigned int size)
-{
-	assert(path);
-
-	struct font *f;
-
-	f = ecalloc(1, sizeof (struct font));
-
-	if (!(f->handle = TTF_OpenFont(path, size))) {
-		error_sdl();
-		free(f);
-		return NULL;
-	}
-
-	return f;
-}
-
-struct font *
-font_openb(const void *buffer, size_t buflen, unsigned int size)
-{
-	assert(buffer);
-
-	struct font *f;
-	SDL_RWops *ops;
-
-	f = ecalloc(1, sizeof (struct font));
-
-	if (!(ops = SDL_RWFromConstMem(buffer, buflen)) ||
-	   (!(f->handle = TTF_OpenFontRW(ops, true, size)))) {
-		error_sdl();
-		free(f);
-		return NULL;
-	}
-
-	return f;
-}
-
-struct texture *
-font_render(struct font *font, const char *text, unsigned long color)
-{
-	assert(font);
-	assert(text);
-
-	SDL_Color fg = {
-		.r = COLOR_R(color),
-		.g = COLOR_G(color),
-		.b = COLOR_B(color),
-		.a = COLOR_A(color)
-	};
-
-	SDL_Surface *surface;
-
-	if (!(surface = TTF_RenderUTF8_Blended(font->handle, text, fg))) {
-		error_sdl();
-		return NULL;
-	}
-
-	return texture_from_surface(surface);
-}
-
-void
-font_close(struct font *font)
-{
-	assert(font);
-
-	TTF_CloseFont(font->handle);
-	free(font);
-}
--- a/src/font.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * font.h -- basic font management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_FONT_H
-#define MOLKO_FONT_H
-
-/**
- * \file font.h
- * \brief Basic font management.
- */
-
-#include <stddef.h>
-
-/**
- * \brief Font object.
- *
- * This object is not publicly defined because it contains
- * implementation-defined data.
- */
-struct font;
-struct texture;
-
-/**
- * Open font from path file.
- *
- * \pre path != NULL
- * \param path the path to the font
- * \param size the desired size
- * \return the font or NULL on error
- */
-struct font *
-font_openf(const char *path, unsigned int size);
-
-/**
- * Open font from memory buffer.
- *
- * \pre buffer != NULL
- * \param buffer the memory buffer
- * \param buflen the memory buffer length
- * \param size the desired size
- * \warning The buffer must remain valid until font is closed
- * \return the font or NULL on error
- */
-struct font *
-font_openb(const void *buffer, size_t buflen, unsigned int size);
-
-/**
- * Render a text.
- *
- * \pre font != NULL
- * \pre text != NULL
- * \param font the font handle
- * \param text the text in UTF-8
- * \param color the color
- */
-struct texture *
-font_render(struct font *font, const char *text, unsigned long color);
-
-/**
- * Close the font.
- *
- * \pre font != NULL
- * \param font the font handle
- */
-void
-font_close(struct font *font);
-
-#endif /* !MOLKO_FONT_H */
--- a/src/game.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/*
- * game.c -- main game object
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stddef.h>
-#include <string.h>
-
-#include "game.h"
-#include "state.h"
-
-struct game game = {
-	.state = NULL,
-	.state_next = NULL
-};
-
-static struct action *
-find_empty_action(void)
-{
-	static struct action null;
-
-	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
-		if (memcmp(&game.actions[i], &null, sizeof (struct action)) == 0)
-			return &game.actions[i];
-
-	return NULL;
-}
-
-static void
-clear_actions(void)
-{
-	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
-		struct action *a = &game.actions[i];
-
-		/* These actions are removed on state change. */
-		if (a->flags & ACTION_AUTO_LEAVE) {
-			if (a->finish)
-				a->finish(a);
-
-			memset(a, 0, sizeof (struct action));
-		}
-	}
-}
-
-static void
-handle_actions(const union event *event)
-{
-	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
-		if (game.actions[i].handle)
-			game.actions[i].handle(&game.actions[i], event);
-}
-
-static void
-update_actions(unsigned int ticks)
-{
-	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i) {
-		struct action *a = &game.actions[i];
-
-		if (!a->update)
-			continue;
-
-		if (a->update(a, ticks)) {
-			if (a->finish)
-				a->finish(a);
-
-			memset(&game.actions[i], 0, sizeof (struct action));
-		}
-	}
-}
-
-static void
-draw_actions(void)
-{
-	for (size_t i = 0; i < GAME_ACTIONS_MAX; ++i)
-		if (game.actions[i].draw)
-			game.actions[i].draw(&game.actions[i]);
-}
-
-
-void
-game_switch(struct state *state, bool quick)
-{
-	assert(state);
-
-	if (quick) {
-		game.state = state;
-		game.state->enter();
-	} else
-		game.state_next = state;
-}
-
-void
-game_handle(const union event *event)
-{
-	assert(event);
-
-	if (game.state)
-		game.state->handle(event);
-
-	handle_actions(event);
-}
-
-void
-game_update(unsigned int ticks)
-{
-	/* Change state if any. */
-	if (game.state_next) {
-		/* Inform the current state we're gonna leave it. */
-		if (game.state)
-			game.state->leave();
-
-		game.state = game.state_next;
-		game.state->enter();
-		game.state_next = NULL;
-
-		/* Remove any actions that must be deleted. */
-		clear_actions();
-	}
-
-	if (game.state)
-		game.state->update(ticks);
-
-	update_actions(ticks);
-}
-
-void
-game_draw(void)
-{
-	if (game.state)
-		game.state->draw();
-
-	draw_actions();
-}
-
-void
-game_add_action(const struct action *action)
-{
-	assert(action);
-
-	struct action *pos;
-
-	if ((pos = find_empty_action()))
-		memcpy(pos, action, sizeof (struct action));
-}
--- a/src/game.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * game.h -- main game object
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_GAME_H
-#define MOLKO_GAME_H
-
-/**
- * \file game.h
- * \brief Main game object.
- */
-
-#include <stdbool.h>
-
-#include "action.h"
-
-/**
- * \brief Max number of actions allowed at the same time.
- */
-#define GAME_ACTIONS_MAX 32
-
-struct state;
-
-union event;
-
-/**
- * \brief Main game object.
- */
-struct game {
-	/* Game states. */
-	struct state *state;            /*!< (RO) Current state  */
-	struct state *state_next;       /*!< (RO) Next state */
-
-	/** Array of actions. */
-	struct action actions[GAME_ACTIONS_MAX];
-};
-
-/**
- * Global game object.
- */
-extern struct game game;
-
-/**
- * Request to change state.
- *
- * This function will only update state after the next \a game_update call.
- *
- * If quick is true, change state immediately.
- *
- * \pre state != NULL
- * \param state the new state
- * \param quick quickly change the state
- */
-void
-game_switch(struct state *state, bool quick);
-
-/**
- * Handle input event.
- *
- * \param event the event
- */
-void
-game_handle(const union event *event);
-
-/**
- * Update the game state.
- *
- * \param ticks the number of milliseconds between last frame
- */
-void
-game_update(unsigned int ticks);
-
-/**
- * Draw the game using the current state.
- */
-void
-game_draw(void);
-
-/**
- * Add an action to the game.
- *
- * If there are no room for the action, action is discarded. Make sure to not
- * exceed the limit GAME_ACTIONS_MAX.
- *
- * \pre action != NULL
- * \param action the action to copy
- */
-void
-game_add_action(const struct action *action);
-
-#endif /* !MOLKO_GAME_H */
--- a/src/image.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * image.c -- basic image management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdbool.h>
-
-#include <SDL_image.h>
-
-#include "error_p.h"
-#include "texture_p.h"
-
-struct texture *
-image_openf(const char *path)
-{
-	assert(path);
-
-	SDL_Surface *surface = IMG_Load(path);
-
-	if (!surface) {
-		error_sdl();
-		return NULL;
-	}
-
-	return texture_from_surface(surface);
-}
-
-struct texture *
-image_openb(const void *buffer, size_t size)
-{
-	assert(buffer);
-
-	SDL_RWops *ops = SDL_RWFromConstMem(buffer, size);
-	SDL_Surface *surface;
-
-	if (!ops || !(surface = IMG_Load_RW(ops, true))) {
-		error_sdl();
-		return NULL;
-	}
-
-	return texture_from_surface(surface);
-}
--- a/src/image.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * image.h -- basic image management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_IMAGE_H
-#define MOLKO_IMAGE_H
-
-/**
- * \file image.h
- * \brief Basic image management.
- */
-
-#include <stddef.h>
-
-struct texture;
-
-/**
- * Open a file from a path.
- *
- * \pre path != NULL
- * \param path the path to the file
- * \return the texture or NULL on error
- */
-struct texture *
-image_openf(const char *path);
-
-/**
- * Open a file from a memory buffer.
- *
- * \pre buffer != NULL
- * \param buffer the memory buffer
- * \param size the memory size
- * \return the texture or NULL on error
- */
-struct texture *
-image_openb(const void *buffer, size_t size);
-
-#endif /* !MOLKO_IMAGE_H */
--- a/src/key.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * key.h -- keyboard definitions
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_KEY_H
-#define MOLKO_KEY_H
-
-/**
- * \file key.h
- * \brief Keyboard definitions.
- */
-
-/**
- * \brief Key codes.
- */
-enum key {
-	KEY_UNKNOWN,
-	KEY_ENTER,
-	KEY_ESCAPE,
-	KEY_BACKSPACE,
-	KEY_TAB,
-	KEY_SPACE,
-	KEY_EXCLAIM,
-	KEY_DOUBLE_QUOTE,
-	KEY_HASH,
-	KEY_PERCENT,
-	KEY_DOLLAR,
-	KEY_AMPERSAND,
-	KEY_QUOTE,
-	KEY_LEFT_PAREN,
-	KEY_RIGHT_PAREN,
-	KEY_ASTERISK,
-	KEY_PLUS,
-	KEY_COMMA,
-	KEY_MINUS,
-	KEY_PERIOD,
-	KEY_SLASH,
-	KEY_0,
-	KEY_1,
-	KEY_2,
-	KEY_3,
-	KEY_4,
-	KEY_5,
-	KEY_6,
-	KEY_7,
-	KEY_8,
-	KEY_9,
-	KEY_COLON,
-	KEY_SEMICOLON,
-	KEY_LESS,
-	KEY_EQUALS,
-	KEY_GREATER,
-	KEY_QUESTION,
-	KEY_AT,
-	KEY_LEFT_BRACKET,
-	KEY_BACKSLASH,
-	KEY_RIGHT_BRACKET,
-	KEY_CARET,
-	KEY_UNDERSCORE,
-	KEY_BACKQUOTE,
-	KEY_a,
-	KEY_b,
-	KEY_c,
-	KEY_d,
-	KEY_e,
-	KEY_f,
-	KEY_g,
-	KEY_h,
-	KEY_i,
-	KEY_j,
-	KEY_k,
-	KEY_l,
-	KEY_m,
-	KEY_n,
-	KEY_o,
-	KEY_p,
-	KEY_q,
-	KEY_r,
-	KEY_s,
-	KEY_t,
-	KEY_u,
-	KEY_v,
-	KEY_w,
-	KEY_x,
-	KEY_y,
-	KEY_z,
-	KEY_CAPSLOCK,
-	KEY_F1,
-	KEY_F2,
-	KEY_F3,
-	KEY_F4,
-	KEY_F5,
-	KEY_F6,
-	KEY_F7,
-	KEY_F8,
-	KEY_F9,
-	KEY_F10,
-	KEY_F11,
-	KEY_F12,
-	KEY_F13,
-	KEY_F14,
-	KEY_F15,
-	KEY_F16,
-	KEY_F17,
-	KEY_F18,
-	KEY_F19,
-	KEY_F20,
-	KEY_F21,
-	KEY_F22,
-	KEY_F23,
-	KEY_F24,
-	KEY_PRINTSCREEN,
-	KEY_SCROLLLOCK,
-	KEY_PAUSE,
-	KEY_INSERT,
-	KEY_HOME,
-	KEY_PAGEUP,
-	KEY_DELETE,
-	KEY_END,
-	KEY_PAGEDOWN,
-	KEY_RIGHT,
-	KEY_LEFT,
-	KEY_DOWN,
-	KEY_UP,
-	KEY_NUMLOCKCLEAR,
-	KEY_KP_DIVIDE,
-	KEY_KP_MULTIPLY,
-	KEY_KP_MINUS,
-	KEY_KP_PLUS,
-	KEY_KP_ENTER,
-	KEY_KP_00,
-	KEY_KP_000,
-	KEY_KP_1,
-	KEY_KP_2,
-	KEY_KP_3,
-	KEY_KP_4,
-	KEY_KP_5,
-	KEY_KP_6,
-	KEY_KP_7,
-	KEY_KP_8,
-	KEY_KP_9,
-	KEY_KP_0,
-	KEY_KP_PERIOD,
-	KEY_KP_COMMA,
-	KEY_MENU,
-	KEY_MUTE,
-	KEY_VOLUME_UP,
-	KEY_VOLUME_DOWN,
-	KEY_LCTRL,
-	KEY_LSHIFT,
-	KEY_LALT,
-	KEY_LSUPER,
-	KEY_RCTRL,
-	KEY_RSHIFT,
-	KEY_RALT,
-	KEY_RSUPER,
-};
-
-/**
- * \brief Keybord modifiers.
- *
- * This enumeration is usually stored as OR'ed flags as several modifiers can
- * be pressed at a time.
- */
-enum keymod {
-	KEYMOD_LSHIFT   = 1 << 0,       /*!< Left shift */
-	KEYMOD_LCTRL    = 1 << 1,       /*!< Left control */
-	KEYMOD_LALT     = 1 << 2,       /*!< Left alt */
-	KEYMOD_LSUPER   = 1 << 3,       /*!< Left super (logo) */
-	KEYMOD_RSHIFT   = 1 << 4,       /*!< Right shift */
-	KEYMOD_RCTRL    = 1 << 5,       /*!< Right control */
-	KEYMOD_RALT     = 1 << 6,       /*!< Right alt */
-	KEYMOD_RSUPER   = 1 << 7        /*!< Right super (logo) */
-};
-
-#endif /* !MOLKO_KEY_H */
--- a/src/main.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-/*
- * main.c -- Molko's Adventure
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include "clock.h"
-#include "error.h"
-#include "event.h"
-#include "game.h"
-#include "image.h"
-#include "map.h"
-#include "map_state.h"
-#include "splashscreen.h"
-#include "sprite.h"
-#include "sys.h"
-#include "util.h"
-#include "window.h"
-
-#define WINDOW_WIDTH 1280
-#define WINDOW_HEIGHT 720
-
-static void
-init(void)
-{
-	if (!sys_init())
-		error_fatal();
-	if (!window_init("Molko's Adventure", WINDOW_WIDTH, WINDOW_HEIGHT))
-		error_fatal();
-
-	/* Default state is splash screen */
-	game_switch(&splashscreen_state, true);
-}
-
-static void
-run(void)
-{
-	union event ev;
-	struct clock clock;
-
-	clock_start(&clock);
-
-	for (;;) {
-		unsigned int elapsed = clock_elapsed(&clock);
-
-		clock_start(&clock);
-
-		while (event_poll(&ev)) {
-			/* TODO: this must be handled by states. */
-			if (ev.type == EVENT_QUIT)
-				return;
-
-			game_handle(&ev);
-		}
-
-		game_update(elapsed);
-		game_draw();
-
-		if ((elapsed = clock_elapsed(&clock)) < 20)
-			delay(20 - elapsed);
-	}
-}
-
-static void
-close(void)
-{
-	window_close();
-	sys_close();
-}
-
-int
-main(int argc, char **argv)
-{
-	(void)argc;
-	(void)argv;
-
-	init();
-	run();
-	close();
-
-	return 0;
-}
--- a/src/map.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-/*
- * map.c -- game map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "error.h"
-#include "error_p.h"
-#include "image.h"
-#include "map.h"
-#include "painter.h"
-#include "sprite.h"
-#include "sys.h"
-#include "texture.h"
-#include "window.h"
-
-#include <SDL.h>
-
-/* Create %<v>c string literal for scanf */
-#define MAX_F(v) MAX_F_(v)
-#define MAX_F_(v) "%" #v "c"
-
-static void
-parse_layer(struct map *map, const char *line, FILE *fp)
-{
-	char layer_name[32 + 1] = { 0 };
-	struct map_layer *layer;
-	size_t amount, current;
-
-	/* Determine layer. */
-	if (sscanf(line, "layer|%32s", layer_name) <= 0)
-		return;
-	if (strcmp(layer_name, "background") == 0)
-		layer = &map->layers[0];
-	else if (strcmp(layer_name, "foreground") == 0)
-		layer = &map->layers[1];
-	else
-		return;
-
-	/* Check if weight/height has been specified. */
-	if (map->width == 0 || map->height == 0)
-		return;
-
-	amount = map->width * map->height;
-	current = 0;
-
-	if (!(layer->tiles = calloc(amount, sizeof (unsigned short))))
-		return;
-
-	for (int tile; fscanf(fp, "%d", &tile) && current < amount; ++current)
-		layer->tiles[current] = tile;
-}
-
-static void
-parse_tileset(struct map *map, const char *line)
-{
-	char filename[128 + 1] = { 0 };
-
-	sscanf(line, "tileset|%128s", filename);
-
-	if (map->tilewidth == 0 || map->tileheight == 0)
-		return;
-	if (!(map->tileset = image_openf(sys_datapath("tilesets/%s", filename))))
-		return;
-
-	sprite_init(&map->sprite, map->tileset, map->tilewidth, map->tileheight);
-}
-
-static void
-parse(struct map *map, const char *line, FILE *fp)
-{
-	if (strncmp(line, "title", 5) == 0)
-		sscanf(line, "title|" MAX_F(MAP_TITLE_MAX), map->title);
-	else if (strncmp(line, "width", 5) == 0)
-		sscanf(line, "width|%u", &map->width);
-	else if (strncmp(line, "height", 6) == 0)
-		sscanf(line, "height|%u", &map->height);
-	else if (strncmp(line, "tilewidth", 9) == 0)
-		sscanf(line, "tilewidth|%hu", &map->tilewidth);
-	else if (strncmp(line, "tileheight", 10) == 0)
-		sscanf(line, "tileheight|%hu", &map->tileheight);
-	else if (strncmp(line, "origin", 6) == 0)
-		sscanf(line, "origin|%d|%d", &map->origin_x, &map->origin_y);
-	else if (strncmp(line, "tileset", 7) == 0)
-		parse_tileset(map, line);
-	else if (strncmp(line, "layer", 5) == 0)
-		parse_layer(map, line, fp);
-}
-
-static bool
-check(struct map *map)
-{
-	if (strlen(map->title) == 0)
-		return error_printf("map has no title");
-	if (!map->tileset)
-		return error_printf("unable to open tileset");
-	if (map->width == 0 || map->height == 0)
-		return error_printf("map has null sizes");
-	if (map->tilewidth == 0 || map->tileheight == 0)
-		return error_printf("map has null tile sizes");
-	if (!map->layers[0].tiles || !map->layers[1].tiles)
-		return error_printf("could not allocate data");
-
-	return true;
-}
-
-static void
-draw_layer(struct map *map, const struct map_layer *layer)
-{
-	assert(map);
-	assert(layer);
-
-	int x = 0, y = 0;
-
-	for (unsigned int r = 0; r < map->width; ++r) {
-		for (unsigned int c = 0; c < map->height; ++c) {
-			unsigned int si = r * map->width + c;
-			unsigned int sr = (layer->tiles[si] - 1) / map->sprite.ncols;
-			unsigned int sc = (layer->tiles[si] - 1) % map->sprite.nrows;
-
-			if (layer->tiles[si] != 0)
-				sprite_draw(&map->sprite, sr, sc, x, y);
-
-			x += map->tilewidth;
-		}
-
-		x = 0;
-		y += map->tileheight;
-	}
-}
-
-bool
-map_open(struct map *map, const char *path)
-{
-	assert(map);
-	assert(path);
-
-	memset(map, 0, sizeof (struct map));
-
-	FILE *fp = fopen(path, "r");
-	char line[BUFSIZ];
-
-	if (!fp)
-		return false;
-
-	while (fgets(line, sizeof (line), fp)) {
-		/* Remove \n if any */
-		line[strcspn(line, "\n")] = '\0';
-		parse(map, line, fp);
-	}
-
-	fclose(fp);
-
-	if (!check(map)) {
-		map_close(map);
-		return false;
-	}
-
-	size_t pw = map->width * map->tilewidth;
-	size_t ph = map->height * map->tileheight;
-
-	if (!(map->picture = texture_new(pw, ph)))
-		return error_sdl();
-
-	return true;
-}
-
-void
-map_draw(struct map *map, int srcx, int srcy)
-{
-	texture_draw_ex(
-		map->picture,
-		srcx,
-		srcy,
-		window_width(),
-		window_height(),
-		0,
-		0,
-		window_width(),
-		window_height(),
-		0
-	);
-}
-
-void
-map_repaint(struct map *map)
-{
-	PAINTER_BEGIN(map->picture);
-	draw_layer(map, &map->layers[0]);
-	draw_layer(map, &map->layers[1]);
-	PAINTER_END();
-}
-
-void
-map_close(struct map *map)
-{
-	assert(map);
-
-	if (map->tileset)
-		texture_close(map->tileset);
-	if (map->picture)
-		texture_close(map->picture);
-
-	free(map->layers[0].tiles);
-	free(map->layers[1].tiles);
-
-	memset(map, 0, sizeof (struct map));
-}
--- a/src/map.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * map.h -- game map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MAP_H
-#define MOLKO_MAP_H
-
-/**
- * \file map.h
- * \brief Game map.
- */
-
-#include <stdbool.h>
-
-#include "sprite.h"
-
-/**
- * \brief Max title length for a map.
- */
-#define MAP_TITLE_MAX   32
-
-struct texture;
-
-/**
- * \brief Map layer.
- */
-struct map_layer {
-	unsigned short *tiles;          /*!< (RO) Array of tiles, depending on the map size. */
-};
-
-/**
- * \brief Map object.
- *
- * This structure only defines the map characteristics. It does not have any
- * logic and is left for game state.
- */
-struct map {
-	char title[MAP_TITLE_MAX];      /*!< (RW) The map title */
-	struct texture *tileset;        /*!< (RW) Tileset to use */
-	struct texture *picture;        /*!< (RO) Map drawn into a picture */
-	struct sprite sprite;           /*!< (RO) Sprite to render */
-	int origin_x;                   /*!< (RO) Where the player starts in X */
-	int origin_y;                   /*!< (RO) Where the player starts in Y */
-	unsigned int width;             /*!< (RO) Map width in cells */
-	unsigned int height;            /*!< (RO) Map height in cells */
-	unsigned short tilewidth;       /*!< (RO) Pixels per cell (width) */
-	unsigned short tileheight;      /*!< (RO) Pixels per cell (height) */
-	struct map_layer layers[2];     /*!< (RO) Layers (background, foreground) */
-};
-
-/**
- * Open a map.
- *
- * \pre map != NULL
- * \pre path != NULL
- * \param map the map to fill
- * \param path the path to the map
- * \return true if successfully loaded
- */
-bool
-map_open(struct map *map, const char *path);
-
-/**
- * Render a map.
- *
- * \pre map != NULL
- * \param map the map to render
- * \param srcx the x coordinate region
- * \param srcy the y coordinate region
- */
-void
-map_draw(struct map *map, int srcx, int srcy);
-
-/**
- * Force map repaint on its texture.
- *
- * \pre map != NULL
- * \param map the map to repaint
- * \warning This function does not render anything on the screen
- */
-void
-map_repaint(struct map *map);
-
-/**
- * Close the map and its resources.
- *
- * \pre map != NULL
- * \param map the map to render
- */
-void
-map_close(struct map *map);
-
-#endif /* !MOLKO_MAP_H */
--- a/src/map_state.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,341 +0,0 @@
-/*
- * map_state.c -- state when player is on a map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <stdio.h>
-
-#include "event.h"
-#include "game.h"
-#include "map.h"
-#include "map_state.h"
-#include "painter.h"
-#include "state.h"
-#include "texture.h"
-#include "walksprite.h"
-#include "window.h"
-
-/*
- * This is the speed the player moves on the map.
- *
- * SPEED represents the number of pixels it must move per SEC.
- * SEC simply represends the number of milliseconds in one second.
- */
-#define SPEED 100
-#define SEC   1000
-
-/*
- * Those are margins within the edge of the screen. The camera always try to
- * keep those padding between the player and the screen.
- */
-#define MARGIN_WIDTH    80
-#define MARGIN_HEIGHT   80
-
-/*
- * This structure defines the possible movement of the player as flags since
- * it's possible to make diagonal movements.
- */
-enum movement {
-	MOVING_UP       = 1 << 0,
-	MOVING_RIGHT    = 1 << 1,
-	MOVING_DOWN     = 1 << 2,
-	MOVING_LEFT     = 1 << 3
-};
-
-/*
- * A bit of explanation within this array. The structure walksprite requires
- * an orientation between 0-7 depending on the user direction.
- *
- * Since keys for moving the character may be pressed at the same time, we need
- * a conversion table from "key pressed" to "orientation".
- *
- * When an orientation is impossible, it is set to -1. Example, when both left
- * and right are pressed.
- *
- * MOVING_UP    = 0001 = 0x1
- * MOVING_RIGHT = 0010 = 0x2
- * MOVING_DOWN  = 0100 = 0x3
- * MOVING_LEFT  = 1000 = 0x4
- */
-static unsigned int orientations[16] = {
-	[0x1] = 0,
-	[0x2] = 2,
-	[0x3] = 1,
-	[0x4] = 4,
-	[0x6] = 3,
-	[0x8] = 6,
-	[0x9] = 7,
-	[0xC] = 5
-};
-
-/*
- * Additional data that is not necessary to expose in map_state_data.
- */
-static struct {
-	struct {
-		enum movement moving;
-		struct walksprite ws;
-	} player;
-
-	struct {
-		int x;
-		int y;
-		unsigned int w;
-		unsigned int h;
-	} margin;
-} cache;
-
-static void
-center(void)
-{
-	map_state_data.view.x = map_state_data.player.x - (map_state_data.view.w / 2);
-	map_state_data.view.y = map_state_data.player.y - (map_state_data.view.h / 2);
-
-	if (map_state_data.view.x < 0)
-		map_state_data.view.x = 0;
-	else if ((unsigned int)map_state_data.view.x > map_state_data.map.w - map_state_data.view.w)
-		map_state_data.view.x = map_state_data.map.w - map_state_data.view.w;
-
-	if (map_state_data.view.y < 0)
-		map_state_data.view.y = 0;
-	else if ((unsigned int)map_state_data.view.y > map_state_data.map.h - map_state_data.view.h)
-		map_state_data.view.y = map_state_data.map.h - map_state_data.view.h;
-}
-
-static void
-enter(void)
-{
-	/* Adjust map properties. */
-	struct map *m = &map_state_data.map.map;
-
-	if (!m->picture)
-		map_repaint(m);
-
-	map_repaint(m);
-	map_state_data.map.w = texture_width(m->picture);
-	map_state_data.map.h = texture_height(m->picture);
-
-	/* Adjust view. */
-	map_state_data.view.w = window_width();
-	map_state_data.view.h = window_height();
-
-	/* Adjust margin. */
-	cache.margin.w = map_state_data.view.w - (MARGIN_WIDTH * 2);
-	cache.margin.h = map_state_data.view.h - (MARGIN_HEIGHT * 2);
-
-	/* Center the view by default. */
-	center();
-
-	/* Final bits. */
-	walksprite_init(&cache.player.ws, &map_state_data.player.sprite, 300);
-}
-
-static void
-leave(void)
-{
-}
-
-static void
-handle_keydown(const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_UP:
-		cache.player.moving |= MOVING_UP;
-		break;
-	case KEY_RIGHT:
-		cache.player.moving |= MOVING_RIGHT;
-		break;
-	case KEY_DOWN:
-		cache.player.moving |= MOVING_DOWN;
-		break;
-	case KEY_LEFT:
-		cache.player.moving |= MOVING_LEFT;
-		break;
-	default:
-		break;
-	}
-
-	map_state_data.player.angle = orientations[cache.player.moving];
-}
-
-static void
-handle_keyup(const union event *event)
-{
-	switch (event->key.key) {
-	case KEY_UP:
-		cache.player.moving &= ~(MOVING_UP);
-		break;
-	case KEY_RIGHT:
-		cache.player.moving &= ~(MOVING_RIGHT);
-		break;
-	case KEY_DOWN:
-		cache.player.moving &= ~(MOVING_DOWN);
-		break;
-	case KEY_LEFT:
-		cache.player.moving &= ~(MOVING_LEFT);
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-move_right(unsigned int delta)
-{
-	map_state_data.player.x += delta;
-
-	if (map_state_data.player.x > (int)(cache.margin.x + cache.margin.w)) {
-		map_state_data.view.x = (map_state_data.player.x - map_state_data.view.w) + MARGIN_WIDTH;
-
-		if (map_state_data.view.x >= (int)(map_state_data.map.w - map_state_data.view.w))
-			map_state_data.view.x = map_state_data.map.w - map_state_data.view.w;
-	}
-
-	if (map_state_data.player.x > (int)map_state_data.map.w - 48)
-		map_state_data.player.x = map_state_data.map.w - 48;
-}
-
-static void
-move_left(unsigned int delta)
-{
-	map_state_data.player.x -= delta;
-
-	if (map_state_data.player.x < cache.margin.x) {
-		map_state_data.view.x = map_state_data.player.x - MARGIN_WIDTH;
-
-		if (map_state_data.view.x < 0)
-			map_state_data.view.x = 0;
-	}
-
-	if (map_state_data.player.x < 0)
-		map_state_data.player.x = 0;
-}
-
-static void
-move_down(unsigned int delta)
-{
-	map_state_data.player.y += delta;
-
-	if (map_state_data.player.y > (int)(cache.margin.y + cache.margin.h)) {
-		map_state_data.view.y = (map_state_data.player.y - map_state_data.view.h) + MARGIN_HEIGHT;
-
-		if (map_state_data.view.y >= (int)(map_state_data.map.h - map_state_data.view.h))
-			map_state_data.view.y = map_state_data.map.h - map_state_data.view.h;
-	}
-
-	if (map_state_data.player.y > (int)map_state_data.map.h - 48)
-		map_state_data.player.y = map_state_data.map.h - 48;
-}
-
-static void
-move_up(unsigned int delta)
-{
-	map_state_data.player.y -= delta;
-
-	if (map_state_data.player.y < cache.margin.y) {
-		map_state_data.view.y = map_state_data.player.y - MARGIN_HEIGHT;
-
-		if (map_state_data.view.y < 0)
-			map_state_data.view.y = 0;
-	}
-
-	if (map_state_data.player.y < 0)
-		map_state_data.player.y = 0;
-}
-
-static void
-move(unsigned int ticks)
-{
-	/* This is the amount of pixels the player must move. */
-	const int delta = SPEED * ticks / SEC;
-
-	/* This is the rectangle within the view where users must be. */
-	cache.margin.x = map_state_data.view.x + MARGIN_WIDTH;
-	cache.margin.y = map_state_data.view.y + MARGIN_HEIGHT;
-
-	int dx = 0;
-	int dy = 0;
-
-	if (cache.player.moving == 0)
-		return;
-
-	if (cache.player.moving & MOVING_UP)
-		dy = -1;
-	if (cache.player.moving & MOVING_DOWN)
-		dy = 1;
-	if (cache.player.moving & MOVING_LEFT)
-		dx = -1;
-	if (cache.player.moving & MOVING_RIGHT)
-		dx = 1;
-
-	/* Move the player and adjust view if needed. */
-	if (dx > 0)
-		move_right(delta);
-	else if (dx < 0)
-		move_left(delta);
-
-	if (dy > 0)
-		move_down(delta);
-	else if (dy < 0)
-		move_up(delta);
-
-	walksprite_update(&cache.player.ws, ticks);
-}
-
-static void
-handle(const union event *event)
-{
-	switch (event->type) {
-	case EVENT_KEYDOWN:
-		handle_keydown(event);
-		break;
-	case EVENT_KEYUP:
-		handle_keyup(event);
-		break;
-	default:
-		break;
-	}
-}
-
-static void
-update(unsigned int ticks)
-{
-	move(ticks);
-}
-
-static void
-draw(void)
-{
-	painter_set_color(0x000000ff);
-	painter_clear();
-	map_draw(&map_state_data.map.map, map_state_data.view.x, map_state_data.view.y);
-	walksprite_draw(
-		&cache.player.ws,
-		map_state_data.player.angle,
-		map_state_data.player.x - map_state_data.view.x,
-		map_state_data.player.y - map_state_data.view.y);
-	painter_present();
-}
-
-struct map_state_data map_state_data;
-
-struct state map_state = {
-	.enter = enter,
-	.leave = leave,
-	.update = update,
-	.handle = handle,
-	.draw = draw
-};
--- a/src/map_state.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * map_state.h -- state when player is on a map
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MAP_STATE_H
-#define MOLKO_MAP_STATE_H
-
-/**
- * \file map_state.h
- * \brief State when player is on a map.
- */
-
-#include "map.h"
-#include "sprite.h"
-
-/**
- * \brief Data for the state.
- *
- * Update this structure before switching to this state.
- */
-extern struct map_state_data {
-	/**
-	 * Map properties.
-	 */
-	struct {
-		struct map map;         /*!< (RO) The map definition */
-		unsigned int w;         /*!< (RO) Map width */
-		unsigned int h;         /*!< (RO) Map height */
-	} map;
-
-	/**
-	 * Player position.
-	 *
-	 * If you adjust this structure, it is strictly encouraged to update
-	 * the view as well.
-	 */
-	struct {
-		struct sprite sprite;   /*!< (RW) The sprite to use */
-		int x;                  /*!< (RO) Player position in x */
-		int y;                  /*!< (RO) Player position in y */
-		int angle;              /*!< (RO) Player angle (see walksprite) */
-	} player;
-
-	/**
-	 * Position and size of the view.
-	 */
-	struct {
-		int x;                  /*!< (RW) Position in x */
-		int y;                  /*!< (RW) Position in y */
-		unsigned int w;         /*!< (RO) View width */
-		unsigned int h;         /*!< (RO) View height */
-	} view;
-} map_state_data; /*!< Access to data. */
-
-/**
- * \brief State when player is on a map.
- */
-extern struct state map_state;
-
-#endif /* !MOLKO_MAP_STATE_H */
--- a/src/message.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * message.c -- message dialog
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "font.h"
-#include "message.h"
-#include "painter.h"
-#include "sprite.h"
-#include "texture.h"
-
-#define MESSAGE_SPEED   1000    /* Time delay for animations */
-#define MESSAGE_TIMEOUT 5000    /* Time for auto-closing */
-
-void
-message_start(struct message *msg)
-{
-	assert(msg);
-
-	msg->elapsed = 0;
-	msg->state = MESSAGE_OPENING;
-}
-
-void
-message_update(struct message *msg, unsigned int ticks)
-{
-	assert(msg);
-
-	msg->elapsed += ticks;
-
-	switch (msg->state) {
-	case MESSAGE_OPENING:
-		if (msg->elapsed >= MESSAGE_SPEED)
-			msg->state = MESSAGE_SHOWING;
-		break;
-	case MESSAGE_SHOWING:
-		/* Do automatically switch state if requested by the user. */
-		if (msg->flags & MESSAGE_AUTOMATIC && msg->elapsed >= MESSAGE_TIMEOUT)
-			msg->state = MESSAGE_HIDING;
-
-		break;
-	default:
-		break;
-	}
-}
-
-void
-message_draw(struct message *msg)
-{
-	assert(msg);
-
-	/* TODO: more constant variables. */
-	struct texture *lines[3];
-	int x = 160;
-	int y = 80;
-
-	painter_set_color(0xff0000ff);
-	painter_draw_rectangle(true, x, y, 960, 160);
-
-	for (int i = 0; msg->text[i]; ++i) {
-		lines[i] = font_render(msg->font, msg->text[i], 0xffffffff);
-
-		if (!lines[i])
-			continue;
-
-		texture_draw(lines[i], x, y);
-		texture_close(lines[i]);
-		y += 53;
-	}
-}
-
-void
-message_hide(struct message *msg)
-{
-	assert(msg);
-
-	msg->elapsed = 0;
-}
-
-bool
-message_is_complete(struct message *msg)
-{
-	assert(msg);
-
-	return msg->state == MESSAGE_HIDING && msg->elapsed >= MESSAGE_SPEED;
-}
--- a/src/message.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * message.h -- message dialog
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MESSAGE_H
-#define MOLKO_MESSAGE_H
-
-/**
- * \file message.h
- * \brief Message dialog.
- */
-
-#include <stdbool.h>
-
-struct sprite;
-struct font;
-
-/**
- * \brief Message flags.
- */
-enum message_flags {
-	MESSAGE_AUTOMATIC = (1 << 0)    /*!< Message will automatically close */
-};
-
-/**
- * \brief Message state.
- */
-enum message_state {
-	MESSAGE_OPENING,                /*!< Message animation is opening */
-	MESSAGE_SHOWING,                /*!< Message is displaying */
-	MESSAGE_HIDING                  /*!< Message animation for hiding */
-};
-
-/**
- * \brief Message object.
- */
-struct message {
-	const char *text[3];            /*!< (RW) lines of text to show */
-	struct sprite *theme;           /*!< (RW) sprite to use for the frame */
-	struct texture *avatar;         /*!< (RW) optional avatar */
-	struct font *font;              /*!< (RW) font to use */
-	enum message_flags flags;       /*!< (RW) message flags */
-	enum message_state state;       /*!< (RO) current state */
-	unsigned int elapsed;           /*!< (RW) elapsed time while displaying */
-};
-
-/**
- * Start opening the message. This function will reset the message state and
- * elapsed time.
- *
- * \pre msg != NULL
- * \param msg the message
- */
-void
-message_start(struct message *msg);
-
-/**
- * Update the message state and elapsed time..
- *
- * \pre msg != NULL
- * \param msg the message
- * \param ticks the elapsed delay since last frame
- */
-void
-message_update(struct message *msg, unsigned int ticks);
-
-/**
- * Draw the message into the screen.
- *
- * \pre msg != NULL
- * \param msg the message
- */
-void
-message_draw(struct message *msg);
-
-/**
- * Start hiding the message.
- *
- * \pre msg != NULL
- * \param msg the message
- * \note You should still continue to draw the message as the animation is not
- *       finished!
- */
-void
-message_hide(struct message *msg);
-
-/**
- * Tells if the message is complete.
- *
- * \pre msg != NULL
- * \param msg the message
- */
-bool
-message_is_complete(struct message *msg);
-
-#endif /* !MOLKO_MESSAGE_H */
--- a/src/mouse.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * mouse.h -- mouse definitions
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_MOUSE_H
-#define MOLKO_MOUSE_H
-
-/**
- * \file mouse.h
- * \brief Mouse definitions.
- */
-
-/**
- * \brief Buttons from mouse.
- *
- * This enumeration is used as both flags or constants. For example when the
- * user press one button on the mouse it generates one constant event. On the
- * other hand, while moving the mouse the user may have one or more buttons
- * pressed, thus the OR'ed combination.
- */
-enum mouse_button {
-	MOUSE_BUTTON_UNKNOWN    = 0,            /*!< No buttons pressed */
-	MOUSE_BUTTON_LEFT       = (1 << 0),     /*!< Left button pressed */
-	MOUSE_BUTTON_MIDDLE     = (1 << 1),     /*!< Middle button pressed */
-	MOUSE_BUTTON_RIGHT      = (1 << 2)      /*!< Right button pressed */
-};
-
-#endif /* !MOLKO_MOUSE_H */
--- a/src/painter.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * painter.c -- basic drawing routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "color.h"
-#include "painter.h"
-#include "texture_p.h"
-#include "window_p.h"
-
-static struct texture *renderer;
-
-struct texture *
-painter_get_target(void)
-{
-	return renderer;
-}
-
-void
-painter_set_target(struct texture *tex)
-{
-	renderer = tex;
-	SDL_SetRenderTarget(win.renderer, tex ? tex->handle : NULL);
-}
-
-unsigned long
-painter_get_color(void)
-{
-	Uint8 r = 0, g = 0, b = 0, a = 0;
-
-	SDL_GetRenderDrawColor(win.renderer, &r, &g, &b, &a);
-
-	return COLOR_HEX(r, g, b, a);
-}
-
-void
-painter_set_color(unsigned long color)
-{
-	SDL_SetRenderDrawColor(
-		win.renderer,
-		COLOR_R(color),
-		COLOR_G(color),
-		COLOR_B(color),
-		COLOR_A(color)
-	);
-}
-
-void
-painter_draw_line(int x1, int y1, int x2, int y2)
-{
-	SDL_RenderDrawLine(win.renderer, x1, y1, x2, y2);
-}
-
-void
-painter_draw_point(int x1, int y1)
-{
-	SDL_RenderDrawPoint(win.renderer, x1, y1);
-}
-
-void
-painter_draw_rectangle(bool fill, int x, int y, unsigned int width, unsigned int height)
-{
-	const SDL_Rect rect = {
-		.w = width,
-		.h = height,
-		.x = x,
-		.y = y
-	};
-
-	if (fill)
-		SDL_RenderFillRect(win.renderer, &rect);
-	else
-		SDL_RenderDrawRect(win.renderer, &rect);
-}
-
-void
-painter_clear(void)
-{
-	SDL_RenderClear(win.renderer);
-}
-
-void
-painter_present(void)
-{
-	SDL_RenderPresent(win.renderer);
-}
--- a/src/painter.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-/*
- * painter.h -- basic drawing routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_PAINTER_H
-#define MOLKO_PAINTER_H
-
-/**
- * \file painter.h
- * \brief Basic drawing routines.
- */
-
-#include <stdbool.h>
-
-struct texture;
-
-/**
- * Give the current texture being used for rendering, maybe NULL if the
- * rendering is on root.
- *
- * \return texture or NULL
- */
-struct texture *
-painter_get_target(void);
-
-/**
- * Set the rendering context to the given texture.
- *
- * Since this function change an internal global variable it is strongly
- * encouraged to store a local backup of the current texture using \a
- * painter_get_target and call this function with it afterwards.
- *
- * If texture is NULL, use default context aka the window.
- *
- * \param tex the texture
- * \see PAINTER_BEGIN
- * \see PAINTER_END
- */
-void
-painter_set_target(struct texture *tex);
-
-/**
- * Get the current drawing color.
- *
- * \return the color in RRGGBBAA format
- */
-unsigned long
-painter_get_color(void);
-
-/**
- * Set the rendering drawing color.
- *
- * \param color in RRGGBBAA format
- */
-void
-painter_set_color(unsigned long color);
-
-/**
- * Draw a line.
- *
- * \param x1 first X coordinate
- * \param y1 first Y coordinate
- * \param x2 second X coordinate
- * \param y2 second Y coordinate
- */
-void
-painter_draw_line(int x1, int y1, int x2, int y2);
-
-/**
- * Draw a pixel point.
- *
- * \param x the X coordinate
- * \param y the Y coordinate
- */
-void
-painter_draw_point(int x, int y);
-
-/**
- * Draw a rectangle
- *
- * \param fill set to true to fill the rectangle
- * \param x the X coordinate
- * \param y the Y coordinate
- * \param w the rectangle width
- * \param h the rectangle height
- */
-void
-painter_draw_rectangle(bool fill, int x, int y, unsigned int w, unsigned int h);
-
-/**
- * Clear the window.
- */
-void
-painter_clear(void);
-
-/**
- * Present the window, only call this function one time in the main loop.
- */
-void
-painter_present(void);
-
-/**
- * Use this macro to start painting on the texture to store the current
- * rendering context and put it back afterwards.
- *
- * \pre tex != NULL
- * \param tex the texture to use
- * \see PAINTER_END
- */
-#define PAINTER_BEGIN(tex    )                                          \
-do {                                                                    \
-        struct texture *__current_texture__;                            \
-                                                                        \
-        __current_texture__ = painter_get_target();                     \
-        painter_set_target((tex))
-
-/**
- * Use this macro at the end of rendering into a given texture.
- *
- * \see PAINTER_BEGIN
- */
-#define PAINTER_END()                                                   \
-        painter_set_target(__current_texture__);                        \
-} while (0)
-
-#endif /* !MOLKO_PAINTER_H */
--- a/src/splashscreen.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * splashscreen.c -- splash screen state
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "error.h"
-#include "font.h"
-#include "game.h"
-#include "image.h"
-#include "map.h"
-#include "map_state.h"
-#include "painter.h"
-#include "splashscreen.h"
-#include "state.h"
-#include "sys.h"
-#include "texture.h"
-#include "window.h"
-
-#define DELAY 3000
-
-static unsigned int elapsed;
-static struct font *font;
-static struct texture *text;
-static int x;
-static int y;
-
-static void
-enter(void)
-{
-	if (!(font = font_openf(sys_datapath("fonts/knights-quest.ttf"), 160)))
-		error_fatal();
-	if (!(text = font_render(font, "Molko's Adventure", 0x000000ff)))
-		error_fatal();
-
-	/* Compute position. */
-	const unsigned int w = texture_width(text);
-	const unsigned int h = texture_height(text);
-
-	x = (window_width() / 2) - (w / 2);
-	y = (window_height() / 2) - (h / 2) - 100;
-}
-
-static void
-leave(void)
-{
-	font_close(font);
-}
-
-static void
-handle(const union event *event)
-{
-	(void)event;
-}
-
-static void
-update(unsigned int ticks)
-{
-	elapsed += ticks;
-
-	/* TODO: change this once map is done. */
-	if (elapsed >= DELAY) {
-		/* TODO: this will be removed too. */
-		static struct texture *image;
-
-		if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
-			error_fatal();
-		if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
-			error_fatal();
-
-		sprite_init(&map_state_data.player.sprite, image, 48, 48);
-		game_switch(&map_state, false);
-	}
-}
-
-static void
-draw(void)
-{
-	painter_set_color(0xffffffff);
-	painter_clear();
-	texture_draw(text, x, y);
-	painter_present();
-}
-
-struct state splashscreen_state = {
-	.enter = enter,
-	.leave = leave,
-	.handle = handle,
-	.update = update,
-	.draw = draw
-};
--- a/src/splashscreen.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * splashscreen.h -- splash screen state
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_SPLASHSCREEN_H
-#define MOLKO_SPLASHSCREEN_H
-
-/**
- * \file splashscreen.h
- * \brief Splash screen state.
- */
-
-/**
- * \brief Splash screen state.
- */
-extern struct state splashscreen_state;
-
-#endif /* !MOLKO_SPLASHSCREEN_H */
--- a/src/sprite.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * sprite.c -- image sprites
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "sprite.h"
-#include "texture_p.h"
-#include "texture.h"
-
-void
-sprite_init(struct sprite *sprite,
-            struct texture *tex,
-            unsigned int cellw,
-            unsigned int cellh)
-{
-	int w = 0;
-	int h = 0;
-
-	assert(sprite);
-	assert(tex);
-
-	/* TODO: use texture_get_size */
-	SDL_QueryTexture(tex->handle, NULL, NULL, &w, &h);
-
-	sprite->texture = tex;
-	sprite->cellw = cellw;
-	sprite->cellh = cellh;
-	sprite->nrows = h / cellh;
-	sprite->ncols = w / cellw;
-}
-
-void
-sprite_draw(struct sprite *sprite, unsigned int r, unsigned int c, int x, int y)
-{
-	assert(sprite);
-
-	texture_draw_ex(
-		sprite->texture,
-		c * sprite->cellw,      /* src y */
-		r * sprite->cellh,      /* src x */
-		sprite->cellw,          /* src width */
-		sprite->cellh,          /* src height */
-		x,                      /* dst x */
-		y,                      /* dst y */
-		sprite->cellw,          /* dst width */
-		sprite->cellh,          /* dst height */
-		0.0                     /* angle */
-	);
-}
--- a/src/sprite.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * sprite.h -- image sprites
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_SPRITE_H
-#define MOLKO_SPRITE_H
-
-/**
- * \file sprite.h
- * \brief Image sprites.
- */
-
-struct texture;
-
-/**
- * \brief Sprite structure.
- */
-struct sprite {
-	struct texture *texture;        /*!< Texture to access (RO) */
-	unsigned int cellw;             /*!< Width per cell (RW) */
-	unsigned int cellh;             /*!< Height per cell (RW) */
-	unsigned int nrows;             /*!< Number of rows (RW) */
-	unsigned int ncols;             /*!< Number of columns (RW) */
-};
-
-/**
- * Initialize a sprite.
- *
- * The sprite does not take ownership of texture and must be valid until the
- * sprite is no longer used.
- *
- * This function is only provided as convenience, user may initialize the
- * sprite by itself if wanted.
- *
- * The fields `nrows' and `ncols' will be determined automatically from the
- * texture size.
- *
- * \pre sprite != NULL
- * \pre tex != NULL
- * \param sprite the sprite to initialize
- * \param tex the texture
- * \param cellw the width per cell in pixels
- * \param cellh the height per cell in pixels
- */
-void
-sprite_init(struct sprite *sprite,
-            struct texture *tex,
-            unsigned int cellw,
-            unsigned int cellh);
-
-/**
- * Draw the sprite component from row `r' and column `c'.
- *
- * \pre sprite != NULL
- * \param sprite the sprite to draw
- * \param r the row number
- * \param c the column number
- * \param x the X destination
- * \param y the Y destination
- * \warning No bounds checking
- */
-void
-sprite_draw(struct sprite *sprite, unsigned int r, unsigned int c, int x, int y);
-
-#endif /* !MOLKO_SPRITE_H */
--- a/src/state.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * state.h -- abstract state
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_STATE_H
-#define MOLKO_STATE_H
-
-/**
- * \file state.h
- * \brief Abstract state.
- */
-
-union event;
-
-/**
- * \brief Abstract state.
- */
-struct state {
-	/**
-	 * This function is called when the state is entered.
-	 */
-	void (*enter)(void);
-
-	/**
-	 * This function is called when the state is about to be left.
-	 */
-	void (*leave)(void);
-
-	/**
-	 * This function is called for each event that happened.
-	 */
-	void (*handle)(const union event *);
-
-	/**
-	 * This function is called to update the game, with the number of
-	 * milliseconds since the last frame.
-	 */
-	void (*update)(unsigned int ticks);
-
-	/**
-	 * This function is supposed to draw the game.
-	 */
-	void (*draw)(void);
-};
-
-#endif /* !MOLKO_STATE_H */
--- a/src/sys.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,162 +0,0 @@
-/*
- * sys.c -- system routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <SDL.h>
-#include <SDL_image.h>
-#include <SDL_mixer.h>
-#include <SDL_ttf.h>
-
-#if !defined(_WIN32)            /* Assuming POSIX */
-#	include <sys/types.h>
-#	include <dirent.h>
-#endif
-
-#include "error.h"
-#include "error_p.h"
-#include "sys.h"
-
-#if defined(_WIN32)
-
-static void
-determine(char path[], size_t pathlen)
-{
-	char *base = SDL_GetBasePath();
-
-	/* On Windows, the data hierarchy is the same as the project. */
-	snprintf(path, pathlen, "%sassets", base);
-	SDL_free(base);
-}
-
-#else                           /* Assuming POSIX */
-
-static bool
-is_absolute(const char *path)
-{
-	assert(path);
-
-	return path[0] == '/';
-}
-
-static void
-determine(char path[], size_t pathlen)
-{
-	char localassets[FILENAME_MAX];
-	char *base = SDL_GetBasePath();
-	DIR *dp;
-
-	/* Try assets directory where executable lives. */
-	snprintf(localassets, sizeof (localassets), "%sassets", base);
-
-	if ((dp = opendir(localassets))) {
-		snprintf(path, pathlen, "%sassets", base);
-		closedir(dp);
-	} else {
-		/* We are not in the project source directory. */
-		if (is_absolute(SHAREDIR)) {
-			/* SHAREDIR is absolute */
-			snprintf(path, pathlen, "%s/molko", SHAREDIR);
-		} else if (is_absolute(BINDIR)) {
-			/* SHAREDIR is relative but BINDIR is absolute */
-			snprintf(path, pathlen, "%s/%s/molko", PREFIX, SHAREDIR);
-		} else {
-			/* SHAREDIR, BINDIR are both relative */
-			char *ptr = strstr(base, BINDIR);
-
-			if (ptr) {
-				*ptr = '\0';
-				snprintf(path, pathlen, "%s%s/molko", base, SHAREDIR);
-			} else {
-				/* Unable to determine. */
-				snprintf(path, pathlen, ".");
-			}
-		}
-	}
-
-	SDL_free(base);
-}
-
-#endif
-
-bool
-sys_init(void)
-{
-#if defined(__MINGW64__)
-	/* On MinGW buffering leads to painful debugging. */
-	setvbuf(stdout, NULL, _IONBF, 0);
-#endif
-
-	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
-		return error_sdl();
-	if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
-		return error_sdl();
-	if (TTF_Init() < 0)
-		return error_sdl();
-	if (Mix_Init(MIX_INIT_OGG) != MIX_INIT_OGG)
-		return error_sdl();
-
-	return true;
-}
-
-const char *
-sys_datadir(void)
-{
-	static char path[1024];
-
-	if (path[0] == '\0')
-		determine(path, sizeof (path));
-
-	return path;
-}
-
-const char *
-sys_datapath(const char *fmt, ...)
-{
-	const char *ret;
-	va_list ap;
-
-	va_start(ap, fmt);
-	ret = sys_datapathv(fmt, ap);
-	va_end(ap);
-
-	return ret;
-}
-
-const char *
-sys_datapathv(const char *fmt, va_list ap)
-{
-	static char path[2048 + FILENAME_MAX];
-	char filename[FILENAME_MAX];
-
-	vsnprintf(filename, sizeof (filename), fmt, ap);
-	snprintf(path, sizeof (path), "%s/%s", sys_datadir(), filename);
-
-	return path;
-}
-
-void
-sys_close(void)
-{
-	Mix_Quit();
-	TTF_Quit();
-	IMG_Quit();
-	SDL_Quit();
-}
--- a/src/sys.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * sys.h -- system routines
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_SYS_H
-#define MOLKO_SYS_H
-
-/**
- * \file sys.h
- * \brief System routines.
- */
-
-#include <stdarg.h>
-#include <stdbool.h>
-
-/**
- * Initialize the system, should be called in the beginning of the main.
- */
-bool
-sys_init(void);
-
-/**
- * Get the base system directory path.
- *
- * \return the path where the executable lives
- */
-const char *
-sys_datadir(void);
-
-/**
- * Construct path to assets directory using printf-like format.
- *
- * \param fmt the format string
- * \return the path to the file
- * \note This function returns pointer to static string.
- */
-const char *
-sys_datapath(const char *fmt, ...);
-
-/**
- * Similar to \a sys_datapath.
- *
- * \param fmt the format string
- * \param ap the variadic arguments pointer
- * \return the path to the file
- * \note This function returns pointer to static string.
- */
-const char *
-sys_datapathv(const char *fmt, va_list ap);
-
-/**
- * Close the system.
- */
-void
-sys_close(void);
-
-#endif /* !MOLKO_SYS_H */
--- a/src/texture.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * texture.c -- basic texture management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include "error.h"
-#include "error_p.h"
-#include "texture.h"
-#include "texture_p.h"
-#include "util.h"
-#include "window_p.h"
-
-struct texture *
-texture_new(unsigned int w, unsigned int h)
-{
-	struct texture *tex = emalloc(sizeof (struct texture));
-
-	if (!(tex->handle = SDL_CreateTexture(win.renderer,
-	    SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h))) {
-		error_sdl();
-		free(tex);
-		return NULL;
-	}
-
-	return tex;
-}
-
-unsigned int
-texture_width(struct texture *tex)
-{
-	assert(tex);
-
-	int width;
-
-	if (SDL_QueryTexture(tex->handle, NULL, NULL, &width, NULL) < 0)
-		return 0;
-
-	return width;
-}
-
-unsigned int
-texture_height(struct texture *tex)
-{
-	assert(tex);
-
-	int height;
-
-	if (SDL_QueryTexture(tex->handle, NULL, NULL, NULL, &height) < 0)
-		return 0;
-
-	return height;
-}
-
-void
-texture_draw(struct texture *tex, int x, int y)
-{
-	SDL_Rect dst = {
-		.x = x,
-		.y = y,
-		.w = 0,
-		.h = 0
-	};
-
-	assert(tex);
-
-	/* We need to determine size */
-	SDL_QueryTexture(tex->handle, NULL, NULL, &dst.w, &dst.h);
-	SDL_RenderCopy(win.renderer, tex->handle, NULL, &dst);
-}
-
-void
-texture_draw_ex(struct texture *tex,
-                int src_x,
-                int src_y,
-                unsigned src_w,
-                unsigned src_h,
-                int dst_x,
-                int dst_y,
-                unsigned dst_w,
-                unsigned dst_h,
-                double angle)
-{
-	const SDL_Rect src = {
-		.x = src_x,
-		.y = src_y,
-		.w = src_w,
-		.h = src_h
-	};
-	const SDL_Rect dst = {
-		.x = dst_x,
-		.y = dst_y,
-		.w = dst_w,
-		.h = dst_h
-	};
-
-	SDL_RenderCopyEx(win.renderer, tex->handle, &src, &dst, angle, NULL, SDL_FLIP_NONE);
-}
-
-void
-texture_close(struct texture *tex)
-{
-	assert(tex);
-
-	SDL_DestroyTexture(tex->handle);
-	free(tex);
-}
-
-/* private */
-
-struct texture *
-texture_from_surface(SDL_Surface *surface)
-{
-	assert(surface);
-
-	struct texture *texture = ecalloc(1, sizeof (struct texture));
-
-	if (!(texture->handle = SDL_CreateTextureFromSurface(win.renderer, surface))) {
-		error_sdl();
-		free(texture);
-		return NULL;
-	}
-
-	return texture;
-}
--- a/src/texture.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,116 +0,0 @@
-/*
- * texture.h -- basic texture management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_TEXTURE_H
-#define MOLKO_TEXTURE_H
-
-/**
- * \file texture.h
- * \brief Basic texture management.
- *
- * See also \a image.h for usage of textures.
- */
-
-#include <stdbool.h>
-
-/**
- * \brief Texture object.
- *
- * This object is not publicly defined because it contains
- * implementation-defined data.
- */
-struct texture;
-
-/**
- * Create a new texture.
- *
- * \param w the width
- * \param h the height
- * \return the texture or NULL on error
- */
-struct texture *
-texture_new(unsigned int w, unsigned int h);
-
-/**
- * Get texture width.
- *
- * \pre tex != NULL
- * \param tex the texture
- * \return the width
- */
-unsigned int
-texture_width(struct texture *tex);
-
-/**
- * Get texture height.
- *
- * \pre tex != NULL
- * \param tex the texture
- * \return the height
- */
-unsigned int
-texture_height(struct texture *tex);
-
-/**
- * Simple texture drawing.
- *
- * \pre tex != NULL
- * \param tex the texture
- * \param x the X coordinate
- * \param y the Y coordinate
- */
-void
-texture_draw(struct texture *tex, int x, int y);
-
-/**
- * Advanced texture drawing.
- *
- * \pre tex != NULL
- * \param tex the texture
- * \param src_x the source rectangle X coordinate
- * \param src_y the source rectangle Y coordinate
- * \param src_w the source rectangle width
- * \param src_h the source rectangle height
- * \param dst_x the destination rectangle X coordinate
- * \param dst_y the destination rectangle Y coordinate
- * \param dst_w the destination rectangle width
- * \param dst_h the destination rectangle height
- * \param angle the angle
- */
-void
-texture_draw_ex(struct texture *tex,
-                int src_x,
-                int src_y,
-                unsigned src_w,
-                unsigned src_h,
-                int dst_x,
-                int dst_y,
-                unsigned dst_w,
-                unsigned dst_h,
-                double angle);
-
-/**
- * Close the texture, do not use afterwards.
- *
- * \pre tex != NULL
- * \param tex the texture
- */
-void
-texture_close(struct texture *tex);
-
-#endif /* !MOLKO_TEXTURE_H */
--- a/src/texture_p.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-/*
- * texture_p.h -- (PRIVATE) basic texture management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_TEXTURE_P_H
-#define MOLKO_TEXTURE_P_H
-
-#include <SDL.h>
-
-struct texture {
-	SDL_Texture *handle;
-};
-
-struct texture *
-texture_from_surface(SDL_Surface *surface);
-
-#endif /* !MOLKO_TEXTURE_P_H */
--- a/src/util.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * util.c -- utilities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <SDL.h>
-
-#include "util.h"
-#include "error.h"
-
-void *
-emalloc(size_t size)
-{
-	void *mem;
-
-	if (!(mem = malloc(size)))
-		error_fatalf("%s\n", strerror(errno));
-
-	return mem;
-}
-
-void *
-ecalloc(size_t n, size_t size)
-{
-	void *mem;
-
-	if (!(mem = calloc(n, size)))
-		error_fatalf("%s\n", strerror(errno));
-
-	return mem;
-}
-
-void
-delay(unsigned int ms)
-{
-	SDL_Delay(ms);
-}
--- a/src/util.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * util.h -- utilities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_UTIL_H
-#define MOLKO_UTIL_H
-
-#include <stddef.h>
-
-/**
- * Get the number of elements in a static array.
- *
- * \param x the array
- * \return the number of elements
- */
-#define nelem(x) sizeof ((x)) / sizeof ((x)[0])
-
-/**
- * Wrapper around malloc(3) that exits on allocation failure.
- *
- * \param size the size
- * \return a pointer
- * \post returned pointer will never be NULL
- */
-void *
-emalloc(size_t size);
-
-/**
- * Wrapper around calloc(3) that exits on allocation failure.
- *
- * \param n the number of objects to allocate
- * \param size the size per n
- * \return a pointer
- * \post returned pointer will never be NULL
- */
-void *
-ecalloc(size_t n, size_t size);
-
-/**
- * Put the thread to sleep for a given amount of milliseconds.
- *
- * \param ms the number of milliseconds to wait
- */
-void
-delay(unsigned int ms);
-
-#endif /* !MOLKO_UTIL_H */
--- a/src/walksprite.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * walksprite.c -- sprite designed for walking entities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-#include <string.h>
-
-#include "walksprite.h"
-#include "sprite.h"
-
-void
-walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay)
-{
-	assert(ws);
-	assert(sprite);
-
-	memset(ws, 0, sizeof (struct walksprite));
-	ws->sprite = sprite;
-	ws->delay = delay;
-}
-
-void
-walksprite_update(struct walksprite *ws, unsigned int ticks)
-{
-	assert(ws);
-
-	ws->elapsed += ticks;
-
-	if (ws->elapsed >= ws->delay) {
-		ws->index += 1;
-
-		if (ws->index >= ws->sprite->ncols)
-			ws->index = 0;
-
-		ws->elapsed = 0;
-	}
-}
-
-void
-walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y)
-{
-	assert(ws);
-	assert(orientation < 8);
-
-	sprite_draw(ws->sprite, orientation, ws->index, x, y);
-}
--- a/src/walksprite.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,102 +0,0 @@
-/*
- * walksprite.h -- sprite designed for walking entities
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_WALKSPRITE_H
-#define MOLKO_WALKSPRITE_H
-
-/**
- * \file walksprite.h
- * \brief Sprite designed for walking entities.
- */
-
-struct sprite;
-
-/**
- * \brief Sprite designed for walking entities.
- *
- * This structure works with sprite images that are defined as using the
- * following conventions:
- *
- * ```
- * 7   0   1
- *   ↖ ↑ ↗
- * 6 ←   → 2
- *   ↙ ↓ ↘
- * 5   4   3
- * ```
- *
- * Where numbers define row in the sprite according to the character
- * orientation. In other terms, your image sprite should look like this:
- *
- * ```
- * row columns in your image
- * ---|---------------------
- *  0 | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
- *  1 | ↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗↗
- *  2 | →→→→→→→→→→→→→→→→→→→→
- *  3 | ↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘↘
- *  4 | ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
- *  5 | ↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙↙
- *  6 | ←←←←←←←←←←←←←←←←←←←←
- *  7 | ↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖↖
- * ```
- */
-struct walksprite {
-	struct sprite *sprite;  /*!< (RW) The sprite to use */
-	unsigned int delay;     /*!< (RW) The delay between frames */
-	unsigned int index;     /*!< (RO) Current column index */
-	unsigned int elapsed;   /*!< (RO) Elapsed time since last frame */
-};
-
-/**
- * Initialize the walking sprite.
- *
- * \pre ws != NULL
- * \pre sprite != NULL
- * \param ws the walking sprite
- * \param sprite the sprite to use
- * \param delay the delay between sprites
- */
-void
-walksprite_init(struct walksprite *ws, struct sprite *sprite, unsigned int delay);
-
-/**
- * Update the walking sprite
- *
- * \pre ws != NULL
- * \param ws the walking sprite
- * \param ticks the number of milliseconds between last frame
- */
-void
-walksprite_update(struct walksprite *ws, unsigned int ticks);
-
-/**
- * Draw the appropriate sprite cell to the screen according to the orientation
- * given.
- *
- * \pre ws != NULL
- * \pre orientation < 8
- * \param ws the walking sprite
- * \param orientation the orientation (or the row between 0 and 7)
- * \param x the x coordinate
- * \param y the y coordinate
- */
-void
-walksprite_draw(struct walksprite *ws, unsigned int orientation, int x, int y);
-
-#endif /* !MOLKO_WALKSPRITE_H */
--- a/src/window.c	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/*
- * window.c -- basic window management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <assert.h>
-
-#include <SDL.h>
-
-#include "error_p.h"
-#include "window.h"
-#include "window_p.h"
-
-/* global object, used by textures */
-struct window win = {
-	.win = NULL,
-	.renderer = NULL
-};
-
-bool
-window_init(const char *title, unsigned int width, unsigned int height)
-{
-	assert(title);
-
-	if (SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL,
-	    &win.win, &win.renderer) < 0)
-		return error_sdl();
-
-	SDL_SetWindowTitle(win.win, title);
-
-	return true;
-}
-
-unsigned int
-window_width(void)
-{
-	int width;
-
-	SDL_GetWindowSize(win.win, &width, NULL);
-
-	return width;
-}
-
-unsigned int
-window_height(void)
-{
-	int height;
-
-	SDL_GetWindowSize(win.win, NULL, &height);
-
-	return height;
-}
-
-void
-window_close(void)
-{
-	SDL_DestroyRenderer(win.renderer);
-	SDL_DestroyWindow(win.win);
-}
--- a/src/window.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * window.h -- basic window management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_WINDOW_H
-#define MOLKO_WINDOW_H
-
-/**
- * \file window.h
- * \brief Basic window management.
- */
-
-#include <stdbool.h>
-
-/**
- * Initialize window.
- *
- * \pre title != NULL
- * \param title the window title
- * \param width the desired width
- * \param height the desired height
- * \return true on success
- */
-bool
-window_init(const char *title, unsigned int width, unsigned int height);
-
-/**
- * Get the current window's width.
- *
- * \return the width
- */
-unsigned int
-window_width(void);
-
-/**
- * Get the current window's height.
- *
- * \return the height
- */
-unsigned int
-window_height(void);
-
-/**
- * Close the window and destroy associated resources.
- */
-void
-window_close(void);
-
-#endif /* !MOLKO_WINDOW_H */
--- a/src/window_p.h	Tue Jan 21 12:31:56 2020 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * window.c -- (PRIVATE) basic window management
- *
- * Copyright (c) 2020 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef MOLKO_WINDOW_P_H
-#define MOLKO_WINDOW_P_H
-
-#include <SDL.h>
-
-struct window {
-	SDL_Window *win;
-	SDL_Renderer *renderer;
-};
-
-/* Global window object */
-extern struct window win;
-
-#endif /* !MOLKO_WINDOW_P_H */