changeset 486:d6757c30658e

core: rework errors
author David Demelier <markand@malikania.fr>
date Tue, 28 Feb 2023 13:04:13 +0100
parents 3ff1fe64d0cd
children f2d3c5a97884
files examples/example-action/example-action.c examples/example-animation/example-animation.c examples/example-audio/example-audio.c examples/example-battle/example-battle.c examples/example-cursor/example-cursor.c examples/example-debug/example-debug.c examples/example-drawable/example-drawable.c examples/example-font/example-font.c examples/example-gridmenu/example-gridmenu.c examples/example-label/example-label.c examples/example-message/example-message.c examples/example-notify/example-notify.c examples/example-sprite/example-sprite.c examples/example-trace/example-trace.c examples/example-ui/example-ui.c libmlk-core/CMakeLists.txt libmlk-core/mlk/core/alloc.c libmlk-core/mlk/core/err.h libmlk-core/mlk/core/error.c libmlk-core/mlk/core/error.h libmlk-core/mlk/core/font.c libmlk-core/mlk/core/image.c libmlk-core/mlk/core/panic.c libmlk-core/mlk/core/panic.h libmlk-core/mlk/core/sound.c libmlk-core/mlk/core/sys.c libmlk-core/mlk/core/texture.c libmlk-example/mlk/example/registry.c libmlk-example/mlk/example/registry.h libmlk-rpg/mlk/rpg/battle-indicator.c libmlk-rpg/mlk/rpg/battle-state-closing.c libmlk-rpg/mlk/rpg/map-file.c libmlk-rpg/mlk/rpg/map.c libmlk-rpg/mlk/rpg/message.c libmlk-rpg/mlk/rpg/save.c libmlk-rpg/mlk/rpg/tileset-file.c libmlk-ui/mlk/ui/label.c tests/CMakeLists.txt tests/test-error.c tests/test-map.c
diffstat 40 files changed, 305 insertions(+), 394 deletions(-) [+]
line wrap: on
line diff
--- a/examples/example-action/example-action.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-action/example-action.c	Tue Feb 28 13:04:13 2023 +0100
@@ -278,7 +278,7 @@
 	int err;
 
 	if ((err = mlk_example_init("example-action")) < 0)
-		mlk_panicf("example_init: %s", mlk_err_string(err));
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
 	chests_init();
 	label_init();
--- a/examples/example-animation/example-animation.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-animation/example-animation.c	Tue Feb 28 13:04:13 2023 +0100
@@ -33,10 +33,8 @@
 #include <mlk/ui/label.h>
 #include <mlk/ui/ui.h>
 
-#include <assets/sprites/numbers.h>
-
-#define W       1280
-#define H       720
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
 static struct label label = {
 	.text = "Keys: <Space> start or reset the animation.",
@@ -46,20 +44,16 @@
 };
 
 static struct mlk_state *states[1];
-static struct mlk_texture numbers;
 static struct mlk_animation animation;
-static struct mlk_sprite sprite;
 static int completed = 1;
 
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-animation") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Animation", W, H) < 0)
-		mlk_panic();
-	if (mlk_image_openmem(&numbers, assets_sprites_numbers, sizeof (assets_sprites_numbers)) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-animation")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
@@ -100,12 +94,17 @@
 {
 	(void)st;
 
+	unsigned int cellw, cellh;
+
+	cellw = registry_sprites[REGISTRY_TEXTURE_NUMBERS].cellw;
+	cellh = registry_sprites[REGISTRY_TEXTURE_NUMBERS].cellh;
+
 	mlk_painter_set_color(0x4f8fbaff);
 	mlk_painter_clear();
 	label_draw(&label);
 
 	if (!completed)
-		mlk_animation_draw(&animation, (mlk_window.w - sprite.cellw) / 2, (mlk_window.h - sprite.cellh) / 2);
+		mlk_animation_draw(&animation, (mlk_window.w - cellw) / 2, (mlk_window.h - cellh) / 2);
 
 	mlk_painter_present();
 }
@@ -119,8 +118,7 @@
 		.draw = draw
 	};
 
-	mlk_sprite_init(&sprite, &numbers, 48, 48);
-	mlk_animation_init(&animation, &sprite, 1000);
+	mlk_animation_init(&animation, &registry_sprites[REGISTRY_TEXTURE_NUMBERS], 1000);
 
 	mlk_game_init(states, MLK_UTIL_SIZE(states));
 	mlk_game_push(&state);
--- a/examples/example-audio/example-audio.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-audio/example-audio.c	Tue Feb 28 13:04:13 2023 +0100
@@ -32,15 +32,12 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#include <assets/music/vabsounds-romance.h>
-#include <assets/sounds/fire.h>
-
-#define W       1280
-#define H       720
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
 static struct mlk_state *states[1];
-static struct mlk_music music;
-static struct mlk_sound sound;
+static struct mlk_music *music;
+static struct mlk_sound *sound;
 
 static struct label label_music = {
 	.text = "Music: <Space> play, <p> pause, <r> resume, <q> stop, <l> loop.",
@@ -59,13 +56,13 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-audio") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Audio", W, H) < 0)
-		mlk_panic();
-	if (mlk_music_openmem(&music, assets_music_vabsounds_romance, sizeof (assets_music_vabsounds_romance)) < 0 ||
-	    mlk_sound_openmem(&sound, assets_sounds_fire, sizeof (assets_sounds_fire)) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-audio")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
+
+	sound = &registry_sounds[REGISTRY_SOUND_FIRE];
+	music = &registry_music[REGISTRY_MUSIC_ROMANCE];
 }
 
 static void
@@ -81,27 +78,29 @@
 {
 	(void)st;
 
+	int err;
+
 	switch (ev->type) {
 	case MLK_EVENT_CLICKDOWN:
-		if (mlk_sound_play(&sound) < 0)
-			mlk_panic();
+		if ((err = mlk_sound_play(sound)) < 0)
+			mlk_panic(err);
 		break;
 	case MLK_EVENT_KEYDOWN:
 		switch (ev->key.key) {
 		case MLK_KEY_p:
-			mlk_music_pause(&music);
+			mlk_music_pause(music);
 			break;
 		case MLK_KEY_r:
-			mlk_music_resume(&music);
+			mlk_music_resume(music);
 			break;
 		case MLK_KEY_q:
-			mlk_music_stop(&music);
+			mlk_music_stop(music);
 			break;
 		case MLK_KEY_l:
-			mlk_music_play(&music, MLK_MUSIC_LOOP);
+			mlk_music_play(music, MLK_MUSIC_LOOP);
 			break;
 		case MLK_KEY_SPACE:
-			mlk_music_play(&music, 0);
+			mlk_music_play(music, 0);
 			break;
 		default:
 			break;
--- a/examples/example-battle/example-battle.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-battle/example-battle.c	Tue Feb 28 13:04:13 2023 +0100
@@ -47,12 +47,10 @@
 #include <mlk/rpg/rpg.h>
 #include <mlk/rpg/spell.h>
 
+#include <mlk/example/example.h>
 #include <mlk/example/registry.h>
 #include <mlk/example/spell-fire.h>
 
-#define W 1280
-#define H 720
-
 static void
 adventurer_reset(struct character *ch)
 {
@@ -130,10 +128,10 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-battle") < 0 || ui_init() < 0 || rpg_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Battle", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-battle")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
 	registry_init();
 
--- a/examples/example-cursor/example-cursor.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-cursor/example-cursor.c	Tue Feb 28 13:04:13 2023 +0100
@@ -19,6 +19,7 @@
 #include <stdio.h>
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/key.h>
@@ -32,8 +33,7 @@
 #include <mlk/ui/label.h>
 #include <mlk/ui/ui.h>
 
-#define W 1280
-#define H 720
+#include <mlk/example/example.h>
 
 static struct mlk_state *states[1];
 static char help_text[128];
@@ -49,23 +49,23 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-cursor") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Cursor", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-cursor")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
 change(enum mlk_window_cursor cursor)
 {
 	static const char *names[] = {
-		[WINDOW_CURSOR_ARROW]           = "WINDOW_CURSOR_ARROW",
-		[WINDOW_CURSOR_EDIT]            = "WINDOW_CURSOR_EDIT",
-		[WINDOW_CURSOR_WAIT]            = "WINDOW_CURSOR_WAIT",
-		[WINDOW_CURSOR_CROSSHAIR]       = "WINDOW_CURSOR_CROSSHAIR",
-		[WINDOW_CURSOR_SIZE]            = "WINDOW_CURSOR_SIZE",
-		[WINDOW_CURSOR_NO]              = "WINDOW_CURSOR_NO",
-		[WINDOW_CURSOR_HAND]            = "WINDOW_CURSOR_HAND"
+		[MLK_WINDOW_CURSOR_ARROW]       = "MLK_WINDOW_CURSOR_ARROW",
+		[MLK_WINDOW_CURSOR_EDIT]        = "MLK_WINDOW_CURSOR_EDIT",
+		[MLK_WINDOW_CURSOR_WAIT]        = "MLK_WINDOW_CURSOR_WAIT",
+		[MLK_WINDOW_CURSOR_CROSSHAIR]   = "MLK_WINDOW_CURSOR_CROSSHAIR",
+		[MLK_WINDOW_CURSOR_SIZE]        = "MLK_WINDOW_CURSOR_SIZE",
+		[MLK_WINDOW_CURSOR_NO]          = "MLK_WINDOW_CURSOR_NO",
+		[MLK_WINDOW_CURSOR_HAND]        = "MLK_WINDOW_CURSOR_HAND"
 	};
 
 	snprintf(help_text, sizeof (help_text), "Keys: <Left>/<Right> to change cursor. Current: %s", names[cursor]);
@@ -92,7 +92,6 @@
 			break;
 		}
 
-
 		break;
 	case MLK_EVENT_QUIT:
 		mlk_game_quit();
--- a/examples/example-debug/example-debug.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-debug/example-debug.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/window.h>
@@ -29,8 +30,7 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#define W 1280
-#define H 720
+#include <mlk/example/example.h>
 
 static struct mlk_state *states[1];
 static int mouse_x;
@@ -39,10 +39,10 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-debug") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Debug", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-debug")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
 	debug_options.enable = 1;
 }
--- a/examples/example-drawable/example-drawable.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-drawable/example-drawable.c	Tue Feb 28 13:04:13 2023 +0100
@@ -41,10 +41,8 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#include <assets/sprites/explosion.h>
-
-#define W       1280
-#define H       720
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
 static struct label help = {
 	.x = 10,
@@ -63,8 +61,8 @@
  */
 
 /* 0: Explosion animation. */
-static struct mlk_texture explosion_tex;
-static struct mlk_sprite explosion_sprite;
+static struct mlk_texture *explosion_tex;
+static struct mlk_sprite *explosion_sprite;
 
 struct explosion {
 	struct mlk_animation anim;
@@ -74,16 +72,13 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-drawable") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Drawable", W, H) < 0)
-		mlk_panic();
+	int err;
 
-	/* 0: Explosion animation. */
-	if (mlk_image_openmem(&explosion_tex, assets_sprites_explosion, sizeof (assets_sprites_explosion)) < 0)
-		mlk_panic();
+	if ((err = mlk_example_init("example-drawable")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
-	mlk_sprite_init(&explosion_sprite, &explosion_tex, 256, 256);
+	explosion_tex = &registry_textures[REGISTRY_TEXTURE_EXPLOSION];
+	explosion_sprite = &registry_sprites[REGISTRY_TEXTURE_EXPLOSION];
 }
 
 static int
@@ -113,11 +108,11 @@
 {
 	struct explosion *ex = mlk_alloc_new0(1, sizeof (*ex));
 
-	mlk_animation_init(&ex->anim, &explosion_sprite, 15);
+	mlk_animation_init(&ex->anim, explosion_sprite, 15);
 
 	ex->dw.data = ex;
-	ex->dw.x = x - (int)(explosion_sprite.cellw / 2);
-	ex->dw.y = y - (int)(explosion_sprite.cellh / 2);
+	ex->dw.x = x - (int)(explosion_sprite->cellw / 2);
+	ex->dw.y = y - (int)(explosion_sprite->cellh / 2);
 	ex->dw.update = explosion_update;
 	ex->dw.draw = explosion_draw;
 	ex->dw.finish = explosion_finish;
@@ -157,7 +152,6 @@
 	(void)st;
 
 	mlk_drawable_stack_update(&stack, ticks);
-
 }
 
 static void
--- a/examples/example-font/example-font.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-font/example-font.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/font.h>
 #include <mlk/core/game.h>
@@ -31,8 +32,7 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#define W       (1280)
-#define H       (720)
+#include <mlk/example/example.h>
 
 /* Friendly taken from: https://lospec.com/palette-list/apollo */
 static const unsigned long colors[] = {
@@ -53,10 +53,10 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-font") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Font", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-font")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
@@ -101,12 +101,13 @@
 
 	struct mlk_font *font = theme_default()->fonts[THEME_FONT_INTERFACE];
 	struct mlk_texture tex;
+	int err;
 
 	mlk_painter_set_color(0xffffffff);
 	mlk_painter_clear();
 
-	if (mlk_font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci]) < 0)
-		mlk_panic();
+	if ((err = mlk_font_render(font, &tex, "Example of text. Use <Left>/<Right> to change color and <Space> to toggle antialiasing.", colors[ci])) < 0)
+		mlk_panic(err);
 
 	mlk_texture_draw(&tex, 10, 10);
 	mlk_painter_present();
--- a/examples/example-gridmenu/example-gridmenu.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-gridmenu/example-gridmenu.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/painter.h>
@@ -33,18 +34,18 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#define W       (1280)
-#define H       (720)
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
 static struct mlk_state *states[1];
 
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-gridmenu") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Grid menu", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-gridmenu")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
@@ -113,7 +114,7 @@
 	gridmenu_init(&menu, 3, 2, items, MLK_UTIL_SIZE(items));
 	gridmenu_resize(&menu, 0, 0, 300, 100);
 
-	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, W, H);
+	align(ALIGN_CENTER, &menu.x, &menu.y, menu.w, menu.h, 0, 0, mlk_window.w, mlk_window.h);
 
 	mlk_game_init(states, MLK_UTIL_SIZE(states));
 	mlk_game_push(&state);
--- a/examples/example-label/example-label.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-label/example-label.c	Tue Feb 28 13:04:13 2023 +0100
@@ -19,6 +19,7 @@
 #include <stddef.h>
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/painter.h>
@@ -33,8 +34,7 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#define W       (1280)
-#define H       (720)
+#include <mlk/example/example.h>
 
 struct {
 	enum align align;
@@ -106,17 +106,17 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-label") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Label", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-label")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
 	for (size_t i = 0; i < MLK_UTIL_SIZE(table); ++i) {
 		struct label *l = &table[i].label;
 		unsigned int w, h;
 
 		label_query(l, &w, &h);
-		align(table[i].align, &l->x, &l->y, w, h, 0, 0, W, H);
+		align(table[i].align, &l->x, &l->y, w, h, 0, 0, mlk_window.w, mlk_window.h);
 	}
 }
 
--- a/examples/example-message/example-message.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-message/example-message.c	Tue Feb 28 13:04:13 2023 +0100
@@ -35,12 +35,11 @@
 #include <mlk/rpg/message.h>
 #include <mlk/rpg/rpg.h>
 
-#define W       (1280)
-#define H       (720)
+#include <mlk/example/example.h>
 
-#define MW      (W * 0.75)
-#define MH      (H * 0.120)
-#define MX      ((W / 2) - (MW / 2))
+#define MW      (MLK_EXAMPLE_W * 0.75)
+#define MH      (MLK_EXAMPLE_H * 0.120)
+#define MX      ((MLK_EXAMPLE_W / 2) - (MW / 2))
 #define MY      (100)
 
 static struct mlk_state *states[1];
@@ -48,10 +47,10 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-message") < 0 || ui_init() < 0 || rpg_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Message", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-message")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
--- a/examples/example-notify/example-notify.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-notify/example-notify.c	Tue Feb 28 13:04:13 2023 +0100
@@ -33,11 +33,8 @@
 #include <mlk/ui/label.h>
 #include <mlk/ui/ui.h>
 
-/* Sword by Icongeek26 (https://www.flaticon.com). */
-#include <assets/images/sword.h>
-
-#define W       1280
-#define H       720
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
 static struct label help = {
 	.text = "Keys: <Space> to generate a notification.",
@@ -45,18 +42,18 @@
 	.y = 10,
 	.flags = LABEL_FLAGS_SHADOW
 };
-static struct mlk_texture icon;
+static struct mlk_texture *icon;
 static struct mlk_state *states[1];
 
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-notify") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Notify", W, H) < 0)
-		mlk_panic();
-	if (mlk_image_openmem(&icon, assets_images_sword, sizeof (assets_images_sword)) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-notify")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
+
+	icon = &registry_textures[REGISTRY_TEXTURE_SWORD];
 }
 
 static void
@@ -70,7 +67,7 @@
 		break;
 	case MLK_EVENT_KEYDOWN:
 		if (ev->key.key == MLK_KEY_SPACE)
-			notify(&icon, "Quest", "Quest finished.");
+			notify(icon, "Quest", "Quest finished.");
 		break;
 	default:
 		break;
--- a/examples/example-sprite/example-sprite.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-sprite/example-sprite.c	Tue Feb 28 13:04:13 2023 +0100
@@ -19,6 +19,7 @@
 #include <stdio.h>
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/image.h>
@@ -37,15 +38,13 @@
 #include <mlk/ui/label.h>
 #include <mlk/ui/ui.h>
 
-#include <assets/sprites/people.h>
+#include <mlk/example/example.h>
+#include <mlk/example/registry.h>
 
-#define W       1280
-#define H       720
 #define HEADER "Keys: <Left>/<Right> and <Up/Down> to select a column/row. Current: %u, %u (total %u/%u)"
 
 static char msg[512];
-static struct mlk_texture texture;
-static struct mlk_sprite sprite;
+static struct mlk_sprite *sprite;
 static unsigned int row, column;
 static struct mlk_state *states[1];
 
@@ -59,20 +58,18 @@
 static void
 changed(void)
 {
-	snprintf(msg, sizeof (msg), HEADER, column, row, sprite.ncols, sprite.nrows);
+	snprintf(msg, sizeof (msg), HEADER, column, row, sprite->ncols, sprite->nrows);
 }
 
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-sprite") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Sprite", W, H) < 0)
-		mlk_panic();
-	if (mlk_image_openmem(&texture, assets_sprites_people, sizeof (assets_sprites_people)) < 0)
-		mlk_panic();
+	int err;
 
-	mlk_sprite_init(&sprite, &texture, 48, 48);
+	if ((err = mlk_example_init("example-sprite")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
+
+	sprite = &registry_sprites[REGISTRY_TEXTURE_PEOPLE];
 }
 
 static void
@@ -88,7 +85,7 @@
 				column--;
 			break;
 		case MLK_KEY_RIGHT:
-			if (column + 1 < sprite.ncols)
+			if (column + 1 < sprite->ncols)
 				column++;
 			break;
 		case MLK_KEY_UP:
@@ -96,7 +93,7 @@
 				row--;
 			break;
 		case MLK_KEY_DOWN:
-			if (row + 1 < sprite.nrows)
+			if (row + 1 < sprite->nrows)
 				row++;
 			break;
 		default:
@@ -122,8 +119,8 @@
 
 	mlk_painter_set_color(0xebede9ff);
 	mlk_painter_clear();
-	align(ALIGN_CENTER, &x, &y, sprite.cellw, sprite.cellh, 0, 0, W, H);
-	mlk_sprite_draw(&sprite, row, column, x, y);
+	align(ALIGN_CENTER, &x, &y, sprite->cellw, sprite->cellh, 0, 0, mlk_window.w, mlk_window.h);
+	mlk_sprite_draw(sprite, row, column, x, y);
 	label_draw(&help);
 	mlk_painter_present();
 }
@@ -146,7 +143,6 @@
 static void
 quit(void)
 {
-	mlk_texture_finish(&texture);
 	mlk_window_finish();
 	ui_finish();
 	mlk_core_finish();
--- a/examples/example-trace/example-trace.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-trace/example-trace.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/sys.h>
@@ -30,20 +31,18 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
+#include <mlk/example/example.h>
 #include <mlk/example/trace-hud.h>
 
-#define W 1280
-#define H 720
-
 static struct mlk_state *states[1];
 
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-trace") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - Trace", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-trace")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 
 	mlk_trace_handler = trace_hud_handler;
 }
--- a/examples/example-ui/example-ui.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/examples/example-ui/example-ui.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,6 +17,7 @@
  */
 
 #include <mlk/core/core.h>
+#include <mlk/core/err.h>
 #include <mlk/core/event.h>
 #include <mlk/core/game.h>
 #include <mlk/core/maths.h>
@@ -35,8 +36,7 @@
 #include <mlk/ui/theme.h>
 #include <mlk/ui/ui.h>
 
-#define W               (1280)
-#define H               (720)
+#include <mlk/example/example.h>
 
 #define FRAME_ORIGIN_X  (10)
 #define FRAME_ORIGIN_Y  (10)
@@ -123,10 +123,10 @@
 static void
 init(void)
 {
-	if (mlk_core_init("fr.malikania", "example-ui") < 0 || ui_init() < 0)
-		mlk_panic();
-	if (mlk_window_open("Example - UI", W, H) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_example_init("example-ui")) < 0)
+		mlk_panicf("mlk_example_init: %s", mlk_err_string(err));
 }
 
 static void
@@ -219,12 +219,12 @@
 			ui.motion.active = 1;
 			ui.motion.x = ev->click.x;
 			ui.motion.y = ev->click.y;
-			mlk_window_set_cursor(WINDOW_CURSOR_SIZE);
+			mlk_window_set_cursor(MLK_WINDOW_CURSOR_SIZE);
 		}
 		break;
 	case MLK_EVENT_CLICKUP:
 		ui.motion.active = 0;
-		mlk_window_set_cursor(WINDOW_CURSOR_ARROW);
+		mlk_window_set_cursor(MLK_WINDOW_CURSOR_ARROW);
 		break;
 	default:
 		break;
--- a/libmlk-core/CMakeLists.txt	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/CMakeLists.txt	Tue Feb 28 13:04:13 2023 +0100
@@ -41,8 +41,6 @@
 	${libmlk-core_SOURCE_DIR}/mlk/core/drawable.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/err.c
 	${libmlk-core_SOURCE_DIR}/mlk/core/err.h
-	${libmlk-core_SOURCE_DIR}/mlk/core/error.c
-	${libmlk-core_SOURCE_DIR}/mlk/core/error.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/event.c
 	${libmlk-core_SOURCE_DIR}/mlk/core/event.h
 	${libmlk-core_SOURCE_DIR}/mlk/core/font.c
--- a/libmlk-core/mlk/core/alloc.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/alloc.c	Tue Feb 28 13:04:13 2023 +0100
@@ -24,7 +24,6 @@
 #include <SDL.h>
 
 #include "alloc.h"
-#include "error.h"
 #include "panic.h"
 
 #define OOM_MSG "out of memory"
--- a/libmlk-core/mlk/core/err.h	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/err.h	Tue Feb 28 13:04:13 2023 +0100
@@ -24,6 +24,9 @@
 #define MLK_ERR_NO_SUPPORT     -2
 #define MLK_ERR_FORMAT         -3
 #define MLK_ERR_SDL            -4
+#define MLK_ERR_ERRNO          -5
+#define MLK_ERR_OPENAL         -6
+#define MLK_ERR_DATABASE       -7
 
 const char *
 mlk_err_string(int e);
--- a/libmlk-core/mlk/core/error.c	Tue Feb 28 08:40:35 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * error.c -- error routines
- *
- * Copyright (c) 2020-2023 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 "error.h"
-
-#include <SDL.h>
-
-static char buffer[2048];
-
-const char *
-error(void)
-{
-	return buffer;
-}
-
-int
-errorf(const char *fmt, ...)
-{
-	assert(fmt);
-
-	va_list ap;
-
-	va_start(ap, fmt);
-	errorva(fmt, ap);
-	va_end(ap);
-
-	return -1;
-}
-
-int
-errorva(const char *fmt, va_list ap)
-{
-	assert(fmt);
-
-	vsnprintf(buffer, sizeof (buffer), fmt, ap);
-
-	return -1;
-}
--- a/libmlk-core/mlk/core/error.h	Tue Feb 28 08:40:35 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * error.h -- error routines
- *
- * Copyright (c) 2020-2023 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 MLK_CORE_ERROR_H
-#define MLK_CORE_ERROR_H
-
-#include <stdarg.h>
-
-#include "core.h"
-
-MLK_CORE_BEGIN_DECLS
-
-const char *
-error(void);
-
-int
-errorf(const char *, ...);
-
-int
-errorva(const char *, va_list);
-
-MLK_CORE_END_DECLS
-
-#endif /* !MLK_CORE_ERROR_H */
--- a/libmlk-core/mlk/core/font.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/font.c	Tue Feb 28 13:04:13 2023 +0100
@@ -23,7 +23,6 @@
 
 #include "color.h"
 #include "err.h"
-#include "error.h"
 #include "font.h"
 #include "texture_p.h"
 #include "util.h"
--- a/libmlk-core/mlk/core/image.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/image.c	Tue Feb 28 13:04:13 2023 +0100
@@ -20,7 +20,6 @@
 
 #include <SDL_image.h>
 
-#include "error.h"
 #include "texture.h"
 #include "window.h"
 #include "window_p.h"
--- a/libmlk-core/mlk/core/panic.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/panic.c	Tue Feb 28 13:04:13 2023 +0100
@@ -20,18 +20,29 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "error.h"
+#include "err.h"
 #include "panic.h"
 
 static void
-terminate(void)
+terminate(const char *what)
 {
-	fprintf(stderr, "abort: %s\n", error());
+	fprintf(stderr, "abort: %s\n", what);
 	abort();
 	exit(1);
 }
 
-void (*mlk_panic_handler)(void) = terminate;
+void (*mlk_panic_handler)(const char *) = terminate;
+
+static void
+alive(void)
+{
+	/*
+	 * This should not happen, if it does it means the user did not fully
+	 * satisfied the constraint of mlk_panic_handler.
+	 */
+	fprintf(stderr, "abort: panic handler returned\n");
+	exit(1);
+}
 
 void
 mlk_panicf(const char *fmt, ...)
@@ -39,16 +50,18 @@
 	assert(fmt);
 
 	va_list ap;
+	char buf[PANIC_LINE_MAX];
 
 	/*
-	 * Store the error before calling panic because va_end would not be
-	 * called.
+	 * We can't use mlk_panicva directly because we won't be able to call
+	 * va_end then.
 	 */
 	va_start(ap, fmt);
-	errorva(fmt, ap);
+	vsnprintf(buf, sizeof (buf), fmt, ap);
 	va_end(ap);
 
-	mlk_panic();
+	mlk_panic_handler(buf);
+	alive();
 }
 
 void
@@ -57,21 +70,15 @@
 	assert(fmt);
 	assert(mlk_panic_handler);
 
-	errorva(fmt, ap);
-	mlk_panic();
+	char buf[PANIC_LINE_MAX];
+
+	vsnprintf(buf, sizeof (buf), fmt, ap);
+	mlk_panic_handler(buf);
+	alive();
 }
 
 void
-mlk_panic(void)
+mlk_panic(int err)
 {
-	assert(mlk_panic_handler);
-
-	mlk_panic_handler();
-
-	/*
-	 * This should not happen, if it does it means the user did not fully
-	 * satisfied the constraint of mlk_panic_handler.
-	 */
-	fprintf(stderr, "abort: panic handler returned\n");
-	exit(1);
+	mlk_panicf("%s", mlk_err_string(err));
 }
--- a/libmlk-core/mlk/core/panic.h	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/panic.h	Tue Feb 28 13:04:13 2023 +0100
@@ -23,7 +23,9 @@
 
 #include "core.h"
 
-extern void (*mlk_panic_handler)(void);
+#define PANIC_LINE_MAX (256)
+
+extern void (*mlk_panic_handler)(const char *);
 
 MLK_CORE_BEGIN_DECLS
 
@@ -34,7 +36,7 @@
 mlk_panicva(const char *, va_list);
 
 void
-mlk_panic(void);
+mlk_panic(int err);
 
 MLK_CORE_END_DECLS
 
--- a/libmlk-core/mlk/core/sound.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/sound.c	Tue Feb 28 13:04:13 2023 +0100
@@ -20,7 +20,6 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "error.h"
 #include "sound.h"
 #include "sys_p.h"
 
--- a/libmlk-core/mlk/core/sys.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/sys.c	Tue Feb 28 13:04:13 2023 +0100
@@ -42,7 +42,6 @@
 
 #include "alloc.h"
 #include "err.h"
-#include "error.h"
 #include "panic.h"
 #include "sound.h"
 #include "sys.h"
@@ -102,7 +101,7 @@
 		return errorf("unable to create directory: %s", path);
 #else
 	if (mkdir(path, 0755) < 0 && errno != EEXIST)
-		return errorf("%s", strerror(errno));
+		return MLK_ERR_ERRNO;
 #endif
 
 	return 0;
@@ -185,11 +184,11 @@
 
 	/* SDL2. */
 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 	if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 	if (TTF_Init() < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	/* OpenAL. */
 #if defined(MLK_OS_WINDOW)
@@ -199,9 +198,9 @@
 #endif
 
 	if (!(mlk__audio_dev = alcOpenDevice(NULL)))
-		return errorf("unable to create audio device");
+		return MLK_ERR_OPENAL;
 	if (!(mlk__audio_ctx = alcCreateContext(mlk__audio_dev, NULL)))
-		return errorf("unable to create audio context");
+		return MLK_ERR_OPENAL;
 
 	alcMakeContextCurrent(mlk__audio_ctx);
 
--- a/libmlk-core/mlk/core/texture.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-core/mlk/core/texture.c	Tue Feb 28 13:04:13 2023 +0100
@@ -20,7 +20,6 @@
 #include <string.h>
 
 #include "color.h"
-#include "error.h"
 #include "texture.h"
 #include "texture_p.h"
 #include "util.h"
@@ -68,7 +67,7 @@
 	};
 
 	if (SDL_SetTextureBlendMode(tex->handle, table[blend]) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	return 0;
 }
@@ -80,7 +79,7 @@
 	assert(alpha <= 255);
 
 	if (SDL_SetTextureAlphaMod(tex->handle, alpha) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	return 0;
 }
@@ -91,7 +90,7 @@
 	assert(mlk_texture_ok(tex));
 
 	if (SDL_SetTextureColorMod(tex->handle, MLK_COLOR_R(color), MLK_COLOR_G(color), MLK_COLOR_B(color)) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	return 0;
 }
@@ -109,7 +108,7 @@
 	};
 
 	if (SDL_RenderCopy(MLK__RENDERER(), tex->handle, NULL, &dst) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	return 0;
 }
@@ -140,7 +139,7 @@
 	};
 
 	if (SDL_RenderCopyEx(MLK__RENDERER(), tex->handle, &src, &dst, angle, NULL, SDL_FLIP_NONE) < 0)
-		return errorf("%s", SDL_GetError());
+		return MLK_ERR_SDL;
 
 	return 0;
 }
--- a/libmlk-example/mlk/example/registry.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-example/mlk/example/registry.c	Tue Feb 28 13:04:13 2023 +0100
@@ -26,6 +26,7 @@
 #include <assets/images/battle-background.h>
 #include <assets/images/black-cat.h>
 #include <assets/images/haunted-wood.h>
+#include <assets/images/sword.h>
 
 #include <assets/sounds/fire.h>
 #include <assets/sounds/open-chest.h>
@@ -34,14 +35,19 @@
 #include <assets/sprites/explosion.h>
 #include <assets/sprites/john-sword.h>
 #include <assets/sprites/john-walk.h>
+#include <assets/sprites/numbers.h>
+#include <assets/sprites/people.h>
 #include <assets/sprites/ui-cursor.h>
 
+#include <assets/music/vabsounds-romance.h>
+
 #include "registry.h"
 
 struct mlk_texture registry_images[REGISTRY_IMAGE_LAST] = {0};
 struct mlk_texture registry_textures[REGISTRY_TEXTURE_LAST] = {0};
 struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_LAST] = {0};
 struct mlk_sound registry_sounds[REGISTRY_SOUND_LAST] = {0};
+struct mlk_music registry_music[REGISTRY_MUSIC_LAST] = {0};
 
 #define REGISTRY_IMAGE(i, data) \
 	{ (i), (data), sizeof (data) }
@@ -70,7 +76,10 @@
 	REGISTRY_TEXTURE(REGISTRY_TEXTURE_JOHN_WALK, assets_sprites_john_walk, 256, 256),
 	REGISTRY_TEXTURE(REGISTRY_TEXTURE_HAUNTED_WOOD, assets_images_haunted_wood, 0, 0),
 	REGISTRY_TEXTURE(REGISTRY_TEXTURE_BLACK_CAT, assets_images_black_cat, 0, 0),
-	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CHEST, assets_sprites_chest, 32, 32)
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_CHEST, assets_sprites_chest, 32, 32),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_NUMBERS, assets_sprites_numbers, 48, 48),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_SWORD, assets_images_sword, 0, 0),
+	REGISTRY_TEXTURE(REGISTRY_TEXTURE_PEOPLE, assets_sprites_people, 48, 48)
 };
 
 #define REGISTRY_SOUND(s, data) \
@@ -85,14 +94,28 @@
 	REGISTRY_SOUND(REGISTRY_SOUND_OPEN_CHEST, assets_sounds_open_chest)
 };
 
+#define REGISTRY_MUSIC(m, data) \
+	{ (m), (data), sizeof (data) }
+
+static const struct {
+	enum registry_music index;
+	const unsigned char *data;
+	size_t datasz;
+} musics[] = {
+	REGISTRY_MUSIC(REGISTRY_MUSIC_ROMANCE, assets_music_vabsounds_romance)
+};
+
 static void
 load_images(void)
 {
-	for (size_t i = 0; i < MLK_UTIL_SIZE(images); ++i) {
-		struct mlk_texture *texture = &registry_images[images[i].index];
+	int err;
+	struct mlk_texture *texture;
 
-		if (mlk_image_openmem(texture, images[i].data, images[i].datasz) < 0)
-			mlk_panic();
+	for (size_t i = 0; i < MLK_UTIL_SIZE(images); ++i) {
+		texture = &registry_images[images[i].index];
+
+		if ((err = mlk_image_openmem(texture, images[i].data, images[i].datasz)) < 0)
+			mlk_panic(err);
 	}
 }
 
@@ -101,13 +124,14 @@
 {
 	struct mlk_texture *texture;
 	struct mlk_sprite *sprite;
+	int err;
 
 	for (size_t i = 0; i < MLK_UTIL_SIZE(textures); ++i) {
 		texture = &registry_textures[textures[i].index];
 		sprite = &registry_sprites[textures[i].index];
 
-		if (mlk_image_openmem(texture, textures[i].data, textures[i].datasz) < 0)
-			mlk_panic();
+		if ((err = mlk_image_openmem(texture, textures[i].data, textures[i].datasz)) < 0)
+			mlk_panic(err);
 
 		if (textures[i].cellw == 0 || textures[i].cellh == 0)
 			mlk_sprite_init(sprite, texture, texture->w, texture->h);
@@ -119,11 +143,28 @@
 static void
 load_sounds(void)
 {
+	int err;
+	struct mlk_sound *sound;
+
 	for (size_t i = 0; i < MLK_UTIL_SIZE(sounds); ++i) {
-		struct mlk_sound *sound = &registry_sounds[sounds[i].index];
+		sound = &registry_sounds[sounds[i].index];
+
+		if ((err = mlk_sound_openmem(sound, sounds[i].data, sounds[i].datasz)) < 0)
+			mlk_panic(err);
+	}
+}
 
-		if (mlk_sound_openmem(sound, sounds[i].data, sounds[i].datasz) < 0)
-			mlk_panic();
+static void
+load_music(void)
+{
+	int err;
+	struct mlk_music *music;
+
+	for (size_t i = 0; i < MLK_UTIL_SIZE(musics); ++i) {
+		music = &registry_music[musics[i].index];
+
+		if ((err = mlk_music_openmem(music, musics[i].data, musics[i].datasz)) < 0)
+			mlk_panic(err);
 	}
 }
 
@@ -133,6 +174,7 @@
 	load_images();
 	load_textures_and_sprites();
 	load_sounds();
+	load_music();
 }
 
 void
@@ -144,4 +186,6 @@
 		mlk_texture_finish(&registry_textures[i]);
 	for (size_t i = 0; i < MLK_UTIL_SIZE(registry_sounds); ++i)
 		mlk_sound_finish(&registry_sounds[i]);
+	for (size_t i = 0; i < MLK_UTIL_SIZE(registry_music); ++i)
+		mlk_music_finish(&registry_music[i]);
 }
--- a/libmlk-example/mlk/example/registry.h	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-example/mlk/example/registry.h	Tue Feb 28 13:04:13 2023 +0100
@@ -19,6 +19,7 @@
 #ifndef EXAMPLES_BATTLE_REGISTRY_H
 #define EXAMPLES_BATTLE_REGISTRY_H
 
+#include <mlk/core/music.h>
 #include <mlk/core/sound.h>
 #include <mlk/core/sprite.h>
 #include <mlk/core/texture.h>
@@ -26,6 +27,7 @@
 enum registry_texture {
 	/* UI. */
 	REGISTRY_TEXTURE_CURSOR,
+	REGISTRY_TEXTURE_NUMBERS,
 
 	/* Animations. */
 	REGISTRY_TEXTURE_EXPLOSION,
@@ -33,6 +35,7 @@
 	/* Characters. */
 	REGISTRY_TEXTURE_JOHN_WALK,
 	REGISTRY_TEXTURE_JOHN_SWORD,
+	REGISTRY_TEXTURE_PEOPLE,
 
 	/* Enemies. */
 	REGISTRY_TEXTURE_HAUNTED_WOOD,
@@ -41,6 +44,9 @@
 	/* Objects. */
 	REGISTRY_TEXTURE_CHEST,
 
+	/* Sword by Icongeek26 (https://www.flaticon.com). */
+	REGISTRY_TEXTURE_SWORD,
+
 	/* Unused.*/
 	REGISTRY_TEXTURE_LAST
 };
@@ -56,10 +62,16 @@
 	REGISTRY_SOUND_LAST
 };
 
+enum registry_music {
+	REGISTRY_MUSIC_ROMANCE,
+	REGISTRY_MUSIC_LAST
+};
+
 extern struct mlk_texture registry_images[REGISTRY_IMAGE_LAST];
 extern struct mlk_texture registry_textures[REGISTRY_TEXTURE_LAST];
 extern struct mlk_sprite registry_sprites[REGISTRY_TEXTURE_LAST];
 extern struct mlk_sound registry_sounds[REGISTRY_SOUND_LAST];
+extern struct mlk_music registry_music[REGISTRY_MUSIC_LAST];
 
 void
 registry_init(void);
--- a/libmlk-rpg/mlk/rpg/battle-indicator.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/battle-indicator.c	Tue Feb 28 13:04:13 2023 +0100
@@ -55,6 +55,7 @@
 
 	char buf[128];
 	const struct theme *theme = THEME(bti);
+	int err;
 
 	snprintf(buf, sizeof (buf), "%u", bti->amount);
 
@@ -62,9 +63,9 @@
 	bti->elapsed = 0;
 	bti->alpha = 250;
 
-	if (mlk_font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[0], buf, bti->cur) < 0||
-	    mlk_font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[1], buf, 0x000000ff) < 0)
-		mlk_panic();
+	if ((err = mlk_font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[0], buf, bti->cur)) < 0 ||
+	    (err = mlk_font_render(theme->fonts[THEME_FONT_INTERFACE], &bti->tex[1], buf, 0x000000ff)) < 0)
+		mlk_panic(err);
 }
 
 int
--- a/libmlk-rpg/mlk/rpg/battle-state-closing.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/battle-state-closing.c	Tue Feb 28 13:04:13 2023 +0100
@@ -65,8 +65,10 @@
 {
 	assert(cls);
 
-	if (mlk_texture_new(&cls->texture, mlk_window.w, mlk_window.h) < 0)
-		mlk_panic();
+	int err;
+
+	if ((err = mlk_texture_new(&cls->texture, mlk_window.w, mlk_window.h)) < 0)
+		mlk_panic(err);
 
 	MLK_PAINTER_BEGIN(&cls->texture);
 	mlk_texture_set_blend_mode(&cls->texture, MLK_TEXTURE_BLEND_BLEND);
--- a/libmlk-rpg/mlk/rpg/map-file.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/map-file.c	Tue Feb 28 13:04:13 2023 +0100
@@ -27,7 +27,6 @@
 #include <mlk/util/util.h>
 
 #include <mlk/core/alloc.h>
-#include <mlk/core/error.h>
 #include <mlk/core/image.h>
 #include <mlk/core/trace.h>
 #include <mlk/core/util.h>
@@ -57,7 +56,7 @@
 	else if (strcmp(layer_name, "above") == 0)
 		layer_type = MAP_LAYER_TYPE_ABOVE;
 	else
-		return errorf("invalid layer type: %s", layer_name);
+		return MLK_ERR_FORMAT;
 
 	amount = ctx->map->columns * ctx->map->rows;
 	current = 0;
@@ -126,11 +125,11 @@
 
 	/* Check if weight/height has been specified. */
 	if (ctx->map->columns == 0 || ctx->map->rows == 0)
-		return errorf("missing map dimensions before layer");
+		return MLK_ERR_FORMAT;
 
 	/* Determine layer type. */
 	if (sscanf(line, "layer|%32s", layer_name) <= 0)
-		return errorf("missing layer type definition");
+		return MLK_ERR_FORMAT;
 
 	if (strcmp(layer_name, "actions") == 0)
 		return parse_actions(ctx);
@@ -146,7 +145,7 @@
 	struct tileset_file *tf = &mf->tileset_file;
 
 	if (!(p = strchr(line, '|')))
-		return errorf("could not parse tileset");
+		return MLK_ERR_FORMAT;
 
 	snprintf(path, sizeof (path), "%s/%s", ctx->basedir, p + 1);
 
@@ -162,7 +161,7 @@
 parse_title(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "title|" MAX_F(MAP_FILE_TITLE_MAX), ctx->mf->title) != 1 || strlen(ctx->mf->title) == 0)
-		return errorf("null map title");
+		return MLK_ERR_FORMAT;
 
 	ctx->map->title = ctx->mf->title;
 
@@ -173,7 +172,7 @@
 parse_columns(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "columns|%u", &ctx->map->columns) != 1 || ctx->map->columns == 0)
-		return errorf("null map columns");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
@@ -182,7 +181,7 @@
 parse_rows(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "rows|%u", &ctx->map->rows) != 1 || ctx->map->rows == 0)
-		return errorf("null map rows");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
@@ -191,7 +190,7 @@
 parse_origin(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "origin|%d|%d", &ctx->map->player_x, &ctx->map->player_y) != 2)
-		return errorf("invalid origin");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
@@ -245,18 +244,18 @@
 	 * Check that we have parsed every required components.
 	 */
 	if (!map->title)
-		return errorf("missing title");
+		return MLK_ERR_FORMAT;
 
 	/*
 	 * We don't need to check width/height because parsing layers and
 	 * tilesets already check for their presence, so only check layers.
 	 */
 	if (!map->layers[0].tiles)
-		return errorf("missing background layer");
+		return MLK_ERR_FORMAT;
 	if (!map->layers[1].tiles)
-		return errorf("missing foreground layer");
+		return MLK_ERR_FORMAT;
 	if (!tileset_ok(map->tileset))
-		return errorf("missing tileset");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
--- a/libmlk-rpg/mlk/rpg/map.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/map.c	Tue Feb 28 13:04:13 2023 +0100
@@ -21,7 +21,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <mlk/core/error.h>
 #include <mlk/core/event.h>
 #include <mlk/core/image.h>
 #include <mlk/core/maths.h>
--- a/libmlk-rpg/mlk/rpg/message.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/message.c	Tue Feb 28 13:04:13 2023 +0100
@@ -58,12 +58,13 @@
 	assert(msg);
 
 	unsigned int maxw = 0, w = 0;
+	int err;
 
 	for (size_t i = 0; i < msg->linesz; ++i) {
 		if (!msg->lines[i])
 			continue;
-		if (mlk_font_query(THEME(msg)->fonts[THEME_FONT_INTERFACE], msg->lines[i], &w, NULL) < 0)
-			mlk_panic();
+		if ((err = mlk_font_query(THEME(msg)->fonts[THEME_FONT_INTERFACE], msg->lines[i], &w, NULL)) < 0)
+			mlk_panic(err);
 		if (w > maxw)
 			maxw = w;
 	}
@@ -88,12 +89,13 @@
 	const struct theme *theme = THEME(msg);
 	struct label label;
 	unsigned int lw, lh;
+	int err;
 
 	for (size_t i = 0; i < msg->linesz; ++i) {
 		if (!msg->lines[i])
 			continue;
-		if (mlk_font_query(theme->fonts[THEME_FONT_INTERFACE], msg->lines[i], &lw, &lh) < 0)
-			mlk_panic();
+		if ((err = mlk_font_query(theme->fonts[THEME_FONT_INTERFACE], msg->lines[i], &lw, &lh)) < 0)
+			mlk_panic(err);
 
 		label.theme = theme;
 		label.x = theme->padding;
@@ -237,7 +239,7 @@
 	assert(msg);
 
 	struct mlk_texture tex;
-	int x, y;
+	int x, y, err;
 	unsigned int w, h;
 
 	if (msg->w == 0 || msg->h == 0) {
@@ -245,8 +247,8 @@
 		return;
 	}
 
-	if (mlk_texture_new(&tex, msg->w, msg->h) < 0)
-		mlk_panic();
+	if ((err = mlk_texture_new(&tex, msg->w, msg->h)) < 0)
+		mlk_panic(err);
 
 	MLK_PAINTER_BEGIN(&tex);
 	draw_frame(msg);
--- a/libmlk-rpg/mlk/rpg/save.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/save.c	Tue Feb 28 13:04:13 2023 +0100
@@ -25,7 +25,7 @@
 
 #include <mlk/util/util.h>
 
-#include <mlk/core/error.h>
+#include <mlk/core/err.h>
 #include <mlk/core/sys.h>
 #include <mlk/core/util.h>
 
@@ -42,7 +42,7 @@
 exec(struct save *db, const char *sql)
 {
 	if (sqlite3_exec(db->handle, sql, NULL, NULL, NULL) != SQLITE_OK)
-		return errorf("%s", sqlite3_errmsg(db->handle));
+		return MLK_ERR_DATABASE;
 
 	return 0;
 }
@@ -74,7 +74,7 @@
 	for (size_t i = 0; i < MLK_UTIL_SIZE(table); ++i) {
 		if (property_load(&table[i].prop, db) < 0) {
 			sqlite3_close(db->handle);
-			return errorf("database not initialized correctly");
+			return -1;
 		}
 
 		*table[i].date = strtoull(table[i].prop.value, NULL, 10);
@@ -110,14 +110,14 @@
 		case ' ':
 			break;
 		default:
-			return errorf("invalid format: %c", *args);
+			return MLK_ERR_DATABASE;
 		}
 	}
 
 	return 0;
 
 sqlite3_err:
-	return errorf("%s", sqlite3_errmsg(s->handle));
+	return MLK_ERR_DATABASE;
 }
 
 static int
@@ -127,7 +127,7 @@
 
 	for (int c = 0; args && *args; ++args) {
 		if (c >= ncols)
-			return errorf("too many arguments");
+			return MLK_ERR_DATABASE;
 
 		/* TODO: type check. */
 		switch (*args) {
@@ -148,7 +148,7 @@
 		case ' ':
 			break;
 		default:
-			return errorf("invalid format: %c", *args);
+			return MLK_ERR_DATABASE;
 		}
 	}
 
@@ -189,12 +189,11 @@
 	return verify(db);
 
 sqlite3_err:
-	errorf("%s", sqlite3_errmsg(db->handle));
 	sqlite3_close(db->handle);
 
 	memset(db, 0, sizeof (*db));
 
-	return -1;
+	return MLK_ERR_DATABASE;
 }
 
 int
@@ -277,7 +276,6 @@
 		ret = SAVE_STMT_DONE;
 		break;
 	default:
-		errorf("%s", sqlite3_errmsg(stmt->parent->handle));
 		break;
 	}
 
--- a/libmlk-rpg/mlk/rpg/tileset-file.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-rpg/mlk/rpg/tileset-file.c	Tue Feb 28 13:04:13 2023 +0100
@@ -28,7 +28,6 @@
 
 #include <mlk/core/alloc.h>
 #include <mlk/core/animation.h>
-#include <mlk/core/error.h>
 #include <mlk/core/image.h>
 #include <mlk/core/util.h>
 
@@ -129,7 +128,7 @@
 parse_tilewidth(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "tilewidth|%u", &ctx->tilewidth) != 1 || ctx->tilewidth == 0)
-		return errorf("tilewidth is null");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
@@ -138,7 +137,7 @@
 parse_tileheight(struct context *ctx, const char *line)
 {
 	if (sscanf(line, "tileheight|%u", &ctx->tileheight) != 1 || ctx->tileheight == 0)
-		return errorf("tileheight is null");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
@@ -230,14 +229,14 @@
 parse_image(struct context *ctx, const char *line)
 {
 	char *p;
+	int err;
 
 	if (ctx->tilewidth == 0 || ctx->tileheight == 0)
-		return errorf("missing tile dimensions before image");
+		return MLK_ERR_FORMAT;
 	if (!(p = strchr(line, '|')))
-		return errorf("could not parse image");
-
-	if (mlk_image_open(&ctx->tf->image, mlk_util_pathf("%s/%s", ctx->basedir, p + 1)) < 0)
-		return -1;
+		return MLK_ERR_FORMAT;
+	if ((err = mlk_image_open(&ctx->tf->image, mlk_util_pathf("%s/%s", ctx->basedir, p + 1))) < 0)
+		return err;
 
 	mlk_sprite_init(&ctx->tf->sprite, &ctx->tf->image, ctx->tilewidth, ctx->tileheight);
 	ctx->tileset->sprite = &ctx->tf->sprite;
@@ -291,7 +290,7 @@
 check(const struct tileset *tileset)
 {
 	if (!tileset->sprite)
-		return errorf("missing tileset image");
+		return MLK_ERR_FORMAT;
 
 	return 0;
 }
--- a/libmlk-ui/mlk/ui/label.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/libmlk-ui/mlk/ui/label.c	Tue Feb 28 13:04:13 2023 +0100
@@ -35,6 +35,7 @@
 	struct mlk_font *font;
 	struct mlk_texture tex;
 	unsigned long color;
+	int err;
 
 	font = label->flags & LABEL_FLAGS_IMPORTANT
 		? t->fonts[THEME_FONT_IMPORTANT]
@@ -45,16 +46,16 @@
 
 	/* Shadow text, only if enabled. */
 	if (label->flags & LABEL_FLAGS_SHADOW) {
-		if (mlk_font_render(font, &tex, label->text, t->colors[THEME_COLOR_SHADOW]) < 0)
-			mlk_panic();
+		if ((err = mlk_font_render(font, &tex, label->text, t->colors[THEME_COLOR_SHADOW])) < 0)
+			mlk_panic(err);
 
 		mlk_texture_draw(&tex, label->x + 1, label->y + 1);
 		mlk_texture_finish(&tex);
 	}
 
 	/* Normal text. */
-	if (mlk_font_render(font, &tex, label->text, color) < 0)
-		mlk_panic();
+	if ((err = mlk_font_render(font, &tex, label->text, color)) < 0)
+		mlk_panic(err);
 
 	mlk_texture_draw(&tex, label->x, label->y);
 	mlk_texture_finish(&tex);
@@ -76,9 +77,10 @@
 	const struct mlk_font *f = label->flags & LABEL_FLAGS_IMPORTANT
 		? t->fonts[THEME_FONT_IMPORTANT]
 		: t->fonts[THEME_FONT_INTERFACE];
+	int err;
 
-	if (mlk_font_query(f, label->text, w, h) < 0)
-		mlk_panic();
+	if ((err = mlk_font_query(f, label->text, w, h)) < 0)
+		mlk_panic(err);
 }
 
 void
--- a/tests/CMakeLists.txt	Tue Feb 28 08:40:35 2023 +0100
+++ b/tests/CMakeLists.txt	Tue Feb 28 13:04:13 2023 +0100
@@ -26,7 +26,6 @@
 	character
 	color
 	drawable
-	error
 	map
 	save
 	save-quest
--- a/tests/test-error.c	Tue Feb 28 08:40:35 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * test-error.c -- test error routines
- *
- * Copyright (c) 2020-2023 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 <mlk/core/error.h>
-
-#include <dt.h>
-
-static void
-test_basics_simple(void)
-{
-	errorf("Failed: %d", 123);
-	DT_EQ_STR("Failed: 123", error());
-}
-
-int
-main(void)
-{
-	DT_RUN(test_basics_simple);
-	DT_SUMMARY();
-}
--- a/tests/test-map.c	Tue Feb 28 08:40:35 2023 +0100
+++ b/tests/test-map.c	Tue Feb 28 13:04:13 2023 +0100
@@ -17,7 +17,6 @@
  */
 
 #include <mlk/core/core.h>
-#include <mlk/core/error.h>
 #include <mlk/core/panic.h>
 #include <mlk/core/sys.h>
 #include <mlk/core/window.h>