changeset 189:f28cb6d04731

Misc: extreme refactoring
author David Demelier <markand@malikania.fr>
date Thu, 25 Oct 2018 21:36:14 +0200
parents 0cecdadfb5c4
children c7ad3adc7305
files CMakeLists.txt examples/js-animation/main.cpp examples/js-font/main.cpp examples/js-image/main.cpp examples/js-sprite/main.cpp examples/js-window/main.cpp libclient-js/CMakeLists.txt libclient-js/malikania/js_animation.cpp libclient-js/malikania/js_animation.hpp libclient-js/malikania/js_animator.cpp libclient-js/malikania/js_animator.hpp libclient-js/malikania/js_client_resources_loader.cpp libclient-js/malikania/js_client_resources_loader.hpp libclient-js/malikania/js_color.cpp libclient-js/malikania/js_color.hpp libclient-js/malikania/js_font.cpp libclient-js/malikania/js_font.hpp libclient-js/malikania/js_image.cpp libclient-js/malikania/js_image.hpp libclient-js/malikania/js_sprite.cpp libclient-js/malikania/js_sprite.hpp libclient-js/malikania/js_window.cpp libclient-js/malikania/js_window.hpp libclient/CMakeLists.txt libclient/assets/dejavu_sans.ttf libclient/malikania/client/animation.hpp libclient/malikania/client/animator.cpp libclient/malikania/client/animator.hpp libclient/malikania/client/button.cpp libclient/malikania/client/button.hpp libclient/malikania/client/client.cpp libclient/malikania/client/client.hpp libclient/malikania/client/color.cpp libclient/malikania/client/color.hpp libclient/malikania/client/connection.cpp libclient/malikania/client/connection.hpp libclient/malikania/client/dispatcher.hpp libclient/malikania/client/font.cpp libclient/malikania/client/font.hpp libclient/malikania/client/image.cpp libclient/malikania/client/image.hpp libclient/malikania/client/key.hpp libclient/malikania/client/label.cpp libclient/malikania/client/label.hpp libclient/malikania/client/loader.cpp libclient/malikania/client/loader.hpp libclient/malikania/client/mouse.hpp libclient/malikania/client/sdl_util.cpp libclient/malikania/client/sdl_util.hpp libclient/malikania/client/sprite.cpp libclient/malikania/client/sprite.hpp libclient/malikania/client/state.hpp libclient/malikania/client/state/lobby_state.cpp libclient/malikania/client/state/lobby_state.hpp libclient/malikania/client/state/login_state.cpp libclient/malikania/client/state/login_state.hpp libclient/malikania/client/state/map_state.cpp libclient/malikania/client/state/map_state.hpp libclient/malikania/client/theme.cpp libclient/malikania/client/theme.hpp libclient/malikania/client/widget.cpp libclient/malikania/client/widget.hpp libclient/malikania/client/window.cpp libclient/malikania/client/window.hpp libcommon-js/CMakeLists.txt libcommon-js/malikania/duk.cpp libcommon-js/malikania/duk.hpp libcommon-js/malikania/duktape.hpp libcommon-js/malikania/js_elapsed_timer.cpp libcommon-js/malikania/js_elapsed_timer.hpp libcommon-js/malikania/js_line.cpp libcommon-js/malikania/js_line.hpp libcommon-js/malikania/js_point.cpp libcommon-js/malikania/js_point.hpp libcommon-js/malikania/js_rectangle.cpp libcommon-js/malikania/js_rectangle.hpp libcommon-js/malikania/js_resources_loader.cpp libcommon-js/malikania/js_resources_loader.hpp libcommon-js/malikania/js_size.cpp libcommon-js/malikania/js_size.hpp libcommon/CMakeLists.txt libcommon/malikania/error/auth_error.cpp libcommon/malikania/error/auth_error.hpp libcommon/malikania/error/error.hpp libcommon/malikania/game.hpp libcommon/malikania/line.hpp libcommon/malikania/loader.cpp libcommon/malikania/loader.hpp libcommon/malikania/locator.cpp libcommon/malikania/locator.hpp libcommon/malikania/point.hpp libcommon/malikania/rectangle.hpp libcommon/malikania/size.hpp libcommon/malikania/socket.cpp libcommon/malikania/socket.hpp libcommon/malikania/tileset.hpp libcommon/malikania/unicode.cpp libcommon/malikania/unicode.hpp libcommon/malikania/util.cpp libcommon/malikania/util.hpp libdb/CMakeLists.txt libdb/malikania/db/account.cpp libdb/malikania/db/account.hpp libdb/malikania/db/character.cpp libdb/malikania/db/character.hpp libdb/malikania/db/database.cpp libdb/malikania/db/database.hpp libdb/malikania/db/model.hpp libdb/malikania/db/spell.cpp libdb/malikania/db/spell.hpp libmlk-client-js-test/malikania/client/js/test/js_api_fixture.hpp libmlk-client-js/CMakeLists.txt libmlk-client-js/malikania/client/js/animation_js_api.cpp libmlk-client-js/malikania/client/js/animation_js_api.hpp libmlk-client-js/malikania/client/js/animator_js_api.cpp libmlk-client-js/malikania/client/js/animator_js_api.hpp libmlk-client-js/malikania/client/js/color_js_api.cpp libmlk-client-js/malikania/client/js/color_js_api.hpp libmlk-client-js/malikania/client/js/font_js_api.cpp libmlk-client-js/malikania/client/js/font_js_api.hpp libmlk-client-js/malikania/client/js/image_js_api.cpp libmlk-client-js/malikania/client/js/image_js_api.hpp libmlk-client-js/malikania/client/js/loader_js_api.cpp libmlk-client-js/malikania/client/js/loader_js_api.hpp libmlk-client-js/malikania/client/js/sprite_js_api.cpp libmlk-client-js/malikania/client/js/sprite_js_api.hpp libmlk-client-js/malikania/client/js/window_js_api.cpp libmlk-client-js/malikania/client/js/window_js_api.hpp libmlk-client/CMakeLists.txt libmlk-client/assets/dejavu_sans.ttf libmlk-client/malikania/client/animation.hpp libmlk-client/malikania/client/animator.cpp libmlk-client/malikania/client/animator.hpp libmlk-client/malikania/client/button.cpp libmlk-client/malikania/client/button.hpp libmlk-client/malikania/client/client.cpp libmlk-client/malikania/client/client.hpp libmlk-client/malikania/client/color.cpp libmlk-client/malikania/client/color.hpp libmlk-client/malikania/client/connection.cpp libmlk-client/malikania/client/connection.hpp libmlk-client/malikania/client/dispatcher.hpp libmlk-client/malikania/client/font.cpp libmlk-client/malikania/client/font.hpp libmlk-client/malikania/client/image.cpp libmlk-client/malikania/client/image.hpp libmlk-client/malikania/client/key.hpp libmlk-client/malikania/client/label.cpp libmlk-client/malikania/client/label.hpp libmlk-client/malikania/client/loader.cpp libmlk-client/malikania/client/loader.hpp libmlk-client/malikania/client/mouse.hpp libmlk-client/malikania/client/sdl_util.cpp libmlk-client/malikania/client/sdl_util.hpp libmlk-client/malikania/client/sprite.cpp libmlk-client/malikania/client/sprite.hpp libmlk-client/malikania/client/state.hpp libmlk-client/malikania/client/state/lobby_state.cpp libmlk-client/malikania/client/state/lobby_state.hpp libmlk-client/malikania/client/state/login_state.cpp libmlk-client/malikania/client/state/login_state.hpp libmlk-client/malikania/client/state/map_state.cpp libmlk-client/malikania/client/state/map_state.hpp libmlk-client/malikania/client/theme.cpp libmlk-client/malikania/client/theme.hpp libmlk-client/malikania/client/widget.cpp libmlk-client/malikania/client/widget.hpp libmlk-client/malikania/client/window.cpp libmlk-client/malikania/client/window.hpp libmlk-db/CMakeLists.txt libmlk-db/malikania/db/account.cpp libmlk-db/malikania/db/account.hpp libmlk-db/malikania/db/character.cpp libmlk-db/malikania/db/character.hpp libmlk-db/malikania/db/database.cpp libmlk-db/malikania/db/database.hpp libmlk-db/malikania/db/model.hpp libmlk-db/malikania/db/spell.cpp libmlk-db/malikania/db/spell.hpp libmlk-js-test/malikania/js/test/js_api_fixture.hpp libmlk-js/CMakeLists.txt libmlk-js/malikania/js/duk.cpp libmlk-js/malikania/js/duk.hpp libmlk-js/malikania/js/elapsed_timer_js_api.cpp libmlk-js/malikania/js/elapsed_timer_js_api.hpp libmlk-js/malikania/js/line_js_api.cpp libmlk-js/malikania/js/line_js_api.hpp libmlk-js/malikania/js/loader_js_api.cpp libmlk-js/malikania/js/loader_js_api.hpp libmlk-js/malikania/js/point_js_api.cpp libmlk-js/malikania/js/point_js_api.hpp libmlk-js/malikania/js/rectangle_js_api.cpp libmlk-js/malikania/js/rectangle_js_api.hpp libmlk-js/malikania/js/size_js_api.cpp libmlk-js/malikania/js/size_js_api.hpp libmlk-server/CMakeLists.txt libmlk-server/malikania/server/client.cpp libmlk-server/malikania/server/client.hpp libmlk-server/malikania/server/hash.hpp libmlk-server/malikania/server/net/auth_handler.cpp libmlk-server/malikania/server/net/auth_handler.hpp libmlk-server/malikania/server/net/handler.hpp libmlk-server/malikania/server/server.cpp libmlk-server/malikania/server/server.hpp libmlk/CMakeLists.txt libmlk/malikania/error/auth_error.cpp libmlk/malikania/error/auth_error.hpp libmlk/malikania/error/error.hpp libmlk/malikania/game.hpp libmlk/malikania/line.hpp libmlk/malikania/loader.cpp libmlk/malikania/loader.hpp libmlk/malikania/locator.cpp libmlk/malikania/locator.hpp libmlk/malikania/point.hpp libmlk/malikania/rectangle.hpp libmlk/malikania/size.hpp libmlk/malikania/socket.cpp libmlk/malikania/socket.hpp libmlk/malikania/tileset.hpp libmlk/malikania/unicode.cpp libmlk/malikania/unicode.hpp libmlk/malikania/util.cpp libmlk/malikania/util.hpp libserver/CMakeLists.txt libserver/malikania/server/client.cpp libserver/malikania/server/client.hpp libserver/malikania/server/hash.hpp libserver/malikania/server/net/auth_handler.cpp libserver/malikania/server/net/auth_handler.hpp libserver/malikania/server/net/handler.hpp libserver/malikania/server/server.cpp libserver/malikania/server/server.hpp tests/libclient/js-color/main.cpp tests/libcommon/js-elapsed-timer/main.cpp tests/libcommon/js-line/main.cpp tests/libcommon/js-point/main.cpp tests/libcommon/js-rectangle/main.cpp tests/libcommon/js-size/main.cpp tests/libcommon/size/CMakeLists.txt tests/libcommon/util/CMakeLists.txt tests/tools/tileset/CMakeLists.txt tools/tileset/CMakeLists.txt
diffstat 243 files changed, 19144 insertions(+), 19475 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ b/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -48,12 +48,12 @@
 add_subdirectory(examples)
 add_subdirectory(extern)
 add_subdirectory(docs)
-add_subdirectory(libdb)
-add_subdirectory(libcommon)
-add_subdirectory(libcommon-js)
-add_subdirectory(libclient)
-add_subdirectory(libclient-js)
-add_subdirectory(libserver)
+add_subdirectory(libmlk)
+add_subdirectory(libmlk-client)
+add_subdirectory(libmlk-client-js)
+add_subdirectory(libmlk-db)
+add_subdirectory(libmlk-js)
+add_subdirectory(libmlk-server)
 add_subdirectory(client)
 add_subdirectory(server)
 
--- a/examples/js-animation/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/examples/js-animation/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -22,19 +22,21 @@
 
 #include <boost/timer/timer.hpp>
 
+#include <malikania/locator.hpp>
+
 #include <malikania/client/loader.hpp>
 
-#include <malikania/js_client_resources_loader.hpp>
-#include <malikania/js_animation.hpp>
-#include <malikania/js_animator.hpp>
-#include <malikania/js_window.hpp>
-#include <malikania/locator.hpp>
+#include <malikania/client/js/loader_js_api.hpp>
+#include <malikania/client/js/animation_js_api.hpp>
+#include <malikania/client/js/animator_js_api.hpp>
+#include <malikania/client/js/window_js_api.hpp>
 
 using namespace std::chrono_literals;
 
-using namespace mlk;
+using namespace mlk::client::js;
 using namespace mlk::client;
-using namespace mlk::duk;
+using namespace mlk::js::duk;
+using namespace mlk;
 
 int main()
 {
--- a/examples/js-font/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/examples/js-font/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -24,15 +24,16 @@
 
 #include <malikania/client/loader.hpp>
 
-#include <malikania/js_client_resources_loader.hpp>
-#include <malikania/js_font.hpp>
-#include <malikania/js_window.hpp>
+#include <malikania/client/js/font_js_api.hpp>
+#include <malikania/client/js/loader_js_api.hpp>
+#include <malikania/client/js/window_js_api.hpp>
 
 using namespace std::chrono_literals;
 
 using namespace mlk;
+using namespace mlk::client::js;
 using namespace mlk::client;
-using namespace mlk::duk;
+using namespace mlk::js::duk;
 
 void basic(duk_context* ctx)
 {
--- a/examples/js-image/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/examples/js-image/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -24,15 +24,16 @@
 
 #include <malikania/client/loader.hpp>
 
-#include <malikania/js_client_resources_loader.hpp>
-#include <malikania/js_image.hpp>
-#include <malikania/js_window.hpp>
+#include <malikania/client/js/image_js_api.hpp>
+#include <malikania/client/js/loader_js_api.hpp>
+#include <malikania/client/js/window_js_api.hpp>
 
 using namespace std::chrono_literals;
 
 using namespace mlk;
+using namespace mlk::client::js;
 using namespace mlk::client;
-using namespace mlk::duk;
+using namespace mlk::js::duk;
 
 void basic(duk_context* ctx)
 {
--- a/examples/js-sprite/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/examples/js-sprite/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -24,16 +24,17 @@
 
 #include <malikania/client/loader.hpp>
 
-#include <malikania/js_client_resources_loader.hpp>
-#include <malikania/js_image.hpp>
-#include <malikania/js_sprite.hpp>
-#include <malikania/js_window.hpp>
+#include <malikania/client/js/loader_js_api.hpp>
+#include <malikania/client/js/image_js_api.hpp>
+#include <malikania/client/js/sprite_js_api.hpp>
+#include <malikania/client/js/window_js_api.hpp>
 
 using namespace std::chrono_literals;
 
 using namespace mlk;
+using namespace mlk::client::js;
 using namespace mlk::client;
-using namespace mlk::duk;
+using namespace mlk::js::duk;
 
 void basic(duk_context* ctx)
 {
--- a/examples/js-window/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/examples/js-window/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -20,13 +20,14 @@
 #include <iostream>
 #include <thread>
 
-#include <malikania/js_window.hpp>
+#include <malikania/client/js/window_js_api.hpp>
 
 using namespace std::chrono_literals;
 
 using namespace mlk;
+using namespace mlk::client::js;
 using namespace mlk::client;
-using namespace mlk::duk;
+using namespace mlk::js::duk;
 
 void basic(duk_context* ctx)
 {
--- a/libclient-js/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-client-js)
-
-set(
-	HEADERS
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_animation.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_animator.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_client_resources_loader.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_color.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_font.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_image.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_sprite.hpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_window.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_animation.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_animator.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_client_resources_loader.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_color.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_font.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_image.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_sprite.cpp
-	${libmlk-client-js_SOURCE_DIR}/malikania/js_window.cpp
-)
-
-malikania_define_library(
-	PROJECT libmlk-client-js
-	TARGET libmlk-client-js
-	SOURCES ${HEADERS} ${SOURCES}
-	LIBRARIES libmlk-client libmlk-common-js
-	PUBLIC_INCLUDES
-		$<BUILD_INTERFACE:${libmlk-client-js_SOURCE_DIR}/malikania>
-)
--- a/libclient-js/malikania/js_animation.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * js_animation.cpp -- animation description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/client/animation.hpp>
-#include <malikania/client/loader.hpp>
-
-#include "js_client_resources_loader.hpp"
-#include "js_animation.hpp"
-
-namespace mlk {
-
-namespace client {
-
-namespace {
-
-const std::string_view signature("\xff""\xff""Malikania.Animation.self");
-
-auto Animation_constructor(duk_context* ctx) -> duk_ret_t
-{
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "animation must be new-constructed");
-
-	try {
-		auto anim = duk::require<client::loader>(ctx, 0).load_animation(duk::require<std::string_view>(ctx, 0));
-
-		duk_push_this(ctx);
-		duk_push_pointer(ctx, new animation(std::move(anim)));
-		duk_put_prop_string(ctx, -2, signature.data());
-		duk_pop(ctx);
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Animation_destructor(duk_context* ctx) -> duk_ret_t
-{
-	duk_get_prop_string(ctx, 0, signature.data());
-	delete static_cast<animation*>(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, signature.data());
-
-	return 0;
-}
-
-} // !namespace
-
-void load_animation_api(duk_context* ctx)
-{
-	assert(ctx);
-
-	duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Animation_constructor, 1);
-	duk_push_object(ctx);
-	duk_push_c_function(ctx, Animation_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Animation");
-	duk_pop(ctx);
-}
-
-} // !client
-
-namespace duk {
-
-using namespace client;
-
-auto type_traits<animation>::require(duk_context* ctx, duk_idx_t index) -> animation&
-{
-	assert(ctx);
-
-	duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Animation object");
-
-	return *static_cast<animation*>(ptr);
-}
-
-} // !duk
-
-} // !mlk
--- a/libclient-js/malikania/js_animation.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * js_animation.hpp -- animation description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_ANIMATION_HPP
-#define MALIKANIA_JS_ANIMATION_HPP
-
-#include "duk.hpp"
-
-namespace mlk {
-
-namespace client {
-
-struct animation;
-
-/**
- * Load Malikania.Animation API into the context.
- *
- * \warning you need to put a mlk::client::loader before use
- * \param ctx the context
- */
-void load_animation_api(duk_context* ctx);
-
-} // !client
-
-namespace duk {
-
-template <>
-struct type_traits<client::animation> {
-	/**
-	 * Get an animation object at the given index or raise a Javascript
-	 * error.
-	 *
-	 * \pre ctx != nullptr
-	 * \param ctx the context
-	 * \param index the value index
-	 * \return the Animation object
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> client::animation&;
-};
-
-} // !duk
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_ANIMATION_HPP
--- a/libclient-js/malikania/js_animator.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * js_animator.cpp -- animation drawing object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-#include <string>
-
-#include <malikania/client/animator.hpp>
-
-#include "js_animation.hpp"
-#include "js_animator.hpp"
-#include "js_point.hpp"
-#include "js_window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-namespace {
-
-const std::string_view signature("\xff""\xff""Malikania.Animator.self");
-
-auto self(duk_context* ctx) -> animator&
-{
-        duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Animator object");
-
-	return *static_cast<client::animator*>(ptr);
-}
-
-auto Animator_constructor(duk_context* ctx) -> duk_ret_t
-{
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "Animator must be new-constructed");
-
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, new animator(duk::require<animation>(ctx, 0)));
-	duk_put_prop_string(ctx, -2, signature.data());
-
-	// Be sure animation get not collected before.
-	duk_dup(ctx, 0);
-	duk_put_prop_string(ctx, -2, "\xff""\xff""animation");
-	duk_pop(ctx);
-
-	return 0;
-}
-
-auto Animator_destructor(duk_context* ctx) -> duk_ret_t
-{
-	duk_get_prop_string(ctx, 0, signature.data());
-	delete static_cast<animator*>(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, signature.data());
-
-	return 0;
-}
-
-auto Animator_draw(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).draw(duk::require<window>(ctx, 0), duk::get<point>(ctx, 1));
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Animator_update(duk_context* ctx) -> duk_ret_t
-{
-	self(ctx).update();
-
-	return 0;
-}
-
-const duk_function_list_entry methods[] = {
-	{ "draw",       Animator_draw,          2 },
-	{ "update",     Animator_update,        0 },
-	{ nullptr,      nullptr,                0 }
-};
-
-} // !namespace
-
-void load_animator_api(duk_context* ctx)
-{
-	assert(ctx);
-
-        duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Animator_constructor, 1);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, Animator_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Animator");
-	duk_pop(ctx);
-}
-
-} // !client
-
-} // !mlk
--- a/libclient-js/malikania/js_animator.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * js_animator.hpp -- animation drawing object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_ANIMATOR_HPP
-#define MALIKANIA_JS_ANIMATOR_HPP
-
-#include "duk.hpp"
-
-namespace mlk::client {
-
-/**
- * Load Malikania.ElapsedTimer API into the context.
- *
- * \param ctx the context
- */
-void load_animator_api(duk_context* ctx);
-
-} // !mlk::client
-
-#endif // !MALIKANIA_JS_ANIMATOR_HPP
--- a/libclient-js/malikania/js_client_resources_loader.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * js_client_resources_loader.cpp -- client resources loader (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/client/loader.hpp>
-
-#include "js_client_resources_loader.hpp"
-#include "js_resources_loader.hpp"
-
-namespace mlk {
-
-namespace {
-
-const std::string_view variable("\xff""\xff""Malikania.ClientLoader");
-
-} // !namespace
-
-namespace duk {
-
-using namespace client;
-
-void type_traits<client::loader>::put(duk_context* ctx, client::loader& loader)
-{
-	assert(ctx);
-
-	// Also store as parent.
-        type_traits<mlk::loader>::put(ctx, loader);
-
-        duk::stack_guard sa(ctx);
-
-	duk_push_pointer(ctx, &loader);
-	duk_put_global_string(ctx, variable.data());
-}
-
-auto type_traits<client::loader>::require(duk_context* ctx, duk_idx_t) -> client::loader&
-{
-	assert(ctx);
-
-        duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, variable.data());
-	auto ptr = static_cast<client::loader*>(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-
-	return *static_cast<client::loader*>(ptr);
-}
-
-} // !duk
-
-} // !mlk
--- a/libclient-js/malikania/js_client_resources_loader.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * js_client_resources_loader.hpp -- client resources loader (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_CLIENT_RESOURCES_LOADER_H
-#define MALIKANIA_JS_CLIENT_RESOURCES_LOADER_H
-
-#include "duk.hpp"
-
-namespace mlk {
-
-namespace client {
-
-class loader;
-
-} // !client
-
-namespace duk {
-
-template <>
-struct type_traits<client::loader> {
-        static void put(duk_context* ctx, client::loader& loader);
-
-        static auto require(duk_context* ctx, duk_idx_t) -> client::loader&;
-};
-
-} // !duk
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_CLIENT_RESOURCES_LOADER_H
--- a/libclient-js/malikania/js_color.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-/*
- * js_color.cpp -- color description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/client/color.hpp>
-
-#include "js_color.hpp"
-
-namespace mlk {
-
-namespace client {
-
-namespace {
-
-auto parse_string(duk_context* ctx, duk_idx_t index, bool required) -> color
-{
-	assert(duk_is_string(ctx, index));
-
-	color ret;
-
-	try {
-		ret = color::from_name(duk_get_string(ctx, index));
-	} catch (const std::exception &ex) {
-		if (required)
-			duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return ret;
-}
-
-auto parse_object(duk_context* ctx, duk_idx_t index, bool required) -> color
-{
-	assert(duk_is_object(ctx, index));
-
-	duk_get_prop_string(ctx, index, "red");
-	const auto red = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "green");
-	const auto green = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "blue");
-	const auto blue = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "alpha");
-	auto alpha = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-
-        if (required) {
-                if (!red)
-                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'red' property");
-                if (!green)
-                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'green' property");
-                if (!blue)
-                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'blue' property");
-        }
-
-        if (!alpha)
-                alpha = 255;
-
-	return {
-                static_cast<std::uint8_t>(*red),
-                static_cast<std::uint8_t>(*green),
-                static_cast<std::uint8_t>(*blue),
-                static_cast<std::uint8_t>(*alpha)
-        };
-}
-
-auto parse(duk_context* ctx, duk_idx_t index, bool required, color ret = {}) -> color
-{
-	switch (duk_get_type(ctx, index)) {
-	case DUK_TYPE_STRING:
-		ret = parse_string(ctx, index, required);
-		break;
-	case DUK_TYPE_OBJECT:
-		ret = parse_object(ctx, index, required);
-		break;
-	default:
-		if (required)
-			duk_error(ctx, DUK_ERR_TYPE_ERROR, "Color (string, object) expected");
-
-		break;
-	}
-
-	return ret;
-}
-
-auto Color_constructor(duk_context* ctx) -> duk_ret_t
-{
-	color obj;
-
-	/*
-	 * The constructor allows an additional signature that takes 4 number
-	 * arguments, otherwise use the literal parsing functions.
-	 */
-	if (duk_get_top(ctx) >= 3) {
-		// Alpha is optional.
-                obj.red = duk::require<unsigned>(ctx, 0);
-                obj.green = duk::require<unsigned>(ctx, 1);
-                obj.blue = duk::require<unsigned>(ctx, 2);
-		obj.alpha = duk_is_number(ctx, 3) ? duk::get<unsigned>(ctx, 3) : 255;
-	} else if (duk_get_top(ctx) == 1)
-		obj = duk::require<color>(ctx, 0);
-
-	duk_ret_t ret;
-
-	// Allow both constructor and non constructor calls.
-	if (duk_is_constructor_call(ctx)) {
-		duk_push_this(ctx);
-                duk::put(ctx, obj);
-		duk_pop(ctx);
-		ret = 0;
-	} else {
-                duk::push(ctx, obj);
-		ret = 1;
-	}
-
-	return ret;
-}
-
-} // !namespace
-
-void load_color_api(duk_context* ctx)
-{
-        duk::stack_guard sa(ctx, 0);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Color_constructor, DUK_VARARGS);
-	duk_put_prop_string(ctx, -2, "Color");
-	duk_pop(ctx);
-}
-
-} // !client
-
-namespace duk {
-
-using namespace client;
-
-auto type_traits<color>::get(duk_context* ctx, duk_idx_t index) -> color
-{
-        return parse(ctx, index, false);
-}
-
-auto type_traits<color>::require(duk_context* ctx, duk_idx_t index) -> color
-{
-        return parse(ctx, index, true);
-}
-
-auto type_traits<color>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<color>
-{
-        try {
-                return parse(ctx, index, false);
-        } catch (...) {
-                return color();
-        }
-}
-
-void type_traits<color>::push(duk_context* ctx, const color& color)
-{
-        duk::stack_guard sa(ctx, 1);
-
-	duk_push_object(ctx);
-	put(ctx, color);
-}
-
-void type_traits<color>::put(duk_context* ctx, const color& color)
-{
-	assert(duk_is_object(ctx, -1));
-
-        duk::stack_guard sa(ctx, 0);
-
-        duk::push<unsigned>(ctx, color.red);
-	duk_put_prop_string(ctx, -2, "red");
-        duk::push<unsigned>(ctx, color.green);
-	duk_put_prop_string(ctx, -2, "green");
-        duk::push<unsigned>(ctx, color.blue);
-	duk_put_prop_string(ctx, -2, "blue");
-        duk::push<unsigned>(ctx, color.alpha);
-	duk_put_prop_string(ctx, -2, "alpha");
-}
-
-} // !duk
-
-} // !mlk
--- a/libclient-js/malikania/js_color.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * js_color.hpp -- color description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_COLOR_HPP
-#define MALIKANIA_JS_COLOR_HPP
-
-/**
- * \file js_color.hpp
- * \brief JavaScript binding for color.
- *
- * Colors can be created from plain JavaScript object.
- *
- * ````
- * {
- *   red: 0,
- *   green: 255,
- *   blue: 255,
- *   alpha: 255
- * }
- * ````
- *
- * It can also takes strings like "#rrggbbaa" and SVG names.
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-namespace client {
-
-struct color;
-
-/**
- * Load Malikania.ElapsedTimer API into the context.
- *
- * \param ctx the context
- */
-void load_color_api(duk_context* ctx);
-
-} // !client
-
-namespace duk {
-
-template <>
-struct type_traits<client::color> {
-        /**
-         * Get a color.
-         *
-         * May return a default value or a color with adjusted components.
-         *
-         * \param ctx the context
-         * \param index the index
-         */
-        static auto get(duk_context* ctx, duk_idx_t index) -> client::color;
-
-        /**
-         * Require a color.
-         *
-         * If the color has any invalid component, raise a JavaScript error.
-         *
-         * \param ctx the context
-         * \param index the index
-         */
-        static auto require(duk_context* ctx, duk_idx_t index) -> client::color;
-
-        /**
-         * Like get, but return the default value only if the value at the given
-         * index is not an object or not a string, otherwise, adjust invalid values.
-         *
-         * \param ctx the context
-         * \param index the index
-         */
-        static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<client::color>;
-
-        /**
-         * Push the color as object.
-         *
-         * \param ctx the context
-         * \param color the color
-         */
-        static void push(duk_context* ctx, const client::color& color);
-
-        /**
-         * Put the color properties into the object at the top of the stack.
-         *
-         * \pre the top value must be an object
-         * \param ctx the context
-         * \param color the color
-         */
-        static void put(duk_context* ctx, const client::color& color);
-};
-
-} // !duk
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_COLOR_HPP
--- a/libclient-js/malikania/js_font.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/*
- * js_font.cpp -- font object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 SOTWARE 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 <cassert>
-
-#include <malikania/size.hpp>
-
-#include <malikania/client/loader.hpp>
-#include <malikania/client/font.hpp>
-
-#include "js_client_resources_loader.hpp"
-#include "js_font.hpp"
-#include "js_size.hpp"
-
-namespace mlk {
-
-namespace client {
-
-namespace {
-
-const std::string signature("\xff""\xff""Malikania.Font.self");
-
-auto self(duk_context* ctx) -> font&
-{
-        duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.c_str());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a font object");
-
-	return *static_cast<font*>(ptr);
-}
-
-auto Font_constructor(duk_context* ctx) -> duk_ret_t
-{
-        duk::stack_guard sa(ctx);
-
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "font must be new-constructed");
-
-	try {
-		auto id = duk::require<std::string_view>(ctx, 0);
-		auto size = duk::require<unsigned>(ctx, 1);
-
-		duk_push_this(ctx);
-		duk_push_pointer(ctx, new font(duk::require<loader>(ctx, 0).load_font(id, size)));
-		duk_put_prop_string(ctx, -2, signature.c_str());
-		duk_pop(ctx);
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Font_prototype_clip(duk_context* ctx) -> duk_ret_t
-{
-	try {
-                duk::push(ctx, self(ctx).clip(duk::require<std::string>(ctx, 0)));
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 1;
-}
-
-const duk_function_list_entry methods[] = {
-	{ "clip",       Font_prototype_clip,    1 },
-	{ nullptr,      nullptr,                0 }
-};
-
-} // !namespace
-
-void load_font_api(duk_context* ctx)
-{
-        duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Font_constructor, 2);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Font");
-	duk_pop(ctx);
-}
-
-} // !client
-
-namespace duk {
-
-using namespace client;
-
-auto type_traits<font>::require(duk_context* ctx, duk_idx_t index) -> font&
-{
-	assert(ctx);
-
-        duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Font object");
-
-	return *static_cast<font*>(ptr);
-}
-
-} // !duk
-
-} // !mlk
--- a/libclient-js/malikania/js_font.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * js_font.hpp -- font object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_FONT_HPP
-#define MALIKANIA_JS_FONT_HPP
-
-#include "duk.hpp"
-
-namespace mlk {
-
-namespace client {
-
-class font;
-
-/**
- * Load Malikania.Font API into the context.
- *
- * \param ctx the context
- */
-void load_font_api(duk_context* ctx);
-
-} // !client
-
-namespace duk {
-
-template <>
-struct type_traits<client::font> {
-        static auto require(duk_context* ctx, duk_idx_t index) -> client::font&;
-};
-
-} // !duk
-
-} // !mlk:
-
-#endif // !MALIKANIA_JS_FONT_HPP
--- a/libclient-js/malikania/js_image.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * js_image.cpp -- image object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/client/loader.hpp>
-#include <malikania/client/image.hpp>
-#include <malikania/client/window.hpp>
-
-#include "js_client_resources_loader.hpp"
-#include "js_image.hpp"
-#include "js_point.hpp"
-#include "js_rectangle.hpp"
-#include "js_size.hpp"
-#include "js_window.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-const std::string_view signature("\xff""\xff""Malikania.Image.self");
-
-auto self(duk_context* ctx) -> image&
-{
-        duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Image object");
-
-	return *static_cast<image*>(ptr);
-}
-
-auto Image_property_get_size(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		duk::push(ctx, self(ctx).get_size());
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 1;
-}
-
-auto Image_prototype_draw(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		auto& image = self(ctx);
-		auto& win= duk::require<window>(ctx, 0);
-
-		if (duk_get_top(ctx) == 2)
-			image.draw(win, duk::get<point>(ctx, 1));
-		else if (duk_get_top(ctx) == 3)
-			image.draw(win, duk::get<rectangle>(ctx, 1), duk::get<rectangle>(ctx, 2));
-		else
-			duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid number of arguments: #%d", duk_get_top(ctx));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Image_constructor(duk_context* ctx) -> duk_ret_t
-{
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "Image must be new-constructed");
-
-	try {
-		duk_push_this(ctx);
-		duk_push_pointer(ctx, new image(duk::require<loader>(ctx, 0)
-                        .load_image(duk::require<std::string_view>(ctx, 0))));
-		duk_put_prop_string(ctx, -2, signature.data());
-		duk_push_string(ctx, "size");
-		duk_push_c_function(ctx, Image_property_get_size, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-		duk_pop(ctx);
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-const duk_function_list_entry methods[] = {
-	{ "draw",       Image_prototype_draw,   DUK_VARARGS     },
-	{ nullptr,      nullptr,                0               }
-};
-
-} // !namespace
-
-void load_image_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Image_constructor, 1);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Image");
-	duk_pop(ctx);
-}
-
-} // !mlk::client
--- a/libclient-js/malikania/js_image.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * js_image.hpp -- image object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_IMAGE_HPP
-#define MALIKANIA_JS_IMAGE_HPP
-
-#include "duk.hpp"
-
-namespace mlk::client {
-
-/**
- * Load Malikania.Image API into the context.
- *
- * \param ctx the context
- */
-void load_image_api(duk_context* ctx);
-
-} // !mlk::client
-
-#endif // !MALIKANIA_JS_IMAGE_HPP
--- a/libclient-js/malikania/js_sprite.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * js_sprite.cpp -- sprite object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <malikania/client/loader.hpp>
-#include <malikania/client/sprite.hpp>
-#include <malikania/client/window.hpp>
-
-#include "js_client_resources_loader.hpp"
-#include "js_point.hpp"
-#include "js_size.hpp"
-#include "js_sprite.hpp"
-#include "js_window.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-const std::string signature("\xff""\xff""Malikania.Sprite.self");
-
-auto self(duk_context *ctx) -> sprite&
-{
-        duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.c_str());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Sprite object");
-
-	return *static_cast<sprite*>(ptr);
-}
-
-auto Sprite_property_get_cell(duk_context* ctx) -> duk_ret_t
-{
-	return duk::push(ctx, self(ctx).get_cell());
-}
-
-auto Sprite_property_get_columns(duk_context* ctx) -> duk_ret_t
-{
-	return duk::push(ctx, self(ctx).get_columns());
-}
-
-auto Sprite_property_get_margins(duk_context* ctx) -> duk_ret_t
-{
-	return duk::push(ctx, self(ctx).get_margin());
-}
-
-auto Sprite_property_get_rows(duk_context* ctx) -> duk_ret_t
-{
-	return duk::push(ctx, self(ctx).get_rows());
-}
-
-auto Sprite_property_get_space(duk_context *ctx) -> duk_ret_t
-{
-	return duk::push(ctx, self(ctx).get_space());
-}
-
-auto Sprite_constructor(duk_context *ctx) -> duk_ret_t
-{
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "Sprite must be new-constructed");
-
-	try {
-		auto sp = duk::require<loader>(ctx, 0).load_sprite(duk_require_string(ctx, 0));
-
-		duk_push_this(ctx);
-		duk_push_pointer(ctx, new sprite(std::move(sp)));
-		duk_put_prop_string(ctx, -2, signature.data());
-
-		// Cell.
-		duk_push_string(ctx, "cell");
-		duk_push_c_function(ctx, Sprite_property_get_cell, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-
-		// Columns.
-		duk_push_string(ctx, "columns");
-		duk_push_c_function(ctx, Sprite_property_get_columns, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-
-		// Margins.
-		duk_push_string(ctx, "margins");
-		duk_push_c_function(ctx, Sprite_property_get_margins, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-
-		// Rows.
-		duk_push_string(ctx, "rows");
-		duk_push_c_function(ctx, Sprite_property_get_rows, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-
-		// Space.
-		duk_push_string(ctx, "space");
-		duk_push_c_function(ctx, Sprite_property_get_space, 0);
-		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
-
-		duk_pop(ctx);
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Sprite_prototype_draw(duk_context *ctx) -> duk_ret_t
-{
-	try {
-		auto& sprite = self(ctx);
-		auto& win = duk::require<window>(ctx, 0);
-		auto cell = duk::require<unsigned>(ctx, 1);
-		auto position = duk::require<point>(ctx, 2);
-
-		if (cell >= sprite.get_total())
-			duk_error(ctx, DUK_ERR_RANGE_ERROR, "cell %d is out of range", cell);
-
-		sprite.draw(win, cell, position);
-	} catch (const std::exception &ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-const duk_function_list_entry methods[] = {
-	{ "draw",       Sprite_prototype_draw,  3 },
-	{ nullptr,      nullptr,                0 }
-};
-
-} // !namespace
-
-void load_sprite_api(duk_context* ctx)
-{
-        duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Sprite_constructor, 1);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Sprite");
-	duk_pop(ctx);
-}
-
-} // !mlk::client
--- a/libclient-js/malikania/js_sprite.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * js_sprite.hpp -- sprite object (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_SPRITE_HPP
-#define MALIKANIA_JS_SPRITE_HPP
-
-#include "duk.hpp"
-
-namespace mlk::client {
-
-/**
- * Load Malikania.Sprite API into the context.
- *
- * \param ctx the context
- */
-void load_sprite_api(duk_context* ctx);
-
-} // !mlk::client
-
-#endif // !MALIKANIA_JS_SPRITE_HPP
--- a/libclient-js/malikania/js_window.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,237 +0,0 @@
-/*
- * js_window.cpp -- window management (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/line.hpp>
-#include <malikania/rectangle.hpp>
-
-#include <malikania/client/color.hpp>
-#include <malikania/client/window.hpp>
-
-#include "js_color.hpp"
-#include "js_font.hpp"
-#include "js_line.hpp"
-#include "js_point.hpp"
-#include "js_rectangle.hpp"
-#include "js_window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-namespace {
-
-const std::string_view signature("\xff""\xff""Malikania.Window.self");
-const std::string_view prototype("\xff""\xff""Malikania.Window.prototype");
-
-auto self(duk_context* ctx) -> window&
-{
-        duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Window object");
-
-	return *static_cast<window*>(ptr);
-}
-
-auto Window_constructor(duk_context* ctx) -> duk_ret_t
-{
-        duk::stack_guard sa(ctx);
-
-	if (!duk_is_constructor_call(ctx))
-		duk_error(ctx, DUK_ERR_ERROR, "window must be new-constructed");
-
-	// TODO: add parameters.
-	try {
-		duk_push_this(ctx);
-		duk_push_pointer(ctx, new window);
-		duk_put_prop_string(ctx, -2, signature.data());
-		duk_pop(ctx);
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_clear(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).clear();
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_present(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).present();
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_drawingColor(duk_context* ctx) -> duk_ret_t
-{
-	try {
-                duk::push(ctx, self(ctx).get_drawing_color());
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 1;
-}
-
-auto Window_prototype_drawLine(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).draw_line(duk::get<line>(ctx, 0));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_drawPoint(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).draw_point(duk::get<point>(ctx, 0));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_drawRectangle(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).draw_rectangle(duk::get<rectangle>(ctx, 0));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_drawText(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		auto& win = self(ctx);
-		auto text = duk::require<std::string>(ctx, 0);
-		auto& fnt = duk::require<font>(ctx, 1);
-		auto rect = duk::get<rectangle>(ctx, 2);
-
-		if (!rect.is_null())
-			win.draw_text(text, fnt, rect);
-		else
-			win.draw_text(text, fnt, point{rect.x, rect.y});
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_fillRectangle(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).fill_rectangle(duk::get<rectangle>(ctx, 0));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-auto Window_prototype_setDrawingColor(duk_context* ctx) -> duk_ret_t
-{
-	try {
-		self(ctx).set_drawing_color(duk::get<color>(ctx, 0));
-	} catch (const std::exception& ex) {
-		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-	}
-
-	return 0;
-}
-
-const duk_function_list_entry methods[] = {
-	{ "clear",              Window_prototype_clear,                 0 },
-	{ "drawingColor",       Window_prototype_drawingColor,          0 },
-	{ "drawLine",           Window_prototype_drawLine,              1 },
-	{ "drawPoint",          Window_prototype_drawPoint,             1 },
-	{ "drawRectangle",      Window_prototype_drawRectangle,         1 },
-	{ "drawText",           Window_prototype_drawText,              3 },
-	{ "fillRectangle",      Window_prototype_fillRectangle,         1 },
-	{ "present",            Window_prototype_present,               0 },
-	{ "setDrawingColor",    Window_prototype_setDrawingColor,       1 },
-	{ nullptr,              nullptr,                                0 }
-};
-
-} // !namespace
-
-void load_window_api(duk_context* ctx)
-{
-        duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Window_constructor, 0);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "Window");
-	duk_pop(ctx);
-}
-
-} // !client
-
-namespace duk {
-
-using namespace client;
-
-auto type_traits<window>::require(duk_context* ctx, duk_idx_t index) -> window&
-{
-	assert(ctx);
-
-        duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, signature.data());
-	auto ptr = duk_to_pointer(ctx, -1);
-	duk_pop(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Window object");
-
-	return *static_cast<window*>(ptr);
-}
-
-} // !duk
-
-} // !mlk
--- a/libclient-js/malikania/js_window.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * js_window.hpp -- window management (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_WINDOW_HPP
-#define MALIKANIA_JS_WINDOW_HPP
-
-#include "duk.hpp"
-
-namespace mlk {
-
-namespace client {
-
-class window;
-
-/**
- * Load Malikania.Window API into the context.
- *
- * \param ctx the context
- */
-void load_window_api(duk_context* ctx);
-
-} // !client
-
-namespace duk {
-
-template <>
-struct type_traits<client::window> {
-        static auto require(duk_context* ctx, duk_idx_t index) -> client::window&;
-};
-
-} // !duk
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_WINDOW_HPP
--- a/libclient/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-client)
-
-find_package(Boost REQUIRED COMPONENTS timer)
-
-set(
-	HEADERS
-	${libmlk-client_SOURCE_DIR}/malikania/client/animation.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/animator.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/button.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/client.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/color.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/connection.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/dispatcher.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/font.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/image.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/key.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/label.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/loader.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/mouse.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/sdl_util.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/sprite.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/lobby_state.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/login_state.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/map_state.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/theme.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/widget.hpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/window.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-client_SOURCE_DIR}/malikania/client/animator.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/button.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/client.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/color.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/connection.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/font.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/image.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/label.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/loader.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/sdl_util.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/sprite.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/lobby_state.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/login_state.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/state/map_state.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/theme.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/widget.cpp
-	${libmlk-client_SOURCE_DIR}/malikania/client/window.cpp
-)
-
-find_package(OpenSSL REQUIRED)
-find_package(SDL2 REQUIRED)
-find_package(SDL2_image REQUIRED)
-find_package(SDL2_ttf REQUIRED)
-
-list(
-	APPEND
-	INCLUDES
-		${SDL2_INCLUDE_DIRS}
-		${SDL2_IMAGE_INCLUDE_DIRS}
-		${SDL2_TTF_INCLUDE_DIRS}
-)
-list(
-	APPEND
-	LIBRARIES
-		${SDL2_LIBRARIES}
-		${SDL2_IMAGE_LIBRARIES}
-		${SDL2_TTF_LIBRARIES}
-)
-
-malikania_define_library(
-	PROJECT libmlk-client
-	TARGET libmlk-client
-	SOURCES ${HEADERS} ${SOURCES}
-	ASSETS ${libmlk-client_SOURCE_DIR}/assets/dejavu_sans.ttf
-	PRIVATE_INCLUDES
-		$<BUILD_INTERFACE:${libmlk-client_SOURCE_DIR}/malikania/client>
-	PUBLIC_INCLUDES
-		$<BUILD_INTERFACE:${libmlk-client_SOURCE_DIR}/malikania/client/${WITH_BACKEND_DIR}>
-		$<BUILD_INTERFACE:${libmlk-client_SOURCE_DIR}/malikania>
-		${INCLUDES}
-	LIBRARIES
-		libmlk-common
-		Boost::boost
-		Boost::timer
-		OpenSSL::SSL
-		OpenSSL::Crypto
-		${LIBRARIES}
-)
Binary file libclient/assets/dejavu_sans.ttf has changed
--- a/libclient/malikania/client/animation.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * animation.hpp -- animation description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_ANIMATION_HPP
-#define MALIKANIA_CLIENT_ANIMATION_HPP
-
-/**
- * \file animation.hpp
- * \brief Describe an animation.
- */
-
-#include <cstdint>
-#include <vector>
-#include <utility>
-
-#include "sprite.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Animation description.
- *
- * An animation is composed of a sprite and a set of frames. For example, if
- * your sprite has several images, you can select only the one you want by
- * specifying the index in each frame.
- *
- * \see animator
- */
-struct animation {
-	/**
-	 * \brief Delay between each frames.
-	 */
-	struct frame {
-		std::uint64_t cell{0U};                 //!< sprite cell index
-		std::uint64_t delay{50U};               //!< delay in milliseconds
-	};
-
-	/**
-	 * \brief List of frames.
-	 */
-	using frame_list = std::vector<frame>;
-
-	mlk::client::sprite sprite;                     //!< sprite used
-	mlk::client::animation::frame_list frames;      //!< list of frames
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_ANIMATION_HPP
--- a/libclient/malikania/client/animator.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * animator.cpp -- animation drawing object
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "animation.hpp"
-#include "animator.hpp"
-
-namespace mlk::client {
-
-animator::animator(animation& animation) noexcept
-	 : animation_(animation)
-	 , current_(animation_.frames.begin())
-{
-}
-
-void animator::reset() noexcept
-{
-	timer_.start();
-	current_ = animation_.frames.begin();
-}
-
-void animator::update() noexcept
-{
-	if (current_ == animation_.frames.end())
-		return;
-
-	if (timer_.elapsed().wall / 1000000LL >= static_cast<long long>(current_->delay)) {
-	         current_ ++;
-	         timer_.start();
-	}
-}
-
-void animator::draw(window& window, const point& point)
-{
-	if (current_ != animation_.frames.end())
-		animation_.sprite.draw(window, current_->cell, point);
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/animator.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * animator.hpp -- animation drawing object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_ANIMATOR_HPP
-#define MALIKANIA_CLIENT_ANIMATOR_HPP
-
-/**
- * \file animator.hpp
- * \brief Draw animations.
- */
-
-#include <boost/timer/timer.hpp>
-
-#include "animation.hpp"
-
-namespace mlk {
-
-struct point;
-
-namespace client {
-
-class window;
-
-/**
- * \brief Object for drawing animations.
- *
- * The animator contains an animation and a state.
- */
-class animator {
-private:
-	boost::timer::cpu_timer timer_;
-	animation& animation_;
-	animation::frame_list::const_iterator current_;
-
-public:
-	/**
-	 * Construct an animator.
-	 *
-	 * \pre animation must not be null
-	 * \param animation the animation
-	 */
-	animator(animation& animation) noexcept;
-	
-	/**
-	 * Reset the animator to the beginning.
-	 */
-	void reset() noexcept;
-
-	/**
-	 * Update the animator state.
-	 *
-	 * This function should be called in the main loop to update the cell
-	 * to draw before calling draw().
-	 */
-	void update() noexcept;
-
-	/**
-	 * Draw the animation.
-	 *
-	 * \param window the window
-	 * \param position the position in the window
-	 */
-	void draw(window& window, const point& position);
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_ANIMATOR_HPP
--- a/libclient/malikania/client/button.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * button.cpp -- GUI button element
- *
- * Copyright (c) 2013-2018 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 "button.hpp"
-#include "theme.hpp"
-#include "window.hpp"
-
-namespace mlk::client {
-
-button::button(std::string text)
-	: text_(std::move(text))
-{
-}
-
-auto button::get_text() const noexcept -> const std::string&
-{
-	return text_;
-}
-
-void button::set_text(std::string text) noexcept
-{
-	text_ = std::move(text);
-}
-
-void button::handle_mouse_down(const mouse_click_event& ev)
-{
-	if (ev.button == mouse::left && on_press_)
-		on_press_();
-
-	// TODO: implement release.
-}
-
-void button::draw(window& w, const size& size)
-{
-	(void)w;
-	(void)size;
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/button.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * button.hpp -- GUI button element
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_BUTTON_HPP
-#define MALIKANIA_CLIENT_BUTTON_HPP
-
-/**
- * \file button.hpp
- * \brief GUI button element.
- */
-
-#include <functional>
-#include <string>
-
-#include "widget.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Basic button element.
- */
-class button : public widget {
-public:
-	using on_press_func = std::function<void ()>;
-	using on_release_func = std::function<void ()>;
-
-private:
-	on_press_func on_press_;
-	on_release_func on_released_;
-
-	std::string text_;
-
-public:
-	/**
-	 * Default constructor with optional text.
-	 *
-	 * \param text the text
-	 */
-	button(std::string text = "");
-
-	/**
-	 * Get the button text.
-	 *
-	 * \return the text
-	 */
-	auto get_text() const noexcept -> const std::string&;
-
-	/**
-	 * Set the button text.
-	 *
-	 * \param text the text
-	 */
-	void set_text(std::string text) noexcept;
-
-	/**
-	 * \copydoc widget::handle_mouse_down
-	 */
-	void handle_mouse_down(const mouse_click_event& ev) override;
-
-	/**
-	 * Bring back other functions.
-	 */
-	using widget::draw;
-
-	/**
-	 * \copydoc widget::draw
-	 */
-	void draw(window& w, const size& size) override;
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_BUTTON_HPP
--- a/libclient/malikania/client/client.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-/*
- * client.cpp -- main client game clas
- *
- * Copyright (c) 2013-2018 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 <iostream>
-#include <thread>
-
-#include "client.hpp"
-#include "connection.hpp"
-#include "state.hpp"
-#include "window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-client::client(boost::asio::io_service& service,
-               mlk::client::connection& connection,
-               mlk::client::window& window) noexcept
-	: service_(service)
-	, connection_(connection)
-	, window_(window)
-{
-}
-
-client::~client() noexcept = default;
-
-void client::recv()
-{
-	// Network thread space.
-	connection_.recv([this] (auto code, auto message) {
-		if (!code)
-			recv();
-
-		std::lock_guard<std::mutex> lock(nmutex_);
-
-		nqueue_.push_back(std::bind(&client::handle_message, this, code, message));
-	});
-}
-
-void client::connect(std::string host, std::uint16_t port)
-{
-	service_.post([this, host, port] () {
-		connection_.connect(host, port, [this] (auto code) {
-			// Start receiveing on success.
-			if (!code)
-				recv();
-
-			// Push the result to the main loop queue.
-			std::lock_guard<std::mutex> lock(nmutex_);
-
-			nqueue_.push_back(std::bind(&client::handle_connect, this, code));
-		});
-	});
-}
-
-void client::send(nlohmann::json message)
-{
-	service_.post([this, message] () {
-		// TODO: error checking.
-		connection_.send(std::move(message), nullptr);
-	});
-}
-
-void client::set_state(std::unique_ptr<state> state)
-{
-	state_next_ = std::move(state);
-}
-
-void client::handle_connect(const std::error_code& code)
-{
-	if (state_)
-		state_->handle_connect(code);
-}
-
-void client::handle_message(const std::error_code& code, const nlohmann::json& msg)
-{
-	if (state_)
-		state_->handle_message(code, msg);
-}
-
-void client::handle_key_down(const key_event& ev)
-{
-	if (state_)
-		state_->handle_key_down(ev);
-}
-
-void client::handle_key_up(const key_event& ev)
-{
-	if (state_)
-		state_->handle_key_up(ev);
-}
-
-void client::handle_mouse_down(const mouse_click_event& ev)
-{
-	if (state_)
-		state_->handle_mouse_down(ev);
-}
-
-void client::handle_mouse_up(const mouse_click_event& ev)
-{
-	if (state_)
-		state_->handle_mouse_up(ev);
-}
-
-void client::handle_mouse_wheel(const mouse_wheel_event& ev)
-{
-	if (state_)
-		state_->handle_mouse_wheel(ev);
-}
-
-void client::handle_text(const text_event& ev)
-{
-	if (state_)
-		state_->handle_text(ev);
-}
-
-void client::handle_quit()
-{
-	window_.close();
-	service_.stop();
-}
-
-void client::run()
-{
-	std::thread t([this] () {
-		// TODO: change to an atomic member variable.
-		while (window_.is_open()) {
-			service_.reset();
-			service_.run();
-		}
-	});
-
-	while (window_.is_open()) {
-		// Network events.
-		{
-			std::lock_guard<std::mutex> lock(nmutex_);
-
-			for (auto& ev : nqueue_)
-				ev();
-
-			nqueue_.clear();
-		}
-
-		// State and input events.
-		if (state_next_)
-			state_ = std::move(state_next_);
-
-		window_.poll(*this);
-
-		if (state_) {
-			state_->update(*this);
-			state_->draw(*this);
-		}
-	}
-
-	t.join();
-}
-
-} // !client
-
-} // !mlk
--- a/libclient/malikania/client/client.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-/*
- * client.hpp -- main client game clas
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_CLIENT_HPP
-#define MALIKANIA_CLIENT_CLIENT_HPP
-
-/**
- * \file client.hpp
- * \brief Main client game class.
- */
-
-#include <functional>
-#include <memory>
-#include <mutex>
-#include <vector>
-#include <system_error>
-
-#include <boost/asio.hpp>
-
-#include <json.hpp>
-
-#include "dispatcher.hpp"
-
-namespace mlk {
-
-namespace client {
-
-class connection;
-class state;
-class window;
-
-/**
- * \brief Main client game class
- */
-class client : public dispatcher {
-private:
-	boost::asio::io_service& service_;
-
-	mlk::client::connection& connection_;
-	mlk::client::window& window_;
-
-	std::unique_ptr<state> state_;
-	std::unique_ptr<state> state_next_;
-	std::mutex nmutex_;
-	std::vector<std::function<void ()>> nqueue_;
-
-	void recv();
-
-public:
-	/**
-	 * Construct the client, no connection attempt will be made yet.
-	 *
-	 * \param service the I/O service
-	 * \param connection the connection object
-	 * \param window the window
-	 */
-	client(boost::asio::io_service& service,
-	       mlk::client::connection& connection,
-	       mlk::client::window& window) noexcept;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~client() noexcept;
-
-	/**
-	 * Get the window.
-	 *
-	 * \return the window
-	 */
-	inline const mlk::client::window& window() const noexcept
-	{
-		return window_;
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the window
-	 */
-	inline mlk::client::window& window() noexcept
-	{
-		return window_;
-	}
-
-	/**
-	 * Connect to the server.
-	 *
-	 * This is an alias of connection_.connect(*this, host, port);
-	 *
-	 * \param host the hostname
-	 * \param port the port number
-	 */
-	void connect(std::string host, std::uint16_t port);
-
-	/**
-	 * Send a message.
-	 *
-	 * \param message the message
-	 */
-	void send(nlohmann::json message);
-
-	/**
-	 * Set the client state.
-	 *
-	 * \param state the state
-	 */
-	void set_state(std::unique_ptr<state>);
-
-	/**
-	 * Run the client until the game window is closed.
-	 */
-	void run();
-
-	/**
-	 * \copydoc dispatcher::handle_connect
-	 */
-	void handle_connect(const std::error_code& code) override;
-
-	/**
-	 * \copydoc dispatcher::handle_message
-	 */
-	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
-
-	/**
-	 * \copydoc dispatcher::handle_key_down
-	 */
-	void handle_key_down(const key_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_key_up
-	 */
-	void handle_key_up(const key_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_down
-	 */
-	void handle_mouse_down(const mouse_click_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_up
-	 */
-	void handle_mouse_up(const mouse_click_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_mouse_wheel
-	 */
-	void handle_mouse_wheel(const mouse_wheel_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_text
-	 */
-	void handle_text(const text_event& ev) override;
-
-	/**
-	 * \copydoc dispatcher::handle_quit
-	 */
-	void handle_quit() override;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_CLIENT_HPP
--- a/libclient/malikania/client/color.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-/*
- * color.cpp -- color description
- *
- * Copyright (c) 2013-2018 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 <cctype>
-#include <locale>
-#include <unordered_map>
-
-#include "color.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-constexpr auto rgb(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept -> unsigned
-{
-	return (0xff000000 | (r << 16) | (g << 8) | b);
-}
-
-/*
- * Convert hexadecimal value to the correct number.
- */
-auto value(char digit) -> std::uint8_t
-{
-	if (std::isdigit(digit, {}))
-		return digit - '0';
-	if ((digit = std::toupper(digit, {})) < 'A' || digit > 'F')
-		throw std::invalid_argument("invalid hexadecimal value: " + std::to_string(digit));
-
-	return digit - 55;
-}
-
-auto value(char digit1, char digit2) -> std::uint8_t
-{
-	return ((value(digit1) << 4) & 0xf0) | value(digit2);
-}
-
-const std::unordered_map<std::string_view, std::uint32_t> colors{
-	{ "aliceblue",                  rgb(240, 248, 255)      },
-	{ "antiquewhite",               rgb(250, 235, 215)      },
-	{ "aqua",                       rgb( 0, 255, 255)       },
-	{ "aquamarine",                 rgb(127, 255, 212)      },
-	{ "azure",                      rgb(240, 255, 255)      },
-	{ "beige",                      rgb(245, 245, 220)      },
-	{ "bisque",                     rgb(255, 228, 196)      },
-	{ "black",                      rgb( 0, 0, 0)           },
-	{ "blanchedalmond",             rgb(255, 235, 205)      },
-	{ "blue",                       rgb( 0, 0, 255)         },
-	{ "blueviolet",                 rgb(138, 43, 226)       },
-	{ "brown",                      rgb(165, 42, 42)        },
-	{ "burlywood",                  rgb(222, 184, 135)      },
-	{ "cadetblue",                  rgb( 95, 158, 160)      },
-	{ "chartreuse",                 rgb(127, 255, 0)        },
-	{ "chocolate",                  rgb(210, 105, 30)       },
-	{ "coral",                      rgb(255, 127, 80)       },
-	{ "cornflowerblue",             rgb(100, 149, 237)      },
-	{ "cornsilk",                   rgb(255, 248, 220)      },
-	{ "crimson",                    rgb(220, 20, 60)        },
-	{ "cyan",                       rgb( 0, 255, 255)       },
-	{ "darkblue",                   rgb( 0, 0, 139)         },
-	{ "darkcyan",                   rgb( 0, 139, 139)       },
-	{ "darkgoldenrod",              rgb(184, 134, 11)       },
-	{ "darkgray",                   rgb(169, 169, 169)      },
-	{ "darkgreen",                  rgb( 0, 100, 0)         },
-	{ "darkgrey",                   rgb(169, 169, 169)      },
-	{ "darkkhaki",                  rgb(189, 183, 107)      },
-	{ "darkmagenta",                rgb(139, 0, 139)        },
-	{ "darkolivegreen",             rgb( 85, 107, 47)       },
-	{ "darkorange",                 rgb(255, 140, 0)        },
-	{ "darkorchid",                 rgb(153, 50, 204)       },
-	{ "darkred",                    rgb(139, 0, 0)          },
-	{ "darksalmon",                 rgb(233, 150, 122)      },
-	{ "darkseagreen",               rgb(143, 188, 143)      },
-	{ "darkslateblue",              rgb( 72, 61, 139)       },
-	{ "darkslategray",              rgb( 47, 79, 79)        },
-	{ "darkslategrey",              rgb( 47, 79, 79)        },
-	{ "darkturquoise",              rgb( 0, 206, 209)       },
-	{ "darkviolet",                 rgb(148, 0, 211)        },
-	{ "deeppink",                   rgb(255, 20, 147)       },
-	{ "deepskyblue",                rgb( 0, 191, 255)       },
-	{ "dimgray",                    rgb(105, 105, 105)      },
-	{ "dimgrey",                    rgb(105, 105, 105)      },
-	{ "dodgerblue",                 rgb( 30, 144, 255)      },
-	{ "firebrick",                  rgb(178, 34, 34)        },
-	{ "floralwhite",                rgb(255, 250, 240)      },
-	{ "forestgreen",                rgb( 34, 139, 34)       },
-	{ "fuchsia",                    rgb(255, 0, 255)        },
-	{ "gainsboro",                  rgb(220, 220, 220)      },
-	{ "ghostwhite",                 rgb(248, 248, 255)      },
-	{ "gold",                       rgb(255, 215, 0)        },
-	{ "goldenrod",                  rgb(218, 165, 32)       },
-	{ "gray",                       rgb(128, 128, 128)      },
-	{ "green",                      rgb( 0, 128, 0)         },
-	{ "greenyellow",                rgb(173, 255, 47)       },
-	{ "grey",                       rgb(128, 128, 128)      },
-	{ "honeydew",                   rgb(240, 255, 240)      },
-	{ "hotpink",                    rgb(255, 105, 180)      },
-	{ "indianred",                  rgb(205, 92, 92)        },
-	{ "indigo",                     rgb( 75, 0, 130)        },
-	{ "ivory",                      rgb(255, 255, 240)      },
-	{ "khaki",                      rgb(240, 230, 140)      },
-	{ "lavender",                   rgb(230, 230, 250)      },
-	{ "lavenderblush",              rgb(255, 240, 245)      },
-	{ "lawngreen",                  rgb(124, 252, 0)        },
-	{ "lemonchiffon",               rgb(255, 250, 205)      },
-	{ "lightblue",                  rgb(173, 216, 230)      },
-	{ "lightcoral",                 rgb(240, 128, 128)      },
-	{ "lightcyan",                  rgb(224, 255, 255)      },
-	{ "lightgoldenrodyellow",       rgb(250, 250, 210)      },
-	{ "lightgray",                  rgb(211, 211, 211)      },
-	{ "lightgreen",                 rgb(144, 238, 144)      },
-	{ "lightgrey",                  rgb(211, 211, 211)      },
-	{ "lightpink",                  rgb(255, 182, 193)      },
-	{ "lightsalmon",                rgb(255, 160, 122)      },
-	{ "lightseagreen",              rgb( 32, 178, 170)      },
-	{ "lightskyblue",               rgb(135, 206, 250)      },
-	{ "lightslategray",             rgb(119, 136, 153)      },
-	{ "lightslategrey",             rgb(119, 136, 153)      },
-	{ "lightsteelblue",             rgb(176, 196, 222)      },
-	{ "lightyellow",                rgb(255, 255, 224)      },
-	{ "lime",                       rgb( 0, 255, 0)         },
-	{ "limegreen",                  rgb( 50, 205, 50)       },
-	{ "linen",                      rgb(250, 240, 230)      },
-	{ "magenta",                    rgb(255, 0, 255)        },
-	{ "maroon",                     rgb(128, 0, 0)          },
-	{ "mediumaquamarine",           rgb(102, 205, 170)      },
-	{ "mediumblue",                 rgb( 0, 0, 205)         },
-	{ "mediumorchid",               rgb(186, 85, 211)       },
-	{ "mediumpurple",               rgb(147, 112, 219)      },
-	{ "mediumseagreen",             rgb( 60, 179, 113)      },
-	{ "mediumslateblue",            rgb(123, 104, 238)      },
-	{ "mediumspringgreen",          rgb( 0, 250, 154)       },
-	{ "mediumturquoise",            rgb( 72, 209, 204)      },
-	{ "mediumvioletred",            rgb(199, 21, 133)       },
-	{ "midnightblue",               rgb( 25, 25, 112)       },
-	{ "mintcream",                  rgb(245, 255, 250)      },
-	{ "mistyrose",                  rgb(255, 228, 225)      },
-	{ "moccasin",                   rgb(255, 228, 181)      },
-	{ "navajowhite",                rgb(255, 222, 173)      },
-	{ "navy",                       rgb( 0, 0, 128)         },
-	{ "oldlace",                    rgb(253, 245, 230)      },
-	{ "olive",                      rgb(128, 128, 0)        },
-	{ "olivedrab",                  rgb(107, 142, 35)       },
-	{ "orange",                     rgb(255, 165, 0)        },
-	{ "orangered",                  rgb(255, 69, 0)         },
-	{ "orchid",                     rgb(218, 112, 214)      },
-	{ "palegoldenrod",              rgb(238, 232, 170)      },
-	{ "palegreen",                  rgb(152, 251, 152)      },
-	{ "paleturquoise",              rgb(175, 238, 238)      },
-	{ "palevioletred",              rgb(219, 112, 147)      },
-	{ "papayawhip",                 rgb(255, 239, 213)      },
-	{ "peachpuff",                  rgb(255, 218, 185)      },
-	{ "peru",                       rgb(205, 133, 63)       },
-	{ "pink",                       rgb(255, 192, 203)      },
-	{ "plum",                       rgb(221, 160, 221)      },
-	{ "powderblue",                 rgb(176, 224, 230)      },
-	{ "purple",                     rgb(128, 0, 128)        },
-	{ "red",                        rgb(255, 0, 0)          },
-	{ "rosybrown",                  rgb(188, 143, 143)      },
-	{ "royalblue",                  rgb( 65, 105, 225)      },
-	{ "saddlebrown",                rgb(139, 69, 19)        },
-	{ "salmon",                     rgb(250, 128, 114)      },
-	{ "sandybrown",                 rgb(244, 164, 96)       },
-	{ "seagreen",                   rgb( 46, 139, 87)       },
-	{ "seashell",                   rgb(255, 245, 238)      },
-	{ "sienna",                     rgb(160, 82, 45)        },
-	{ "silver",                     rgb(192, 192, 192)      },
-	{ "skyblue",                    rgb(135, 206, 235)      },
-	{ "slateblue",                  rgb(106, 90, 205)       },
-	{ "slategray",                  rgb(112, 128, 144)      },
-	{ "slategrey",                  rgb(112, 128, 144)      },
-	{ "snow",                       rgb(255, 250, 250)      },
-	{ "springgreen",                rgb( 0, 255, 127)       },
-	{ "steelblue",                  rgb( 70, 130, 180)      },
-	{ "tan",                        rgb(210, 180, 140)      },
-	{ "teal",                       rgb( 0, 128, 128)       },
-	{ "thistle",                    rgb(216, 191, 216)      },
-	{ "tomato",                     rgb(255, 99, 71)        },
-	{ "transparent",                0                       },
-	{ "turquoise",                  rgb( 64, 224, 208)      },
-	{ "violet",                     rgb(238, 130, 238)      },
-	{ "wheat",                      rgb(245, 222, 179)      },
-	{ "white",                      rgb(255, 255, 255)      },
-	{ "whitesmoke",                 rgb(245, 245, 245)      },
-	{ "yellow",                     rgb(255, 255, 0)        },
-	{ "yellowgreen",                rgb(154, 205, 50)       }
-};
-
-} // !namespace
-
-auto color::from_hex(std::uint32_t hex) noexcept -> color
-{
-	return {
-		static_cast<std::uint8_t>((hex >> 16) & 0xff),
-		static_cast<std::uint8_t>((hex >> 8) & 0xff),
-		static_cast<std::uint8_t>((hex & 0xff)),
-		static_cast<std::uint8_t>((hex >> 24) & 0xff)
-	};
-}
-
-auto color::from_name(std::string_view name) -> color
-{
-	if (name.empty())
-		return color();
-
-	color result;
-
-	// Parse #rrggbb or #rgb.
-	if (name[0] == '#') {
-		if (name.length() == 7) {
-			result.red   = value(name[1], name[2]);
-			result.green = value(name[3], name[4]);
-			result.blue  = value(name[5], name[6]);
-		} else if (name.length() == 4) {
-			result.red   = value(name[1], name[1]);
-			result.green = value(name[2], name[2]);
-			result.blue  = value(name[3], name[3]);
-		} else
-			throw std::invalid_argument("invalid format");
-	} else {
-		// Name lookup.
-		const auto it = colors.find(name);
-
-		if (it == colors.end())
-			throw std::invalid_argument("invalid color");
-
-		result = from_hex(it->second);
-	}
-
-	return result;
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/color.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * color.hpp -- color description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_COLOR_HPP
-#define MALIKANIA_CLIENT_COLOR_HPP
-
-/**
- * \file color.hpp
- * \brief Colors.
- */
-
-#include <cstdint>
-#include <string_view>
-
-namespace mlk::client {
-
-/**
- * \brief color description
- */
-struct color {
-	std::uint8_t red{0};
-	std::uint8_t green{0};
-	std::uint8_t blue{0};
-	std::uint8_t alpha{255};
-
-	/**
-	 * Constructor with an hexadecimal value.
-	 *
-	 * \param hex the color
-	 */
-	static auto from_hex(std::uint32_t hex) noexcept -> color;
-
-	/**
-	 * Construct a color from #rrggbb or name.
-	 *
-	 * See the SVG this [list](http://www.december.com/html/spec/colorsvg.html) for supported names.
-	 *
-	 * \param name the color name
-	 * \throw std::invalid_argument if the color does not exist or is invalid
-	 */
-	static auto from_name(std::string_view name) -> color;
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_COLOR_HPP
--- a/libclient/malikania/client/connection.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * connection.hpp -- connection to server
- *
- * Copyright (c) 2013-2018 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 "connection.hpp"
-
-namespace mlk {
-
-namespace client {
-
-/*
- * connection::connection
- * ------------------------------------------------------------------
- */
-connection::connection(boost::asio::io_service& service)
-	: service_(service)
-	, context_(boost::asio::ssl::context::sslv23)
-	, resolver_(service_)
-{
-}
-
-/*
- * connection::handshake
- * ------------------------------------------------------------------
- */
-void connection::handshake(connect_t handler)
-{
-	socket_->async_handshake(boost::asio::ssl::stream_base::client, std::move(handler));
-}
-
-/*
- * connection::flush
- * ------------------------------------------------------------------
- */
-void connection::flush()
-{
-	if (output_.empty())
-		return;
-
-	auto buffer = boost::asio::buffer(std::get<1>(output_[0]));
-
-	boost::asio::async_write(*socket_, buffer, [this] (auto code, auto xfer) {
-		auto handler = std::get<2>(output_[0]);
-
-		output_.pop_front();
-
-		if (handler)
-			handler(std::move(code), std::get<0>(output_[0]));
-
-		// TODO: precise error code.
-		if (!code && xfer != 0)
-			flush();
-	});
-}
-
-/*
- * connection::connect
- * ------------------------------------------------------------------
- */
-void connection::connect(const std::string& host, std::uint16_t port, connect_t handler)
-{
-	socket_ = std::make_unique<socket_t>(service_, context_);
-
-	using tcp = boost::asio::ip::tcp;
-
-	auto str = std::to_string(port);
-
-	resolver_.async_resolve(tcp::resolver::query(host, str), [this, handler] (auto code, auto ep) {
-		if (code)
-			handler(std::move(code));
-		else {
-			boost::asio::async_connect(socket_->lowest_layer(), ep, [this, handler] (auto code, auto) {
-				if (code)
-					handler(std::move(code));
-				else
-					handshake(std::move(handler));
-			});
-		}
-	});
-}
-
-/*
- * connection::send
- * ------------------------------------------------------------------
- */
-void connection::send(nlohmann::json message, send_t handler)
-{
-	assert(socket_);
-	assert(message.is_object());
-
-	auto in_progress = !output_.empty();
-	auto str = message.dump() + "\r\n\r\n";
-
-	output_.push_back(std::make_tuple(std::move(message), std::move(str), std::move(handler)));
-
-	if (!in_progress)
-		flush();
-}
-
-/*
- * connection::recv
- * ------------------------------------------------------------------
- */
-void connection::recv(recv_t handler)
-{
-	assert(socket_);
-
-#if !defined(NDEBUG)
-	assert(!is_reading);
-
-	is_reading = true;
-#endif
-
-	boost::asio::async_read_until(*socket_, input_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
-#if !defined(NDEBUG)
-		is_reading = false;
-#endif
-		if (code)
-			handler(std::move(code), nullptr);
-		else if (xfer == 0)
-			handler(make_error_code(boost::system::errc::network_down), nullptr);
-		else {
-			std::string command{
-				boost::asio::buffers_begin(input_.data()),
-				boost::asio::buffers_begin(input_.data()) + xfer - /* \r\n\r\n */ 4
-			};
-
-			input_.consume(xfer);
-
-			nlohmann::json msg;
-
-			try {
-				msg = nlohmann::json::parse(command);
-			} catch (const std::exception&) {
-				// TODO: add custom error code.
-			}
-
-			handler(std::move(code), std::move(msg));
-		}
-	});
-}
-
-} // !client
-
-} // !mlk
--- a/libclient/malikania/client/connection.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * connection.hpp -- connection to server
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_CONNECTION_HPP
-#define MALIKANIA_CLIENT_CONNECTION_HPP
-
-/**
- * \file connection.hpp
- * \brief Connection to server.
- */
-
-#include <deque>
-#include <functional>
-#include <utility>
-
-#include <boost/asio.hpp>
-#include <boost/asio/ssl.hpp>
-
-#include <json.hpp>
-
-namespace mlk {
-
-namespace client {
-
-class client;
-
-/**
- * \brief connection to server.
- */
-class connection {
-public:
-	/**
-	 * Connection handler.
-	 */
-	using connect_t = std::function<void (boost::system::error_code)>;
-
-	/**
-	 * Receive handler.
-	 */
-	using recv_t = std::function<void (boost::system::error_code, nlohmann::json)>;
-
-	/**
-	 * Send handler.
-	 */
-	using send_t = std::function<void (boost::system::error_code, nlohmann::json)>;
-
-private:
-	/**
-	 * Wrap an handler, the JSON message and its dump string.
-	 */
-	using output_item_t = std::tuple<nlohmann::json, std::string, send_t>;
-
-	using socket_t = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
-
-	boost::asio::io_service& service_;
-	boost::asio::ssl::context context_;
-	boost::asio::ip::tcp::resolver resolver_;
-	std::unique_ptr<socket_t> socket_;
-	boost::asio::streambuf input_;
-	std::deque<output_item_t> output_;
-
-#if !defined(NDEBUG)
-	bool is_reading{false};
-#endif
-
-	void handshake(connect_t);
-	void flush();
-
-public:
-	/**
-	 * Create a connection object.
-	 */
-	connection(boost::asio::io_service& service);
-
-	/**
-	 * Connect to the given end point.
-	 *
-	 * \pre handler != nullptr
-	 * \param host the hostname
-	 * \param port the port number
-	 * \param handler the handler
-	 */
-	virtual void connect(const std::string& host, std::uint16_t port, connect_t handler);
-
-	/**
-	 * Send the given message to the server.
-	 *
-	 * \pre message.is_object()
-	 * \param message the message object
-	 * \param handler the handler (may be null)
-	 */
-	virtual void send(nlohmann::json message, send_t handler);
-
-	/**
-	 * Request for a recv operation.
-	 *
-	 * \pre no reading operation must be pending
-	 * \pre handler != nullptr
-	 * \param handler the handler
-	 */
-	virtual void recv(recv_t handler);
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_CONNECTION_HPP
--- a/libclient/malikania/client/dispatcher.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,182 +0,0 @@
-/*
- * dispatcher.hpp -- client event dispatcher
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_DISPATCHER_HPP
-#define MALIKANIA_CLIENT_DISPATCHER_HPP
-
-/**
- * \file dispatcher.hpp
- * \brief Client event dispatcher.
- */
-
-#include <system_error>
-
-#include <json.hpp>
-
-#include "mouse.hpp"
-#include "key.hpp"
-#include "point.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Describe key event.
- */
-struct key_event {
-	key keycode{key::unknown};      //!< layout-dependant key
-	key scancode{key::unknown};     //!< physical key
-	mod modifiers{mod::none};       //!< optional modifiers
-};
-
-/**
- * \brief Describe mouse click event.
- */
-struct mouse_click_event {
-	mouse button{mouse::none};      //!< which mouse button
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe a mouse motion event.
- */
-struct mouse_motion_event {
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe mouse wheel (up/down) event.
- */
-struct mouse_wheel_event {
-	point pos;                      //!< current mouse position
-};
-
-/**
- * \brief Describe text edition event.
- */
-struct text_event {
-	std::u32string text;            //!< text added from this event
-};
-
-/**
- * \brief Client event dispatcher.
- */
-class dispatcher {
-public:
-	/**
-	 * Default constructor.
-	 */
-	dispatcher() noexcept = default;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~dispatcher() noexcept = default;
-
-	/**
-	 * Connection event.
-	 *
-	 * \param code the result code
-	 */
-	virtual void handle_connect(const std::error_code& code)
-	{
-		(void)code;
-	}
-
-	/**
-	 * Message event.
-	 *
-	 * \param code the result code
-	 * \param msg the network message
-	 */
-	virtual void handle_message(const std::error_code& code, const nlohmann::json& msg)
-	{
-		(void)code;
-		(void)msg;
-	}
-
-	/**
-	 * Key down event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_key_down(const key_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Key released event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_key_up(const key_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse click event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_down(const mouse_click_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse click release event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_up(const mouse_click_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Mouse wheel event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_mouse_wheel(const mouse_wheel_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Text editing event.
-	 *
-	 * \param ev the event
-	 */
-	virtual void handle_text(const text_event& ev)
-	{
-		(void)ev;
-	}
-
-	/**
-	 * Quit request.
-	 */
-	virtual void handle_quit()
-	{
-	}
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_DISPATCHER_HPP
--- a/libclient/malikania/client/font.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * font.cpp -- font object
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "font.hpp"
-#include "sdl_util.hpp"
-#include "size.hpp"
-
-namespace mlk::client {
-
-font::font(std::string data, std::size_t size)
-	: size_(size)
-{
-	assert(size > 0);
-
-	auto rw = SDLx_RWFromBinary(std::move(data));
-
-	if (rw == nullptr)
-		throw std::runtime_error(SDL_GetError());
-
-	font_ = { TTF_OpenFontRW(rw, true, size), TTF_CloseFont };
-
-	if (font_ == nullptr)
-		throw std::runtime_error(TTF_GetError());
-}
-
-auto font::get_handle() const -> TTF_Font*
-{
-	return font_.get();
-}
-
-auto font::get_handle() -> TTF_Font*
-{
-	return font_.get();
-}
-
-auto font::get_size() const noexcept -> std::size_t
-{
-	return size_;
-}
-
-auto font::clip(const std::string& text) const -> size
-{
-	int width, height;
-
-	if (TTF_SizeUTF8(font_.get(), text.c_str(), &width, &height) != 0)
-		throw std::runtime_error(SDL_GetError());
-
-	return {
-		static_cast<unsigned>(width),
-		static_cast<unsigned>(height)
-	};
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/font.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * font.hpp -- font object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_FONT_HPP
-#define MALIKANIA_CLIENT_FONT_HPP
-
-/**
- * \file font.hpp
- * \brief fonts.
- */
-
-#include <memory>
-#include <string>
-
-#include <SDL.h>
-#include <SDL_ttf.h>
-
-namespace mlk {
-
-struct size;
-
-namespace client {
-
-/**
- * \brief font object.
- */
-class font {
-private:
-	std::unique_ptr<TTF_Font, void (*)(TTF_Font*)> font_{nullptr, nullptr};
-	std::size_t size_;
-
-public:
-	/**
-	 * Construct a font from binary data.
-	 *
-	 * \pre size > 0
-	 * \param data the raw data
-	 * \param size the size
-	 */
-	font(std::string data, std::size_t size);
-
-	/**
-	 * Get the underlying SDL TTF_Font.
-	 *
-	 * \return the font
-	 */
-	auto get_handle() const -> TTF_Font*;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the font
-	 */
-	auto get_handle() -> TTF_Font*;
-
-	/**
-	 * Get the font size.
-	 *
-	 * \return the font size
-	 */
-	auto get_size() const noexcept -> std::size_t;
-
-	/**
-	 * Get the clipping size required to draw the given text.
-	 *
-	 * \param text the text to clip
-	 * \return the required size
-	 */
-	auto clip(const std::string& text) const -> size;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // MALIKANIA_CLIENT_FONT_HPP
--- a/libclient/malikania/client/image.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * image.cpp -- image object
- *
- * Copyright (c) 2013-2018 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_image.h>
-
-#include "image.hpp"
-#include "sdl_util.hpp"
-#include "window.hpp"
-
-namespace mlk::client {
-
-void image::create_texture(window& w)
-{
-	texture_ = {
-		SDL_CreateTextureFromSurface(w.get_renderer(), surface_.get()),
-		SDL_DestroyTexture
-	};
-
-	if (texture_ == nullptr)
-		throw std::runtime_error(SDL_GetError());
-}
-
-image::image(std::string data)
-{
-	auto rw = SDLx_RWFromBinary(std::move(data));
-
-	if (rw == nullptr)
-		throw std::runtime_error(SDL_GetError());
-
-	surface_ = { IMG_Load_RW(rw, true), SDL_FreeSurface };
-
-	if (!surface_)
-		throw std::runtime_error(SDL_GetError());
-
-	size_ = {
-		static_cast<unsigned>(surface_->w),
-		static_cast<unsigned>(surface_->h)
-	};
-}
-
-auto image::get_size() const noexcept -> const mlk::size&
-{
-	return size_;
-}
-
-void image::draw(window& window, const point& point)
-{
-	/*
-	 * Create texture at this step so the image constructor does not need the
-	 * window.
-	 */
-	if (!texture_)
-		create_texture(window);
-
-	SDL_Rect target;
-
-	target.x = static_cast<int>(point.x);
-	target.y = static_cast<int>(point.y);
-	target.w = static_cast<int>(size_.width);
-	target.h = static_cast<int>(size_.height);
-
-	if (SDL_RenderCopy(window.get_renderer(), texture_.get(), nullptr, &target) < 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void image::draw(window& window, const rectangle& source, const rectangle& target)
-{
-	if (!texture_)
-		create_texture(window);
-
-	SDL_Rect sr, st;
-
-	sr.x = source.x;
-	sr.y = source.y;
-	sr.w = static_cast<int>(source.width);
-	sr.h = static_cast<int>(source.height);
-
-	st.x = target.x;
-	st.y = target.y;
-	st.w = static_cast<int>(target.width);
-	st.h = static_cast<int>(target.height);
-
-	// Readjust .w, .h if null.
-	if (source.is_null()) {
-		sr.w = static_cast<int>(size_.width);
-		sr.h = static_cast<int>(size_.height);
-	}
-	if (target.is_null()) {
-		st.w = static_cast<int>(size_.width);
-		st.h = static_cast<int>(size_.height);
-	}
-
-	if (SDL_RenderCopy(window.get_renderer(), texture_.get(), &sr, &st) < 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/image.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * image.hpp -- image object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_IMAGE_HPP
-#define MALIKANIA_CLIENT_IMAGE_HPP
-
-/**
- * \file image.hpp
- * \brief Images.
- */
-
-#include <memory>
-#include <string>
-
-#include <SDL.h>
-
-#include "point.hpp"
-#include "rectangle.hpp"
-#include "size.hpp"
-
-namespace mlk::client {
-
-class window;
-
-/**
- * \brief Image object.
- */
-class image {
-private:
-	std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> surface_{nullptr, nullptr};
-	std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)> texture_{nullptr, nullptr};
-
-	size size_;
-
-	void create_texture(window& window);
-
-public:
-	/**
-	 * Construct an image from the binary data.
-	 *
-	 * \param window the window
-	 * \param data the data
-	 */
-	image(std::string data);
-
-	/**
-	 * Get the image size.
-	 *
-	 * \return the size
-	 */
-	auto get_size() const noexcept -> const mlk::size&;
-
-	/**
-	 * Draw the image to the window.
-	 *
-	 * \param window the window
-	 * \param position the position
-	 */
-	void draw(window& window, const point& position = {0, 0});
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \param window the window
-	 * \param source the source to clip
-	 * \param target the target destination
-	 */
-	void draw(window& window, const rectangle& source, const rectangle& target);
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_IMAGE_HPP
--- a/libclient/malikania/client/key.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,256 +0,0 @@
-/*
- * key.hpp -- key definitions
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_KEY_HPP
-#define MALIKANIA_CLIENT_KEY_HPP
-
-namespace mlk::client {
-
-/**
- * \brief Describe keyboard modifiers
- *
- * This enumeration is a satisfied the Bitmask concept.
- */
-enum class mod {
-	none            = 0,                    //!< no modifiers
-	left_control    = (1 << 1),             //!< left control
-	left_alt        = (1 << 2),             //!< left alt
-	left_shift      = (1 << 3),             //!< left shift
-	left_super      = (1 << 4),             //!< left logo
-	right_control   = (1 << 5),             //!< right control
-	right_alt       = (1 << 6),             //!< right alt
-	right_shift     = (1 << 7),             //!< right shift
-	right_super     = (1 << 8),             //!< right logo
-	num_lock        = (1 << 9),             //!< num lock is on
-	caps_lock       = (1 << 10),            //!< caps lock is on
-
-	/**
-	 * Left or right control.
-	 */
-	control         = left_control | right_control,
-
-        /**
-         * Left or right shift.
-         */
-	shift           = left_shift | right_shift,
-
-        /**
-         * Left or right alt.
-         */
-	alt             = left_alt | right_alt,
-
-        /**
-         * Left or right super.
-         */
-	super           = left_super | right_super
-};
-
-/**
- * \cond ENUM_HIDDEN_SYMBOLS
- */
-
-inline auto operator^(mod v1, mod v2) noexcept -> mod
-{
-	return static_cast<mod>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2));
-}
-
-inline auto operator&(mod v1, mod v2) noexcept -> mod
-{
-	return static_cast<mod>(static_cast<unsigned>(v1)&  static_cast<unsigned>(v2));
-}
-
-inline auto operator|(mod v1, mod v2) noexcept -> mod
-{
-	return static_cast<mod>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2));
-}
-
-inline auto operator~(mod v) noexcept -> mod
-{
-	return static_cast<mod>(~static_cast<unsigned>(v));
-}
-
-inline auto operator|=(mod& v1, mod v2) noexcept -> mod&
-{
-	v1 = static_cast<mod>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2));
-
-	return v1;
-}
-
-inline auto operator&=(mod& v1, mod v2) noexcept -> mod&
-{
-	v1 = static_cast<mod>(static_cast<unsigned>(v1)&  static_cast<unsigned>(v2));
-
-	return v1;
-}
-
-inline auto operator^=(mod& v1, mod v2) noexcept -> mod&
-{
-	v1 = static_cast<mod>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2));
-
-	return v1;
-}
-
-/**
- * \endcond
- */
-
-/**
- * \brief Key description.
- */
-enum class key {
-	unknown,
-	a,
-	b,
-	c,
-	d,
-	e,
-	f,
-	g,
-	h,
-	i,
-	j,
-	k,
-	l,
-	m,
-	n,
-	o,
-	p,
-	q,
-	r,
-	s,
-	t,
-	u,
-	v,
-	w,
-	x,
-	y,
-	z,
-	exclaim,
-	double_quote,
-	percent,
-	dollar,
-	ampersand,
-	left_parenthese,
-	right_parenthese,
-	asterisk,
-	plus,
-	colon,
-	less,
-	greater,
-	question,
-	at,
-	caret,
-	underscore,
-	back_quote,
-	quote,
-	one,
-	two,
-	three,
-	four,
-	five,
-	six,
-	seven,
-	eight,
-	nine,
-	zero,
-	enter,
-	escape,
-	backspace,
-	tab,
-	space,
-	minus,
-	equals,
-	left_bracket,
-	right_bracket,
-	backslash,
-	hash,
-	semicolon,
-	apostrophe,
-	grave,
-	comma,
-	period,
-	slash,
-	caps_lock,
-	f1,
-	f2,
-	f3,
-	f4,
-	f5,
-	f6,
-	f7,
-	f8,
-	f9,
-	f10,
-	f11,
-	f12,
-	f13,
-	f14,
-	f15,
-	f16,
-	f17,
-	f18,
-	f19,
-	f20,
-	f21,
-	f22,
-	f23,
-	f24,
-	print_screen,
-	scroll_lock,
-	pause,
-	insert,
-	home,
-	page_up,
-	page_down,
-	del,
-	end,
-	right,
-	left,
-	down,
-	up,
-	kp_divide,
-	kp_multiply,
-	kp_minus,
-	kp_plus,
-	kp_enter,
-	kp_one,
-	kp_two,
-	kp_three,
-	kp_four,
-	kp_five,
-	kp_six,
-	kp_seven,
-	kp_eight,
-	kp_nine,
-	kp_zero,
-	mute,
-	volume_up,
-	volume_down,
-	left_control,
-	left_alt,
-	left_shift,
-	left_super,
-	right_control,
-	right_alt,
-	right_shift,
-	right_super
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_KEY_HPP
--- a/libclient/malikania/client/label.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * label.cpp -- GUI label element
- *
- * Copyright (c) 2013-2018 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 "label.hpp"
-#include "size.hpp"
-#include "theme.hpp"
-#include "window.hpp"
-
-namespace mlk::client {
-
-label::label(std::string text) noexcept
-	: text_(std::move(text))
-{
-}
-
-auto label::get_text() const noexcept -> const std::string&
-{
-	return text_;
-}
-
-void label::set_text(std::string text) noexcept
-{
-	text_ = std::move(text);
-}
-
-void label::draw(window& w, const size& size)
-{
-	// TODO: implement.
-	(void)w;
-	(void)size;
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/label.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * label.hpp -- GUI label element
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LABEL_HPP
-#define MALIKANIA_CLIENT_LABEL_HPP
-
-/**
- * \file label.hpp
- * \brief GUI label element.
- */
-
-#include <string>
-
-#include "widget.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Basic label for displaying test.
- */
-class label : public widget {
-private:
-	std::string text_;
-
-public:
-	/**
-	 * Create a label with an optional text.
-	 *
-	 * \param text the text
-	 */
-	label(std::string text = "") noexcept;
-
-	/**
-	 * Get the text.
-	 *
-	 * \return the label text
-	 */
-	auto get_text() const noexcept -> const std::string&;
-
-	/**
-	 * Set the label text.
-	 *
-	 * \param text the text
-	 */
-	void set_text(std::string text) noexcept;
-
-	/**
-	 * \copydoc widget::draw
-	 */
-	void draw(window& w, const size& size) override;
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_LABEL_HPP
--- a/libclient/malikania/client/loader.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * client_resources_loader.cpp -- load shared resources files for the client
- *
- * Copyright (c) 2013-2018 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 <json.hpp>
-
-#include "animation.hpp"
-#include "font.hpp"
-#include "image.hpp"
-#include "loader.hpp"
-#include "locator.hpp"
-#include "size.hpp"
-#include "sprite.hpp"
-#include "util.hpp"
-
-using namespace nlohmann;
-
-namespace mlk::client {
-
-namespace {
-
-auto load_animation_frames(const json& value)
-{
-	animation::frame_list frames;
-
-	for (const auto& v : value) {
-		if (!v.is_object())
-			throw std::runtime_error("not a JSON object");
-
-		const auto cell = v.find("cell");
-		const auto delay = v.find("delay");
-
-		if (cell == v.end() || !cell->is_number_unsigned())
-			throw std::runtime_error("invalid cell");
-		if (delay == v.end() || !delay->is_number_unsigned())
-			throw std::runtime_error("invalid delay");
-
-		// TODO: range check.
-		frames.push_back({cell->get<unsigned>(), delay->get<unsigned>()});
-	}
-
-	return frames;
-}
-
-} // !namespace
-
-loader::loader(locator& locator) noexcept
-	: mlk::loader(locator)
-{
-}
-
-auto loader::load_font(std::string_view id, unsigned size) -> font
-{
-	return font(get_locator().read(id), size);
-}
-
-auto loader::load_image(std::string_view id) -> image
-{
-	return image(get_locator().read(id));
-}
-
-auto loader::load_sprite(std::string_view id) -> sprite
-{
-	auto value = json::parse(get_locator().read(id));
-
-	if (!value.is_object())
-		throw std::runtime_error(": not a JSON object");
-
-	return sprite(
-		load_image(util::json::require_string(value, "/image"_json_pointer)),
-		util::json::require_size(value, "/cell"_json_pointer),
-		util::json::get_size(value, "/margin"_json_pointer),
-		util::json::get_size(value, "/space"_json_pointer),
-		util::json::get_size(value, "/size"_json_pointer)
-	);
-}
-
-auto loader::load_animation(std::string_view id) -> animation
-{
-	const auto value = json::parse(get_locator().read(id));
-
-	if (!value.is_object())
-		throw std::runtime_error("not a JSON object");
-
-	const auto s = load_sprite(util::json::require_string(value, "/sprite"_json_pointer));
-
-	return {
-		load_sprite(util::json::require_string(value, "/sprite"_json_pointer)),
-		load_animation_frames(util::json::require_array(value, "/frames"_json_pointer))
-	};
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/loader.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * client_resources_loader.hpp -- load shared resources files for the client
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOADER_HPP
-#define MALIKANIA_CLIENT_LOADER_HPP
-
-/*
- * \file loader.hpp
- * \brief Load client assets.
- */
-
-#include <malikania/loader.hpp>
-
-#include <string>
-
-namespace mlk {
-
-struct size;
-
-namespace client {
-
-struct animation;
-
-class font;
-class image;
-class sprite;
-
-/**
- * \brief Load client resources.
- */
-class loader : public mlk::loader {
-public:
-	/**
-	 * Client resources loader constructor.
-	 *
-	 * The window is required because some of the resources require it.
-	 *
-	 * \param locator the resources locator
-	 */
-	loader(locator& locator) noexcept;
-
-	/**
-	 * Load a font.
-	 *
-	 * \param id the resource id
-	 * \param size the desired size
-	 * \return the font
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_font(std::string_view id, unsigned size) -> font;
-
-	/**
-	 * Load an image.
-	 *
-	 * \param id the resource id
-	 * \return the image
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_image(std::string_view id) -> image;
-
-	/**
-	 * Load a sprite.
-	 *
-	 * \param id the resource id
-	 * \return the sprite
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_sprite(std::string_view id) -> sprite;
-
-	/**
-	 * Load an animation.
-	 *
-	 * \param id the resource id
-	 * \return the animation
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_animation(std::string_view id) -> animation;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_LOADER_HPP
--- a/libclient/malikania/client/mouse.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * mouse.hpp -- mouse definitions
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_MOUSE_HPP
-#define MALIKANIA_CLIENT_MOUSE_HPP
-
-/**
- * \file mouse.hpp
- * \brief Mouse definitions.
- */
-
-namespace mlk::client {
-
-/**
- * \brief Describe mouse button
- */
-enum class mouse {
-	none,                   //!< no buttons are pressed
-	left,                   //!< left click is pressed
-	right,                  //!< right click is pressed
-	middle                  //!< middle click is pressed
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_MOUSE_HPP
--- a/libclient/malikania/client/sdl_util.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-/*
- * sdl_util.cpp -- common SDL2 related code
- *
- * Copyright (c) 2013-2018 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 <cerrno>
-#include <cstdint>
-#include <cstring>
-#include <new>
-
-#include "sdl_util.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-/*
- * RWFromBinary implementation
- * ------------------------------------------------------------------
- *
- * A little bit inspired by official SDL_RWFromMem implementation, largely
- * modified to match our conventions and the C++ code.
- */
-
-struct buffer {
-	std::string data;
-	std::uint64_t position;
-	std::uint64_t length;
-
-	buffer(std::string input) noexcept
-		: data(std::move(input))
-		, position(0ULL)
-		, length(data.length())
-	{
-	}
-};
-
-auto size(SDL_RWops* ops) noexcept -> Sint64
-{
-	return reinterpret_cast<buffer*>(ops->hidden.unknown.data1)->length;
-}
-
-auto seek(SDL_RWops* ops, Sint64 offset, int whence) noexcept -> Sint64
-{
-	auto data = reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
-	auto position = data->position;
-
-	switch (whence) {
-	case RW_SEEK_SET:
-		position = offset;
-		break;
-	case RW_SEEK_CUR:
-		position = data->position + offset;
-		break;
-	case RW_SEEK_END:
-		position = data->length + offset;
-		break;
-	default:
-		break;
-	}
-
-	if (static_cast<std::uint64_t>(position) > data->length)
-		position = data->length;
-
-	return (data->position = position);
-}
-
-auto read(SDL_RWops* ops, void* dst, std::size_t size, std::size_t number) noexcept -> std::size_t
-{
-	auto data = reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
-	auto total = number * size;
-	auto avail = data->length - data->position;
-
-	if (number <= 0U || size <= 0U || ((total / number) != static_cast<std::size_t>(size)))
-		return 0;
-	if (total > avail)
-		total = avail;
-
-	SDL_memcpy(dst, &data->data[data->position], total);
-	data->position += total;
-
-	return total / size;
-}
-
-auto write(SDL_RWops*, const void*, std::size_t, std::size_t) noexcept -> std::size_t
-{
-	SDL_SetError("write not supported");
-	return -1;
-}
-
-auto close(SDL_RWops* ops) noexcept -> int
-{
-	if (ops != nullptr) {
-		delete reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
-		SDL_FreeRW(ops);
-	}
-
-	return 0;
-}
-
-} // !namespace
-
-auto SDLx_RWFromBinary(std::string data) noexcept -> SDL_RWops*
-{
-	SDL_RWops* ops = SDL_AllocRW();
-
-	if (ops == nullptr)
-		return nullptr;
-
-	ops->hidden.unknown.data1 = new (std::nothrow) buffer(std::move(data));
-
-	if (ops->hidden.unknown.data1 == nullptr) {
-		SDL_SetError("%s", std::strerror(errno));
-		SDL_FreeRW(ops);
-
-		return nullptr;
-	}
-
-	ops->type = SDL_RWOPS_UNKNOWN;
-	ops->seek = seek;
-	ops->size = size;
-	ops->read = read;
-	ops->write = write;
-	ops->close = close;
-
-	return ops;
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/sdl_util.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * sdl_util.hpp -- common SDL2 related code
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_SDL_UTIL_HPP
-#define MALIKANIA_SDL_UTIL_HPP
-
-/**
- * \file sdl_util.hpp
- * \brief Utilities for SDL backend.
- */
-
-#include <SDL.h>
-
-#include <string>
-
-namespace mlk::client {
-
-/**
- * Create a SDL_RWops that owns the binary data.
- *
- * This is a safe alternative to SDL_RWFromMem because it owns the memory
- * pointed by data until it is closed. The data is moved so there are no copies.
- *
- * The stream has read-only support and can not write.
- *
- * Seeking past-the-end or past-the-begin readjust the position to the end or
- * begin respectively.
- *
- * \param data the data
- * \return the object or nullptr on errors
- */
-auto SDLx_RWFromBinary(std::string data) noexcept -> SDL_RWops*;
-
-} // !mlk::client
-
-#endif // !MALIKANIA_SDL_UTIL_HPP
--- a/libclient/malikania/client/sprite.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * sprite.cpp -- image sprite
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "sprite.hpp"
-
-namespace mlk::client {
-
-sprite::sprite(image image,
-               size cell,
-               size margin,
-               size space,
-               size dimension) noexcept
-	: image_(std::move(image))
-	, cell_(std::move(cell))
-	, margin_(std::move(margin))
-	, space_(std::move(space))
-	, size_(std::move(dimension))
-{
-	assert(cell_.width > 0);
-	assert(cell_.height > 0);
-
-	// If size is not specified, take from image.
-	if (size_.is_null())
-		size_ = image_.get_size();
-
-	// Compute number of cells.
-	rows_ = (size_.height - (margin_.height * 2) + space_.height) / (cell_.height + space_.height);
-	columns_ = (size_.width - (margin_.width * 2) + space_.width) / (cell_.width + space_.width);
-}
-
-auto sprite::get_image() const noexcept -> const image&
-{
-        return image_;
-}
-
-auto sprite::get_image() noexcept -> image&
-{
-        return image_;
-}
-
-auto sprite::get_cell() const noexcept -> const size&
-{
-        return cell_;
-}
-
-auto sprite::get_margin() const noexcept -> const size&
-{
-        return margin_;
-}
-
-auto sprite::get_space() const noexcept -> const size&
-{
-        return space_;
-}
-
-auto sprite::get_rows() const noexcept -> unsigned
-{
-        return rows_;
-}
-
-auto sprite::get_columns() const noexcept -> unsigned
-{
-        return columns_;
-}
-
-auto sprite::get_total() const noexcept -> unsigned
-{
-        return columns_ * rows_;
-}
-
-void sprite::draw(window& window, unsigned cell, const point& point)
-{
-	assert(cell < rows_ * columns_);
-
-	// Compute index in the grid.
-	unsigned hindex = (cell % columns_);
-	unsigned vindex = (cell / columns_);
-
-	// Compute the pixel boundaries.
-	int x = margin_.width + (hindex * space_.width) + (hindex * cell_.width);
-	int y = margin_.height + (vindex * space_.height) + (vindex * cell_.height);
-
-	rectangle source{x, y, cell_.width, cell_.height};
-	rectangle target{point.x, point.y, cell_.width, cell_.height};
-
-	image_.draw(window, source, target);
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/sprite.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-/*
- * sprite.hpp -- image sprite
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_SPRITE_HPP
-#define MALIKANIA_CLIENT_SPRITE_HPP
-
-/**
- * \file sprite.hpp
- * \brief Sprite description.
- */
-
-#include "image.hpp"
-
-namespace mlk {
-
-struct point;
-
-namespace client {
-
-/**
- * \brief A Sprite is an image divided into cells.
- */
-class sprite {
-private:
-	image image_;
-	size cell_;
-	size margin_;
-	size space_;
-	size size_;
-	unsigned rows_;
-	unsigned columns_;
-
-public:
-	/**
-	 * Construct a sprite.
-	 *
-	 * \pre cell must not have height or width null
-	 * \param image the image to use
-	 * \param cell size of cell in the image
-	 * \param margin the optional space from borders
-	 * \param space the optional space between cells
-	 * \param dimension the sprite size (if 0, taken from the image)
-	 */
-	sprite(image image,
-	       size cell,
-	       size margin = { 0, 0 },
-	       size space = { 0, 0 },
-	       size dimension = { 0, 0 }) noexcept;
-
-	/**
-	 * Get the underlying image.
-	 *
-	 * \return the image
-	 */
-	auto get_image() const noexcept -> const image&;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the image
-	 */
-	auto get_image() noexcept -> image&;
-
-	/**
-	 * Get the cell size.
-	 *
-	 * \return the cell size
-	 */
-	auto get_cell() const noexcept -> const size&;
-
-	/**
-	 * Get the margin size.
-	 *
-	 * \return the margin size
-	 */
-	auto get_margin() const noexcept -> const size&;
-
-	/**
-	 * Get the space size.
-	 *
-	 * \return the space size
-	 */
-	auto get_space() const noexcept -> const size&;
-
-	/**
-	 * Get the number of rows in the grid.
-	 *
-	 * \return the number of rows
-	 */
-	auto get_rows() const noexcept -> unsigned;
-
-	/**
-	 * Get the number of columns in the grid.
-	 *
-	 * \return the number of columns
-	 */
-	auto get_columns() const noexcept -> unsigned;
-
-	/**
-	 * Get the max number of image in the sprite.
-	 *
-	 * \return the total
-	 */
-	auto get_total() const noexcept -> unsigned;
-
-	/**
-	 * Draw the sprite into the window at the specified position.
-	 *
-	 * \pre cell < rows() * columns()
-	 * \param window the window
-	 * \param cell the cell index
-	 * \param position the position in the window
-	 */
-	void draw(window& window, unsigned cell, const point& position);
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_SPRITE_HPP
--- a/libclient/malikania/client/state.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * state.hpp -- game client state
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_STATE_HPP
-#define MALIKANIA_CLIENT_STATE_HPP
-
-/**
- * \file state.hpp
- * \brief Game client state.
- */
-
-#include "dispatcher.hpp"
-
-namespace mlk::client {
-
-class client;
-
-/**
- * \brief Game client state.
- */
-class state : public dispatcher {
-public:
-	/**
-	 * Default constructor.
-	 */
-	state() noexcept = default;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~state() noexcept = default;
-
-	/**
-	 * Update the state.
-	 *
-	 * \param clt the client
-	 */
-	virtual void update(client& clt)
-	{
-		(void)clt;
-	}
-
-	/**
-	 * Draw the state.
-	 *
-	 * \param clt the client
-	 */
-	virtual void draw(client& clt)
-	{
-		(void)clt;
-	}
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_STATE_HPP
--- a/libclient/malikania/client/state/lobby_state.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- * lobby_state.cpp -- character management state
- *
- * Copyright (c) 2013-2018 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 "client.hpp"
-#include "color.hpp"
-#include "lobby_state.hpp"
-#include "map_state.hpp"
-#include "theme.hpp"
-#include "window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-lobby_state::lobby_state(client& client)
-	: client_(client)
-	, names_{"erekin", "luna"}
-{
-}
-
-void lobby_state::handle_key_down(const key_event& ev)
-{
-	if (ev.keycode == key::down)
-		index_ = index_ == names_.size() - 1 ? 0 : index_ + 1;
-	else if (ev.keycode == key::up)
-		index_ = index_ == 0 ? names_.size() - 1 : index_ - 1;
-	else
-		client_.set_state(std::make_unique<map_state>());
-}
-
-void lobby_state::update(client&)
-{
-}
-
-void lobby_state::draw(client& clt)
-{
-	// TODO: lobby_window.
-	auto& win = clt.window();
-
-	win.set_drawing_color(color::from_hex(0xffffffff));
-	win.clear();
-	win.set_drawing_color(color::from_hex(0xff000000));
-	win.draw_text("Select your player", theme::get_default().get_font(), {20, 10});
-
-	std::size_t i = 0U;
-	for (const auto& n : names_) {
-		int y = 30 + (10 * i);
-
-		if (i++ == index_)
-			win.draw_text("»", theme::get_default().get_font(), {10, y});
-
-		win.draw_text(n, theme::get_default().get_font(), {20, y});
-	}
-
-	win.present();
-}
-
-} // !client
-
-} // !mlk
--- a/libclient/malikania/client/state/lobby_state.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * lobby_state.hpp -- character management state
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOBBY_STATE_HPP
-#define MALIKANIA_CLIENT_LOBBY_STATE_HPP
-
-/**
- * \file lobby_state.hpp
- * \brief Character management state.
- */
-
-#include <string>
-#include <vector>
-
-#include "state.hpp"
-
-namespace mlk::client {
-
-/**
- * \brief Character management state.
- */
-class lobby_state : public state {
-private:
-	client& client_;
-	std::vector<std::string> names_;
-	std::size_t index_{0};
-
-public:
-	lobby_state(client& client);
-
-	void handle_key_down(const key_event& ev) override;
-
-	void update(client&) override;
-
-	void draw(client& clt) override;
-};
-
-} // !mlk::client
-
-#endif // !MALIKANIA_CLIENT_LOBBY_STATE_HPP
--- a/libclient/malikania/client/state/login_state.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * login_state.cpp -- login state
- *
- * Copyright (c) 2013-2018 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.hpp"
-#include "client.hpp"
-#include "login_state.hpp"
-#include "lobby_state.hpp"
-#include "theme.hpp"
-#include "unicode.hpp"
-#include "window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-void login_state::do_connect()
-{
-	state_ = state_t::connecting;
-	status_ = "Connecting...";
-	client_.connect("localhost", 3320);
-}
-
-void login_state::do_auth()
-{
-	status_ = "Authenticating";
-	state_ = state_t::authenticating;
-	client_.send({
-		{ "command",    "auth"                          },
-		{ "login",      unicode::to_utf8(login_)        },
-		{ "password",   unicode::to_utf8(password_)     }
-	});
-}
-
-login_state::login_state(client& clt)
-	: client_(clt)
-{
-	client_.window().start_edit();
-}
-
-void login_state::handle_text(const text_event& ev)
-{
-	if (index_ == 0)
-		login_ += ev.text;
-	else
-		password_ += ev.text;
-}
-
-void login_state::update(client&)
-{
-}
-
-void login_state::handle_connect(const std::error_code& code)
-{
-	if (code)
-		status_ = code.message();
-	else
-		do_auth();
-}
-
-void login_state::handle_message(const std::error_code& code, const nlohmann::json& msg)
-{
-	if (code)
-		status_ = code.message();
-	else
-		status_ = msg["error"].get<std::string>();
-}
-
-void login_state::handle_key_down(const key_event& ev)
-{
-	switch (ev.keycode) {
-	case key::tab:
-		index_ ++;
-
-		if (index_ == 2)
-			index_ = 0;
-		break;
-	case key::backspace:
-		if (index_ == 0)
-			login_.pop_back();
-		else
-			password_.pop_back();
-		break;
-	case key::enter:
-		if (index_ == 1 && state_ == state_t::disconnected)
-			do_connect();
-		break;
-	default:
-		break;
-	}
-}
-
-void login_state::draw(client& clt)
-{
-	auto& win = clt.window();
-	const auto& font = theme::get_default().get_font();
-
-	win.set_drawing_color(color::from_hex(0xffffffff));
-	win.clear();
-	win.set_drawing_color(color::from_hex(0xff000000));
-	win.draw_text("Please log in", font, {10, 10});
-	win.draw_text("login: ", font, {10, 50});
-	win.draw_text("password: ", font, {10, 70});
-
-	if (!login_.empty())
-		win.draw_text(unicode::to_utf8(login_), font, {70, 50});
-	if (!password_.empty())
-		win.draw_text(std::string(password_.length(), '*'), font, {70, 70});
-	if (!status_.empty())
-		win.draw_text(status_, font, {70, 90});
-
-	win.present();
-}
-
-} // !client
-
-} // !mlk
--- a/libclient/malikania/client/state/login_state.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * login_state.cpp -- login state
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOGIN_STATE_HPP
-#define MALIKANIA_CLIENT_LOGIN_STATE_HPP
-
-/**
- * \file login_state.hpp
- * \brief Login state.
- */
-
-#include <malikania/client/state.hpp>
-
-namespace mlk {
-
-namespace client {
-
-/**
- * \brief Login state.
- */
-class login_state : public state {
-private:
-	enum class state_t {
-		disconnected,
-		connecting,
-		authenticating
-	};
-
-	client& client_;
-	state_t state_{state_t::disconnected};
-	std::size_t index_{0};
-	std::u32string login_;
-	std::u32string password_;
-	std::string status_;
-
-	void do_connect();
-	void do_auth();
-
-public:
-	login_state(client& clt);
-
-	void update(client&) override;
-	void draw(client& clt) override;
-	void handle_connect(const std::error_code& code) override;
-	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
-	void handle_key_down(const key_event& ev) override;
-	void handle_text(const text_event& ev) override;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_LOGIN_STATE_HPP
--- a/libclient/malikania/client/state/map_state.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-#include "client.hpp"
-#include "color.hpp"
-#include "map_state.hpp"
-#include "point.hpp"
-#include "rectangle.hpp"
-#include "theme.hpp"
-#include "window.hpp"
-
-namespace mlk {
-
-namespace client {
-
-void map_state::handle_key_down(const key_event& ev)
-{
-	if (ev.keycode == key::down)
-		delta_ = {delta_.x, 1};
-	else if (ev.keycode == key::up)
-		delta_ = {delta_.x, -1};
-	else if (ev.keycode == key::left)
-		delta_ = {-1, delta_.y};
-	else if (ev.keycode == key::right)
-		delta_ = {1, delta_.y};
-}
-
-void map_state::handle_key_up(const key_event& ev)
-{
-	if (ev.keycode == key::down || ev.keycode == key::up)
-		delta_ = {delta_.x, 0};
-	else if (ev.keycode == key::left || ev.keycode == key::right)
-		delta_ = {0, delta_.y};
-}
-
-void map_state::update(client&)
-{
-	position_ = {
-		position_.x + delta_.x,
-		position_.y + delta_.y
-	};
-}
-
-void map_state::draw(client& clt)
-{
-	auto& win = clt.window();
-
-	win.set_drawing_color(color::from_hex(0xffffffff));
-	win.clear();
-	win.set_drawing_color(color::from_hex(0xff000000));
-
-	// Draw a little square as user position.
-	win.fill_rectangle({ position_.x, position_.y, 10, 10 });
-
-	// Draw nickname on top of it.
-	auto clip = theme::get_default().get_font().clip("molko");
-
-	win.draw_text("molko", theme::get_default().get_font(), point{
-		static_cast<int>(position_.x - (clip.width / 2)),
-		static_cast<int>(position_.y - clip.height - 10)
-	});
-	win.present();
-}
-
-} // !client
-
-} // !mlk
--- a/libclient/malikania/client/state/map_state.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-/*
- * map_state.hpp -- main game state
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_MAP_STATE_HPP
-#define MALIKANIA_CLIENT_MAP_STATE_HPP
-
-/**
- * \file map_state.hpp
- * \brief Main game state.
- */
-
-#include "point.hpp"
-#include "state.hpp"
-
-namespace mlk {
-
-namespace client {
-
-/**
- * \brief Main game state.
- */
-class map_state : public state {
-private:
-	point position_{320, 240};
-	point delta_;
-
-public:
-	void update(client&) override;
-	void draw(client& clt) override;
-	void handle_key_down(const key_event& ev) override;
-	void handle_key_up(const key_event& ev) override;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_MAP_STATE_HPP
--- a/libclient/malikania/client/theme.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * theme.cpp -- theming support for gui elements
- *
- * Copyright (c) 2013-2018 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 "button.hpp"
-#include "color.hpp"
-#include "label.hpp"
-#include "point.hpp"
-#include "rectangle.hpp"
-#include "theme.hpp"
-#include "window.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-#include <dejavu_sans.hpp>
-
-} // !namespace
-
-std::unique_ptr<theme> theme::global;
-
-auto theme::get_default() noexcept -> theme&
-{
-	if (!global)
-		global = std::make_unique<theme>();
-
-	return *global;
-}
-
-void theme::set_default(std::unique_ptr<theme> theme) noexcept
-{
-	global = std::move(theme);
-}
-
-theme::theme()
-	: font_(std::string(dejavu_sans, sizeof (dejavu_sans)), 10)
-	, background_color_{228, 228, 228, 255}
-	, border_color_{102, 102, 102, 255}
-	, text_color_{50, 50, 50, 255}
-{
-}
-
-auto theme::get_font() const noexcept -> const font&
-{
-	return font_;
-}
-
-auto theme::get_font() noexcept -> font&
-{
-	return font_;
-}
-
-auto theme::get_background_color() const noexcept -> const color&
-{
-	return background_color_;
-}
-
-auto theme::get_border_color() const noexcept -> const color&
-{
-	return border_color_;
-}
-
-auto theme::get_text_color() const noexcept -> const color&
-{
-	return text_color_;
-}
-
-void theme::draw_button(window& w, const button& b, const rectangle& rect)
-{
-	// Border.
-	w.set_drawing_color(border_color_);
-	w.draw_rectangle({ rect.x, rect.y, rect.width, rect.height });
-
-	// Box content.
-	w.set_drawing_color(background_color_);
-	w.fill_rectangle({ rect.x + 2, rect.y + 2, rect.width - 2, rect.height - 2 });
-
-	// Text.
-	// TODO: alignment.
-	w.set_drawing_color(text_color_);
-	w.draw_text(b.get_text(), font_, point{rect.x + 5, rect.y + 5});
-}
-
-void theme::draw_label(window& w, const label& label, const rectangle& rect)
-{
-	w.set_drawing_color(text_color_);
-	w.draw_text(label.get_text(), font_, point{rect.x, rect.y});
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/theme.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * theme.hpp -- theming support for gui elements
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_THEME_HPP
-#define MALIKANIA_CLIENT_THEME_HPP
-
-/**
- * \file theme.hpp
- * \brief Theming support for gui elements.
- */
-
-#include <memory>
-
-#include "color.hpp"
-#include "font.hpp"
-#include "size.hpp"
-
-namespace mlk {
-
-struct rectangle;
-
-namespace client {
-
-class button;
-class label;
-class window;
-
-/**
- * \brief Reimplement this class to provide your own theme.
- */
-class theme {
-private:
-	static std::unique_ptr<theme> global;
-
-	font font_;
-	color background_color_;
-	color border_color_;
-	color text_color_;
-
-public:
-	static auto get_default() noexcept -> theme&;
-
-	static void set_default(std::unique_ptr<theme> theme) noexcept;
-
-	/**
-	 * Default constructor.
-	 */
-	theme();
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~theme() noexcept = default;
-
-	/**
-	 * Get the font.
-	 *
-	 * \return the font
-	 */
-	virtual auto get_font() const noexcept -> const font&;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the font
-	 */
-	virtual auto get_font() noexcept -> font&;
-
-	/**
-	 * Get the background color for basic widgets.
-	 *
-	 * \return the color
-	 */
-	virtual auto get_background_color() const noexcept -> const color&;
-
-	/**
-	 * Get the border color for basic widgets.
-	 *
-	 * \return the color
-	 */
-	virtual auto get_border_color() const noexcept -> const color&;
-
-	/**
-	 * Get the text color for basic widgets.
-	 *
-	 * \return the color
-	 */
-	virtual auto get_text_color() const noexcept -> const color&;
-
-	/**
-	 * Draw a button.
-	 *
-	 * \param w the main window
-	 * \param b the button
-	 */
-	virtual void draw_button(window& w, const button& b, const rectangle& rect);
-
-	/**
-	 * Draw a label.
-	 *
-	 * \param w the main window
-	 * \param l the label
-	 * \param rect the bounding rectangle.
-	 */
-	virtual void draw_label(window& w, const label& l, const rectangle& rect);
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_THEME_HPP
--- a/libclient/malikania/client/widget.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * widget.cpp -- basic abstract widget for GUI
- *
- * Copyright (c) 2013-2018 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 "theme.hpp"
-#include "widget.hpp"
-
-namespace mlk::client {
-
-widget::widget() noexcept
-	: theme_(std::addressof(theme::get_default()))
-{
-}
-
-auto widget::get_position() const noexcept -> const point&
-{
-	return position_;
-}
-
-void widget::set_position(point position) noexcept
-{
-	position_ = std::move(position);
-}
-
-auto widget::get_theme() const noexcept -> const theme&
-{
-	return *theme_;
-}
-
-auto widget::get_theme() noexcept -> theme&
-{
-	return *theme_;
-}
-
-void widget::set_theme(theme& theme) noexcept
-{
-	theme_ = std::addressof(theme);
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/widget.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-/*
- * widget.hpp -- basic abstract widget for GUI
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_WIDGET_HPP
-#define MALIKANIA_CLIENT_WIDGET_HPP
-
-/**
- * \file widget.hpp
- * \brief Basic abstract widget for GUI
- */
-
-#include "dispatcher.hpp"
-#include "point.hpp"
-
-namespace mlk {
-
-struct rectangle;
-struct size;
-
-namespace client {
-
-class window;
-class theme;
-
-/**
- * \brief Abstract widget
- */
-class widget : public dispatcher {
-protected:
-	theme* theme_;                  //!< widget theme
-	point position_;                //!< widget position
-
-public:
-	/**
-	 * Constructor.
-	 *
-	 * Initialize the theme with the global one.
-	 */
-	widget() noexcept;
-
-	/**
-	 * Default destructor.
-	 */
-	virtual ~widget() noexcept = default;
-
-	/**
-	 * Get the widget position.
-	 *
-	 * \return the position
-	 */
-	auto get_position() const noexcept -> const point&;
-
-	/**
-	 * Set the widget position.
-	 *
-	 * \param position the new position
-	 */
-	void set_position(point position) noexcept;
-
-	/**
-	 * Get the theme.
-	 *
-	 * \return the theme
-	 */
-	auto get_theme() const noexcept -> const theme&;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the theme
-	 */
-	auto get_theme() noexcept -> theme&;
-
-	/**
-	 * Change the widget theme.
-	 *
-	 * \param theme the new theme
-	 */
-	void set_theme(theme& theme) noexcept;
-
-	/**
-	 * Draw the widget at the specified position.
-	 *
-	 * \param w the window
-	 * \param size the maximum size allowed
-	 */
-	virtual void draw(window& w, const size& size) = 0;
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_WIDGET_HPP
--- a/libclient/malikania/client/window.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,588 +0,0 @@
-/*
- * window.cpp -- main window and basic drawing
- *
- * Copyright (c) 2013-2018 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 <iostream>
-#include <stdexcept>
-
-#include <malikania/unicode.hpp>
-
-#include "color.hpp"
-#include "line.hpp"
-#include "rectangle.hpp"
-#include "theme.hpp"
-#include "widget.hpp"
-#include "window.hpp"
-
-namespace mlk::client {
-
-namespace {
-
-/*
- * conversion maps
- * ------------------------------------------------------------------
- *
- * There is a difference between scancode and key codes. The scancode is the
- * key located on the keyboard which is always the same, the key code is the
- * translated to the user layout.
- *
- * For example, on azerty, hitting the 'a' key makes a key code of 'a' but a
- * scancode of 'q'.
- *
- * - scancodes_map	: convert SDL scancode do key
- * - keycodes_map	: convert SDL key code to key
- * - modifiers_map	: convert masks
- * - button_map		: convert SDL mouse buttons
- */
-
-const std::unordered_map<SDL_Keycode, key> scancodes_map{
-	{ SDL_SCANCODE_A,                       key::a                  },
-	{ SDL_SCANCODE_B,                       key::b                  },
-	{ SDL_SCANCODE_C,                       key::c                  },
-	{ SDL_SCANCODE_D,                       key::d                  },
-	{ SDL_SCANCODE_E,                       key::e                  },
-	{ SDL_SCANCODE_F,                       key::f                  },
-	{ SDL_SCANCODE_G,                       key::g                  },
-	{ SDL_SCANCODE_H,                       key::h                  },
-	{ SDL_SCANCODE_I,                       key::i                  },
-	{ SDL_SCANCODE_J,                       key::j                  },
-	{ SDL_SCANCODE_K,                       key::k                  },
-	{ SDL_SCANCODE_L,                       key::l                  },
-	{ SDL_SCANCODE_M,                       key::m                  },
-	{ SDL_SCANCODE_N,                       key::n                  },
-	{ SDL_SCANCODE_O,                       key::o                  },
-	{ SDL_SCANCODE_P,                       key::p                  },
-	{ SDL_SCANCODE_Q,                       key::q                  },
-	{ SDL_SCANCODE_R,                       key::r                  },
-	{ SDL_SCANCODE_S,                       key::s                  },
-	{ SDL_SCANCODE_T,                       key::t                  },
-	{ SDL_SCANCODE_U,                       key::u                  },
-	{ SDL_SCANCODE_V,                       key::v                  },
-	{ SDL_SCANCODE_W,                       key::w                  },
-	{ SDL_SCANCODE_X,                       key::x                  },
-	{ SDL_SCANCODE_Y,                       key::y                  },
-	{ SDL_SCANCODE_Z,                       key::z                  },
-	{ SDL_SCANCODE_1,                       key::one                },
-	{ SDL_SCANCODE_2,                       key::two                },
-	{ SDL_SCANCODE_3,                       key::three              },
-	{ SDL_SCANCODE_4,                       key::four               },
-	{ SDL_SCANCODE_5,                       key::five               },
-	{ SDL_SCANCODE_6,                       key::six                },
-	{ SDL_SCANCODE_7,                       key::seven              },
-	{ SDL_SCANCODE_8,                       key::eight              },
-	{ SDL_SCANCODE_9,                       key::nine               },
-	{ SDL_SCANCODE_0,                       key::zero               },
-	{ SDL_SCANCODE_RETURN,                  key::enter              },
-	{ SDL_SCANCODE_ESCAPE,                  key::escape             },
-	{ SDL_SCANCODE_BACKSPACE,               key::backspace          },
-	{ SDL_SCANCODE_TAB,                     key::tab                },
-	{ SDL_SCANCODE_SPACE,                   key::space              },
-	{ SDL_SCANCODE_MINUS,                   key::minus              },
-	{ SDL_SCANCODE_EQUALS,                  key::equals             },
-	{ SDL_SCANCODE_LEFTBRACKET,             key::left_bracket       },
-	{ SDL_SCANCODE_RIGHTBRACKET,            key::right_bracket      },
-	{ SDL_SCANCODE_BACKSLASH,               key::backslash          },
-	{ SDL_SCANCODE_NONUSHASH,               key::hash               },
-	{ SDL_SCANCODE_SEMICOLON,               key::semicolon          },
-	{ SDL_SCANCODE_APOSTROPHE,              key::apostrophe         },
-	{ SDL_SCANCODE_GRAVE,                   key::grave              },
-	{ SDL_SCANCODE_COMMA,                   key::comma              },
-	{ SDL_SCANCODE_PERIOD,                  key::period             },
-	{ SDL_SCANCODE_SLASH,                   key::slash              },
-	{ SDL_SCANCODE_CAPSLOCK,                key::caps_lock          },
-	{ SDL_SCANCODE_F1,                      key::f1                 },
-	{ SDL_SCANCODE_F2,                      key::f2                 },
-	{ SDL_SCANCODE_F3,                      key::f3                 },
-	{ SDL_SCANCODE_F4,                      key::f4                 },
-	{ SDL_SCANCODE_F5,                      key::f5                 },
-	{ SDL_SCANCODE_F6,                      key::f6                 },
-	{ SDL_SCANCODE_F7,                      key::f7                 },
-	{ SDL_SCANCODE_F8,                      key::f8                 },
-	{ SDL_SCANCODE_F9,                      key::f9                 },
-	{ SDL_SCANCODE_F10,                     key::f10                },
-	{ SDL_SCANCODE_F11,                     key::f11                },
-	{ SDL_SCANCODE_F12,                     key::f12                },
-	{ SDL_SCANCODE_F13,                     key::f13                },
-	{ SDL_SCANCODE_F14,                     key::f14                },
-	{ SDL_SCANCODE_F15,                     key::f15                },
-	{ SDL_SCANCODE_F16,                     key::f16                },
-	{ SDL_SCANCODE_F17,                     key::f17                },
-	{ SDL_SCANCODE_F18,                     key::f18                },
-	{ SDL_SCANCODE_F19,                     key::f19                },
-	{ SDL_SCANCODE_F20,                     key::f20                },
-	{ SDL_SCANCODE_F21,                     key::f21                },
-	{ SDL_SCANCODE_F22,                     key::f22                },
-	{ SDL_SCANCODE_F23,                     key::f23                },
-	{ SDL_SCANCODE_F24,                     key::f24                },
-	{ SDL_SCANCODE_PRINTSCREEN,             key::print_screen       },
-	{ SDL_SCANCODE_SCROLLLOCK,              key::scroll_lock        },
-	{ SDL_SCANCODE_PAUSE,                   key::pause              },
-	{ SDL_SCANCODE_INSERT,                  key::insert             },
-	{ SDL_SCANCODE_HOME,                    key::home               },
-	{ SDL_SCANCODE_PAGEUP,                  key::page_up            },
-	{ SDL_SCANCODE_PAGEDOWN,                key::page_down          },
-	{ SDL_SCANCODE_DELETE,                  key::del                },
-	{ SDL_SCANCODE_END,                     key::end                },
-	{ SDL_SCANCODE_RIGHT,                   key::right              },
-	{ SDL_SCANCODE_LEFT,                    key::left               },
-	{ SDL_SCANCODE_DOWN,                    key::down               },
-	{ SDL_SCANCODE_UP,                      key::up	                },
-	{ SDL_SCANCODE_KP_DIVIDE,               key::kp_divide          },
-	{ SDL_SCANCODE_KP_MULTIPLY,             key::kp_multiply        },
-	{ SDL_SCANCODE_KP_MINUS,                key::kp_minus           },
-	{ SDL_SCANCODE_KP_PLUS,                 key::kp_plus            },
-	{ SDL_SCANCODE_KP_ENTER,                key::kp_enter           },
-	{ SDL_SCANCODE_KP_1,                    key::kp_one             },
-	{ SDL_SCANCODE_KP_2,                    key::kp_two             },
-	{ SDL_SCANCODE_KP_3,                    key::kp_three           },
-	{ SDL_SCANCODE_KP_4,                    key::kp_four            },
-	{ SDL_SCANCODE_KP_5,                    key::kp_five            },
-	{ SDL_SCANCODE_KP_6,                    key::kp_six             },
-	{ SDL_SCANCODE_KP_7,                    key::kp_seven           },
-	{ SDL_SCANCODE_KP_8,                    key::kp_eight           },
-	{ SDL_SCANCODE_KP_9,                    key::kp_nine            },
-	{ SDL_SCANCODE_KP_0,                    key::kp_zero            },
-	{ SDL_SCANCODE_MUTE,                    key::mute               },
-	{ SDL_SCANCODE_VOLUMEUP,                key::volume_up          },
-	{ SDL_SCANCODE_VOLUMEDOWN,              key::volume_down        },
-	{ SDL_SCANCODE_LCTRL,                   key::left_control       },
-	{ SDL_SCANCODE_LALT,                    key::left_alt           },
-	{ SDL_SCANCODE_LSHIFT,                  key::left_shift         },
-	{ SDL_SCANCODE_LGUI,                    key::left_super         },
-	{ SDL_SCANCODE_RCTRL,                   key::right_control      },
-	{ SDL_SCANCODE_RALT,                    key::right_alt          },
-	{ SDL_SCANCODE_RSHIFT,                  key::right_shift        },
-	{ SDL_SCANCODE_RGUI,                    key::right_super        }
-};
-
-const std::unordered_map<SDL_Keycode, key> keycodes_map{
-	{ 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_parenthese    },
-	{ SDLK_RIGHTPAREN,                      key::right_parenthese   },
-	{ 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::zero               },
-	{ SDLK_1,                               key::one                },
-	{ SDLK_2,                               key::two                },
-	{ SDLK_3,                               key::three              },
-	{ SDLK_4,                               key::four               },
-	{ SDLK_5,                               key::five               },
-	{ SDLK_6,                               key::six                },
-	{ SDLK_7,                               key::seven              },
-	{ SDLK_8,                               key::eight              },
-	{ SDLK_9,                               key::nine               },
-	{ 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::back_quote         },
-	{ 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::caps_lock          },
-	{ 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::print_screen       },
-	{ SDLK_SCROLLLOCK,                      key::scroll_lock        },
-	{ SDLK_PAUSE,                           key::pause              },
-	{ SDLK_INSERT,                          key::insert             },
-	{ SDLK_HOME,                            key::home               },
-	{ SDLK_PAGEUP,                          key::page_up            },
-	{ SDLK_PAGEDOWN,                        key::page_down          },
-	{ SDLK_DELETE,                          key::del                },
-	{ SDLK_END,                             key::end                },
-	{ 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_one             },
-	{ SDLK_KP_2,                            key::kp_two             },
-	{ SDLK_KP_3,                            key::kp_three           },
-	{ SDLK_KP_4,                            key::kp_four            },
-	{ SDLK_KP_5,                            key::kp_five            },
-	{ SDLK_KP_6,                            key::kp_six             },
-	{ SDLK_KP_7,                            key::kp_seven           },
-	{ SDLK_KP_8,                            key::kp_eight           },
-	{ SDLK_KP_9,                            key::kp_nine            },
-	{ SDLK_KP_0,                            key::kp_zero            },
-	{ SDLK_MUTE,                            key::mute               },
-	{ SDLK_VOLUMEUP,                        key::volume_up          },
-	{ SDLK_VOLUMEDOWN,                      key::volume_down        },
-	{ SDLK_LCTRL,                           key::left_control       },
-	{ SDLK_LALT,                            key::left_alt           },
-	{ SDLK_LSHIFT,                          key::left_shift         },
-	{ SDLK_LGUI,                            key::left_super         },
-	{ SDLK_RCTRL,                           key::right_control      },
-	{ SDLK_RALT,                            key::right_alt          },
-	{ SDLK_RSHIFT,                          key::right_shift        },
-	{ SDLK_RGUI,                            key::right_super        }
-};
-
-const std::unordered_map<SDL_Keymod, mod> modifiers_map{
-	{ KMOD_LCTRL,                           mod::left_control       },
-	{ KMOD_LALT,                            mod::left_alt           },
-	{ KMOD_LSHIFT,                          mod::left_shift         },
-	{ KMOD_LGUI,                            mod::left_super         },
-	{ KMOD_RCTRL,                           mod::right_control      },
-	{ KMOD_RALT,                            mod::right_alt          },
-	{ KMOD_RSHIFT,                          mod::right_shift        },
-	{ KMOD_RGUI,                            mod::right_super        },
-	{ KMOD_NUM,                             mod::num_lock           },
-	{ KMOD_CAPS,                            mod::caps_lock          }
-};
-
-const std::unordered_map<Uint32, mouse> button_map{
-	{ SDL_BUTTON_LEFT,                      mouse::left             },
-	{ SDL_BUTTON_RIGHT,                     mouse::right            },
-	{ SDL_BUTTON_MIDDLE,                    mouse::middle           }
-};
-
-} // !namespace
-
-void window::dispatch_key_event(dispatcher& dp, const SDL_Event& ev) const
-{
-	assert(ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP);
-
-	// Key and scan code.
-	const auto kc = keycodes_map.find(ev.key.keysym.sym);
-	const auto sc = scancodes_map.find(ev.key.keysym.scancode);
-
-	if (kc == keycodes_map.end() || sc == scancodes_map.end())
-		return;
-
-	key_event kev{kc->second, sc->second, mod::none};
-
-	// Modifier is a mask.
-	for (const auto& pair : modifiers_map)
-		if (ev.key.keysym.mod & pair.first)
-			kev.modifiers |= pair.second;
-
-	if (ev.type == SDL_KEYDOWN)
-		dp.handle_key_down(kev);
-	else
-		dp.handle_key_up(kev);
-}
-
-void window::dispatch_mouse_event(dispatcher& dp, const SDL_Event& ev) const
-{
-	assert(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP);
-
-	const auto which = button_map.find(ev.button.button);
-
-	if (which == button_map.end())
-		return;
-
-	mouse_click_event mev{
-		which->second,
-		point{ev.button.x, ev.button.y}
-	};
-
-	if (ev.type == SDL_MOUSEBUTTONDOWN)
-		dp.handle_mouse_down(mev);
-	else
-		dp.handle_mouse_up(mev);
-}
-
-void window::dispatch_mouse_wheel_event(dispatcher& dp, const SDL_Event& ev) const
-{
-	assert(ev.type == SDL_MOUSEWHEEL);
-
-	dp.handle_mouse_wheel({{ev.wheel.x, ev.wheel.y}});
-}
-
-void window::dispatch_text_event(dispatcher& dp, const SDL_Event& ev) const
-{
-	assert(ev.type == SDL_TEXTINPUT);
-
-	dp.handle_text({unicode::to_utf32(ev.text.text)});
-}
-
-
-void window::init()
-{
-	static bool is_initialized{false};
-
-	if (!is_initialized) {
-		if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
-			throw std::runtime_error(SDL_GetError());
-		if (TTF_Init() < 0)
-			throw std::runtime_error(TTF_GetError());
-
-		is_initialized = true;
-	}
-}
-
-window::window(unsigned width, unsigned height, const std::string& title)
-{
-	init();
-
-	window_ = {
-		SDL_CreateWindow(
-			title.c_str(),
-			SDL_WINDOWPOS_UNDEFINED,
-			SDL_WINDOWPOS_UNDEFINED,
-			width, height,
-			SDL_WINDOW_OPENGL
-		),
-		SDL_DestroyWindow
-	};
-
-	if (window_ == nullptr)
-		throw std::runtime_error(SDL_GetError());
-
-	// Create renderer.
-	renderer_ = {
-		SDL_CreateRenderer(window_.get(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
-		SDL_DestroyRenderer
-	};
-
-	if (renderer_ == nullptr)
-		throw std::runtime_error(SDL_GetError());
-}
-
-auto window::get_renderer() const noexcept -> const SDL_Renderer*
-{
-	assert(is_open_);
-
-	return renderer_.get();
-}
-
-auto window::get_renderer() noexcept -> SDL_Renderer*
-{
-	assert(is_open_);
-
-	return renderer_.get();
-}
-
-auto window::is_open() const noexcept -> bool
-{
-	return is_open_;
-}
-
-void window::start_edit()
-{
-	if (!is_editing_) {
-		SDL_StartTextInput();
-		is_editing_ = true;
-	}
-}
-
-void window::stop_edit()
-{
-	if (is_editing_) {
-		SDL_StopTextInput();
-		is_editing_ = false;
-	}
-}
-
-void window::clear()
-{
-	SDL_RenderClear(renderer_.get());
-}
-
-void window::present()
-{
-	SDL_RenderPresent(renderer_.get());
-}
-
-void window::close() noexcept
-{
-	is_open_ = false;
-	renderer_ = nullptr;
-	window_ = nullptr;
-}
-
-auto window::get_drawing_color() const -> color
-{
-	SDL_Color color;
-
-	if (SDL_GetRenderDrawColor(renderer_.get(), &color.r, &color.g, &color.b, &color.a) < 0)
-		throw std::runtime_error(SDL_GetError());
-
-	return { color.r, color.g, color.b, color.a };
-}
-
-void window::set_drawing_color(const color& color)
-{
-	if (SDL_SetRenderDrawColor(renderer_.get(), color.red, color.green, color.blue, color.alpha) < 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void window::draw_line(const line& line)
-{
-	if (SDL_RenderDrawLine(renderer_.get(), line.x1, line.y1, line.x2, line.y2) != 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void window::draw_point(const point& point)
-{
-	if (SDL_RenderDrawPoint(renderer_.get(), point.x, point.y) != 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void window::draw_rectangle(const rectangle& rectangle)
-{
-	SDL_Rect rect{
-		rectangle.x,
-		rectangle.y,
-		static_cast<int>(rectangle.width),
-		static_cast<int>(rectangle.height)
-	};
-
-	if (SDL_RenderDrawRect(renderer_.get(), &rect) < 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void window::fill_rectangle(const rectangle& rectangle)
-{
-	SDL_Rect rect{
-		rectangle.x,
-		rectangle.y,
-		static_cast<int>(rectangle.width),
-		static_cast<int>(rectangle.height)
-	};
-
-	if (SDL_RenderFillRect(renderer_.get(), &rect) < 0)
-		throw std::runtime_error(SDL_GetError());
-}
-
-void window::draw_text(const std::string& text, const font& font, const rectangle& rectangle)
-{
-	SDL_Color color = { 0, 0, 0, 255 };
-	SDL_Surface* message = TTF_RenderUTF8_Blended(font.get_handle(), text.c_str(), color);
-	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get(), message);
-	SDL_Rect rect = { rectangle.x, rectangle.y, (int)rectangle.width, (int)rectangle.height };
-	SDL_RenderCopy(renderer_.get(), texture, nullptr, &rect);
-	SDL_FreeSurface(message);
-	SDL_DestroyTexture(texture);
-}
-
-void window::draw_text(const std::string& text, const font& font, const point& point)
-{
-	SDL_Color color = { 0, 0, 0, 0 };
-	SDL_GetRenderDrawColor(renderer_.get(), &color.r, &color.g, &color.b, &color.a);
-	SDL_Surface* message = TTF_RenderUTF8_Blended(font.get_handle(), text.c_str(), color);
-	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get(), message);
-	SDL_Rect rect = { point.x, point.y, message->w, message->h };
-	SDL_RenderCopy(renderer_.get(), texture, nullptr, &rect);
-	SDL_FreeSurface(message);
-	SDL_DestroyTexture(texture);
-}
-
-void window::poll(dispatcher& dp)
-{
-	SDL_Event event;
-
-	while (SDL_PollEvent(&event)) {
-		switch (event.type) {
-		case SDL_KEYUP:
-		case SDL_KEYDOWN:
-			dispatch_key_event(dp, event);
-			break;
-		case SDL_MOUSEBUTTONDOWN:
-		case SDL_MOUSEBUTTONUP:
-			dispatch_mouse_event(dp, event);
-			break;
-		case SDL_MOUSEWHEEL:
-			dispatch_mouse_wheel_event(dp, event);
-			break;
-		case SDL_TEXTINPUT:
-			dispatch_text_event(dp, event);
-			break;
-		case SDL_QUIT:
-			dp.handle_quit();
-			break;
-		default:
-			break;
-		}
-	}
-}
-
-} // !mlk::client
--- a/libclient/malikania/client/window.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-/*
- * window.hpp -- main window and basic drawing
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_WINDOW_HPP
-#define MALIKANIA_CLIENT_WINDOW_HPP
-
-/**
- * \file window.hpp
- * \brief Window and drawing.
- */
-
-#include <memory>
-#include <string>
-
-#include <SDL.h>
-
-#include "dispatcher.hpp"
-
-namespace mlk {
-
-struct line;
-struct point;
-struct rectangle;
-
-namespace client {
-
-struct color;
-
-class font;
-
-/**
- * \brief Main window class and drawing.
- */
-class window {
-private:
-	std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> window_{nullptr, nullptr};
-	std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer *)> renderer_{nullptr, nullptr};
-
-	bool is_open_{true};
-	bool is_editing_{false};
-
-	void dispatch_key_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_mouse_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_mouse_wheel_event(dispatcher&, const SDL_Event&) const;
-	void dispatch_text_event(dispatcher&, const SDL_Event&) const;
-
-	void init();
-
-public:
-	/**
-	 * Create a window.
-	 *
-	 * \param width the initial width
-	 * \param height the initial height
-	 * \param title the optional title
-	 * \throw std::runtime_error on errors
-	 */
-	window(unsigned width = 640, unsigned height = 480, const std::string& title = "Game");
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~window() noexcept = default;
-
-	/**
-	 * Get the renderer.
-	 *
-	 * \pre is_open()
-	 * \return the renderer
-	 */
-	auto get_renderer() const noexcept -> const SDL_Renderer*;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \pre is_open()
-	 * \return the renderer
-	 */
-	auto get_renderer() noexcept -> SDL_Renderer*;
-
-	/**
-	 * Tells if the window is open.
-	 *
-	 * \return true if open
-	 */
-	auto is_open() const noexcept -> bool;
-
-	/**
-	 * Start editing input.
-	 *
-	 * \note may open a visual keyboard on some platforms
-	 * \note can safely be called multiple times
-	 */
-	void start_edit();
-
-	/**
-	 * Stop editing input.
-	 *
-	 * \note may close the visual keyboard on some platforms
-	 * \note can safely be called multiple times
-	 */
-	void stop_edit();
-
-	/**
-	 * Clear the window content with the current drawing color.
-	 */
-	void clear();
-
-	/**
-	 * Render the content of the window to the screen.
-	 */
-	void present();
-
-	/**
-	 * Close the window.
-	 */
-	void close() noexcept;
-
-	/**
-	 * Get the current drawing color.
-	 *
-	 * \return the color
-	 */
-	auto get_drawing_color() const -> color;
-
-	/**
-	 * Set the drawing color.
-	 *
-	 * \param color the color
-	 */
-	void set_drawing_color(const color& color);
-
-	/**
-	 * Draw a line.
-	 *
-	 * \param line the line
-	 */
-	void draw_line(const line& line);
-
-	/**
-	 * Draw a point.
-	 *
-	 * \param point the point
-	 */
-	void draw_point(const point& point);
-
-	/**
-	 * Draw a rectangle (only borders).
-	 *
-	 * \param rect the rectangle
-	 * \see fillRectangle
-	 */
-	void draw_rectangle(const rectangle& rect);
-
-	/**
-	 * Fill the given rectangle with the current color.
-	 *
-	 * \param rect the rectangle
-	 * \see drawRectangle
-	 */
-	void fill_rectangle(const rectangle& rect);
-
-	/**
-	 * Draw some text.
-	 *
-	 * This function may stretch the text texture.
-	 *
-	 * \param text the text (UTF-8)
-	 * \param font the font
-	 * \param rectangle the rectangle target
-	 */
-	void draw_text(const std::string& text, const font& font, const rectangle& rectangle);
-
-	/**
-	 * Overloaded function.
-	 *
-	 * Draw the text at the given position.
-	 *
-	 * \param text the text (UTF-8)
-	 * \param font the font
-	 * \param point the text position
-	 */
-	void draw_text(const std::string& text, const font& font, const point& point);
-
-	/**
-	 * Poll all pending events and call appropriate functions.
-	 *
-	 * \param dispatcher the event dispatcher
-	 */
-	void poll(dispatcher& dp);
-};
-
-} // !client
-
-} // !mlk
-
-#endif // !MALIKANIA_CLIENT_WINDOW_HPP
--- a/libcommon-js/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-common-js)
-
-set(
-	HEADERS
-	${libmlk-common-js_SOURCE_DIR}/malikania/duktape.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/duk.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_elapsed_timer.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_line.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_point.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_rectangle.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_resources_loader.hpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_size.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-common-js_SOURCE_DIR}/malikania/duk.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_elapsed_timer.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_line.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_point.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_rectangle.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_resources_loader.cpp
-	${libmlk-common-js_SOURCE_DIR}/malikania/js_size.cpp
-)
-
-malikania_define_library(
-	PROJECT libmlk-common-js
-	TARGET libmlk-common-js
-	SOURCES ${HEADERS} ${SOURCES}
-	FLAGS "MALIKANIA_COMMON_BUILD"
-	PUBLIC_INCLUDES
-		$<BUILD_INTERFACE:${libmlk-common-js_SOURCE_DIR}/malikania>
-	LIBRARIES duktape json libmlk-common
-)
--- a/libcommon-js/malikania/duk.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,447 +0,0 @@
-/*
- * duk.cpp -- miscellaneous Duktape extras
- *
- * Copyright (c) 2017-2018 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 <cassert>
-#include <cstdio>
-
-#include "duk.hpp"
-
-namespace mlk::duk {
-
-// {{{ stack_guard
-
-stack_guard::stack_guard(duk_context* ctx, unsigned expected) noexcept
-#if !defined(NDEBUG)
-	: context_(ctx)
-	, expected_(expected)
-	, at_start_(duk_get_top(ctx))
-#endif
-{
-#if defined(NDEBUG)
-	(void)ctx;
-	(void)expected;
-#endif
-}
-
-stack_guard::~stack_guard() noexcept
-{
-#if !defined(NDEBUG)
-	auto result = duk_get_top(context_) - at_start_;
-
-	if (result != static_cast<int>(expected_)) {
-		std::fprintf(stderr, "Corrupt stack detection in stack_guard:\n");
-		std::fprintf(stderr, "  Size at start:          %d\n", at_start_);
-		std::fprintf(stderr, "  Size at end:            %d\n", duk_get_top(context_));
-		std::fprintf(stderr, "  Expected (user):        %u\n", expected_);
-		std::fprintf(stderr, "  Expected (adjusted):    %u\n", expected_ + at_start_);
-		std::fprintf(stderr, "  Difference count:       %+d\n", result - expected_);
-		std::abort();
-	}
-#endif
-}
-
-// }}}
-
-// {{{ context
-
-context::context() noexcept
-	: handle_(duk_create_heap_default(), duk_destroy_heap)
-{
-	duk_push_object(handle_.get());
-	duk_put_global_string(handle_.get(), "Malikania");
-}
-
-context::operator duk_context*() noexcept
-{
-	return handle_.get();
-}
-
-context::operator duk_context*() const noexcept
-{
-	return handle_.get();
-}
-
-// }}}
-
-// {{{ stack_info
-
-stack_info::stack_info(std::string name,
-                       std::string message,
-                       std::string stack,
-                       std::string file_name,
-                       unsigned line_number) noexcept
-	: name_(std::move(name))
-	, message_(std::move(message))
-	, stack_(std::move(stack))
-	, file_name_(std::move(file_name))
-	, line_number_(line_number)
-{
-}
-
-auto stack_info::get_name() const noexcept -> const std::string&
-{
-	return name_;
-}
-
-auto stack_info::get_message() const noexcept -> const std::string&
-{
-	return message_;
-}
-
-auto stack_info::get_stack() const noexcept -> const std::string&
-{
-	return stack_;
-}
-
-auto stack_info::get_file_name() const noexcept -> const std::string&
-{
-	return file_name_;
-}
-
-auto stack_info::get_line_number() const noexcept -> unsigned
-{
-	return line_number_;
-}
-
-auto stack_info::what() const noexcept -> const char*
-{
-	return message_.c_str();
-}
-
-// }}}
-
-// {{{ error
-
-error::error(int type, std::string message) noexcept
-	: type_(type)
-	, message_(std::move(message))
-{
-}
-
-error::error(std::string message) noexcept
-	: message_(std::move(message))
-{
-}
-
-void error::create(duk_context* ctx) const
-{
-	duk_push_error_object(ctx, type_, "%s", message_.c_str());
-}
-
-// }}}
-
-// {{{ eval_error
-
-eval_error::eval_error(std::string message) noexcept
-	: error(DUK_ERR_EVAL_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ range_error
-
-range_error::range_error(std::string message) noexcept
-	: error(DUK_ERR_RANGE_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ reference_error
-
-reference_error::reference_error(std::string message) noexcept
-	: error(DUK_ERR_REFERENCE_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ syntax_error
-
-syntax_error::syntax_error(std::string message) noexcept
-	: error(DUK_ERR_SYNTAX_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ type_error
-
-type_error::type_error(std::string message) noexcept
-	: error(DUK_ERR_TYPE_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ uri_error
-
-uri_error::uri_error(std::string message) noexcept
-	: error(DUK_ERR_URI_ERROR, std::move(message))
-{
-}
-
-// }}}
-
-// {{{ get_stack
-
-auto get_stack(duk_context* ctx, int index, bool pop) -> stack_info
-{
-	index = duk_normalize_index(ctx, index);
-
-	duk_get_prop_string(ctx, index, "name");
-	auto name = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "message");
-	auto message = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "fileName");
-	auto file_name = duk_to_string(ctx, -1);
-	duk_get_prop_string(ctx, index, "lineNumber");
-	auto line_number = duk_to_uint(ctx, -1);
-	duk_get_prop_string(ctx, index, "stack");
-	auto stack = duk_to_string(ctx, -1);
-	duk_pop_n(ctx, 5);
-
-	if (pop)
-		duk_remove(ctx, index);
-
-	return {
-		std::move(name),
-		std::move(message),
-		std::move(stack),
-		std::move(file_name),
-		line_number
-	};
-}
-
-// }}}
-
-// {{{ type_traits<std::exception>
-
-void type_traits<std::exception>::raise(duk_context* ctx, const std::exception& ex)
-{
-	duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
-}
-
-// }}}
-
-// {{{ type_traits<std::system_error>
-
-void type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex)
-{
-	duk_push_error_object(ctx, DUK_ERR_ERROR, ex.what());
-	duk_push_int(ctx, ex.code().value());
-	duk_put_prop_string(ctx, -2, "code");
-	duk_push_string(ctx, ex.code().category().name());
-	duk_put_prop_string(ctx, -2, "category");
-	duk_throw(ctx);
-}
-
-// }}}
-
-// {{{ type_traits<error>
-
-void type_traits<error>::raise(duk_context* ctx, const error& ex)
-{
-	ex.create(ctx);
-	duk_throw(ctx);
-}
-
-// }}}
-
-// {{{ type_traits<bool>
-
-void type_traits<bool>::push(duk_context* ctx, bool value)
-{
-	duk_push_boolean(ctx, value);
-}
-
-auto type_traits<bool>::get(duk_context* ctx, duk_idx_t index) -> bool
-{
-	return duk_get_boolean(ctx, index);
-}
-
-auto type_traits<bool>::require(duk_context* ctx, duk_idx_t index) -> bool
-{
-	return duk_require_boolean(ctx, index);
-}
-
-auto type_traits<bool>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<bool>
-{
-	return duk_is_boolean(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<duk_double_t>
-
-void type_traits<duk_double_t>::push(duk_context* ctx, duk_double_t value)
-{
-	duk_push_number(ctx, value);
-}
-
-auto type_traits<duk_double_t>::get(duk_context* ctx, duk_idx_t index) -> duk_double_t
-{
-	return duk_get_number(ctx, index);
-}
-
-auto type_traits<duk_double_t>::require(duk_context* ctx, duk_idx_t index) -> duk_double_t
-{
-	return duk_require_number(ctx, index);
-}
-
-auto type_traits<duk_double_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_double_t>
-{
-	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<duk_int_t>
-
-void type_traits<duk_int_t>::push(duk_context* ctx, duk_int_t value)
-{
-	duk_push_int(ctx, value);
-}
-
-auto type_traits<duk_int_t>::get(duk_context* ctx, duk_idx_t index) -> duk_int_t
-{
-	return duk_get_int(ctx, index);
-}
-
-auto type_traits<duk_int_t>::require(duk_context* ctx, duk_idx_t index) -> duk_int_t
-{
-	return duk_require_int(ctx, index);
-}
-
-auto type_traits<duk_int_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_int_t>
-{
-	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<duk_uint_t>
-
-void type_traits<duk_uint_t>::push(duk_context* ctx, duk_uint_t value)
-{
-	duk_push_uint(ctx, value);
-}
-
-auto type_traits<duk_uint_t>::get(duk_context* ctx, duk_idx_t index) -> duk_uint_t
-{
-	return duk_get_uint(ctx, index);
-}
-
-auto type_traits<duk_uint_t>::require(duk_context* ctx, duk_idx_t index) -> duk_uint_t
-{
-	return duk_require_uint(ctx, index);
-}
-
-auto type_traits<duk_uint_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_uint_t>
-{
-	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<const char*>
-
-void type_traits<const char*>::push(duk_context* ctx, const char* value)
-{
-	duk_push_string(ctx, value);
-}
-
-auto type_traits<const char*>::get(duk_context* ctx, duk_idx_t index) -> const char*
-{
-	return duk_get_string(ctx, index);
-}
-
-auto type_traits<const char*>::require(duk_context* ctx, duk_idx_t index) -> const char*
-{
-	return duk_require_string(ctx, index);
-}
-
-auto type_traits<const char*>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<const char*>
-{
-	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<std::string>
-
-void type_traits<std::string>::push(duk_context* ctx, const std::string& value)
-{
-	duk_push_lstring(ctx, value.data(), value.size());
-}
-
-auto type_traits<std::string>::get(duk_context* ctx, duk_idx_t index) -> std::string
-{
-	duk_size_t length;
-	const char* str = duk_get_lstring(ctx, index, &length);
-
-	return { str, length };
-}
-
-auto type_traits<std::string>::require(duk_context* ctx, duk_idx_t index) -> std::string
-{
-	duk_size_t length;
-	const char* str = duk_require_lstring(ctx, index, &length);
-
-	return { str, length };
-}
-
-auto type_traits<std::string>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string>
-{
-	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-// {{{ type_traits<std::string_view>
-
-void type_traits<std::string_view>::push(duk_context* ctx, std::string_view value)
-{
-	duk_push_lstring(ctx, value.data(), value.size());
-}
-
-auto type_traits<std::string_view>::get(duk_context* ctx, duk_idx_t index) -> std::string_view
-{
-	duk_size_t length;
-	const char* str = duk_get_lstring(ctx, index, &length);
-
-	return { str, length };
-}
-
-auto type_traits<std::string_view>::require(duk_context* ctx, duk_idx_t index) -> std::string_view
-{
-	duk_size_t length;
-	const char* str = duk_require_lstring(ctx, index, &length);
-
-	return { str, length };
-}
-
-auto type_traits<std::string_view>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string_view>
-{
-	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-// }}}
-
-} // !mlk::duk
--- a/libcommon-js/malikania/duk.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,988 +0,0 @@
-/*
- * duk.hpp -- miscellaneous Duktape extras
- *
- * Copyright (c) 2017-2018 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 MALIKANIA_JS_DUK_HPP
-#define MALIKANIA_JS_DUK_HPP
-
-/**
- * \file duk.hpp
- * \brief Miscellaneous Duktape extras
- * \author David Demelier <markand@malikania.fr>
- * \version 0.2.0
- */
-
-#include <exception>
-#include <memory>
-#include <optional>
-#include <string>
-#include <string_view>
-#include <system_error>
-
-#include "duktape.h"
-
-/**
- * \brief Miscellaneous Duktape extras.
- */
-namespace mlk::duk {
-
-// {{{ stack_guard
-
-/**
- * \brief Stack sanity checker.
- *
- * Instanciate this class where you need to manipulate the Duktape stack outside
- * a Duktape/C function, its destructor will examinate if the stack size matches
- * the user expected size.
- *
- * When compiled with NDEBUG, this class does nothing.
- *
- * To use it, just declare an lvalue at the beginning of your function.
- */
-class stack_guard {
-#if !defined(NDEBUG)
-private:
-	duk_context* context_;
-	unsigned expected_;
-	int at_start_;
-#endif
-
-public:
-	/**
-	 * Create the stack checker.
-	 *
-	 * No-op if NDEBUG is set.
-	 *
-	 * \param ctx the context
-	 * \param expected the size expected relative to the already existing values
-	 */
-	stack_guard(duk_context* ctx, unsigned expected = 0) noexcept;
-
-	/**
-	 * Verify the expected size.
-	 *
-	 * No-op if NDEBUG is set.
-	 */
-	~stack_guard() noexcept;
-};
-
-// }}}
-
-// {{{ context
-
-/**
- * \brief RAII based Duktape handler.
- *
- * This class is implicitly convertible to duk_context for convenience.
- */
-class context {
-private:
-	std::unique_ptr<duk_context, void (*)(duk_context*)> handle_;
-
-	context(const context&) = delete;
-	void operator=(const context&) = delete;
-
-public:
-	/**
-	 * Create default context.
-	 */
-	context() noexcept;
-
-	/**
-	 * Default move constructor.
-	 */
-	context(context&&) noexcept = default;
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * \return the duk_context
-	 */
-	operator duk_context*() noexcept;
-
-	/**
-	 * Convert the context to the native Duktape/C type.
-	 *
-	 * \return the duk_context
-	 */
-	operator duk_context*() const noexcept;
-
-	/**
-	 * Default move assignment operator.
-	 *
-	 * \return this
-	 */
-	auto operator=(context&&) noexcept -> context& = default;
-};
-
-// }}}
-
-// {{{ stack_info
-
-/**
- * \brief Error description.
- *
- * This class fills the fields got in an Error object.
- */
-class stack_info : public std::exception {
-private:
-	std::string name_;
-	std::string message_;
-	std::string stack_;
-	std::string file_name_;
-	unsigned line_number_;
-
-public:
-	/**
-	 * Construct the stack information.
-	 *
-	 * \param name the exception name (e.g. ReferenceError)
-	 * \param message the error message
-	 * \param stack the stack trace
-	 * \param file_name the optional filename
-	 * \param line_number the optional line number
-	 */
-	stack_info(std::string name,
-	           std::string message,
-	           std::string stack,
-	           std::string file_name,
-	           unsigned line_number = 0) noexcept;
-
-	/**
-	 * Get the exception name.
-	 *
-	 * \return the exception name (e.g. ReferenceError)
-	 */
-	auto get_name() const noexcept -> const std::string&;
-
-	/**
-	 * Get the error message.
-	 *
-	 * \return the message
-	 */
-	auto get_message() const noexcept -> const std::string&;
-
-	/**
-	 * Get the stack trace.
-	 *
-	 * \return the stack
-	 */
-	auto get_stack() const noexcept -> const std::string&;
-
-	/**
-	 * Get the optional file name.
-	 *
-	 * \return the file name
-	 */
-	auto get_file_name() const noexcept -> const std::string&;
-
-	/**
-	 * Get the line number.
-	 *
-	 * \return the line number
-	 */
-	auto get_line_number() const noexcept -> unsigned;
-
-	/**
-	 * Get the error message. This effectively returns message field.
-	 *
-	 * \return the message
-	 */
-	auto what() const noexcept -> const char* override;
-};
-
-// }}}
-
-// {{{ type_traits
-
-/**
- * \brief Operations on different types.
- *
- * This class provides some functions for the given type, depending on the
- * nature of the function.
- *
- * For example, push will call type_traits<T>::push static function
- * if the type_traits is implemented for that given T type.
- *
- * This helps passing/getting function between Javascript and C++ code.
- *
- * Example:
- *
- * ```cpp
- * push(ctx, 123);	 // Uses type_traits<int>
- * push(ctx, true);	// Uses type_traits<bool>
- * ```
- *
- * This class is specialized for the following types:
- *
- *   - `bool`,
- *   - `duk_int_t`,
- *   - `duk_uint_t`,
- *   - `duk_double_t`,
- *   - `const char*`,
- *   - `std::string`,
- *   - `std::string_view`.
- *
- * Regarding exceptions, this class is specialized for the following types:
- *
- *   - error,
- *   - std::exception,
- *   - std::system_error.
- *
- * \see push
- * \see get
- * \see require
- * \see raise
- */
-template <typename T>
-struct type_traits;
-
-// }}}
-
-// {{{ push
-
-/**
- * Generic push function.
- *
- * This function calls type_traits<T>::push if specialized.
- *
- * \param ctx the Duktape context
- * \param value the forwarded value
- * \return 1 for convenience
- */
-template <typename T>
-auto push(duk_context* ctx, T&& value) -> int
-{
-	using Type = typename std::decay<T>::type;
-
-	type_traits<Type>::push(ctx, std::forward<T>(value));
-
-	return 1;
-}
-
-// }}}
-
-// {{{ put
-
-/**
- * Generic put function.
- *
- * This function calls type_traits<T>::put if specialized.
- *
- * The purpose of this function is to inject the argument through the value at
- * the top of the stack.
- *
- * \param ctx the Duktape context
- * \param value the forwarded value
- * \return 1 for convenience
- */
-template <typename T>
-auto put(duk_context* ctx, T&& value) -> int
-{
-	using Type = typename std::decay<T>::type;
-
-	type_traits<Type>::put(ctx, std::forward<T>(value));
-
-	return 1;
-}
-
-// }}}
-
-// {{{ get
-
-/**
- * Generic get function.
- *
- * This functions calls type_traits<T>::get if specialized.
- *
- * \param ctx the Duktape context
- * \param index the value index
- * \return the converted value
- */
-template <typename T>
-auto get(duk_context* ctx, duk_idx_t index) -> decltype(auto)
-{
-	using Type = typename std::decay<T>::type;
-
-	return type_traits<Type>::get(ctx, index);
-}
-
-// }}}
-
-// {{{ optional
-
-/**
- * Generic optional function.
- *
- * This functions calls type_traits<T>::optional if specialized.
- *
- * \param ctx the Duktape context
- * \param index the value index
- * \return the converted value
- */
-template <typename T>
-auto optional(duk_context* ctx, duk_idx_t index) -> decltype(auto)
-{
-	using Type = typename std::decay<T>::type;
-
-	return type_traits<Type>::optional(ctx, index);
-}
-
-// }}}
-
-// {{{ require
-
-/**
- * Generic require function.
- *
- * This functions calls type_traits<T>::require if specialized.
- *
- * \param ctx the Duktape context
- * \param index the value index
- * \return the converted value
- */
-template <typename T>
-auto require(duk_context* ctx, duk_idx_t index) -> decltype(auto)
-{
-	using Type = typename std::decay<T>::type;
-
-	return type_traits<Type>::require(ctx, index);
-}
-
-// }}}
-
-// {{{ error
-
-/**
- * \brief Base ECMAScript error class.
- * \warning Override the function create for your own exceptions
- */
-class error {
-private:
-	int type_{DUK_ERR_ERROR};
-	std::string message_;
-
-protected:
-	/**
-	 * Constructor with a type of error specified, specially designed for
-	 * derived errors.
-	 *
-	 * \param type of error (e.g. DUK_ERR_ERROR)
-	 * \param message the message
-	 */
-	error(int type, std::string message) noexcept;
-
-public:
-	/**
-	 * Constructor with a message.
-	 *
-	 * \param message the message
-	 */
-	error(std::string message) noexcept;
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	~error() = default;
-
-	/**
-	 * Create the exception on the stack.
-	 *
-	 * \note the default implementation search for the global variables
-	 * \param ctx the context
-	 */
-	void create(duk_context* ctx) const;
-};
-
-// }}}
-
-// {{{ eval_error
-
-/**
- * \brief Error in eval() function.
- */
-class eval_error : public error {
-public:
-	/**
-	 * Construct an EvalError.
-	 *
-	 * \param message the message
-	 */
-	eval_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ range_error
-
-/**
- * \brief Value is out of range.
- */
-class range_error : public error {
-public:
-	/**
-	 * Construct an RangeError.
-	 *
-	 * \param message the message
-	 */
-	range_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ reference_error
-
-/**
- * \brief Trying to use a variable that does not exist.
- */
-class reference_error : public error {
-public:
-	/**
-	 * Construct an ReferenceError.
-	 *
-	 * \param message the message
-	 */
-	reference_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ syntax_error
-
-/**
- * \brief Syntax error in the script.
- */
-class syntax_error : public error {
-public:
-	/**
-	 * Construct an SyntaxError.
-	 *
-	 * \param message the message
-	 */
-	syntax_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ type_error
-
-/**
- * \brief Invalid type given.
- */
-class type_error : public error {
-public:
-	/**
-	 * Construct an TypeError.
-	 *
-	 * \param message the message
-	 */
-	type_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ uri_error
-
-/**
- * \brief URI manipulation failure.
- */
-class uri_error : public error {
-public:
-	/**
-	 * Construct an URIError.
-	 *
-	 * \param message the message
-	 */
-	uri_error(std::string message) noexcept;
-};
-
-// }}}
-
-// {{{ raise
-
-/**
- * Create an exception into the stack and throws it.
- *
- * This function needs the following requirements in type_traits
- *
- * ```cpp
- * static void raise(duk_context*, Error);
- * ```
- *
- * Error can be any kind of value, it is forwarded.
- *
- * \param ctx the Duktape context
- * \param error the error object
- */
-template <typename Error>
-void raise(duk_context* ctx, Error&& error)
-{
-	using type = std::decay_t<Error>;
-
-	type_traits<type>::raise(ctx, std::forward<Error>(error));
-}
-
-// }}}
-
-// {{{ get_stack
-
-/**
- * Get the error object when a JavaScript error has been thrown (e.g. eval
- * failure).
- *
- * \param ctx the context
- * \param index the index
- * \param pop if true, also remove the exception from the stack
- * \return the information
- */
-auto get_stack(duk_context* ctx, int index, bool pop = true) -> stack_info;
-
-// }}}
-
-// {{{ type_traits<std::exception>
-
-/**
- * \brief Specialization for std::exception.
- */
-template <>
-struct type_traits<std::exception> {
-	/**
-	 * Raise a Error object.
-	 *
-	 * \param ctx the Duktape context
-	 * \param ex the exception
-	 */
-	static void raise(duk_context* ctx, const std::exception& ex);
-};
-
-// }}}
-
-// {{{ type_traits<std::system_error>
-
-/**
- * \brief Specialization for std::system_error.
- */
-template <>
-struct type_traits<std::system_error> {
-	/**
-	 * Raise a Error object.
-	 *
-	 * The error object has the following properties defined in it:
-	 *
-	 * - code: (int) error code,
-	 * - category: (string) error category string.
-	 *
-	 * \param ctx the Duktape context
-	 * \param ex the exception
-	 */
-	static void raise(duk_context* ctx, const std::system_error& ex);
-};
-
-// }}}
-
-// {{{ type_traits<error>
-
-/**
- * \brief Specialization for error.
- */
-template <>
-struct type_traits<error> {
-	/**
-	 * Raise a error.
-	 *
-	 * \param ctx the Duktape context
-	 * \param ex the exception
-	 */
-	static void raise(duk_context* ctx, const error& ex);
-};
-
-// }}}
-
-// {{{ type_traits<bool>
-
-/**
- * \brief Specialization for bool.
- */
-template <>
-struct type_traits<bool> {
-	/**
-	 * Push a boolean.
-	 *
-	 * Uses duk_push_boolean
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, bool value);
-
-	/**
-	 * Get a boolean.
-	 *
-	 * Uses duk_get_boolean.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> bool;
-
-	/**
-	 * Require a boolean.
-	 *
-	 * Uses duk_require_boolean.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> bool;
-
-	/**
-	 * Get a boolean if convertible.
-	 *
-	 * Uses duk_get_boolean.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<bool>;
-};
-
-// }}}
-
-// {{{ type_traits<duk_double_t>
-
-/**
- * \brief Specialization for duk_double_t.
- */
-template <>
-struct type_traits<duk_double_t> {
-	/**
-	 * Push a double.
-	 *
-	 * Uses duk_push_number
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, duk_double_t value);
-
-	/**
-	 * Get a double.
-	 *
-	 * Uses duk_get_number.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> duk_double_t;
-
-	/**
-	 * Require a double.
-	 *
-	 * Uses duk_require_double.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> duk_double_t;
-
-	/**
-	 * Get a double if convertible
-	 *
-	 * Uses duk_get_number.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_double_t>;
-};
-
-// }}}
-
-// {{{ type_traits<duk_int_t>
-
-/**
- * \brief Specialization for duk_int_t.
- */
-template <>
-struct type_traits<duk_int_t> {
-	/**
-	 * Push an int.
-	 *
-	 * Uses duk_push_int
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, duk_int_t value);
-
-	/**
-	 * Get an int.
-	 *
-	 * Uses duk_get_int.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> duk_int_t;
-
-	/**
-	 * Require an int.
-	 *
-	 * Uses duk_require_int.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> duk_int_t;
-
-	/**
-	 * Get an int if convertible.
-	 *
-	 * Uses duk_get_int.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_int_t>;
-};
-
-// }}}
-
-// {{{ type_traits<duk_uint_t>
-
-/**
- * \brief Specialization for duk_uint_t.
- */
-template <>
-struct type_traits<duk_uint_t> {
-	/**
-	 * Push an unsigned int.
-	 *
-	 * Uses duk_push_uint
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, duk_uint_t value);
-
-	/**
-	 * Get an unsigned int.
-	 *
-	 * Uses duk_get_uint.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> duk_uint_t;
-
-	/**
-	 * Require an unsigned int.
-	 *
-	 * Uses duk_require_uint.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> duk_uint_t;
-
-	/**
-	 * Get an unsigned int if convertible.
-	 *
-	 * Uses duk_get_uint.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_uint_t>;
-};
-
-// }}}
-
-// {{{ type_traits<const char*>
-
-/**
- * \brief Specialization for C strings.
- */
-template <>
-struct type_traits<const char*> {
-	/**
-	 * Push a C string.
-	 *
-	 * Uses duk_push_string
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, const char* value);
-
-	/**
-	 * Get a C string.
-	 *
-	 * Uses duk_get_string.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> const char*;
-
-	/**
-	 * Require a C string.
-	 *
-	 * Uses duk_require_string.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> const char*;
-
-	/**
-	 * Get a C string if convertible.
-	 *
-	 * Uses duk_get_string.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<const char*>;
-};
-
-// }}}
-
-// {{{ type_traits<std::string>
-
-/**
- * \brief Specialization for C++ std::strings.
- */
-template <>
-struct type_traits<std::string> {
-	/**
-	 * Push a C++ std::string.
-	 *
-	 * Uses duk_push_lstring
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, const std::string& value);
-
-	/**
-	 * Get a C++ std::string.
-	 *
-	 * Uses duk_get_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> std::string;
-
-	/**
-	 * Require a C++ std::string.
-	 *
-	 * Uses duk_require_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> std::string;
-
-	/**
-	 * Get a C++ std::string if convertible.
-	 *
-	 * Uses duk_get_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string>;
-};
-
-// }}}
-
-// {{{ type_traits<std::string_view>
-
-/**
- * \brief Specialization for C++ std::string_views.
- */
-template <>
-struct type_traits<std::string_view> : public std::true_type {
-	/**
-	 * Push a C++ std::string_view.
-	 *
-	 * Uses duk_push_lstring
-	 *
-	 * \param ctx the Duktape context
-	 * \param value the value
-	 */
-	static void push(duk_context* ctx, std::string_view value);
-
-	/**
-	 * Get a C++ std::string_view.
-	 *
-	 * Uses duk_get_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> std::string_view;
-
-	/**
-	 * Require a C++ std::string_view.
-	 *
-	 * Uses duk_require_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> std::string_view;
-
-	/**
-	 * Get a C++ std::string_view if convertible.
-	 *
-	 * Uses duk_get_lstring.
-	 *
-	 * \param ctx the Duktape context
-	 * \param index the value index
-	 * \return the converted value
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string_view>;
-};
-
-// }}}
-
-} // !mlk::duk
-
-#endif // !MALIKANIA_JS_DUK_HPP
--- a/libcommon-js/malikania/duktape.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,342 +0,0 @@
-/*
- * duktape.hpp -- Duktape extras
- *
- * Copyright (c) 2016-2018 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 MALIKANIA_COMMON_DUKTAPE_HPP
-#define MALIKANIA_COMMON_DUKTAPE_HPP
-
-/**
- * \file duktape.hpp
- * \brief Bring some extras to Duktape C library.
- * \author David Demelier <markand@malikania.fr>
- */
-
-#include <cstdio>
-#include <cstdlib>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-#include <duktape.h>
-
-/**
- * \brief Stack sanity checker.
- *
- * Instanciate this class where you need to manipulate the Duktape stack outside
- * a Duktape/C function, its destructor will examinate if the stack size matches
- * the user expected size.
- *
- * When compiled with NDEBUG, this class does nothing.
- *
- * To use it, just declare an lvalue at the beginning of your function.
- */
-class dukx_stack_assert {
-#if !defined(NDEBUG)
-private:
-    duk_context* m_context;
-    unsigned m_expected;
-    int m_at_start;
-#endif
-
-public:
-    /**
-     * Create the stack checker.
-     *
-     * No-op if NDEBUG is set.
-     *
-     * \param ctx the context
-     * \param expected the size expected relative to the already existing values
-     */
-    inline dukx_stack_assert(duk_context *ctx, unsigned expected = 0) noexcept
-#if !defined(NDEBUG)
-        : m_context(ctx)
-        , m_expected(expected)
-        , m_at_start(duk_get_top(ctx))
-#endif
-    {
-#if defined(NDEBUG)
-        (void)ctx;
-        (void)expected;
-#endif
-    }
-
-    /**
-     * Verify the expected size.
-     *
-     * No-op if NDEBUG is set.
-     */
-    inline ~dukx_stack_assert() noexcept
-    {
-#if !defined(NDEBUG)
-        auto result = duk_get_top(m_context) - m_at_start;
-
-        if (result != static_cast<int>(m_expected)) {
-            std::fprintf(stderr, "Corrupt stack detection in dukx_stack_assert:\n");
-            std::fprintf(stderr, "  Size at start:           %d\n", m_at_start);
-            std::fprintf(stderr, "  Size at end:             %d\n", duk_get_top(m_context));
-            std::fprintf(stderr, "  Expected (user):         %u\n", m_expected);
-            std::fprintf(stderr, "  Expected (adjusted):     %u\n", m_expected + m_at_start);
-            std::fprintf(stderr, "  Difference count:       %+d\n", result - m_expected);
-            std::abort();
-        }
-#endif
-    }
-};
-
-/**
- * \brief Error description.
- *
- * This class fills the fields got in an Error object.
- */
-class dukx_exception : public std::exception {
-public:
-    std::string name;               //!< name of error
-    std::string message;            //!< error message
-    std::string stack;              //!< stack if available
-    std::string fileName;           //!< filename if applicable
-    int lineNumber{0};              //!< line number if applicable
-
-    /**
-     * Get the error message. This effectively returns message field.
-     *
-     * \return the message
-     */
-    const char *what() const noexcept override
-    {
-        return message.c_str();
-    }
-};
-
-/**
- * \brief RAII based Duktape handler.
- *
- * This class is implicitly convertible to duk_context for convenience.
- */
-class dukx_context {
-private:
-    std::unique_ptr<duk_context, void (*)(duk_context*)> m_handle;
-
-    dukx_context(const dukx_context&) = delete;
-    dukx_context &operator=(const dukx_context&) = delete;
-
-public:
-    /**
-     * Create default context.
-     */
-    inline dukx_context() noexcept
-        : m_handle(duk_create_heap_default(), duk_destroy_heap)
-    {
-    }
-
-    /**
-     * Default move constructor.
-     */
-    dukx_context(dukx_context&&) noexcept = default;
-
-    /**
-     * Convert the context to the native Duktape/C type.
-     *
-     * \return the duk_context
-     */
-    inline operator duk_context*() noexcept
-    {
-        return m_handle.get();
-    }
-
-    /**
-     * Convert the context to the native Duktape/C type.
-     *
-     * \return the duk_context
-     */
-    inline operator duk_context*() const noexcept
-    {
-        return m_handle.get();
-    }
-
-    /**
-     * Default move assignment operator.
-     *
-     * \return this
-     */
-    dukx_context& operator=(dukx_context&&) noexcept = delete;
-};
-
-/**
- * Get the error object when a JavaScript error has been thrown (e.g. eval
- * failure).
- *
- * \param ctx the context
- * \param index the index
- * \param pop if true, also remove the exception from the stack
- * \return the information
- */
-inline dukx_exception dukx_get_exception(duk_context* ctx, int index, bool pop = true)
-{
-    dukx_exception ex;
-
-    index = duk_normalize_index(ctx, index);
-
-    duk_get_prop_string(ctx, index, "name");
-    ex.name = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "message");
-    ex.message = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "fileName");
-    ex.fileName = duk_to_string(ctx, -1);
-    duk_get_prop_string(ctx, index, "lineNumber");
-    ex.lineNumber = duk_to_int(ctx, -1);
-    duk_get_prop_string(ctx, index, "stack");
-    ex.stack = duk_to_string(ctx, -1);
-    duk_pop_n(ctx, 5);
-
-    if (pop) {
-        duk_remove(ctx, index);
-    }
-
-    return ex;
-}
-
-/**
- * Get a string, return 0 if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_get_std_string(duk_context* ctx, int index)
-{
-    duk_size_t size;
-    const char* text = duk_get_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Require a string, throws a JavaScript exception if not a string.
- *
- * \param ctx the context
- * \param index the index
- * \return the string
- */
-inline std::string dukx_require_std_string(duk_context* ctx, int index)
-{
-    duk_size_t size;
-    const char* text = duk_require_lstring(ctx, index, &size);
-
-    return std::string(text, size);
-}
-
-/**
- * Push a C++ string.
- *
- * \param ctx the context
- * \param str the string
- */
-inline void dukx_push_std_string(duk_context* ctx, const std::string& str)
-{
-    duk_push_lstring(ctx, str.data(), str.length());
-}
-
-/**
- * Get an array.
- *
- * \param ctx the context
- * \param index the array index
- * \param get the conversion function (e.g. duk_get_int)
- */
-template <typename Getter>
-auto dukx_get_array(duk_context* ctx, duk_idx_t index, Getter&& get)
-{
-    using T = decltype(get(ctx, 0));
-
-    std::vector<T> result;
-    std::size_t length = duk_get_length(ctx, index);
-
-    for (std::size_t i = 0; i < length; ++i) {
-        duk_get_prop_index(ctx, -1, i);
-        result.push_back(get(ctx, -1));
-        duk_pop(ctx);
-    }
-
-    return result;
-}
-
-/**
- * Push an array.
- *
- * \param ctx the context
- * \param values the values
- * \param push the function to push values
- */
-template <typename T, typename Pusher>
-void dukx_push_array(duk_context* ctx, const std::vector<T>& values, Pusher&& push)
-{
-    duk_push_array(ctx);
-
-    int i = 0;
-    for (auto x : values) {
-        push(ctx, x);
-        duk_put_prop_index(ctx, -2, i++);
-    }
-}
-
-/**
- * Get an object.
- *
- * \param ctx the context
- * \param index the object index
- * \param get the conversion function (e.g. duk_get_int)
- */
-template <typename Getter>
-auto dukx_get_object(duk_context* ctx, duk_idx_t index, Getter&& get)
-{
-    using T = decltype(get(ctx, 0));
-
-    std::unordered_map<std::string, T> result;
-
-    duk_enum(ctx, index, 0);
-
-    while (duk_next(ctx, -1, true)) {
-        result.emplace(dukx_get_std_string(ctx, -2), get(ctx, -1));
-        duk_pop_2(ctx);
-    }
-
-    duk_pop(ctx);
-
-    return result;
-}
-
-/**
- * Push an object.
- *
- * \param ctx the context
- * \param values the values
- * \param push the function to push values
- */
-template <typename T, typename Pusher>
-void dukx_push_object(duk_context* ctx, const std::unordered_map<std::string, T>& values, Pusher&& push)
-{
-    duk_push_object(ctx);
-
-    for (const auto& pair : values) {
-        push(ctx, pair.second);
-        duk_put_prop_string(ctx, -2, pair.first.c_str());
-    }
-}
-
-#endif // !MALIKANIA_COMMON_DUKTAPE_HPP
--- a/libcommon-js/malikania/js_elapsed_timer.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-/*
- * js_elapsed_timer.cpp -- Malikania.ElapsedTimer API
- *
- * Copyright (c) 2013-2018 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 <boost/timer/timer.hpp>
-
-#include "js_elapsed_timer.hpp"
-
-namespace mlk {
-
-namespace {
-
-const std::string_view signature("\xff""\xff""Malikania.ElapsedTimer");
-
-// {{{ self
-
-auto self(duk_context* ctx) -> boost::timer::cpu_timer*
-{
-	duk::stack_guard sa(ctx);
-
-	duk_push_this(ctx);
-	duk_get_prop_string(ctx, -1, signature.data());
-	const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1));
-	duk_pop_2(ctx);
-
-	if (!ptr)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object");
-
-	return ptr;
-}
-
-// }}}
-
-// {{{ Irccd.ElapsedTimer.prototype.pause
-
-/*
- * Method: ElapsedTimer.prototype.pause
- * ------------------------------------------------------------------
- *
- * Pause the timer, without resetting the current elapsed time stored.
- */
-auto ElapsedTimer_prototype_pause(duk_context* ctx) -> duk_ret_t
-{
-	self(ctx)->stop();
-
-	return 0;
-}
-
-// }}}
-
-// {{{ Irccd.ElapsedTimer.prototype.restart
-
-/*
- * Method: Irccd.ElapsedTimer.prototype.restart
- * ------------------------------------------------------------------
- *
- * Restart the timer without resetting the current elapsed time.
- */
-auto ElapsedTimer_prototype_restart(duk_context* ctx) -> duk_ret_t
-{
-	self(ctx)->resume();
-
-	return 0;
-}
-
-// }}}
-
-// {{{ Irccd.ElapsedTimer.prototype.elapsed
-
-/*
- * Method: ElapsedTimer.prototype.elapsed
- * ------------------------------------------------------------------
- *
- * Get the number of elapsed milliseconds.
- *
- * Returns:
- *   The time elapsed.
- */
-auto ElapsedTimer_prototype_elapsed(duk_context* ctx) -> duk_ret_t
-{
-	duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL);
-
-	return 1;
-}
-
-// }}}
-
-// {{{ Irccd.ElapsedTimer [constructor]
-
-/*
- * Function: Irccd.ElapsedTimer [constructor]
- * ------------------------------------------------------------------
- *
- * Construct a new ElapsedTimer object.
- */
-auto ElapsedTimer_constructor(duk_context* ctx) -> duk_ret_t
-{
-	duk_push_this(ctx);
-	duk_push_pointer(ctx, new boost::timer::cpu_timer);
-	duk_put_prop_string(ctx, -2, signature.data());
-	duk_pop(ctx);
-
-	return 0;
-}
-
-// }}}
-
-// {{{ Irccd.ElapsedTimer [destructor]
-
-/*
- * Function: Irccd.ElapsedTimer [destructor]
- * ------------------------------------------------------------------
- *
- * Delete the property.
- */
-auto ElapsedTimer_destructor(duk_context* ctx) -> duk_ret_t
-{
-	duk_get_prop_string(ctx, 0, signature.data());
-	delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-	duk_del_prop_string(ctx, 0, signature.data());
-
-	return 0;
-}
-
-// }}}
-
-// {{{ definitions
-
-const duk_function_list_entry methods[] = {
-	{ "elapsed",    ElapsedTimer_prototype_elapsed, 0 },
-	{ "pause",      ElapsedTimer_prototype_pause,   0 },
-	{ "restart",    ElapsedTimer_prototype_restart, 0 },
-	{ nullptr,      nullptr,                        0 }
-};
-
-// }}}
-
-} // !namespace
-
-void load_elapsed_timer_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, ElapsedTimer_constructor, 0);
-	duk_push_object(ctx);
-	duk_put_function_list(ctx, -1, methods);
-	duk_push_c_function(ctx, ElapsedTimer_destructor, 1);
-	duk_set_finalizer(ctx, -2);
-	duk_put_prop_string(ctx, -2, "prototype");
-	duk_put_prop_string(ctx, -2, "ElapsedTimer");
-	duk_pop(ctx);
-}
-
-} // !mlk
--- a/libcommon-js/malikania/js_elapsed_timer.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * js_elapsed_timer.hpp -- Malikania.ElapsedTimer API
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_ELAPSED_TIMER_HPP
-#define MALIKANIA_JS_ELAPSED_TIMER_HPP
-
-/**
- * \file js_elapsed_timer.hpp
- * \brief Malikania.ElapsedTimer Javascript API.
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-/**
- * Load Malikania.ElapsedTimer API into the context.
- *
- * \param ctx the context
- */
-void load_elapsed_timer_api(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_ELAPSED_TIMER_HPP
--- a/libcommon-js/malikania/js_line.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,155 +0,0 @@
-/*
- * js_line.cpp -- line description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/line.hpp>
-
-#include "js_line.hpp"
-
-namespace mlk {
-
-namespace {
-
-auto Line_constructor(duk_context* ctx) -> duk_ret_t
-{
-	line obj;
-
-	if (duk_get_top(ctx) == 4)
-		obj = line{
-			duk::require<int>(ctx, 0),
-			duk::require<int>(ctx, 1),
-			duk::require<int>(ctx, 2),
-			duk::require<int>(ctx, 3)
-		};
-	else if (duk_get_top(ctx) == 1)
-		obj = duk::require<line>(ctx, 0);
-
-	duk_ret_t ret;
-
-	// Allow both constructor and non constructor calls.
-	if (duk_is_constructor_call(ctx)) {
-		duk_push_this(ctx);
-		duk::put(ctx, obj);
-		duk_pop(ctx);
-		ret = 0;
-	} else {
-		duk::push(ctx, obj);
-		ret = 1;
-	}
-
-	return ret;
-}
-
-} // !namespace
-
-namespace duk {
-
-auto type_traits<line>::get(duk_context* ctx, duk_idx_t index) -> line
-{
-	duk::stack_guard sa(ctx);
-
-	line ret;
-
-	duk_get_prop_string(ctx, index, "x1");
-	ret.x1 = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y1");
-	ret.y1 = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "x2");
-	ret.x2 = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y2");
-	ret.y2 = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-
-	return ret;
-}
-
-auto type_traits<line>::require(duk_context* ctx, duk_idx_t index) -> line
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, "x1");
-	const auto x1 = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y1");
-	const auto y1 = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "x2");
-	const auto x2 = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y2");
-	const auto y2 = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-
-	if (!x1)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x1' property");
-	if (!y1)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y1' property");
-	if (!x2)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x2' property");
-	if (!y2)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y2' property");
-
-	return { *x1, *y1, *x2, *y2 };
-}
-
-auto type_traits<line>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<line>
-{
-	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-void type_traits<line>::push(duk_context* ctx, const line& line)
-{
-	duk::stack_guard sa(ctx, 1);
-
-	duk_push_object(ctx);
-	put(ctx, line);
-}
-
-void type_traits<line>::put(duk_context* ctx, const line& line)
-{
-	assert(duk_is_object(ctx, -1));
-
-	duk::stack_guard sa(ctx);
-
-	duk::push(ctx, line.x1);
-	duk_put_prop_string(ctx, -2, "x1");
-	duk::push(ctx, line.y1);
-	duk_put_prop_string(ctx, -2, "y1");
-	duk::push(ctx, line.x2);
-	duk_put_prop_string(ctx, -2, "x2");
-	duk::push(ctx, line.y2);
-	duk_put_prop_string(ctx, -2, "y2");
-}
-
-} // !duk
-
-void load_line_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx, 0);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Line_constructor, DUK_VARARGS);
-	duk_put_prop_string(ctx, -2, "Line");
-	duk_pop(ctx);
-}
-
-} // !mlk
--- a/libcommon-js/malikania/js_line.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-/*
- * js_line.hpp -- line description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_LINE_HPP
-#define MALIKANIA_JS_LINE_HPP
-
-/**
- * \file js_line.hpp
- * \brief JavaScript binding for line.
- *
- * Lines are plain objects.
- *
- * ````
- * {
- *   x1: 10,
- *   y1: 10,
- *   x2: 50,
- *   y2: 50
- * }
- * ````
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-struct line;
-
-namespace duk {
-
-template <>
-struct type_traits<line> {
-	/**
-	 * Get a line.
-	 *
-	 * \param ctx the context
-	 * \param index the index
-	 * \return the line
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> line;
-
-	/**
-	 * Require a line.
-	 *
-	 * If value is not an object or any property is invalid, raise a
-	 * JavaScript error.
-	 *
-	 * \param ctx the context
-	 * \param index the index
-	 * \return the line
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> line;
-
-	/**
-	 * Like get but return def if the value at the given index is not an object.
-	 *
-	 * \param ctx the context
-	 * \param index the index
-	 * \return the line
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<line>;
-
-	/**
-	 * Push the line as object.
-	 *
-	 * \param ctx the context
-	 * \param line the line
-	 */
-	static void push(duk_context* ctx, const line& line);
-
-	/**
-	 * Put the line properties into the object at the top of the stack.
-	 *
-	 * \param ctx the context
-	 * \param line the line
-	 */
-	static void put(duk_context* ctx, const line& line);
-};
-
-} // !duk
-
-/**
- * Load Malikania.Line API into the context.
- *
- * \param ctx the context
- */
-void load_line_api(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_LINE_HPP
--- a/libcommon-js/malikania/js_point.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * js_point.cpp -- point description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/point.hpp>
-
-#include "js_point.hpp"
-
-namespace mlk {
-
-namespace {
-
-auto Point_constructor(duk_context* ctx) -> duk_ret_t
-{
-	point obj;
-
-	if (duk_get_top(ctx) == 2)
-		obj = point{
-			duk::require<int>(ctx, 0),
-			duk::require<int>(ctx, 1)
-		};
-	else if (duk_get_top(ctx) == 1)
-		obj = duk::require<point>(ctx, 0);
-
-	duk_ret_t ret;
-
-	// Allow both constructor and non constructor calls.
-	if (duk_is_constructor_call(ctx)) {
-		duk_push_this(ctx);
-		duk::put(ctx, obj);
-		duk_pop(ctx);
-		ret = 0;
-	} else {
-		duk::push(ctx, obj);
-		ret = 1;
-	}
-
-	return ret;
-}
-
-} // !namespace
-
-namespace duk {
-
-auto type_traits<point>::get(duk_context* ctx, duk_idx_t index) -> point
-{
-	duk::stack_guard sa(ctx);
-
-	point ret;
-
-	duk_get_prop_string(ctx, index, "x");
-	ret.x = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y");
-	ret.y = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-
-	return ret;
-}
-
-auto type_traits<point>::require(duk_context* ctx, duk_idx_t index) -> point
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, "x");
-	const auto x = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y");
-	const auto y = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-
-	if (!x)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x' property");
-	if (!y)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y' property");
-
-	return { *x, *y };
-}
-
-auto type_traits<point>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<point>
-{
-	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-void type_traits<point>::push(duk_context* ctx, const point &point)
-{
-	duk::stack_guard sa(ctx, 1);
-
-	duk_push_object(ctx);
-	put(ctx, point);
-}
-
-void type_traits<point>::put(duk_context* ctx, const point& point)
-{
-	assert(duk_is_object(ctx, -1));
-
-	duk::stack_guard sa(ctx);
-
-	duk::push(ctx, point.x);
-	duk_put_prop_string(ctx, -2, "x");
-	duk::push(ctx, point.y);
-	duk_put_prop_string(ctx, -2, "y");
-}
-
-} // !duk
-
-void load_point_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx, 0);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Point_constructor, DUK_VARARGS);
-	duk_put_prop_string(ctx, -2, "Point");
-	duk_pop(ctx);
-}
-
-} // !mlk
--- a/libcommon-js/malikania/js_point.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * js_point.hpp -- point description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_POINT_HPP
-#define MALIKANIA_JS_POINT_HPP
-
-/**
- * \file js_point.hpp
- * \brief JavaScript binding for line.
- *
- * Points are plain objects.
- *
- * ````
- * {
- *   x: 10,
- *   y: 10,
- * }
- * ````
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-struct point;
-
-namespace duk {
-
-template <>
-struct type_traits<point> {
-        static auto require(duk_context* ctx, duk_idx_t index) -> point;
-
-        static auto get(duk_context* ctx, duk_idx_t index) -> point;
-
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<point>;
-
-        static void push(duk_context* ctx, const point& point);
-
-        static void put(duk_context* ctx, const point& point);
-};
-
-} // !duk
-
-/**
- * Load Malikania.Point API into the context.
- *
- * \param ctx the context
- */
-void load_point_api(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_POINT_HPP
--- a/libcommon-js/malikania/js_rectangle.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/*
- * js_rectangle.cpp -- rectangle description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/rectangle.hpp>
-
-#include "js_rectangle.hpp"
-
-namespace mlk {
-
-namespace {
-
-auto Rectangle_constructor(duk_context* ctx) -> duk_ret_t
-{
-	rectangle rect;
-
-	if (duk_get_top(ctx) == 4) {
-		rect = rectangle{
-			duk::require<int>(ctx, 0),
-			duk::require<int>(ctx, 1),
-			duk::require<unsigned>(ctx, 2),
-			duk::require<unsigned>(ctx, 3)
-		};
-	} else if (duk_get_top(ctx) == 1)
-		rect = duk::require<rectangle>(ctx, 0);
-
-	duk_ret_t ret;
-
-	// Allow both constructor and non constructor calls.
-	if (duk_is_constructor_call(ctx)) {
-		duk_push_this(ctx);
-		duk::put(ctx, rect);
-		duk_pop(ctx);
-		ret = 0;
-	} else {
-		duk::push(ctx, rect);
-		ret = 1;
-	}
-
-	return ret;
-}
-
-} // !namespace
-
-namespace duk {
-
-auto type_traits<rectangle>::get(duk_context* ctx, duk_idx_t index) -> rectangle
-{
-	duk::stack_guard sa(ctx);
-
-	rectangle ret;
-
-	if (!duk_is_object(ctx, index))
-		return ret;
-
-	duk_get_prop_string(ctx, index, "x");
-	ret.x = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y");
-	ret.y = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "width");
-	ret.width = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "height");
-	ret.height = duk::get<int>(ctx, -1);
-	duk_pop(ctx);
-
-	return ret;
-}
-
-auto type_traits<rectangle>::require(duk_context* ctx, duk_idx_t index) -> rectangle
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, "x");
-	const auto x = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "y");
-	const auto y = duk::optional<int>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "width");
-	const auto width = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "height");
-	const auto height = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-
-	if (!x)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x' property");
-	if (!y)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y' property");
-	if (!width)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'width' property");
-	if (!height)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'height' property");
-
-	return { *x, *y, *width, *height };
-}
-
-auto type_traits<rectangle>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<rectangle>
-{
-	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;;
-}
-
-void type_traits<rectangle>::push(duk_context* ctx, const rectangle& rect)
-{
-	duk::stack_guard sa(ctx, 1);
-
-	duk_push_object(ctx);
-	put(ctx, rect);
-}
-
-void type_traits<rectangle>::put(duk_context* ctx, const rectangle& rect)
-{
-	assert(duk_is_object(ctx, -1));
-
-	duk::stack_guard sa(ctx);
-
-        duk::push(ctx, rect.x);
-	duk_put_prop_string(ctx, -2, "x");
-        duk::push(ctx, rect.y);
-	duk_put_prop_string(ctx, -2, "y");
-        duk::push(ctx, rect.width);
-	duk_put_prop_string(ctx, -2, "width");
-        duk::push(ctx, rect.height);
-	duk_put_prop_string(ctx, -2, "height");
-}
-
-} // !duk
-
-void load_rectangle_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx, 0);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Rectangle_constructor, DUK_VARARGS);
-	duk_put_prop_string(ctx, -2, "Rectangle");
-	duk_pop(ctx);
-}
-
-} // !mlk
--- a/libcommon-js/malikania/js_rectangle.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,107 +0,0 @@
-/*
- * js_rectangle.hpp -- rectangle description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_RECTANGLE_HPP
-#define MALIKANIA_JS_RECTANGLE_HPP
-
-/**
- * \file js_rectangle.hpp
- * \brief JavaScript binding for rectangle.
- *
- * Rectangles are plain objects.
- *
- * ````
- * {
- *   x: 10,
- *   y: 20,
- *   width: 100,
- *   height: 200
- * }
- * ````
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-struct rectangle;
-
-namespace duk {
-
-template <>
-struct type_traits<rectangle> {
-	/**
-	 * Get a rectangle.
-	 *
-	 * The rectangle may be adjusted if any values are incorrect.
-	 *
-	 * \param ctx the context
-	 * \param index the value index
-	 * \return the rectangle
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> rectangle;
-
-	/**
-	 * Require a rectangle.
-	 *
-	 * If the object is not a rectangle or if width, height are invalid, raise a JavaScript error.
-	 *
-	 * \param ctx the context
-	 * \param index the index
-	 * \return the rectangle
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> rectangle;
-
-	/**
-	 * Like get but return the default value if the value at the given index is not an object or invalid
-	 *
-	 * \param ctx the context
-	 * \param index the idnex
-	 * \return the rectangle
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<rectangle>;
-
-	/**
-	 * Push the rectangle as object.
-	 *
-	 * \param ctx the context
-	 * \param rect the rectangle
-	 */
-	static void push(duk_context* ctx, const rectangle &rect);
-
-	/**
-	 * Put the rectangle properties into the object at the top of the stack.
-	 *
-	 * \param ctx the context
-	 * \param rect the rectangle
-	 */
-	static void put(duk_context* ctx, const rectangle& rect);
-};
-
-} // !duk
-
-/**
- * Load Malikania.Rectangle API into the context.
- *
- * \param ctx the context
- */
-void load_rectangle_api(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_RECTANGLE_HPP
--- a/libcommon-js/malikania/js_resources_loader.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * js_resources_loader.cpp -- resources loader (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/loader.hpp>
-
-#include "js_resources_loader.hpp"
-
-namespace mlk::duk {
-
-namespace {
-
-const std::string_view variable("\xff""\xff""Malikania.Loader");
-
-} // !namespace
-
-void type_traits<loader>::put(duk_context *ctx, loader& loader)
-{
-	assert(ctx);
-
-	duk::stack_guard sa(ctx);
-
-	duk_push_pointer(ctx, &loader);
-	duk_put_global_string(ctx, variable.data());
-}
-
-auto type_traits<loader>::require(duk_context* ctx, duk_idx_t) -> loader&
-{
-	assert(ctx);
-
-	duk::stack_guard sa(ctx);
-
-	duk_get_global_string(ctx, variable.data());
-	auto ptr = static_cast<loader*>(duk_to_pointer(ctx, -1));
-	duk_pop(ctx);
-
-	return *static_cast<loader*>(ptr);
-}
-
-} // !mlk::duk
--- a/libcommon-js/malikania/js_resources_loader.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +0,0 @@
-/*
- * js_resources_loader.hpp -- resources loader (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_RESOURCES_LOADER_H
-#define MALIKANIA_JS_RESOURCES_LOADER_H
-
-#include "duk.hpp"
-
-namespace mlk {
-
-class loader;
-
-namespace duk {
-
-template <>
-struct type_traits<loader> {
-	static void put(duk_context* ctx, loader& loader);
-
-	static auto require(duk_context* ctx, duk_idx_t index) -> loader&;
-};
-
-} // !duk
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_RESOURCES_LOADER_H
--- a/libcommon-js/malikania/js_size.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * js_size.cpp -- size description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include <malikania/size.hpp>
-
-#include "js_size.hpp"
-
-namespace mlk {
-
-namespace {
-
-auto Size_constructor(duk_context* ctx) -> duk_ret_t
-{
-	size obj;
-
-	if (duk_get_top(ctx) == 2) {
-		obj = size{
-			duk::require<unsigned>(ctx, 0),
-			duk::require<unsigned>(ctx, 1)
-		};
-	} else if (duk_get_top(ctx) == 1)
-		obj = duk::require<size>(ctx, 0);
-
-	duk_ret_t ret;
-
-	// Allow both constructor and non constructor calls.
-	if (duk_is_constructor_call(ctx)) {
-		duk_push_this(ctx);
-		duk::put(ctx, obj);
-		duk_pop(ctx);
-		ret = 0;
-	} else {
-		duk::push(ctx, obj);
-		ret = 1;
-	}
-
-	return ret;
-}
-
-} // !namespace
-
-namespace duk {
-
-auto type_traits<size>::get(duk_context* ctx, duk_idx_t index) -> size
-{
-	duk::stack_guard sa(ctx);
-
-	size ret;
-
-	duk_get_prop_string(ctx, index, "width");
-	ret.width = duk::get<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "height");
-	ret.height = duk::get<unsigned>(ctx, -1);
-	duk_pop(ctx);
-
-	return ret;
-}
-
-auto type_traits<size>::require(duk_context* ctx, duk_idx_t index) -> size
-{
-	duk::stack_guard sa(ctx);
-
-	duk_get_prop_string(ctx, index, "width");
-	const auto width = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-	duk_get_prop_string(ctx, index, "height");
-	const auto height = duk::optional<unsigned>(ctx, -1);
-	duk_pop(ctx);
-
-	if (!width)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'width' property");
-	if (!height)
-		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'height' property");
-
-	return { *width, *height };
-}
-
-auto type_traits<size>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<size>
-{
-	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
-}
-
-void type_traits<size>::push(duk_context* ctx, const size& size)
-{
-	duk::stack_guard sa(ctx, 1);
-
-	duk_push_object(ctx);
-	put(ctx, size);
-}
-
-void type_traits<size>::put(duk_context* ctx, const size& size)
-{
-	assert(duk_is_object(ctx, -1));
-
-	duk::stack_guard sa(ctx, 0);
-
-	duk::push(ctx, size.width);
-	duk_put_prop_string(ctx, -2, "width");
-	duk::push(ctx, size.height);
-	duk_put_prop_string(ctx, -2, "height");
-}
-
-} // !duk
-
-void load_size_api(duk_context* ctx)
-{
-	duk::stack_guard sa(ctx, 0);
-
-	duk_get_global_string(ctx, "Malikania");
-	duk_push_c_function(ctx, Size_constructor, DUK_VARARGS);
-	duk_put_prop_string(ctx, -2, "Size");
-	duk_pop(ctx);
-}
-
-} // !mlk
--- a/libcommon-js/malikania/js_size.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * js_size.hpp -- size description (JavaScript binding)
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_JS_SIZE_HPP
-#define MALIKANIA_JS_SIZE_HPP
-
-/**
- * \file js_size.hpp
- * \brief JavaScript binding for size.
- *
- * Sizes are plain objects.
- *
- * ````
- * {
- *   width: 1000,
- *   height: 2000
- * }
- * ````
- */
-
-#include "duk.hpp"
-
-namespace mlk {
-
-struct size;
-
-namespace duk {
-
-template <>
-struct type_traits<size> {
-	/**
-	 * Get a size.
-	 *
-	 * The size may be adjusted if any values are incorrect.
-	 *
-	 * \param ctx the context
-	 * \param index the value index
-	 * \return the size
-	 */
-	static auto get(duk_context* ctx, duk_idx_t index) -> size;
-
-	/**
-	 * Require a size
-	 *
-	 * If the object is not a size, raise a JavaScript error.
-	 *
-	 * \param ctx the context
-	 * \param index the index
-	 * \return the size
-	 */
-	static auto require(duk_context* ctx, duk_idx_t index) -> size;
-
-	/**
-	 * \param ctx the context
-	 * \param index the idnex
-	 * \param def the default value
-	 * \return the size
-	 */
-	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<size>;
-
-	/**
-	 * Push the size as object.
-	 *
-	 * \param ctx the context
-	 * \param size the size
-	 */
-	static void push(duk_context* ctx, const size& size);
-
-	/**
-	 * Put the size properties into the object at the top of the stack.
-	 *
-	 * \param ctx the context
-	 * \param size the size
-	 */
-	static void put(duk_context* ctx, const size& size);
-};
-
-} // !duk
-
-/**
- * Load Malikania.Size API into the context.
- *
- * \param ctx the context
- */
-void load_size_api(duk_context* ctx);
-
-} // !mlk
-
-#endif // !MALIKANIA_JS_SIZE_HPP
--- a/libcommon/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-common)
-
-find_package(Boost REQUIRED filesystem system)
-find_package(OpenSSL REQUIRED)
-
-set(
-	HEADERS
-	${libmlk-common_SOURCE_DIR}/malikania/error/auth_error.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/error/error.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/game.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/line.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/loader.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/locator.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/point.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/rectangle.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/size.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/socket.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/tileset.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/unicode.hpp
-	${libmlk-common_SOURCE_DIR}/malikania/util.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-common_SOURCE_DIR}/malikania/error/auth_error.cpp
-	${libmlk-common_SOURCE_DIR}/malikania/loader.cpp
-	${libmlk-common_SOURCE_DIR}/malikania/locator.cpp
-	${libmlk-common_SOURCE_DIR}/malikania/socket.cpp
-	${libmlk-common_SOURCE_DIR}/malikania/unicode.cpp
-	${libmlk-common_SOURCE_DIR}/malikania/util.cpp
-)
-
-malikania_define_library(
-	PROJECT libmlk-common
-	TARGET libmlk-common
-	SOURCES ${HEADERS} ${SOURCES}
-	PUBLIC_INCLUDES
-		${INCLUDES}
-		$<BUILD_INTERFACE:${libmlk-common_SOURCE_DIR}/malikania>
-		$<BUILD_INTERFACE:${libmlk-common_SOURCE_DIR}>
-	LIBRARIES
-		Boost::boost
-		Boost::filesystem
-		Boost::system
-		OpenSSL::Crypto
-		OpenSSL::SSL
-		json
-		${LIBRARIES}
-)
--- a/libcommon/malikania/error/auth_error.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/*
- * auth_error.hpp -- auth command error
- *
- * Copyright (c) 2013-2018 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 "auth_error.hpp"
-
-namespace mlk {
-
-auto auth_error::error_category() -> std::string
-{
-	return "auth";
-}
-
-auto auth_error::error_message(error code) -> std::string
-{
-	switch (code) {
-	case invalid_credentials:
-		return "invalid credentials";
-	case already_connected:
-		return "already connected";
-	default:
-		return "no error";
-	}
-}
-
-} // !mlk
--- a/libcommon/malikania/error/auth_error.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * auth_error.hpp -- auth command error
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_AUTH_ERROR_HPP
-#define MALIKANIA_AUTH_ERROR_HPP
-
-/**
- * \file auth_error.hpp
- * \brief Auth command error.
- */
-
-#include "error.hpp"
-
-namespace mlk {
-
-/**
- * \brief Auth command error.
- */
-struct auth_error {
-	/**
-	 * \brief Error description
-	 */
-	enum error {
-		/**
-		 * \brief No error.
-		 */
-		no_error = 0,
-
-		/**
-		 * \brief Invalid password or login.
-		 */
-		invalid_credentials,
-
-		/**
-		 * \brief User is already connected.
-		 */
-		already_connected
-	};
-
-	static auto error_category() -> std::string;
-
-	/**
-	 * Convert the code to a human message.
-	 *
-	 * \param code the code
-	 */
-	static auto error_message(error code) -> std::string;
-};
-
-} // !mlk
-
-MALIKANIA_ERROR_CODE(
-	mlk::auth_error::error,
-	mlk::auth_error::error_category,
-	mlk::auth_error::error_message
-)
-
-#endif // !MALIKANIA_AUTH_ERROR_HPP
--- a/libcommon/malikania/error/error.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * error.hpp -- error code creation
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_ERROR_HPP
-#define MALIKANIA_ERROR_HPP
-
-/**
- * \file error.hpp
- * \brief Error code creation.
- */
-
-#include <string>
-#include <system_error>
-#include <type_traits>
-
-/**
- * Generate a custom std::error_code with the given type. The macro must be
- * called outside mlk namespace.
- *
- * See the following example.
- *
- * ```cpp
- * namespace mlk {
- *
- * class foo {
- * public:
- *     enum class error {
- *         no_error = 0,
- *         error_kind_foo,
- *         error_kind_bar
- *     };
- *
- *     static auto error_category() -> std::string
- *     {
- *         return "sometext";
- *     }
- *
- *     static auto error_message(error code) -> std::string
- *     {
- *         // convert code to a string.
- *     }
- * };
- *
- * } // !mlk
- *
- * MALIKANIA_ERROR_CODE(
- *     my_error::error,
- *     my_error::error_category,
- *     my_error::error_message
- * )
- * ```
- */
-#define MALIKANIA_ERROR_CODE(Enum, Category, Message)                       \
-namespace std {                                                             \
-                                                                            \
-template <>                                                                 \
-struct is_error_code_enum<Enum> : public std::true_type {                   \
-};                                                                          \
-                                                                            \
-}                                                                           \
-                                                                            \
-namespace mlk {                                                             \
-                                                                            \
-inline auto make_error_code(Enum code) noexcept -> std::error_code          \
-{                                                                           \
-        static const class : public std::error_category {                   \
-        public:                                                             \
-            auto name() const noexcept -> const char* override              \
-            {                                                               \
-                    return Category().c_str();                              \
-            }                                                               \
-                                                                            \
-            auto message(int e) const -> std::string override               \
-            {                                                               \
-                    return Message(static_cast<Enum>(e));                   \
-            }                                                               \
-        } category;                                                         \
-                                                                            \
-        return { static_cast<int>(code), category };                        \
-}                                                                           \
-                                                                            \
-}
-
-#endif // !MALIKANIA_ERROR_HPP
--- a/libcommon/malikania/game.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * game.hpp -- basic game class
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_GAME_HPP
-#define MALIKANIA_GAME_HPP
-
-/**
- * \file game.hpp
- * \brief Game description.
- */
-
-#include <string>
-
-namespace mlk {
-
-/**
- * \brief Basic game description.
- */
-struct game {
-	std::string name;
-	std::string version;
-	std::string requires;
-	std::string license;
-	std::string author;
-};
-
-} // !mlk
-
-#endif // !MALIKANIA_GAME_HPP
--- a/libcommon/malikania/line.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * line.hpp -- line description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_LINE_HPP
-#define MALIKANIA_LINE_HPP
-
-/**
- * \file line.hpp
- * \brief line description.
- */
-
-namespace mlk {
-
-/**
- * \brief line description.
- *
- * A line has an origin (x, y) and a destination (x, y).
- */
-struct line {
-	int x1{0};
-	int y1{0};
-	int x2{0};
-	int y2{0};
-};
-
-/**
- * Compare equality.
- *
- * \param l1 the first line
- * \param l2 the second line
- * \return true if they equal
- */
-inline auto operator==(const line& l1, const line& l2) noexcept -> bool
-{
-	return l1.x1 == l2.x1 && l1.x2 == l2.x2 &&
-	       l1.y1 == l2.y1 && l1.y2 == l2.y2;
-}
-
-/**
- * Compare equality.
- *
- * \param l1 the first line
- * \param l2 the second line
- * \return false if they equal
- */
-inline auto operator!=(const line& l1, const line& l2) noexcept -> bool
-{
-	return !(l1 == l2);
-}
-
-} // !mlk
-
-#endif // !MALIKANIA_LINE_HPP
--- a/libcommon/malikania/loader.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * loader.cpp -- load shared resources files
- *
- * Copyright (c) 2013-2018 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 "game.hpp"
-#include "loader.hpp"
-#include "locator.hpp"
-#include "tileset.hpp"
-#include "util.hpp"
-
-namespace mlk {
-
-namespace {
-
-auto load_tileset_tile_properties(const nlohmann::json& json) -> tileset::properties_map
-{
-	auto it = json.find("properties");
-
-	if (it == json.end())
-		return {};
-
-	tileset::properties_map props;
-
-	for (auto pair = it->begin(); pair != it->end(); ++pair) {
-		// TODO: maxi convert
-		if (!pair->is_string())
-			continue;
-
-		props.emplace(pair.key(), pair->get<std::string>());
-	}
-
-	return props;
-}
-
-auto load_tileset_tiles_properties(const nlohmann::json& json) -> tileset::tile_properties_map
-{
-	auto tiles = json.find("tiles");
-
-	if (tiles == json.end())
-		return {};
-
-	tileset::tile_properties_map props;
-
-	for (auto it = tiles->begin(); it != tiles->end(); ++it)
-		props.emplace(std::stoi(it.key()), load_tileset_tile_properties(*it));
-
-	return props;
-}
-
-} // !namespace
-
-loader::loader(mlk::locator& locator)
-	: locator_(locator)
-{
-}
-
-auto loader::get_locator() noexcept -> locator&
-{
-	return locator_;
-}
-
-auto loader::load_game() const -> game
-{
-	auto value = nlohmann::json::parse(locator_.read("game.json"));
-
-	if (!value.is_object())
-		throw std::runtime_error("game.json: not a JSON object");
-
-	return game{
-		util::json::require_string(value, "/name"_json_pointer),
-		util::json::require_string(value, "/version"_json_pointer),
-		util::json::require_string(value, "/requires"_json_pointer),
-		value.count("license") > 0 ? value["license"] : "",
-		value.count("author") > 0 ? value["author"] : ""
-	};
-}
-
-auto loader::load_tileset(std::string_view id) const -> tileset
-{
-	auto value = nlohmann::json::parse(locator_.read(id));
-
-	if (!value.is_object())
-		throw std::runtime_error("not a valid tileset");
-
-	tileset tileset;
-
-	tileset.image = util::json::require_string(value, "/image"_json_pointer);
-	tileset.cell = util::json::require_size(value, "/cell"_json_pointer);
-	tileset.tile_properties = load_tileset_tiles_properties(value);
-
-	return tileset;
-}
-
-} // !mlk
--- a/libcommon/malikania/loader.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * loader.hpp -- load shared resources files
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_LOADER_HPP
-#define MALIKANIA_LOADER_HPP
-
-#include <string>
-
-namespace mlk {
-
-struct game;
-struct tileset;
-
-class locator;
-
-/**
- * \brief Open resources files using a locator.
- *
- * This class is used to load resources files that are common to the server and
- * the client.
- *
- * \see client_loader
- */
-class loader {
-private:
-	locator& locator_;
-
-public:
-	/**
-	 * Construct the resources loader.
-	 *
-	 * \param locator the locator
-	 */
-	loader(locator& locator);
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~loader() noexcept = default;
-
-	/**
-	 * Get the underlying locator.
-	 *
-	 * \return the locator
-	 */
-	auto get_locator() noexcept -> locator&;
-
-	/**
-	 * Load a game.
-	 *
-	 * \return the game
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_game() const -> game;
-
-	/**
-	 * Load a tileset.
-	 *
-	 * \param id the tileset id
-	 * \return a tileset ready to use
-	 * \throw std::runtime_error on errors
-	 */
-	virtual auto load_tileset(std::string_view id) const -> tileset;
-};
-
-} // !mlk
-
-#endif // !MALIKANIA_LOADER_HPP
--- a/libcommon/malikania/locator.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * locator.cpp -- file and stream loader
- *
- * Copyright (c) 2013-2018 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 <cerrno>
-#include <cstring>
-#include <fstream>
-#include <iterator>
-#include <sstream>
-#include <stdexcept>
-
-#include "locator.hpp"
-
-namespace mlk {
-
-auto directory_locator::make_path(std::string_view id) -> std::string
-{
-	std::ostringstream oss;
-
-	oss << path_;
-	oss << "/";
-	oss << id;
-
-	return oss.str();
-}
-
-directory_locator::directory_locator(std::string path) noexcept
-	: path_(std::move(path))
-{
-}
-
-auto directory_locator::read(std::string_view id) -> std::string 
-{
-	std::ifstream in(make_path(id), std::ifstream::in | std::ifstream::binary);
-
-	if (!in)
-		throw std::runtime_error(std::strerror(errno));
-
-	return std::string(
-		std::istreambuf_iterator<char>(in.rdbuf()),
-		std::istreambuf_iterator<char>()
-	);
-}
-
-auto directory_locator::open(std::string_view id) -> std::unique_ptr<std::istream> 
-{
-	auto ptr = std::make_unique<std::ifstream>(make_path(id));
-
-	if (!(*ptr))
-		throw std::runtime_error(std::strerror(errno));
-
-	return std::move(ptr);
-}
-
-} // !mlk
--- a/libcommon/malikania/locator.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * locator.hpp -- file and stream loader
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_LOCATOR_HPP
-#define MALIKANIA_LOCATOR_HPP
-
-#include <istream>
-#include <memory>
-#include <string>
-#include <string_view>
-
-namespace mlk {
-
-/**
- * \brief Abstract assets locator.
- */
-class locator {
-public:
-	/**
-	 * Default destructor.
-	 */
-	virtual ~locator() = default;
-
-	/**
-	 * Read a whole resource as a string.
-	 *
-	 * \param id the resource id
-	 * \return the string
-	 * \throw std::runtime_error on any errors
-	 */
-	virtual auto read(std::string_view id) -> std::string = 0;
-
-	/**
-	 * Open a resource as a stream.
-	 *
-	 * \param id the resource id
-	 * \return the stream
-	 * \throw std::runtime_error on any errors
-	 */
-	virtual auto open(std::string_view id) -> std::unique_ptr<std::istream> = 0;
-};
-
-/**
- * \brief Load a game from a directory.
- */
-class directory_locator : public locator {
-private:
-	std::string path_;
-
-	auto make_path(std::string_view) -> std::string;
-
-public:
-	/**
-	 * Load the game from the directory.
-	 *
-	 * \param path the base directory
-	 */
-	directory_locator(std::string path) noexcept;
-
-	/**
-	 * \copydoc locator::read
-	 */
-	auto read(std::string_view id) -> std::string override;
-
-	/**
-	 * \copydoc locator::open
-	 */
-	auto open(std::string_view id) -> std::unique_ptr<std::istream> override;
-};
-
-} // !mlk
-
-#endif // !MALIKANIA_LOCATOR_HPP
--- a/libcommon/malikania/point.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * point.hpp -- point description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_POINT_HPP
-#define MALIKANIA_POINT_HPP
-
-/**
- * \file point.hpp
- * \brief point description.
- */
-
-namespace mlk {
-
-/**
- * \brief point coordinate.
- */
-struct point {
-    int x{0};
-    int y{0};
-};
-
-/**
- * Compare equality.
- *
- * \param p1 the first point
- * \param p2 the second point
- * \return true if they equal
- */
-inline auto operator==(const point& p1, const point& p2) noexcept -> bool
-{
-    return p1.x == p2.x && p1.y == p2.y;
-}
-
-/**
- * Compare equality.
- *
- * \param p1 the first point
- * \param p2 the second point
- * \return false if they equal
- */
-inline auto operator!=(const point& p1, const point& p2) noexcept -> bool
-{
-    return !(p1 == p2);
-}
-
-} // !mlk
-
-#endif // !MALIKANIA_POINT_HPP
--- a/libcommon/malikania/rectangle.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * rectangle.hpp -- rectangle description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_RECTANGLE_HPP
-#define MALIKANIA_RECTANGLE_HPP
-
-/**
- * \file rectangle.hpp
- * \brief rectangle description.
- */
-
-namespace mlk {
-
-/**
- * \brief rectangle description.
- *
- * A rectangle has coordinates (x, y) and dimensions (width, height).
- *
- * They are commonly used for clipping images into the window.
- */
-struct rectangle {
-	int x{0};                       //!< x coordinate
-	int y{0};                       //!< y coordinate
-	unsigned width{0};              //!< width
-	unsigned height{0};             //!< height
-
-	/**
-	 * Check if the rectangle has null dimensions.
-	 *
-	 * \return true if weight and height are 0
-	 */
-	inline auto is_null() const noexcept -> bool
-	{
-		return width == 0 && height == 0;
-	}
-};
-
-/**
- * Compare equality.
- *
- * \param r1 the first rectangle
- * \param r2 the second rectangle
- * \return true if they equal
- */
-inline auto operator==(const rectangle& r1, const rectangle& r2) noexcept -> bool
-{
-	return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height;
-}
-
-/**
- * Compare equality.
- *
- * \param r1 the first rectangle
- * \param r2 the second rectangle
- * \return false if they equal
- */
-inline auto operator!=(const rectangle& r1, const rectangle& r2) noexcept -> bool
-{
-	return !(r1 == r2);
-}
-
-} // !mlk
-
-#endif // !MALIKANIA_RECTANGLE_HPP
--- a/libcommon/malikania/size.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * size.hpp -- size description
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_SIZE_HPP
-#define MALIKANIA_SIZE_HPP
-
-#include <ostream>
-
-namespace mlk {
-
-/**
- * \brief size description.
- */
-struct size {
-    /**
-     * \brief Width.
-     */
-    unsigned width{0};
-
-    /**
-     * \brief Height.
-     */
-    unsigned height{0};
-
-    /**
-     * Check if the size is 0, 0.
-     *
-     * \return true if height and width are 0
-     */
-    inline auto is_null() const noexcept -> bool
-    {
-        return height == 0 && width == 0;
-    }
-};
-
-/**
- * Compare equality.
- *
- * \param s1 the first size
- * \param s2 the second size
- * \return true if they equal
- */
-inline auto operator==(const size& s1, const size& s2) noexcept -> bool
-{
-    return s1.width == s2.width && s1.height == s2.height;
-}
-
-/**
- * Compare equality.
- *
- * \param s1 the first size
- * \param s2 the second size
- * \return false if they equal
- */
-inline auto operator!=(const size& s1, const size& s2) noexcept -> bool
-{
-    return !(s1 == s2);
-}
-
-/**
- * Prints the size object.
- *
- * \param out the output
- * \param size the size object
- * \return out
- */
-inline auto operator<<(std::ostream& out, const size& size) -> std::ostream&
-{
-    out << "{" << size.width << ", " << size.height << "}";
-
-    return out;
-}
-
-} // !mlk
-
-#endif // !MALIKANIA_SIZE_HPP
--- a/libcommon/malikania/socket.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * socket.cpp -- SSL socket using JSON messages
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "socket.hpp"
-
-namespace mlk {
-
-void socket::do_recv(recv_handler handler)
-{
-    boost::asio::async_read_until(socket_, rbuffer_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
-        if (code)
-            handler(std::move(code), nullptr);
-        else if (xfer == 0U)
-            handler(make_error_code(boost::system::errc::network_down), nullptr);
-        else {
-            std::string str(
-                boost::asio::buffers_begin(rbuffer_.data()),
-                boost::asio::buffers_begin(rbuffer_.data()) + xfer - 4
-            );
-
-            // Remove early in case of errors.
-            rbuffer_.consume(xfer);
-
-            // TODO: catch nlohmann::json::parse_error when 3.0.0 is released.
-            nlohmann::json message;
-
-            try {
-                message = nlohmann::json::parse(str);
-            } catch (...) {}
-
-            if (!message.is_object())
-                handler(make_error_code(boost::system::errc::invalid_argument), nullptr);
-            else
-                handler(code, std::move(message));
-        }
-    });
-}
-
-void socket::do_send(const std::string& str, send_handler handler)
-{
-    boost::asio::async_write(socket_, boost::asio::buffer(str), [handler] (auto code, auto xfer) {
-        if (xfer == 0U)
-            handler(make_error_code(boost::system::errc::network_down));
-        else
-            handler(code);
-    });
-}
-
-socket::socket(boost::asio::io_service& service,
-               boost::asio::ssl::context& ctx)
-	: socket_(service, ctx)
-{
-}
-
-auto socket::get_socket() const noexcept -> const socket_t&
-{
-	return socket_;
-}
-
-auto socket::get_socket() noexcept -> socket_t&
-{
-	return socket_;
-}
-
-auto socket::is_receiving() const noexcept -> bool
-{
-	return !rqueue_.empty();
-}
-
-auto socket::is_sending() const noexcept -> bool
-{
-	return !squeue_.empty();
-}
-
-auto socket::is_active() const noexcept -> bool
-{
-	return is_receiving() || is_sending();
-}
-
-void socket::rflush()
-{
-    if (rqueue_.empty())
-        return;
-
-    do_recv([this] (auto code, auto json) {
-        auto handler = rqueue_.front();
-
-        rqueue_.pop_front();
-        handler(code, std::move(json));
-
-        if (!code)
-            rflush();
-    });
-}
-
-void socket::sflush()
-{
-    if (squeue_.empty())
-        return;
-
-    do_send(squeue_.front().first, [this] (auto code) {
-        auto handler = squeue_.front().second;
-
-        squeue_.pop_front();
-
-        if (handler)
-            handler(code);
-        if (!code)
-            sflush();
-    });
-}
-
-void socket::recv(recv_handler handler)
-{
-    assert(handler);
-
-    auto in_progress = !rqueue_.empty();
-
-    rqueue_.push_back(std::move(handler));
-
-    if (!in_progress)
-        rflush();
-}
-
-void socket::send(nlohmann::json json, send_handler handler)
-{
-    assert(json.is_object());
-
-    auto in_progress = !squeue_.empty();
-
-    squeue_.emplace_back(json.dump(0) + "\r\n\r\n", std::move(handler));
-
-    if (!in_progress)
-        sflush();
-}
-
-} // !mlk
--- a/libcommon/malikania/socket.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * socket.hpp -- SSL socket using JSON messages
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_COMMON_SOCKET_HPP
-#define MALIKANIA_COMMON_SOCKET_HPP
-
-/**
- * \file socket.hpp
- * \brief SSL socket using JSON messages
- */
-
-#include "sysconfig.hpp"
-
-#include <deque>
-#include <functional>
-#include <string>
-#include <system_error>
-#include <utility>
-
-#include <boost/asio.hpp>
-#include <boost/asio/ssl.hpp>
-
-#include <json.hpp>
-
-namespace mlk {
-
-/**
- * \brief SSL socket using JSON messages
- *
- * This class can be used to perform I/O over a networking socket, it is
- * implemented as asynchronous operations over Boost.Asio.
- *
- * All recv/send operations are placed in a queue and performed when possible.
- */
-class socket {
-public:
-	/**
-	 * Read handler.
-	 *
-	 * Call this function when a receive operation has finished on success or
-	 * failure.
-	 */
-	using recv_handler = std::function<void (std::error_code, nlohmann::json)>;
-
-	/**
-	 * Send handler.
-	 *
-	 * Call this function when a send operation has finished on success or failure.
-	 */
-	using send_handler = std::function<void (std::error_code)>;
-
-private:
-	using socket_t = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
-	using rbuffer_t = boost::asio::streambuf;
-	using rqueue_t = std::deque<recv_handler>;
-	using squeue_t = std::deque<std::pair<std::string, send_handler>>;
-
-	socket_t socket_;
-	rbuffer_t rbuffer_;
-	rqueue_t rqueue_;
-	squeue_t squeue_;
-
-	void rflush();
-	void sflush();
-	void do_recv(recv_handler);
-	void do_send(const std::string&, send_handler);
-
-public:
-	/**
-	 * Construct the socket.
-	 *
-	 * \param service the IO service
-	 * \param ctx the SSL context
-	 */
-	socket(boost::asio::io_service& service,
-               boost::asio::ssl::context& ctx);
-
-	/**
-	 * Get the underlying socket.
-	 *
-	 * \return the socket
-	 */
-	auto get_socket() const noexcept -> const socket_t&;
-
-	/**
-	 * Overloaded function.
-	 *
-	 * \return the socket
-	 */
-	auto get_socket() noexcept -> socket_t&;
-
-	/**
-	 * Tells if receive operations are pending.
-	 *
-	 * \return true if receiving is in progress
-	 */
-	auto is_receiving() const noexcept -> bool;
-
-	/**
-	 * Tells if send operations are pending.
-	 *
-	 * \return true if sending is in progress
-	 */
-	auto is_sending() const noexcept -> bool;
-
-	/**
-	 * Tells if there are any I/O pending.
-	 *
-	 * \return true if sending is in progress
-	 */
-	auto is_active() const noexcept -> bool;
-
-	/**
-	 * Request a receive operation.
-	 *
-	 * \pre handler != nullptr
-	 * \param handler the handler
-	 */
-	void recv(recv_handler);
-
-	/**
-	 * Request a send operation.
-	 *
-	 * \pre json.is_object()
-	 * \param json the json message
-	 * \param handler the optional handler
-	 */
-	void send(nlohmann::json json, send_handler = nullptr);
-};
-
-} // !mlk
-
-#endif // MALIKANIA_COMMON_SOCKET_HPP
--- a/libcommon/malikania/tileset.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * tileset.hpp -- map tileset definition
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_COMMON_TILESET_HPP
-#define MALIKANIA_COMMON_TILESET_HPP
-
-/**
- * \file tileset.hpp
- * \brief Map tileset definition.
- */
-
-#include <boost/variant.hpp>
-
-#include <string>
-#include <unordered_map>
-
-#include "size.hpp"
-
-namespace mlk {
-
-/**
- * \brief Map tileset definition.
- */
-struct tileset {
-	/**
-	 * Map of general properties.
-	 */
-	using properties_map = std::unordered_map<std::string, std::string>;
-
-	/**
-	 * Per tile properties.
-	 */
-	using tile_properties_map = std::unordered_map<int, properties_map>;
-
-	std::string image;
-	size cell;
-	properties_map properties;
-	tile_properties_map tile_properties;
-};
-
-} // !mlk
-
-#endif // !MALIKANIA_COMMON_TILESET_HPP
--- a/libcommon/malikania/unicode.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4796 +0,0 @@
-/*
- * unicode.cpp -- UTF-8 to UTF-32 conversions and various operations
- *
- * Copyright (c) 2013-2018 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 "unicode.hpp"
-
-/*
- * The following code has been generated from Go mkrunetype adapted to our
- * needs.
- */
-
-namespace mlk {
-
-namespace unicode {
-
-#define nelem(x) (sizeof (x) / sizeof ((x)[0]))
-
-namespace {
-
-const char32_t *rbsearch(char32_t c, const char32_t* t, int n, int ne) noexcept
-{
-    const char32_t* p;
-    int m;
-
-    while (n > 1) {
-        m = n >> 1;
-        p = t + m * ne;
-
-        if (c >= p[0]) {
-            t = p;
-            n = n - m;
-        } else
-            n = m;
-    }
-
-    if (n && c >= t[0])
-        return t;
-
-    return nullptr;
-}
-
-} // !namespace
-
-namespace {
-
-const char32_t isspacer[] = {
-    0x0009, 0x000d,
-    0x0020, 0x0020,
-    0x0085, 0x0085,
-    0x00a0, 0x00a0,
-    0x1680, 0x1680,
-    0x2000, 0x200a,
-    0x2028, 0x2029,
-    0x202f, 0x202f,
-    0x205f, 0x205f,
-    0x3000, 0x3000,
-    0xfeff, 0xfeff,
-};
-
-} // !namespace
-
-bool isspace(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, isspacer, nelem (isspacer) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t isdigitr[] = {
-    0x0030, 0x0039,
-    0x0660, 0x0669,
-    0x06f0, 0x06f9,
-    0x07c0, 0x07c9,
-    0x0966, 0x096f,
-    0x09e6, 0x09ef,
-    0x0a66, 0x0a6f,
-    0x0ae6, 0x0aef,
-    0x0b66, 0x0b6f,
-    0x0be6, 0x0bef,
-    0x0c66, 0x0c6f,
-    0x0ce6, 0x0cef,
-    0x0d66, 0x0d6f,
-    0x0de6, 0x0def,
-    0x0e50, 0x0e59,
-    0x0ed0, 0x0ed9,
-    0x0f20, 0x0f29,
-    0x1040, 0x1049,
-    0x1090, 0x1099,
-    0x17e0, 0x17e9,
-    0x1810, 0x1819,
-    0x1946, 0x194f,
-    0x19d0, 0x19d9,
-    0x1a80, 0x1a89,
-    0x1a90, 0x1a99,
-    0x1b50, 0x1b59,
-    0x1bb0, 0x1bb9,
-    0x1c40, 0x1c49,
-    0x1c50, 0x1c59,
-    0xa620, 0xa629,
-    0xa8d0, 0xa8d9,
-    0xa900, 0xa909,
-    0xa9d0, 0xa9d9,
-    0xa9f0, 0xa9f9,
-    0xaa50, 0xaa59,
-    0xabf0, 0xabf9,
-    0xff10, 0xff19,
-    0x104a0, 0x104a9,
-    0x11066, 0x1106f,
-    0x110f0, 0x110f9,
-    0x11136, 0x1113f,
-    0x111d0, 0x111d9,
-    0x112f0, 0x112f9,
-    0x114d0, 0x114d9,
-    0x11650, 0x11659,
-    0x116c0, 0x116c9,
-    0x118e0, 0x118e9,
-    0x16a60, 0x16a69,
-    0x16b50, 0x16b59,
-    0x1d7ce, 0x1d7ff,
-};
-
-} // !namespace
-
-bool isdigit(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, isdigitr, nelem (isdigitr) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t isalphar[] = {
-    0x0041, 0x005a,
-    0x0061, 0x007a,
-    0x00c0, 0x00d6,
-    0x00d8, 0x00f6,
-    0x00f8, 0x02c1,
-    0x02c6, 0x02d1,
-    0x02e0, 0x02e4,
-    0x0370, 0x0374,
-    0x0376, 0x0377,
-    0x037a, 0x037d,
-    0x0388, 0x038a,
-    0x038e, 0x03a1,
-    0x03a3, 0x03f5,
-    0x03f7, 0x0481,
-    0x048a, 0x052f,
-    0x0531, 0x0556,
-    0x0561, 0x0587,
-    0x05d0, 0x05ea,
-    0x05f0, 0x05f2,
-    0x0620, 0x064a,
-    0x066e, 0x066f,
-    0x0671, 0x06d3,
-    0x06e5, 0x06e6,
-    0x06ee, 0x06ef,
-    0x06fa, 0x06fc,
-    0x0712, 0x072f,
-    0x074d, 0x07a5,
-    0x07ca, 0x07ea,
-    0x07f4, 0x07f5,
-    0x0800, 0x0815,
-    0x0840, 0x0858,
-    0x08a0, 0x08b2,
-    0x0904, 0x0939,
-    0x0958, 0x0961,
-    0x0971, 0x0980,
-    0x0985, 0x098c,
-    0x098f, 0x0990,
-    0x0993, 0x09a8,
-    0x09aa, 0x09b0,
-    0x09b6, 0x09b9,
-    0x09dc, 0x09dd,
-    0x09df, 0x09e1,
-    0x09f0, 0x09f1,
-    0x0a05, 0x0a0a,
-    0x0a0f, 0x0a10,
-    0x0a13, 0x0a28,
-    0x0a2a, 0x0a30,
-    0x0a32, 0x0a33,
-    0x0a35, 0x0a36,
-    0x0a38, 0x0a39,
-    0x0a59, 0x0a5c,
-    0x0a72, 0x0a74,
-    0x0a85, 0x0a8d,
-    0x0a8f, 0x0a91,
-    0x0a93, 0x0aa8,
-    0x0aaa, 0x0ab0,
-    0x0ab2, 0x0ab3,
-    0x0ab5, 0x0ab9,
-    0x0ae0, 0x0ae1,
-    0x0b05, 0x0b0c,
-    0x0b0f, 0x0b10,
-    0x0b13, 0x0b28,
-    0x0b2a, 0x0b30,
-    0x0b32, 0x0b33,
-    0x0b35, 0x0b39,
-    0x0b5c, 0x0b5d,
-    0x0b5f, 0x0b61,
-    0x0b85, 0x0b8a,
-    0x0b8e, 0x0b90,
-    0x0b92, 0x0b95,
-    0x0b99, 0x0b9a,
-    0x0b9e, 0x0b9f,
-    0x0ba3, 0x0ba4,
-    0x0ba8, 0x0baa,
-    0x0bae, 0x0bb9,
-    0x0c05, 0x0c0c,
-    0x0c0e, 0x0c10,
-    0x0c12, 0x0c28,
-    0x0c2a, 0x0c39,
-    0x0c58, 0x0c59,
-    0x0c60, 0x0c61,
-    0x0c85, 0x0c8c,
-    0x0c8e, 0x0c90,
-    0x0c92, 0x0ca8,
-    0x0caa, 0x0cb3,
-    0x0cb5, 0x0cb9,
-    0x0ce0, 0x0ce1,
-    0x0cf1, 0x0cf2,
-    0x0d05, 0x0d0c,
-    0x0d0e, 0x0d10,
-    0x0d12, 0x0d3a,
-    0x0d60, 0x0d61,
-    0x0d7a, 0x0d7f,
-    0x0d85, 0x0d96,
-    0x0d9a, 0x0db1,
-    0x0db3, 0x0dbb,
-    0x0dc0, 0x0dc6,
-    0x0e01, 0x0e30,
-    0x0e32, 0x0e33,
-    0x0e40, 0x0e46,
-    0x0e81, 0x0e82,
-    0x0e87, 0x0e88,
-    0x0e94, 0x0e97,
-    0x0e99, 0x0e9f,
-    0x0ea1, 0x0ea3,
-    0x0eaa, 0x0eab,
-    0x0ead, 0x0eb0,
-    0x0eb2, 0x0eb3,
-    0x0ec0, 0x0ec4,
-    0x0edc, 0x0edf,
-    0x0f40, 0x0f47,
-    0x0f49, 0x0f6c,
-    0x0f88, 0x0f8c,
-    0x1000, 0x102a,
-    0x1050, 0x1055,
-    0x105a, 0x105d,
-    0x1065, 0x1066,
-    0x106e, 0x1070,
-    0x1075, 0x1081,
-    0x10a0, 0x10c5,
-    0x10d0, 0x10fa,
-    0x10fc, 0x1248,
-    0x124a, 0x124d,
-    0x1250, 0x1256,
-    0x125a, 0x125d,
-    0x1260, 0x1288,
-    0x128a, 0x128d,
-    0x1290, 0x12b0,
-    0x12b2, 0x12b5,
-    0x12b8, 0x12be,
-    0x12c2, 0x12c5,
-    0x12c8, 0x12d6,
-    0x12d8, 0x1310,
-    0x1312, 0x1315,
-    0x1318, 0x135a,
-    0x1380, 0x138f,
-    0x13a0, 0x13f4,
-    0x1401, 0x166c,
-    0x166f, 0x167f,
-    0x1681, 0x169a,
-    0x16a0, 0x16ea,
-    0x16f1, 0x16f8,
-    0x1700, 0x170c,
-    0x170e, 0x1711,
-    0x1720, 0x1731,
-    0x1740, 0x1751,
-    0x1760, 0x176c,
-    0x176e, 0x1770,
-    0x1780, 0x17b3,
-    0x1820, 0x1877,
-    0x1880, 0x18a8,
-    0x18b0, 0x18f5,
-    0x1900, 0x191e,
-    0x1950, 0x196d,
-    0x1970, 0x1974,
-    0x1980, 0x19ab,
-    0x19c1, 0x19c7,
-    0x1a00, 0x1a16,
-    0x1a20, 0x1a54,
-    0x1b05, 0x1b33,
-    0x1b45, 0x1b4b,
-    0x1b83, 0x1ba0,
-    0x1bae, 0x1baf,
-    0x1bba, 0x1be5,
-    0x1c00, 0x1c23,
-    0x1c4d, 0x1c4f,
-    0x1c5a, 0x1c7d,
-    0x1ce9, 0x1cec,
-    0x1cee, 0x1cf1,
-    0x1cf5, 0x1cf6,
-    0x1d00, 0x1dbf,
-    0x1e00, 0x1f15,
-    0x1f18, 0x1f1d,
-    0x1f20, 0x1f45,
-    0x1f48, 0x1f4d,
-    0x1f50, 0x1f57,
-    0x1f5f, 0x1f7d,
-    0x1f80, 0x1fb4,
-    0x1fb6, 0x1fbc,
-    0x1fc2, 0x1fc4,
-    0x1fc6, 0x1fcc,
-    0x1fd0, 0x1fd3,
-    0x1fd6, 0x1fdb,
-    0x1fe0, 0x1fec,
-    0x1ff2, 0x1ff4,
-    0x1ff6, 0x1ffc,
-    0x2090, 0x209c,
-    0x210a, 0x2113,
-    0x2119, 0x211d,
-    0x212a, 0x212d,
-    0x212f, 0x2139,
-    0x213c, 0x213f,
-    0x2145, 0x2149,
-    0x2183, 0x2184,
-    0x2c00, 0x2c2e,
-    0x2c30, 0x2c5e,
-    0x2c60, 0x2ce4,
-    0x2ceb, 0x2cee,
-    0x2cf2, 0x2cf3,
-    0x2d00, 0x2d25,
-    0x2d30, 0x2d67,
-    0x2d80, 0x2d96,
-    0x2da0, 0x2da6,
-    0x2da8, 0x2dae,
-    0x2db0, 0x2db6,
-    0x2db8, 0x2dbe,
-    0x2dc0, 0x2dc6,
-    0x2dc8, 0x2dce,
-    0x2dd0, 0x2dd6,
-    0x2dd8, 0x2dde,
-    0x3005, 0x3006,
-    0x3031, 0x3035,
-    0x303b, 0x303c,
-    0x3041, 0x3096,
-    0x309d, 0x309f,
-    0x30a1, 0x30fa,
-    0x30fc, 0x30ff,
-    0x3105, 0x312d,
-    0x3131, 0x318e,
-    0x31a0, 0x31ba,
-    0x31f0, 0x31ff,
-    0x3400, 0x4db5,
-    0x4e00, 0x9fcc,
-    0xa000, 0xa48c,
-    0xa4d0, 0xa4fd,
-    0xa500, 0xa60c,
-    0xa610, 0xa61f,
-    0xa62a, 0xa62b,
-    0xa640, 0xa66e,
-    0xa67f, 0xa69d,
-    0xa6a0, 0xa6e5,
-    0xa717, 0xa71f,
-    0xa722, 0xa788,
-    0xa78b, 0xa78e,
-    0xa790, 0xa7ad,
-    0xa7b0, 0xa7b1,
-    0xa7f7, 0xa801,
-    0xa803, 0xa805,
-    0xa807, 0xa80a,
-    0xa80c, 0xa822,
-    0xa840, 0xa873,
-    0xa882, 0xa8b3,
-    0xa8f2, 0xa8f7,
-    0xa90a, 0xa925,
-    0xa930, 0xa946,
-    0xa960, 0xa97c,
-    0xa984, 0xa9b2,
-    0xa9e0, 0xa9e4,
-    0xa9e6, 0xa9ef,
-    0xa9fa, 0xa9fe,
-    0xaa00, 0xaa28,
-    0xaa40, 0xaa42,
-    0xaa44, 0xaa4b,
-    0xaa60, 0xaa76,
-    0xaa7e, 0xaaaf,
-    0xaab5, 0xaab6,
-    0xaab9, 0xaabd,
-    0xaadb, 0xaadd,
-    0xaae0, 0xaaea,
-    0xaaf2, 0xaaf4,
-    0xab01, 0xab06,
-    0xab09, 0xab0e,
-    0xab11, 0xab16,
-    0xab20, 0xab26,
-    0xab28, 0xab2e,
-    0xab30, 0xab5a,
-    0xab5c, 0xab5f,
-    0xab64, 0xab65,
-    0xabc0, 0xabe2,
-    0xac00, 0xd7a3,
-    0xd7b0, 0xd7c6,
-    0xd7cb, 0xd7fb,
-    0xf900, 0xfa6d,
-    0xfa70, 0xfad9,
-    0xfb00, 0xfb06,
-    0xfb13, 0xfb17,
-    0xfb1f, 0xfb28,
-    0xfb2a, 0xfb36,
-    0xfb38, 0xfb3c,
-    0xfb40, 0xfb41,
-    0xfb43, 0xfb44,
-    0xfb46, 0xfbb1,
-    0xfbd3, 0xfd3d,
-    0xfd50, 0xfd8f,
-    0xfd92, 0xfdc7,
-    0xfdf0, 0xfdfb,
-    0xfe70, 0xfe74,
-    0xfe76, 0xfefc,
-    0xff21, 0xff3a,
-    0xff41, 0xff5a,
-    0xff66, 0xffbe,
-    0xffc2, 0xffc7,
-    0xffca, 0xffcf,
-    0xffd2, 0xffd7,
-    0xffda, 0xffdc,
-    0x10000, 0x1000b,
-    0x1000d, 0x10026,
-    0x10028, 0x1003a,
-    0x1003c, 0x1003d,
-    0x1003f, 0x1004d,
-    0x10050, 0x1005d,
-    0x10080, 0x100fa,
-    0x10280, 0x1029c,
-    0x102a0, 0x102d0,
-    0x10300, 0x1031f,
-    0x10330, 0x10340,
-    0x10342, 0x10349,
-    0x10350, 0x10375,
-    0x10380, 0x1039d,
-    0x103a0, 0x103c3,
-    0x103c8, 0x103cf,
-    0x10400, 0x1049d,
-    0x10500, 0x10527,
-    0x10530, 0x10563,
-    0x10600, 0x10736,
-    0x10740, 0x10755,
-    0x10760, 0x10767,
-    0x10800, 0x10805,
-    0x1080a, 0x10835,
-    0x10837, 0x10838,
-    0x1083f, 0x10855,
-    0x10860, 0x10876,
-    0x10880, 0x1089e,
-    0x10900, 0x10915,
-    0x10920, 0x10939,
-    0x10980, 0x109b7,
-    0x109be, 0x109bf,
-    0x10a10, 0x10a13,
-    0x10a15, 0x10a17,
-    0x10a19, 0x10a33,
-    0x10a60, 0x10a7c,
-    0x10a80, 0x10a9c,
-    0x10ac0, 0x10ac7,
-    0x10ac9, 0x10ae4,
-    0x10b00, 0x10b35,
-    0x10b40, 0x10b55,
-    0x10b60, 0x10b72,
-    0x10b80, 0x10b91,
-    0x10c00, 0x10c48,
-    0x11003, 0x11037,
-    0x11083, 0x110af,
-    0x110d0, 0x110e8,
-    0x11103, 0x11126,
-    0x11150, 0x11172,
-    0x11183, 0x111b2,
-    0x111c1, 0x111c4,
-    0x11200, 0x11211,
-    0x11213, 0x1122b,
-    0x112b0, 0x112de,
-    0x11305, 0x1130c,
-    0x1130f, 0x11310,
-    0x11313, 0x11328,
-    0x1132a, 0x11330,
-    0x11332, 0x11333,
-    0x11335, 0x11339,
-    0x1135d, 0x11361,
-    0x11480, 0x114af,
-    0x114c4, 0x114c5,
-    0x11580, 0x115ae,
-    0x11600, 0x1162f,
-    0x11680, 0x116aa,
-    0x118a0, 0x118df,
-    0x11ac0, 0x11af8,
-    0x12000, 0x12398,
-    0x13000, 0x1342e,
-    0x16800, 0x16a38,
-    0x16a40, 0x16a5e,
-    0x16ad0, 0x16aed,
-    0x16b00, 0x16b2f,
-    0x16b40, 0x16b43,
-    0x16b63, 0x16b77,
-    0x16b7d, 0x16b8f,
-    0x16f00, 0x16f44,
-    0x16f93, 0x16f9f,
-    0x1b000, 0x1b001,
-    0x1bc00, 0x1bc6a,
-    0x1bc70, 0x1bc7c,
-    0x1bc80, 0x1bc88,
-    0x1bc90, 0x1bc99,
-    0x1d400, 0x1d454,
-    0x1d456, 0x1d49c,
-    0x1d49e, 0x1d49f,
-    0x1d4a5, 0x1d4a6,
-    0x1d4a9, 0x1d4ac,
-    0x1d4ae, 0x1d4b9,
-    0x1d4bd, 0x1d4c3,
-    0x1d4c5, 0x1d505,
-    0x1d507, 0x1d50a,
-    0x1d50d, 0x1d514,
-    0x1d516, 0x1d51c,
-    0x1d51e, 0x1d539,
-    0x1d53b, 0x1d53e,
-    0x1d540, 0x1d544,
-    0x1d54a, 0x1d550,
-    0x1d552, 0x1d6a5,
-    0x1d6a8, 0x1d6c0,
-    0x1d6c2, 0x1d6da,
-    0x1d6dc, 0x1d6fa,
-    0x1d6fc, 0x1d714,
-    0x1d716, 0x1d734,
-    0x1d736, 0x1d74e,
-    0x1d750, 0x1d76e,
-    0x1d770, 0x1d788,
-    0x1d78a, 0x1d7a8,
-    0x1d7aa, 0x1d7c2,
-    0x1d7c4, 0x1d7cb,
-    0x1e800, 0x1e8c4,
-    0x1ee00, 0x1ee03,
-    0x1ee05, 0x1ee1f,
-    0x1ee21, 0x1ee22,
-    0x1ee29, 0x1ee32,
-    0x1ee34, 0x1ee37,
-    0x1ee4d, 0x1ee4f,
-    0x1ee51, 0x1ee52,
-    0x1ee61, 0x1ee62,
-    0x1ee67, 0x1ee6a,
-    0x1ee6c, 0x1ee72,
-    0x1ee74, 0x1ee77,
-    0x1ee79, 0x1ee7c,
-    0x1ee80, 0x1ee89,
-    0x1ee8b, 0x1ee9b,
-    0x1eea1, 0x1eea3,
-    0x1eea5, 0x1eea9,
-    0x1eeab, 0x1eebb,
-    0x20000, 0x2a6d6,
-    0x2a700, 0x2b734,
-    0x2b740, 0x2b81d,
-    0x2f800, 0x2fa1d,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t isalphas[] = {
-    0x00aa,
-    0x00b5,
-    0x00ba,
-    0x02ec,
-    0x02ee,
-    0x037f,
-    0x0386,
-    0x038c,
-    0x0559,
-    0x06d5,
-    0x06ff,
-    0x0710,
-    0x07b1,
-    0x07fa,
-    0x081a,
-    0x0824,
-    0x0828,
-    0x093d,
-    0x0950,
-    0x09b2,
-    0x09bd,
-    0x09ce,
-    0x0a5e,
-    0x0abd,
-    0x0ad0,
-    0x0b3d,
-    0x0b71,
-    0x0b83,
-    0x0b9c,
-    0x0bd0,
-    0x0c3d,
-    0x0cbd,
-    0x0cde,
-    0x0d3d,
-    0x0d4e,
-    0x0dbd,
-    0x0e84,
-    0x0e8a,
-    0x0e8d,
-    0x0ea5,
-    0x0ea7,
-    0x0ebd,
-    0x0ec6,
-    0x0f00,
-    0x103f,
-    0x1061,
-    0x108e,
-    0x10c7,
-    0x10cd,
-    0x1258,
-    0x12c0,
-    0x17d7,
-    0x17dc,
-    0x18aa,
-    0x1aa7,
-    0x1f59,
-    0x1f5b,
-    0x1f5d,
-    0x1fbe,
-    0x2071,
-    0x207f,
-    0x2102,
-    0x2107,
-    0x2115,
-    0x2124,
-    0x2126,
-    0x2128,
-    0x214e,
-    0x2d27,
-    0x2d2d,
-    0x2d6f,
-    0x2e2f,
-    0xa8fb,
-    0xa9cf,
-    0xaa7a,
-    0xaab1,
-    0xaac0,
-    0xaac2,
-    0xfb1d,
-    0xfb3e,
-    0x10808,
-    0x1083c,
-    0x10a00,
-    0x11176,
-    0x111da,
-    0x1133d,
-    0x114c7,
-    0x11644,
-    0x118ff,
-    0x16f50,
-    0x1d4a2,
-    0x1d4bb,
-    0x1d546,
-    0x1ee24,
-    0x1ee27,
-    0x1ee39,
-    0x1ee3b,
-    0x1ee42,
-    0x1ee47,
-    0x1ee49,
-    0x1ee4b,
-    0x1ee54,
-    0x1ee57,
-    0x1ee59,
-    0x1ee5b,
-    0x1ee5d,
-    0x1ee5f,
-    0x1ee64,
-    0x1ee7e,
-};
-
-} // !namespace
-
-bool isalpha(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, isalphar, nelem (isalphar) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    p = rbsearch(c, isalphas, nelem (isalphas), 1);
-
-    if (p && c == p[0])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t isupperr[] = {
-    0x0041, 0x005a,
-    0x00c0, 0x00d6,
-    0x00d8, 0x00de,
-    0x0178, 0x0179,
-    0x0181, 0x0182,
-    0x0186, 0x0187,
-    0x0189, 0x018b,
-    0x018e, 0x0191,
-    0x0193, 0x0194,
-    0x0196, 0x0198,
-    0x019c, 0x019d,
-    0x019f, 0x01a0,
-    0x01a6, 0x01a7,
-    0x01ae, 0x01af,
-    0x01b1, 0x01b3,
-    0x01b7, 0x01b8,
-    0x01f6, 0x01f8,
-    0x023a, 0x023b,
-    0x023d, 0x023e,
-    0x0243, 0x0246,
-    0x0388, 0x038a,
-    0x038e, 0x038f,
-    0x0391, 0x03a1,
-    0x03a3, 0x03ab,
-    0x03d2, 0x03d4,
-    0x03f9, 0x03fa,
-    0x03fd, 0x042f,
-    0x04c0, 0x04c1,
-    0x0531, 0x0556,
-    0x10a0, 0x10c5,
-    0x1f08, 0x1f0f,
-    0x1f18, 0x1f1d,
-    0x1f28, 0x1f2f,
-    0x1f38, 0x1f3f,
-    0x1f48, 0x1f4d,
-    0x1f68, 0x1f6f,
-    0x1f88, 0x1f8f,
-    0x1f98, 0x1f9f,
-    0x1fa8, 0x1faf,
-    0x1fb8, 0x1fbc,
-    0x1fc8, 0x1fcc,
-    0x1fd8, 0x1fdb,
-    0x1fe8, 0x1fec,
-    0x1ff8, 0x1ffc,
-    0x210b, 0x210d,
-    0x2110, 0x2112,
-    0x2119, 0x211d,
-    0x212a, 0x212d,
-    0x2130, 0x2133,
-    0x213e, 0x213f,
-    0x2160, 0x216f,
-    0x24b6, 0x24cf,
-    0x2c00, 0x2c2e,
-    0x2c62, 0x2c64,
-    0x2c6d, 0x2c70,
-    0x2c7e, 0x2c80,
-    0xa77d, 0xa77e,
-    0xa7aa, 0xa7ad,
-    0xa7b0, 0xa7b1,
-    0xff21, 0xff3a,
-    0x10400, 0x10427,
-    0x118a0, 0x118bf,
-    0x1d400, 0x1d419,
-    0x1d434, 0x1d44d,
-    0x1d468, 0x1d481,
-    0x1d49e, 0x1d49f,
-    0x1d4a5, 0x1d4a6,
-    0x1d4a9, 0x1d4ac,
-    0x1d4ae, 0x1d4b5,
-    0x1d4d0, 0x1d4e9,
-    0x1d504, 0x1d505,
-    0x1d507, 0x1d50a,
-    0x1d50d, 0x1d514,
-    0x1d516, 0x1d51c,
-    0x1d538, 0x1d539,
-    0x1d53b, 0x1d53e,
-    0x1d540, 0x1d544,
-    0x1d54a, 0x1d550,
-    0x1d56c, 0x1d585,
-    0x1d5a0, 0x1d5b9,
-    0x1d5d4, 0x1d5ed,
-    0x1d608, 0x1d621,
-    0x1d63c, 0x1d655,
-    0x1d670, 0x1d689,
-    0x1d6a8, 0x1d6c0,
-    0x1d6e2, 0x1d6fa,
-    0x1d71c, 0x1d734,
-    0x1d756, 0x1d76e,
-    0x1d790, 0x1d7a8,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t isuppers[] = {
-    0x0100,
-    0x0102,
-    0x0104,
-    0x0106,
-    0x0108,
-    0x010a,
-    0x010c,
-    0x010e,
-    0x0110,
-    0x0112,
-    0x0114,
-    0x0116,
-    0x0118,
-    0x011a,
-    0x011c,
-    0x011e,
-    0x0120,
-    0x0122,
-    0x0124,
-    0x0126,
-    0x0128,
-    0x012a,
-    0x012c,
-    0x012e,
-    0x0130,
-    0x0132,
-    0x0134,
-    0x0136,
-    0x0139,
-    0x013b,
-    0x013d,
-    0x013f,
-    0x0141,
-    0x0143,
-    0x0145,
-    0x0147,
-    0x014a,
-    0x014c,
-    0x014e,
-    0x0150,
-    0x0152,
-    0x0154,
-    0x0156,
-    0x0158,
-    0x015a,
-    0x015c,
-    0x015e,
-    0x0160,
-    0x0162,
-    0x0164,
-    0x0166,
-    0x0168,
-    0x016a,
-    0x016c,
-    0x016e,
-    0x0170,
-    0x0172,
-    0x0174,
-    0x0176,
-    0x017b,
-    0x017d,
-    0x0184,
-    0x01a2,
-    0x01a4,
-    0x01a9,
-    0x01ac,
-    0x01b5,
-    0x01bc,
-    0x01c4,
-    0x01c7,
-    0x01ca,
-    0x01cd,
-    0x01cf,
-    0x01d1,
-    0x01d3,
-    0x01d5,
-    0x01d7,
-    0x01d9,
-    0x01db,
-    0x01de,
-    0x01e0,
-    0x01e2,
-    0x01e4,
-    0x01e6,
-    0x01e8,
-    0x01ea,
-    0x01ec,
-    0x01ee,
-    0x01f1,
-    0x01f4,
-    0x01fa,
-    0x01fc,
-    0x01fe,
-    0x0200,
-    0x0202,
-    0x0204,
-    0x0206,
-    0x0208,
-    0x020a,
-    0x020c,
-    0x020e,
-    0x0210,
-    0x0212,
-    0x0214,
-    0x0216,
-    0x0218,
-    0x021a,
-    0x021c,
-    0x021e,
-    0x0220,
-    0x0222,
-    0x0224,
-    0x0226,
-    0x0228,
-    0x022a,
-    0x022c,
-    0x022e,
-    0x0230,
-    0x0232,
-    0x0241,
-    0x0248,
-    0x024a,
-    0x024c,
-    0x024e,
-    0x0370,
-    0x0372,
-    0x0376,
-    0x037f,
-    0x0386,
-    0x038c,
-    0x03cf,
-    0x03d8,
-    0x03da,
-    0x03dc,
-    0x03de,
-    0x03e0,
-    0x03e2,
-    0x03e4,
-    0x03e6,
-    0x03e8,
-    0x03ea,
-    0x03ec,
-    0x03ee,
-    0x03f4,
-    0x03f7,
-    0x0460,
-    0x0462,
-    0x0464,
-    0x0466,
-    0x0468,
-    0x046a,
-    0x046c,
-    0x046e,
-    0x0470,
-    0x0472,
-    0x0474,
-    0x0476,
-    0x0478,
-    0x047a,
-    0x047c,
-    0x047e,
-    0x0480,
-    0x048a,
-    0x048c,
-    0x048e,
-    0x0490,
-    0x0492,
-    0x0494,
-    0x0496,
-    0x0498,
-    0x049a,
-    0x049c,
-    0x049e,
-    0x04a0,
-    0x04a2,
-    0x04a4,
-    0x04a6,
-    0x04a8,
-    0x04aa,
-    0x04ac,
-    0x04ae,
-    0x04b0,
-    0x04b2,
-    0x04b4,
-    0x04b6,
-    0x04b8,
-    0x04ba,
-    0x04bc,
-    0x04be,
-    0x04c3,
-    0x04c5,
-    0x04c7,
-    0x04c9,
-    0x04cb,
-    0x04cd,
-    0x04d0,
-    0x04d2,
-    0x04d4,
-    0x04d6,
-    0x04d8,
-    0x04da,
-    0x04dc,
-    0x04de,
-    0x04e0,
-    0x04e2,
-    0x04e4,
-    0x04e6,
-    0x04e8,
-    0x04ea,
-    0x04ec,
-    0x04ee,
-    0x04f0,
-    0x04f2,
-    0x04f4,
-    0x04f6,
-    0x04f8,
-    0x04fa,
-    0x04fc,
-    0x04fe,
-    0x0500,
-    0x0502,
-    0x0504,
-    0x0506,
-    0x0508,
-    0x050a,
-    0x050c,
-    0x050e,
-    0x0510,
-    0x0512,
-    0x0514,
-    0x0516,
-    0x0518,
-    0x051a,
-    0x051c,
-    0x051e,
-    0x0520,
-    0x0522,
-    0x0524,
-    0x0526,
-    0x0528,
-    0x052a,
-    0x052c,
-    0x052e,
-    0x10c7,
-    0x10cd,
-    0x1e00,
-    0x1e02,
-    0x1e04,
-    0x1e06,
-    0x1e08,
-    0x1e0a,
-    0x1e0c,
-    0x1e0e,
-    0x1e10,
-    0x1e12,
-    0x1e14,
-    0x1e16,
-    0x1e18,
-    0x1e1a,
-    0x1e1c,
-    0x1e1e,
-    0x1e20,
-    0x1e22,
-    0x1e24,
-    0x1e26,
-    0x1e28,
-    0x1e2a,
-    0x1e2c,
-    0x1e2e,
-    0x1e30,
-    0x1e32,
-    0x1e34,
-    0x1e36,
-    0x1e38,
-    0x1e3a,
-    0x1e3c,
-    0x1e3e,
-    0x1e40,
-    0x1e42,
-    0x1e44,
-    0x1e46,
-    0x1e48,
-    0x1e4a,
-    0x1e4c,
-    0x1e4e,
-    0x1e50,
-    0x1e52,
-    0x1e54,
-    0x1e56,
-    0x1e58,
-    0x1e5a,
-    0x1e5c,
-    0x1e5e,
-    0x1e60,
-    0x1e62,
-    0x1e64,
-    0x1e66,
-    0x1e68,
-    0x1e6a,
-    0x1e6c,
-    0x1e6e,
-    0x1e70,
-    0x1e72,
-    0x1e74,
-    0x1e76,
-    0x1e78,
-    0x1e7a,
-    0x1e7c,
-    0x1e7e,
-    0x1e80,
-    0x1e82,
-    0x1e84,
-    0x1e86,
-    0x1e88,
-    0x1e8a,
-    0x1e8c,
-    0x1e8e,
-    0x1e90,
-    0x1e92,
-    0x1e94,
-    0x1e9e,
-    0x1ea0,
-    0x1ea2,
-    0x1ea4,
-    0x1ea6,
-    0x1ea8,
-    0x1eaa,
-    0x1eac,
-    0x1eae,
-    0x1eb0,
-    0x1eb2,
-    0x1eb4,
-    0x1eb6,
-    0x1eb8,
-    0x1eba,
-    0x1ebc,
-    0x1ebe,
-    0x1ec0,
-    0x1ec2,
-    0x1ec4,
-    0x1ec6,
-    0x1ec8,
-    0x1eca,
-    0x1ecc,
-    0x1ece,
-    0x1ed0,
-    0x1ed2,
-    0x1ed4,
-    0x1ed6,
-    0x1ed8,
-    0x1eda,
-    0x1edc,
-    0x1ede,
-    0x1ee0,
-    0x1ee2,
-    0x1ee4,
-    0x1ee6,
-    0x1ee8,
-    0x1eea,
-    0x1eec,
-    0x1eee,
-    0x1ef0,
-    0x1ef2,
-    0x1ef4,
-    0x1ef6,
-    0x1ef8,
-    0x1efa,
-    0x1efc,
-    0x1efe,
-    0x1f59,
-    0x1f5b,
-    0x1f5d,
-    0x1f5f,
-    0x2102,
-    0x2107,
-    0x2115,
-    0x2124,
-    0x2126,
-    0x2128,
-    0x2145,
-    0x2183,
-    0x2c60,
-    0x2c67,
-    0x2c69,
-    0x2c6b,
-    0x2c72,
-    0x2c75,
-    0x2c82,
-    0x2c84,
-    0x2c86,
-    0x2c88,
-    0x2c8a,
-    0x2c8c,
-    0x2c8e,
-    0x2c90,
-    0x2c92,
-    0x2c94,
-    0x2c96,
-    0x2c98,
-    0x2c9a,
-    0x2c9c,
-    0x2c9e,
-    0x2ca0,
-    0x2ca2,
-    0x2ca4,
-    0x2ca6,
-    0x2ca8,
-    0x2caa,
-    0x2cac,
-    0x2cae,
-    0x2cb0,
-    0x2cb2,
-    0x2cb4,
-    0x2cb6,
-    0x2cb8,
-    0x2cba,
-    0x2cbc,
-    0x2cbe,
-    0x2cc0,
-    0x2cc2,
-    0x2cc4,
-    0x2cc6,
-    0x2cc8,
-    0x2cca,
-    0x2ccc,
-    0x2cce,
-    0x2cd0,
-    0x2cd2,
-    0x2cd4,
-    0x2cd6,
-    0x2cd8,
-    0x2cda,
-    0x2cdc,
-    0x2cde,
-    0x2ce0,
-    0x2ce2,
-    0x2ceb,
-    0x2ced,
-    0x2cf2,
-    0xa640,
-    0xa642,
-    0xa644,
-    0xa646,
-    0xa648,
-    0xa64a,
-    0xa64c,
-    0xa64e,
-    0xa650,
-    0xa652,
-    0xa654,
-    0xa656,
-    0xa658,
-    0xa65a,
-    0xa65c,
-    0xa65e,
-    0xa660,
-    0xa662,
-    0xa664,
-    0xa666,
-    0xa668,
-    0xa66a,
-    0xa66c,
-    0xa680,
-    0xa682,
-    0xa684,
-    0xa686,
-    0xa688,
-    0xa68a,
-    0xa68c,
-    0xa68e,
-    0xa690,
-    0xa692,
-    0xa694,
-    0xa696,
-    0xa698,
-    0xa69a,
-    0xa722,
-    0xa724,
-    0xa726,
-    0xa728,
-    0xa72a,
-    0xa72c,
-    0xa72e,
-    0xa732,
-    0xa734,
-    0xa736,
-    0xa738,
-    0xa73a,
-    0xa73c,
-    0xa73e,
-    0xa740,
-    0xa742,
-    0xa744,
-    0xa746,
-    0xa748,
-    0xa74a,
-    0xa74c,
-    0xa74e,
-    0xa750,
-    0xa752,
-    0xa754,
-    0xa756,
-    0xa758,
-    0xa75a,
-    0xa75c,
-    0xa75e,
-    0xa760,
-    0xa762,
-    0xa764,
-    0xa766,
-    0xa768,
-    0xa76a,
-    0xa76c,
-    0xa76e,
-    0xa779,
-    0xa77b,
-    0xa780,
-    0xa782,
-    0xa784,
-    0xa786,
-    0xa78b,
-    0xa78d,
-    0xa790,
-    0xa792,
-    0xa796,
-    0xa798,
-    0xa79a,
-    0xa79c,
-    0xa79e,
-    0xa7a0,
-    0xa7a2,
-    0xa7a4,
-    0xa7a6,
-    0xa7a8,
-    0x1d49c,
-    0x1d4a2,
-    0x1d546,
-    0x1d7ca,
-};
-
-} // !namespace
-
-bool isupper(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, isupperr, nelem (isupperr) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    p = rbsearch(c, isuppers, nelem (isuppers), 1);
-
-    if (p && c == p[0])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t islowerr[] = {
-    0x0061, 0x007a,
-    0x00df, 0x00f6,
-    0x00f8, 0x00ff,
-    0x0137, 0x0138,
-    0x0148, 0x0149,
-    0x017e, 0x0180,
-    0x018c, 0x018d,
-    0x0199, 0x019b,
-    0x01aa, 0x01ab,
-    0x01b9, 0x01ba,
-    0x01bd, 0x01bf,
-    0x01dc, 0x01dd,
-    0x01ef, 0x01f0,
-    0x0233, 0x0239,
-    0x023f, 0x0240,
-    0x024f, 0x0293,
-    0x0295, 0x02af,
-    0x037b, 0x037d,
-    0x03ac, 0x03ce,
-    0x03d0, 0x03d1,
-    0x03d5, 0x03d7,
-    0x03ef, 0x03f3,
-    0x03fb, 0x03fc,
-    0x0430, 0x045f,
-    0x04ce, 0x04cf,
-    0x0561, 0x0587,
-    0x1d00, 0x1d2b,
-    0x1d6b, 0x1d77,
-    0x1d79, 0x1d9a,
-    0x1e95, 0x1e9d,
-    0x1eff, 0x1f07,
-    0x1f10, 0x1f15,
-    0x1f20, 0x1f27,
-    0x1f30, 0x1f37,
-    0x1f40, 0x1f45,
-    0x1f50, 0x1f57,
-    0x1f60, 0x1f67,
-    0x1f70, 0x1f7d,
-    0x1f80, 0x1f87,
-    0x1f90, 0x1f97,
-    0x1fa0, 0x1fa7,
-    0x1fb0, 0x1fb4,
-    0x1fb6, 0x1fb7,
-    0x1fc2, 0x1fc4,
-    0x1fc6, 0x1fc7,
-    0x1fd0, 0x1fd3,
-    0x1fd6, 0x1fd7,
-    0x1fe0, 0x1fe7,
-    0x1ff2, 0x1ff4,
-    0x1ff6, 0x1ff7,
-    0x210e, 0x210f,
-    0x213c, 0x213d,
-    0x2146, 0x2149,
-    0x2170, 0x217f,
-    0x24d0, 0x24e9,
-    0x2c30, 0x2c5e,
-    0x2c65, 0x2c66,
-    0x2c73, 0x2c74,
-    0x2c76, 0x2c7b,
-    0x2ce3, 0x2ce4,
-    0x2d00, 0x2d25,
-    0xa72f, 0xa731,
-    0xa771, 0xa778,
-    0xa793, 0xa795,
-    0xab30, 0xab5a,
-    0xab64, 0xab65,
-    0xfb00, 0xfb06,
-    0xfb13, 0xfb17,
-    0xff41, 0xff5a,
-    0x10428, 0x1044f,
-    0x118c0, 0x118df,
-    0x1d41a, 0x1d433,
-    0x1d44e, 0x1d454,
-    0x1d456, 0x1d467,
-    0x1d482, 0x1d49b,
-    0x1d4b6, 0x1d4b9,
-    0x1d4bd, 0x1d4c3,
-    0x1d4c5, 0x1d4cf,
-    0x1d4ea, 0x1d503,
-    0x1d51e, 0x1d537,
-    0x1d552, 0x1d56b,
-    0x1d586, 0x1d59f,
-    0x1d5ba, 0x1d5d3,
-    0x1d5ee, 0x1d607,
-    0x1d622, 0x1d63b,
-    0x1d656, 0x1d66f,
-    0x1d68a, 0x1d6a5,
-    0x1d6c2, 0x1d6da,
-    0x1d6dc, 0x1d6e1,
-    0x1d6fc, 0x1d714,
-    0x1d716, 0x1d71b,
-    0x1d736, 0x1d74e,
-    0x1d750, 0x1d755,
-    0x1d770, 0x1d788,
-    0x1d78a, 0x1d78f,
-    0x1d7aa, 0x1d7c2,
-    0x1d7c4, 0x1d7c9,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t islowers[] = {
-    0x00b5,
-    0x0101,
-    0x0103,
-    0x0105,
-    0x0107,
-    0x0109,
-    0x010b,
-    0x010d,
-    0x010f,
-    0x0111,
-    0x0113,
-    0x0115,
-    0x0117,
-    0x0119,
-    0x011b,
-    0x011d,
-    0x011f,
-    0x0121,
-    0x0123,
-    0x0125,
-    0x0127,
-    0x0129,
-    0x012b,
-    0x012d,
-    0x012f,
-    0x0131,
-    0x0133,
-    0x0135,
-    0x013a,
-    0x013c,
-    0x013e,
-    0x0140,
-    0x0142,
-    0x0144,
-    0x0146,
-    0x014b,
-    0x014d,
-    0x014f,
-    0x0151,
-    0x0153,
-    0x0155,
-    0x0157,
-    0x0159,
-    0x015b,
-    0x015d,
-    0x015f,
-    0x0161,
-    0x0163,
-    0x0165,
-    0x0167,
-    0x0169,
-    0x016b,
-    0x016d,
-    0x016f,
-    0x0171,
-    0x0173,
-    0x0175,
-    0x0177,
-    0x017a,
-    0x017c,
-    0x0183,
-    0x0185,
-    0x0188,
-    0x0192,
-    0x0195,
-    0x019e,
-    0x01a1,
-    0x01a3,
-    0x01a5,
-    0x01a8,
-    0x01ad,
-    0x01b0,
-    0x01b4,
-    0x01b6,
-    0x01c6,
-    0x01c9,
-    0x01cc,
-    0x01ce,
-    0x01d0,
-    0x01d2,
-    0x01d4,
-    0x01d6,
-    0x01d8,
-    0x01da,
-    0x01df,
-    0x01e1,
-    0x01e3,
-    0x01e5,
-    0x01e7,
-    0x01e9,
-    0x01eb,
-    0x01ed,
-    0x01f3,
-    0x01f5,
-    0x01f9,
-    0x01fb,
-    0x01fd,
-    0x01ff,
-    0x0201,
-    0x0203,
-    0x0205,
-    0x0207,
-    0x0209,
-    0x020b,
-    0x020d,
-    0x020f,
-    0x0211,
-    0x0213,
-    0x0215,
-    0x0217,
-    0x0219,
-    0x021b,
-    0x021d,
-    0x021f,
-    0x0221,
-    0x0223,
-    0x0225,
-    0x0227,
-    0x0229,
-    0x022b,
-    0x022d,
-    0x022f,
-    0x0231,
-    0x023c,
-    0x0242,
-    0x0247,
-    0x0249,
-    0x024b,
-    0x024d,
-    0x0371,
-    0x0373,
-    0x0377,
-    0x0390,
-    0x03d9,
-    0x03db,
-    0x03dd,
-    0x03df,
-    0x03e1,
-    0x03e3,
-    0x03e5,
-    0x03e7,
-    0x03e9,
-    0x03eb,
-    0x03ed,
-    0x03f5,
-    0x03f8,
-    0x0461,
-    0x0463,
-    0x0465,
-    0x0467,
-    0x0469,
-    0x046b,
-    0x046d,
-    0x046f,
-    0x0471,
-    0x0473,
-    0x0475,
-    0x0477,
-    0x0479,
-    0x047b,
-    0x047d,
-    0x047f,
-    0x0481,
-    0x048b,
-    0x048d,
-    0x048f,
-    0x0491,
-    0x0493,
-    0x0495,
-    0x0497,
-    0x0499,
-    0x049b,
-    0x049d,
-    0x049f,
-    0x04a1,
-    0x04a3,
-    0x04a5,
-    0x04a7,
-    0x04a9,
-    0x04ab,
-    0x04ad,
-    0x04af,
-    0x04b1,
-    0x04b3,
-    0x04b5,
-    0x04b7,
-    0x04b9,
-    0x04bb,
-    0x04bd,
-    0x04bf,
-    0x04c2,
-    0x04c4,
-    0x04c6,
-    0x04c8,
-    0x04ca,
-    0x04cc,
-    0x04d1,
-    0x04d3,
-    0x04d5,
-    0x04d7,
-    0x04d9,
-    0x04db,
-    0x04dd,
-    0x04df,
-    0x04e1,
-    0x04e3,
-    0x04e5,
-    0x04e7,
-    0x04e9,
-    0x04eb,
-    0x04ed,
-    0x04ef,
-    0x04f1,
-    0x04f3,
-    0x04f5,
-    0x04f7,
-    0x04f9,
-    0x04fb,
-    0x04fd,
-    0x04ff,
-    0x0501,
-    0x0503,
-    0x0505,
-    0x0507,
-    0x0509,
-    0x050b,
-    0x050d,
-    0x050f,
-    0x0511,
-    0x0513,
-    0x0515,
-    0x0517,
-    0x0519,
-    0x051b,
-    0x051d,
-    0x051f,
-    0x0521,
-    0x0523,
-    0x0525,
-    0x0527,
-    0x0529,
-    0x052b,
-    0x052d,
-    0x052f,
-    0x1e01,
-    0x1e03,
-    0x1e05,
-    0x1e07,
-    0x1e09,
-    0x1e0b,
-    0x1e0d,
-    0x1e0f,
-    0x1e11,
-    0x1e13,
-    0x1e15,
-    0x1e17,
-    0x1e19,
-    0x1e1b,
-    0x1e1d,
-    0x1e1f,
-    0x1e21,
-    0x1e23,
-    0x1e25,
-    0x1e27,
-    0x1e29,
-    0x1e2b,
-    0x1e2d,
-    0x1e2f,
-    0x1e31,
-    0x1e33,
-    0x1e35,
-    0x1e37,
-    0x1e39,
-    0x1e3b,
-    0x1e3d,
-    0x1e3f,
-    0x1e41,
-    0x1e43,
-    0x1e45,
-    0x1e47,
-    0x1e49,
-    0x1e4b,
-    0x1e4d,
-    0x1e4f,
-    0x1e51,
-    0x1e53,
-    0x1e55,
-    0x1e57,
-    0x1e59,
-    0x1e5b,
-    0x1e5d,
-    0x1e5f,
-    0x1e61,
-    0x1e63,
-    0x1e65,
-    0x1e67,
-    0x1e69,
-    0x1e6b,
-    0x1e6d,
-    0x1e6f,
-    0x1e71,
-    0x1e73,
-    0x1e75,
-    0x1e77,
-    0x1e79,
-    0x1e7b,
-    0x1e7d,
-    0x1e7f,
-    0x1e81,
-    0x1e83,
-    0x1e85,
-    0x1e87,
-    0x1e89,
-    0x1e8b,
-    0x1e8d,
-    0x1e8f,
-    0x1e91,
-    0x1e93,
-    0x1e9f,
-    0x1ea1,
-    0x1ea3,
-    0x1ea5,
-    0x1ea7,
-    0x1ea9,
-    0x1eab,
-    0x1ead,
-    0x1eaf,
-    0x1eb1,
-    0x1eb3,
-    0x1eb5,
-    0x1eb7,
-    0x1eb9,
-    0x1ebb,
-    0x1ebd,
-    0x1ebf,
-    0x1ec1,
-    0x1ec3,
-    0x1ec5,
-    0x1ec7,
-    0x1ec9,
-    0x1ecb,
-    0x1ecd,
-    0x1ecf,
-    0x1ed1,
-    0x1ed3,
-    0x1ed5,
-    0x1ed7,
-    0x1ed9,
-    0x1edb,
-    0x1edd,
-    0x1edf,
-    0x1ee1,
-    0x1ee3,
-    0x1ee5,
-    0x1ee7,
-    0x1ee9,
-    0x1eeb,
-    0x1eed,
-    0x1eef,
-    0x1ef1,
-    0x1ef3,
-    0x1ef5,
-    0x1ef7,
-    0x1ef9,
-    0x1efb,
-    0x1efd,
-    0x1fbe,
-    0x210a,
-    0x2113,
-    0x212f,
-    0x2134,
-    0x2139,
-    0x214e,
-    0x2184,
-    0x2c61,
-    0x2c68,
-    0x2c6a,
-    0x2c6c,
-    0x2c71,
-    0x2c81,
-    0x2c83,
-    0x2c85,
-    0x2c87,
-    0x2c89,
-    0x2c8b,
-    0x2c8d,
-    0x2c8f,
-    0x2c91,
-    0x2c93,
-    0x2c95,
-    0x2c97,
-    0x2c99,
-    0x2c9b,
-    0x2c9d,
-    0x2c9f,
-    0x2ca1,
-    0x2ca3,
-    0x2ca5,
-    0x2ca7,
-    0x2ca9,
-    0x2cab,
-    0x2cad,
-    0x2caf,
-    0x2cb1,
-    0x2cb3,
-    0x2cb5,
-    0x2cb7,
-    0x2cb9,
-    0x2cbb,
-    0x2cbd,
-    0x2cbf,
-    0x2cc1,
-    0x2cc3,
-    0x2cc5,
-    0x2cc7,
-    0x2cc9,
-    0x2ccb,
-    0x2ccd,
-    0x2ccf,
-    0x2cd1,
-    0x2cd3,
-    0x2cd5,
-    0x2cd7,
-    0x2cd9,
-    0x2cdb,
-    0x2cdd,
-    0x2cdf,
-    0x2ce1,
-    0x2cec,
-    0x2cee,
-    0x2cf3,
-    0x2d27,
-    0x2d2d,
-    0xa641,
-    0xa643,
-    0xa645,
-    0xa647,
-    0xa649,
-    0xa64b,
-    0xa64d,
-    0xa64f,
-    0xa651,
-    0xa653,
-    0xa655,
-    0xa657,
-    0xa659,
-    0xa65b,
-    0xa65d,
-    0xa65f,
-    0xa661,
-    0xa663,
-    0xa665,
-    0xa667,
-    0xa669,
-    0xa66b,
-    0xa66d,
-    0xa681,
-    0xa683,
-    0xa685,
-    0xa687,
-    0xa689,
-    0xa68b,
-    0xa68d,
-    0xa68f,
-    0xa691,
-    0xa693,
-    0xa695,
-    0xa697,
-    0xa699,
-    0xa69b,
-    0xa723,
-    0xa725,
-    0xa727,
-    0xa729,
-    0xa72b,
-    0xa72d,
-    0xa733,
-    0xa735,
-    0xa737,
-    0xa739,
-    0xa73b,
-    0xa73d,
-    0xa73f,
-    0xa741,
-    0xa743,
-    0xa745,
-    0xa747,
-    0xa749,
-    0xa74b,
-    0xa74d,
-    0xa74f,
-    0xa751,
-    0xa753,
-    0xa755,
-    0xa757,
-    0xa759,
-    0xa75b,
-    0xa75d,
-    0xa75f,
-    0xa761,
-    0xa763,
-    0xa765,
-    0xa767,
-    0xa769,
-    0xa76b,
-    0xa76d,
-    0xa76f,
-    0xa77a,
-    0xa77c,
-    0xa77f,
-    0xa781,
-    0xa783,
-    0xa785,
-    0xa787,
-    0xa78c,
-    0xa78e,
-    0xa791,
-    0xa797,
-    0xa799,
-    0xa79b,
-    0xa79d,
-    0xa79f,
-    0xa7a1,
-    0xa7a3,
-    0xa7a5,
-    0xa7a7,
-    0xa7a9,
-    0xa7fa,
-    0x1d4bb,
-    0x1d7cb,
-};
-
-} // !namespace
-
-bool islower(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, islowerr, nelem (islowerr) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    p = rbsearch(c, islowers, nelem (islowers), 1);
-
-    if (p && c == p[0])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t istitler[] = {
-    0x0041, 0x005a,
-    0x00c0, 0x00d6,
-    0x00d8, 0x00de,
-    0x0178, 0x0179,
-    0x0181, 0x0182,
-    0x0186, 0x0187,
-    0x0189, 0x018b,
-    0x018e, 0x0191,
-    0x0193, 0x0194,
-    0x0196, 0x0198,
-    0x019c, 0x019d,
-    0x019f, 0x01a0,
-    0x01a6, 0x01a7,
-    0x01ae, 0x01af,
-    0x01b1, 0x01b3,
-    0x01b7, 0x01b8,
-    0x01f6, 0x01f8,
-    0x023a, 0x023b,
-    0x023d, 0x023e,
-    0x0243, 0x0246,
-    0x0388, 0x038a,
-    0x038e, 0x038f,
-    0x0391, 0x03a1,
-    0x03a3, 0x03ab,
-    0x03f9, 0x03fa,
-    0x03fd, 0x042f,
-    0x04c0, 0x04c1,
-    0x0531, 0x0556,
-    0x10a0, 0x10c5,
-    0x1f08, 0x1f0f,
-    0x1f18, 0x1f1d,
-    0x1f28, 0x1f2f,
-    0x1f38, 0x1f3f,
-    0x1f48, 0x1f4d,
-    0x1f68, 0x1f6f,
-    0x1f88, 0x1f8f,
-    0x1f98, 0x1f9f,
-    0x1fa8, 0x1faf,
-    0x1fb8, 0x1fbc,
-    0x1fc8, 0x1fcc,
-    0x1fd8, 0x1fdb,
-    0x1fe8, 0x1fec,
-    0x1ff8, 0x1ffc,
-    0x2160, 0x216f,
-    0x24b6, 0x24cf,
-    0x2c00, 0x2c2e,
-    0x2c62, 0x2c64,
-    0x2c6d, 0x2c70,
-    0x2c7e, 0x2c80,
-    0xa77d, 0xa77e,
-    0xa7aa, 0xa7ad,
-    0xa7b0, 0xa7b1,
-    0xff21, 0xff3a,
-    0x10400, 0x10427,
-    0x118a0, 0x118bf,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t istitles[] = {
-    0x0100,
-    0x0102,
-    0x0104,
-    0x0106,
-    0x0108,
-    0x010a,
-    0x010c,
-    0x010e,
-    0x0110,
-    0x0112,
-    0x0114,
-    0x0116,
-    0x0118,
-    0x011a,
-    0x011c,
-    0x011e,
-    0x0120,
-    0x0122,
-    0x0124,
-    0x0126,
-    0x0128,
-    0x012a,
-    0x012c,
-    0x012e,
-    0x0132,
-    0x0134,
-    0x0136,
-    0x0139,
-    0x013b,
-    0x013d,
-    0x013f,
-    0x0141,
-    0x0143,
-    0x0145,
-    0x0147,
-    0x014a,
-    0x014c,
-    0x014e,
-    0x0150,
-    0x0152,
-    0x0154,
-    0x0156,
-    0x0158,
-    0x015a,
-    0x015c,
-    0x015e,
-    0x0160,
-    0x0162,
-    0x0164,
-    0x0166,
-    0x0168,
-    0x016a,
-    0x016c,
-    0x016e,
-    0x0170,
-    0x0172,
-    0x0174,
-    0x0176,
-    0x017b,
-    0x017d,
-    0x0184,
-    0x01a2,
-    0x01a4,
-    0x01a9,
-    0x01ac,
-    0x01b5,
-    0x01bc,
-    0x01c5,
-    0x01c8,
-    0x01cb,
-    0x01cd,
-    0x01cf,
-    0x01d1,
-    0x01d3,
-    0x01d5,
-    0x01d7,
-    0x01d9,
-    0x01db,
-    0x01de,
-    0x01e0,
-    0x01e2,
-    0x01e4,
-    0x01e6,
-    0x01e8,
-    0x01ea,
-    0x01ec,
-    0x01ee,
-    0x01f2,
-    0x01f4,
-    0x01fa,
-    0x01fc,
-    0x01fe,
-    0x0200,
-    0x0202,
-    0x0204,
-    0x0206,
-    0x0208,
-    0x020a,
-    0x020c,
-    0x020e,
-    0x0210,
-    0x0212,
-    0x0214,
-    0x0216,
-    0x0218,
-    0x021a,
-    0x021c,
-    0x021e,
-    0x0220,
-    0x0222,
-    0x0224,
-    0x0226,
-    0x0228,
-    0x022a,
-    0x022c,
-    0x022e,
-    0x0230,
-    0x0232,
-    0x0241,
-    0x0248,
-    0x024a,
-    0x024c,
-    0x024e,
-    0x0370,
-    0x0372,
-    0x0376,
-    0x037f,
-    0x0386,
-    0x038c,
-    0x03cf,
-    0x03d8,
-    0x03da,
-    0x03dc,
-    0x03de,
-    0x03e0,
-    0x03e2,
-    0x03e4,
-    0x03e6,
-    0x03e8,
-    0x03ea,
-    0x03ec,
-    0x03ee,
-    0x03f7,
-    0x0460,
-    0x0462,
-    0x0464,
-    0x0466,
-    0x0468,
-    0x046a,
-    0x046c,
-    0x046e,
-    0x0470,
-    0x0472,
-    0x0474,
-    0x0476,
-    0x0478,
-    0x047a,
-    0x047c,
-    0x047e,
-    0x0480,
-    0x048a,
-    0x048c,
-    0x048e,
-    0x0490,
-    0x0492,
-    0x0494,
-    0x0496,
-    0x0498,
-    0x049a,
-    0x049c,
-    0x049e,
-    0x04a0,
-    0x04a2,
-    0x04a4,
-    0x04a6,
-    0x04a8,
-    0x04aa,
-    0x04ac,
-    0x04ae,
-    0x04b0,
-    0x04b2,
-    0x04b4,
-    0x04b6,
-    0x04b8,
-    0x04ba,
-    0x04bc,
-    0x04be,
-    0x04c3,
-    0x04c5,
-    0x04c7,
-    0x04c9,
-    0x04cb,
-    0x04cd,
-    0x04d0,
-    0x04d2,
-    0x04d4,
-    0x04d6,
-    0x04d8,
-    0x04da,
-    0x04dc,
-    0x04de,
-    0x04e0,
-    0x04e2,
-    0x04e4,
-    0x04e6,
-    0x04e8,
-    0x04ea,
-    0x04ec,
-    0x04ee,
-    0x04f0,
-    0x04f2,
-    0x04f4,
-    0x04f6,
-    0x04f8,
-    0x04fa,
-    0x04fc,
-    0x04fe,
-    0x0500,
-    0x0502,
-    0x0504,
-    0x0506,
-    0x0508,
-    0x050a,
-    0x050c,
-    0x050e,
-    0x0510,
-    0x0512,
-    0x0514,
-    0x0516,
-    0x0518,
-    0x051a,
-    0x051c,
-    0x051e,
-    0x0520,
-    0x0522,
-    0x0524,
-    0x0526,
-    0x0528,
-    0x052a,
-    0x052c,
-    0x052e,
-    0x10c7,
-    0x10cd,
-    0x1e00,
-    0x1e02,
-    0x1e04,
-    0x1e06,
-    0x1e08,
-    0x1e0a,
-    0x1e0c,
-    0x1e0e,
-    0x1e10,
-    0x1e12,
-    0x1e14,
-    0x1e16,
-    0x1e18,
-    0x1e1a,
-    0x1e1c,
-    0x1e1e,
-    0x1e20,
-    0x1e22,
-    0x1e24,
-    0x1e26,
-    0x1e28,
-    0x1e2a,
-    0x1e2c,
-    0x1e2e,
-    0x1e30,
-    0x1e32,
-    0x1e34,
-    0x1e36,
-    0x1e38,
-    0x1e3a,
-    0x1e3c,
-    0x1e3e,
-    0x1e40,
-    0x1e42,
-    0x1e44,
-    0x1e46,
-    0x1e48,
-    0x1e4a,
-    0x1e4c,
-    0x1e4e,
-    0x1e50,
-    0x1e52,
-    0x1e54,
-    0x1e56,
-    0x1e58,
-    0x1e5a,
-    0x1e5c,
-    0x1e5e,
-    0x1e60,
-    0x1e62,
-    0x1e64,
-    0x1e66,
-    0x1e68,
-    0x1e6a,
-    0x1e6c,
-    0x1e6e,
-    0x1e70,
-    0x1e72,
-    0x1e74,
-    0x1e76,
-    0x1e78,
-    0x1e7a,
-    0x1e7c,
-    0x1e7e,
-    0x1e80,
-    0x1e82,
-    0x1e84,
-    0x1e86,
-    0x1e88,
-    0x1e8a,
-    0x1e8c,
-    0x1e8e,
-    0x1e90,
-    0x1e92,
-    0x1e94,
-    0x1ea0,
-    0x1ea2,
-    0x1ea4,
-    0x1ea6,
-    0x1ea8,
-    0x1eaa,
-    0x1eac,
-    0x1eae,
-    0x1eb0,
-    0x1eb2,
-    0x1eb4,
-    0x1eb6,
-    0x1eb8,
-    0x1eba,
-    0x1ebc,
-    0x1ebe,
-    0x1ec0,
-    0x1ec2,
-    0x1ec4,
-    0x1ec6,
-    0x1ec8,
-    0x1eca,
-    0x1ecc,
-    0x1ece,
-    0x1ed0,
-    0x1ed2,
-    0x1ed4,
-    0x1ed6,
-    0x1ed8,
-    0x1eda,
-    0x1edc,
-    0x1ede,
-    0x1ee0,
-    0x1ee2,
-    0x1ee4,
-    0x1ee6,
-    0x1ee8,
-    0x1eea,
-    0x1eec,
-    0x1eee,
-    0x1ef0,
-    0x1ef2,
-    0x1ef4,
-    0x1ef6,
-    0x1ef8,
-    0x1efa,
-    0x1efc,
-    0x1efe,
-    0x1f59,
-    0x1f5b,
-    0x1f5d,
-    0x1f5f,
-    0x2132,
-    0x2183,
-    0x2c60,
-    0x2c67,
-    0x2c69,
-    0x2c6b,
-    0x2c72,
-    0x2c75,
-    0x2c82,
-    0x2c84,
-    0x2c86,
-    0x2c88,
-    0x2c8a,
-    0x2c8c,
-    0x2c8e,
-    0x2c90,
-    0x2c92,
-    0x2c94,
-    0x2c96,
-    0x2c98,
-    0x2c9a,
-    0x2c9c,
-    0x2c9e,
-    0x2ca0,
-    0x2ca2,
-    0x2ca4,
-    0x2ca6,
-    0x2ca8,
-    0x2caa,
-    0x2cac,
-    0x2cae,
-    0x2cb0,
-    0x2cb2,
-    0x2cb4,
-    0x2cb6,
-    0x2cb8,
-    0x2cba,
-    0x2cbc,
-    0x2cbe,
-    0x2cc0,
-    0x2cc2,
-    0x2cc4,
-    0x2cc6,
-    0x2cc8,
-    0x2cca,
-    0x2ccc,
-    0x2cce,
-    0x2cd0,
-    0x2cd2,
-    0x2cd4,
-    0x2cd6,
-    0x2cd8,
-    0x2cda,
-    0x2cdc,
-    0x2cde,
-    0x2ce0,
-    0x2ce2,
-    0x2ceb,
-    0x2ced,
-    0x2cf2,
-    0xa640,
-    0xa642,
-    0xa644,
-    0xa646,
-    0xa648,
-    0xa64a,
-    0xa64c,
-    0xa64e,
-    0xa650,
-    0xa652,
-    0xa654,
-    0xa656,
-    0xa658,
-    0xa65a,
-    0xa65c,
-    0xa65e,
-    0xa660,
-    0xa662,
-    0xa664,
-    0xa666,
-    0xa668,
-    0xa66a,
-    0xa66c,
-    0xa680,
-    0xa682,
-    0xa684,
-    0xa686,
-    0xa688,
-    0xa68a,
-    0xa68c,
-    0xa68e,
-    0xa690,
-    0xa692,
-    0xa694,
-    0xa696,
-    0xa698,
-    0xa69a,
-    0xa722,
-    0xa724,
-    0xa726,
-    0xa728,
-    0xa72a,
-    0xa72c,
-    0xa72e,
-    0xa732,
-    0xa734,
-    0xa736,
-    0xa738,
-    0xa73a,
-    0xa73c,
-    0xa73e,
-    0xa740,
-    0xa742,
-    0xa744,
-    0xa746,
-    0xa748,
-    0xa74a,
-    0xa74c,
-    0xa74e,
-    0xa750,
-    0xa752,
-    0xa754,
-    0xa756,
-    0xa758,
-    0xa75a,
-    0xa75c,
-    0xa75e,
-    0xa760,
-    0xa762,
-    0xa764,
-    0xa766,
-    0xa768,
-    0xa76a,
-    0xa76c,
-    0xa76e,
-    0xa779,
-    0xa77b,
-    0xa780,
-    0xa782,
-    0xa784,
-    0xa786,
-    0xa78b,
-    0xa78d,
-    0xa790,
-    0xa792,
-    0xa796,
-    0xa798,
-    0xa79a,
-    0xa79c,
-    0xa79e,
-    0xa7a0,
-    0xa7a2,
-    0xa7a4,
-    0xa7a6,
-    0xa7a8,
-};
-
-} // !namespace
-
-bool istitle(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, istitler, nelem (istitler) / 2, 2);
-
-    if (p && c >= p[0] && c <= p[1])
-        return true;
-
-    p = rbsearch(c, istitles, nelem (istitles), 1);
-
-    if (p && c == p[0])
-        return true;
-
-    return false;
-}
-
-namespace {
-
-const char32_t toupperr[] = {
-    0x0061, 0x007a, 1048544,
-    0x00e0, 0x00f6, 1048544,
-    0x00f8, 0x00fe, 1048544,
-    0x023f, 0x0240, 1059391,
-    0x0256, 0x0257, 1048371,
-    0x028a, 0x028b, 1048359,
-    0x037b, 0x037d, 1048706,
-    0x03ad, 0x03af, 1048539,
-    0x03b1, 0x03c1, 1048544,
-    0x03c3, 0x03cb, 1048544,
-    0x03cd, 0x03ce, 1048513,
-    0x0430, 0x044f, 1048544,
-    0x0450, 0x045f, 1048496,
-    0x0561, 0x0586, 1048528,
-    0x1f00, 0x1f07, 1048584,
-    0x1f10, 0x1f15, 1048584,
-    0x1f20, 0x1f27, 1048584,
-    0x1f30, 0x1f37, 1048584,
-    0x1f40, 0x1f45, 1048584,
-    0x1f60, 0x1f67, 1048584,
-    0x1f70, 0x1f71, 1048650,
-    0x1f72, 0x1f75, 1048662,
-    0x1f76, 0x1f77, 1048676,
-    0x1f78, 0x1f79, 1048704,
-    0x1f7a, 0x1f7b, 1048688,
-    0x1f7c, 0x1f7d, 1048702,
-    0x1f80, 0x1f87, 1048584,
-    0x1f90, 0x1f97, 1048584,
-    0x1fa0, 0x1fa7, 1048584,
-    0x1fb0, 0x1fb1, 1048584,
-    0x1fd0, 0x1fd1, 1048584,
-    0x1fe0, 0x1fe1, 1048584,
-    0x2170, 0x217f, 1048560,
-    0x24d0, 0x24e9, 1048550,
-    0x2c30, 0x2c5e, 1048528,
-    0x2d00, 0x2d25, 1041312,
-    0xff41, 0xff5a, 1048544,
-    0x10428, 0x1044f, 1048536,
-    0x118c0, 0x118df, 1048544,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t touppers[] = {
-    0x00b5, 1049319,
-    0x00ff, 1048697,
-    0x0101, 1048575,
-    0x0103, 1048575,
-    0x0105, 1048575,
-    0x0107, 1048575,
-    0x0109, 1048575,
-    0x010b, 1048575,
-    0x010d, 1048575,
-    0x010f, 1048575,
-    0x0111, 1048575,
-    0x0113, 1048575,
-    0x0115, 1048575,
-    0x0117, 1048575,
-    0x0119, 1048575,
-    0x011b, 1048575,
-    0x011d, 1048575,
-    0x011f, 1048575,
-    0x0121, 1048575,
-    0x0123, 1048575,
-    0x0125, 1048575,
-    0x0127, 1048575,
-    0x0129, 1048575,
-    0x012b, 1048575,
-    0x012d, 1048575,
-    0x012f, 1048575,
-    0x0131, 1048344,
-    0x0133, 1048575,
-    0x0135, 1048575,
-    0x0137, 1048575,
-    0x013a, 1048575,
-    0x013c, 1048575,
-    0x013e, 1048575,
-    0x0140, 1048575,
-    0x0142, 1048575,
-    0x0144, 1048575,
-    0x0146, 1048575,
-    0x0148, 1048575,
-    0x014b, 1048575,
-    0x014d, 1048575,
-    0x014f, 1048575,
-    0x0151, 1048575,
-    0x0153, 1048575,
-    0x0155, 1048575,
-    0x0157, 1048575,
-    0x0159, 1048575,
-    0x015b, 1048575,
-    0x015d, 1048575,
-    0x015f, 1048575,
-    0x0161, 1048575,
-    0x0163, 1048575,
-    0x0165, 1048575,
-    0x0167, 1048575,
-    0x0169, 1048575,
-    0x016b, 1048575,
-    0x016d, 1048575,
-    0x016f, 1048575,
-    0x0171, 1048575,
-    0x0173, 1048575,
-    0x0175, 1048575,
-    0x0177, 1048575,
-    0x017a, 1048575,
-    0x017c, 1048575,
-    0x017e, 1048575,
-    0x017f, 1048276,
-    0x0180, 1048771,
-    0x0183, 1048575,
-    0x0185, 1048575,
-    0x0188, 1048575,
-    0x018c, 1048575,
-    0x0192, 1048575,
-    0x0195, 1048673,
-    0x0199, 1048575,
-    0x019a, 1048739,
-    0x019e, 1048706,
-    0x01a1, 1048575,
-    0x01a3, 1048575,
-    0x01a5, 1048575,
-    0x01a8, 1048575,
-    0x01ad, 1048575,
-    0x01b0, 1048575,
-    0x01b4, 1048575,
-    0x01b6, 1048575,
-    0x01b9, 1048575,
-    0x01bd, 1048575,
-    0x01bf, 1048632,
-    0x01c5, 1048575,
-    0x01c6, 1048574,
-    0x01c8, 1048575,
-    0x01c9, 1048574,
-    0x01cb, 1048575,
-    0x01cc, 1048574,
-    0x01ce, 1048575,
-    0x01d0, 1048575,
-    0x01d2, 1048575,
-    0x01d4, 1048575,
-    0x01d6, 1048575,
-    0x01d8, 1048575,
-    0x01da, 1048575,
-    0x01dc, 1048575,
-    0x01dd, 1048497,
-    0x01df, 1048575,
-    0x01e1, 1048575,
-    0x01e3, 1048575,
-    0x01e5, 1048575,
-    0x01e7, 1048575,
-    0x01e9, 1048575,
-    0x01eb, 1048575,
-    0x01ed, 1048575,
-    0x01ef, 1048575,
-    0x01f2, 1048575,
-    0x01f3, 1048574,
-    0x01f5, 1048575,
-    0x01f9, 1048575,
-    0x01fb, 1048575,
-    0x01fd, 1048575,
-    0x01ff, 1048575,
-    0x0201, 1048575,
-    0x0203, 1048575,
-    0x0205, 1048575,
-    0x0207, 1048575,
-    0x0209, 1048575,
-    0x020b, 1048575,
-    0x020d, 1048575,
-    0x020f, 1048575,
-    0x0211, 1048575,
-    0x0213, 1048575,
-    0x0215, 1048575,
-    0x0217, 1048575,
-    0x0219, 1048575,
-    0x021b, 1048575,
-    0x021d, 1048575,
-    0x021f, 1048575,
-    0x0223, 1048575,
-    0x0225, 1048575,
-    0x0227, 1048575,
-    0x0229, 1048575,
-    0x022b, 1048575,
-    0x022d, 1048575,
-    0x022f, 1048575,
-    0x0231, 1048575,
-    0x0233, 1048575,
-    0x023c, 1048575,
-    0x0242, 1048575,
-    0x0247, 1048575,
-    0x0249, 1048575,
-    0x024b, 1048575,
-    0x024d, 1048575,
-    0x024f, 1048575,
-    0x0250, 1059359,
-    0x0251, 1059356,
-    0x0252, 1059358,
-    0x0253, 1048366,
-    0x0254, 1048370,
-    0x0259, 1048374,
-    0x025b, 1048373,
-    0x025c, 1090895,
-    0x0260, 1048371,
-    0x0261, 1090891,
-    0x0263, 1048369,
-    0x0265, 1090856,
-    0x0266, 1090884,
-    0x0268, 1048367,
-    0x0269, 1048365,
-    0x026b, 1059319,
-    0x026c, 1090881,
-    0x026f, 1048365,
-    0x0271, 1059325,
-    0x0272, 1048363,
-    0x0275, 1048362,
-    0x027d, 1059303,
-    0x0280, 1048358,
-    0x0283, 1048358,
-    0x0287, 1090858,
-    0x0288, 1048358,
-    0x0289, 1048507,
-    0x028c, 1048505,
-    0x0292, 1048357,
-    0x029e, 1090834,
-    0x0345, 1048660,
-    0x0371, 1048575,
-    0x0373, 1048575,
-    0x0377, 1048575,
-    0x03ac, 1048538,
-    0x03c2, 1048545,
-    0x03cc, 1048512,
-    0x03d0, 1048514,
-    0x03d1, 1048519,
-    0x03d5, 1048529,
-    0x03d6, 1048522,
-    0x03d7, 1048568,
-    0x03d9, 1048575,
-    0x03db, 1048575,
-    0x03dd, 1048575,
-    0x03df, 1048575,
-    0x03e1, 1048575,
-    0x03e3, 1048575,
-    0x03e5, 1048575,
-    0x03e7, 1048575,
-    0x03e9, 1048575,
-    0x03eb, 1048575,
-    0x03ed, 1048575,
-    0x03ef, 1048575,
-    0x03f0, 1048490,
-    0x03f1, 1048496,
-    0x03f2, 1048583,
-    0x03f3, 1048460,
-    0x03f5, 1048480,
-    0x03f8, 1048575,
-    0x03fb, 1048575,
-    0x0461, 1048575,
-    0x0463, 1048575,
-    0x0465, 1048575,
-    0x0467, 1048575,
-    0x0469, 1048575,
-    0x046b, 1048575,
-    0x046d, 1048575,
-    0x046f, 1048575,
-    0x0471, 1048575,
-    0x0473, 1048575,
-    0x0475, 1048575,
-    0x0477, 1048575,
-    0x0479, 1048575,
-    0x047b, 1048575,
-    0x047d, 1048575,
-    0x047f, 1048575,
-    0x0481, 1048575,
-    0x048b, 1048575,
-    0x048d, 1048575,
-    0x048f, 1048575,
-    0x0491, 1048575,
-    0x0493, 1048575,
-    0x0495, 1048575,
-    0x0497, 1048575,
-    0x0499, 1048575,
-    0x049b, 1048575,
-    0x049d, 1048575,
-    0x049f, 1048575,
-    0x04a1, 1048575,
-    0x04a3, 1048575,
-    0x04a5, 1048575,
-    0x04a7, 1048575,
-    0x04a9, 1048575,
-    0x04ab, 1048575,
-    0x04ad, 1048575,
-    0x04af, 1048575,
-    0x04b1, 1048575,
-    0x04b3, 1048575,
-    0x04b5, 1048575,
-    0x04b7, 1048575,
-    0x04b9, 1048575,
-    0x04bb, 1048575,
-    0x04bd, 1048575,
-    0x04bf, 1048575,
-    0x04c2, 1048575,
-    0x04c4, 1048575,
-    0x04c6, 1048575,
-    0x04c8, 1048575,
-    0x04ca, 1048575,
-    0x04cc, 1048575,
-    0x04ce, 1048575,
-    0x04cf, 1048561,
-    0x04d1, 1048575,
-    0x04d3, 1048575,
-    0x04d5, 1048575,
-    0x04d7, 1048575,
-    0x04d9, 1048575,
-    0x04db, 1048575,
-    0x04dd, 1048575,
-    0x04df, 1048575,
-    0x04e1, 1048575,
-    0x04e3, 1048575,
-    0x04e5, 1048575,
-    0x04e7, 1048575,
-    0x04e9, 1048575,
-    0x04eb, 1048575,
-    0x04ed, 1048575,
-    0x04ef, 1048575,
-    0x04f1, 1048575,
-    0x04f3, 1048575,
-    0x04f5, 1048575,
-    0x04f7, 1048575,
-    0x04f9, 1048575,
-    0x04fb, 1048575,
-    0x04fd, 1048575,
-    0x04ff, 1048575,
-    0x0501, 1048575,
-    0x0503, 1048575,
-    0x0505, 1048575,
-    0x0507, 1048575,
-    0x0509, 1048575,
-    0x050b, 1048575,
-    0x050d, 1048575,
-    0x050f, 1048575,
-    0x0511, 1048575,
-    0x0513, 1048575,
-    0x0515, 1048575,
-    0x0517, 1048575,
-    0x0519, 1048575,
-    0x051b, 1048575,
-    0x051d, 1048575,
-    0x051f, 1048575,
-    0x0521, 1048575,
-    0x0523, 1048575,
-    0x0525, 1048575,
-    0x0527, 1048575,
-    0x0529, 1048575,
-    0x052b, 1048575,
-    0x052d, 1048575,
-    0x052f, 1048575,
-    0x1d79, 1083908,
-    0x1d7d, 1052390,
-    0x1e01, 1048575,
-    0x1e03, 1048575,
-    0x1e05, 1048575,
-    0x1e07, 1048575,
-    0x1e09, 1048575,
-    0x1e0b, 1048575,
-    0x1e0d, 1048575,
-    0x1e0f, 1048575,
-    0x1e11, 1048575,
-    0x1e13, 1048575,
-    0x1e15, 1048575,
-    0x1e17, 1048575,
-    0x1e19, 1048575,
-    0x1e1b, 1048575,
-    0x1e1d, 1048575,
-    0x1e1f, 1048575,
-    0x1e21, 1048575,
-    0x1e23, 1048575,
-    0x1e25, 1048575,
-    0x1e27, 1048575,
-    0x1e29, 1048575,
-    0x1e2b, 1048575,
-    0x1e2d, 1048575,
-    0x1e2f, 1048575,
-    0x1e31, 1048575,
-    0x1e33, 1048575,
-    0x1e35, 1048575,
-    0x1e37, 1048575,
-    0x1e39, 1048575,
-    0x1e3b, 1048575,
-    0x1e3d, 1048575,
-    0x1e3f, 1048575,
-    0x1e41, 1048575,
-    0x1e43, 1048575,
-    0x1e45, 1048575,
-    0x1e47, 1048575,
-    0x1e49, 1048575,
-    0x1e4b, 1048575,
-    0x1e4d, 1048575,
-    0x1e4f, 1048575,
-    0x1e51, 1048575,
-    0x1e53, 1048575,
-    0x1e55, 1048575,
-    0x1e57, 1048575,
-    0x1e59, 1048575,
-    0x1e5b, 1048575,
-    0x1e5d, 1048575,
-    0x1e5f, 1048575,
-    0x1e61, 1048575,
-    0x1e63, 1048575,
-    0x1e65, 1048575,
-    0x1e67, 1048575,
-    0x1e69, 1048575,
-    0x1e6b, 1048575,
-    0x1e6d, 1048575,
-    0x1e6f, 1048575,
-    0x1e71, 1048575,
-    0x1e73, 1048575,
-    0x1e75, 1048575,
-    0x1e77, 1048575,
-    0x1e79, 1048575,
-    0x1e7b, 1048575,
-    0x1e7d, 1048575,
-    0x1e7f, 1048575,
-    0x1e81, 1048575,
-    0x1e83, 1048575,
-    0x1e85, 1048575,
-    0x1e87, 1048575,
-    0x1e89, 1048575,
-    0x1e8b, 1048575,
-    0x1e8d, 1048575,
-    0x1e8f, 1048575,
-    0x1e91, 1048575,
-    0x1e93, 1048575,
-    0x1e95, 1048575,
-    0x1e9b, 1048517,
-    0x1ea1, 1048575,
-    0x1ea3, 1048575,
-    0x1ea5, 1048575,
-    0x1ea7, 1048575,
-    0x1ea9, 1048575,
-    0x1eab, 1048575,
-    0x1ead, 1048575,
-    0x1eaf, 1048575,
-    0x1eb1, 1048575,
-    0x1eb3, 1048575,
-    0x1eb5, 1048575,
-    0x1eb7, 1048575,
-    0x1eb9, 1048575,
-    0x1ebb, 1048575,
-    0x1ebd, 1048575,
-    0x1ebf, 1048575,
-    0x1ec1, 1048575,
-    0x1ec3, 1048575,
-    0x1ec5, 1048575,
-    0x1ec7, 1048575,
-    0x1ec9, 1048575,
-    0x1ecb, 1048575,
-    0x1ecd, 1048575,
-    0x1ecf, 1048575,
-    0x1ed1, 1048575,
-    0x1ed3, 1048575,
-    0x1ed5, 1048575,
-    0x1ed7, 1048575,
-    0x1ed9, 1048575,
-    0x1edb, 1048575,
-    0x1edd, 1048575,
-    0x1edf, 1048575,
-    0x1ee1, 1048575,
-    0x1ee3, 1048575,
-    0x1ee5, 1048575,
-    0x1ee7, 1048575,
-    0x1ee9, 1048575,
-    0x1eeb, 1048575,
-    0x1eed, 1048575,
-    0x1eef, 1048575,
-    0x1ef1, 1048575,
-    0x1ef3, 1048575,
-    0x1ef5, 1048575,
-    0x1ef7, 1048575,
-    0x1ef9, 1048575,
-    0x1efb, 1048575,
-    0x1efd, 1048575,
-    0x1eff, 1048575,
-    0x1f51, 1048584,
-    0x1f53, 1048584,
-    0x1f55, 1048584,
-    0x1f57, 1048584,
-    0x1fb3, 1048585,
-    0x1fbe, 1041371,
-    0x1fc3, 1048585,
-    0x1fe5, 1048583,
-    0x1ff3, 1048585,
-    0x214e, 1048548,
-    0x2184, 1048575,
-    0x2c61, 1048575,
-    0x2c65, 1037781,
-    0x2c66, 1037784,
-    0x2c68, 1048575,
-    0x2c6a, 1048575,
-    0x2c6c, 1048575,
-    0x2c73, 1048575,
-    0x2c76, 1048575,
-    0x2c81, 1048575,
-    0x2c83, 1048575,
-    0x2c85, 1048575,
-    0x2c87, 1048575,
-    0x2c89, 1048575,
-    0x2c8b, 1048575,
-    0x2c8d, 1048575,
-    0x2c8f, 1048575,
-    0x2c91, 1048575,
-    0x2c93, 1048575,
-    0x2c95, 1048575,
-    0x2c97, 1048575,
-    0x2c99, 1048575,
-    0x2c9b, 1048575,
-    0x2c9d, 1048575,
-    0x2c9f, 1048575,
-    0x2ca1, 1048575,
-    0x2ca3, 1048575,
-    0x2ca5, 1048575,
-    0x2ca7, 1048575,
-    0x2ca9, 1048575,
-    0x2cab, 1048575,
-    0x2cad, 1048575,
-    0x2caf, 1048575,
-    0x2cb1, 1048575,
-    0x2cb3, 1048575,
-    0x2cb5, 1048575,
-    0x2cb7, 1048575,
-    0x2cb9, 1048575,
-    0x2cbb, 1048575,
-    0x2cbd, 1048575,
-    0x2cbf, 1048575,
-    0x2cc1, 1048575,
-    0x2cc3, 1048575,
-    0x2cc5, 1048575,
-    0x2cc7, 1048575,
-    0x2cc9, 1048575,
-    0x2ccb, 1048575,
-    0x2ccd, 1048575,
-    0x2ccf, 1048575,
-    0x2cd1, 1048575,
-    0x2cd3, 1048575,
-    0x2cd5, 1048575,
-    0x2cd7, 1048575,
-    0x2cd9, 1048575,
-    0x2cdb, 1048575,
-    0x2cdd, 1048575,
-    0x2cdf, 1048575,
-    0x2ce1, 1048575,
-    0x2ce3, 1048575,
-    0x2cec, 1048575,
-    0x2cee, 1048575,
-    0x2cf3, 1048575,
-    0x2d27, 1041312,
-    0x2d2d, 1041312,
-    0xa641, 1048575,
-    0xa643, 1048575,
-    0xa645, 1048575,
-    0xa647, 1048575,
-    0xa649, 1048575,
-    0xa64b, 1048575,
-    0xa64d, 1048575,
-    0xa64f, 1048575,
-    0xa651, 1048575,
-    0xa653, 1048575,
-    0xa655, 1048575,
-    0xa657, 1048575,
-    0xa659, 1048575,
-    0xa65b, 1048575,
-    0xa65d, 1048575,
-    0xa65f, 1048575,
-    0xa661, 1048575,
-    0xa663, 1048575,
-    0xa665, 1048575,
-    0xa667, 1048575,
-    0xa669, 1048575,
-    0xa66b, 1048575,
-    0xa66d, 1048575,
-    0xa681, 1048575,
-    0xa683, 1048575,
-    0xa685, 1048575,
-    0xa687, 1048575,
-    0xa689, 1048575,
-    0xa68b, 1048575,
-    0xa68d, 1048575,
-    0xa68f, 1048575,
-    0xa691, 1048575,
-    0xa693, 1048575,
-    0xa695, 1048575,
-    0xa697, 1048575,
-    0xa699, 1048575,
-    0xa69b, 1048575,
-    0xa723, 1048575,
-    0xa725, 1048575,
-    0xa727, 1048575,
-    0xa729, 1048575,
-    0xa72b, 1048575,
-    0xa72d, 1048575,
-    0xa72f, 1048575,
-    0xa733, 1048575,
-    0xa735, 1048575,
-    0xa737, 1048575,
-    0xa739, 1048575,
-    0xa73b, 1048575,
-    0xa73d, 1048575,
-    0xa73f, 1048575,
-    0xa741, 1048575,
-    0xa743, 1048575,
-    0xa745, 1048575,
-    0xa747, 1048575,
-    0xa749, 1048575,
-    0xa74b, 1048575,
-    0xa74d, 1048575,
-    0xa74f, 1048575,
-    0xa751, 1048575,
-    0xa753, 1048575,
-    0xa755, 1048575,
-    0xa757, 1048575,
-    0xa759, 1048575,
-    0xa75b, 1048575,
-    0xa75d, 1048575,
-    0xa75f, 1048575,
-    0xa761, 1048575,
-    0xa763, 1048575,
-    0xa765, 1048575,
-    0xa767, 1048575,
-    0xa769, 1048575,
-    0xa76b, 1048575,
-    0xa76d, 1048575,
-    0xa76f, 1048575,
-    0xa77a, 1048575,
-    0xa77c, 1048575,
-    0xa77f, 1048575,
-    0xa781, 1048575,
-    0xa783, 1048575,
-    0xa785, 1048575,
-    0xa787, 1048575,
-    0xa78c, 1048575,
-    0xa791, 1048575,
-    0xa793, 1048575,
-    0xa797, 1048575,
-    0xa799, 1048575,
-    0xa79b, 1048575,
-    0xa79d, 1048575,
-    0xa79f, 1048575,
-    0xa7a1, 1048575,
-    0xa7a3, 1048575,
-    0xa7a5, 1048575,
-    0xa7a7, 1048575,
-    0xa7a9, 1048575,
-};
-
-} // !namespace
-
-char32_t toupper(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, toupperr, nelem (toupperr) / 3, 3);
-
-    if (p && c >= p[0] && c <= p[1])
-        return c + p[2] - 1048576;
-
-    p = rbsearch(c, touppers, nelem (touppers) / 2, 2);
-
-    if (p && c == p[0])
-        return c + p[1] - 1048576;
-
-    return c;
-}
-
-namespace {
-
-const char32_t tolowerr[] = {
-    0x0041, 0x005a, 1048608,
-    0x00c0, 0x00d6, 1048608,
-    0x00d8, 0x00de, 1048608,
-    0x0189, 0x018a, 1048781,
-    0x01b1, 0x01b2, 1048793,
-    0x0388, 0x038a, 1048613,
-    0x038e, 0x038f, 1048639,
-    0x0391, 0x03a1, 1048608,
-    0x03a3, 0x03ab, 1048608,
-    0x03fd, 0x03ff, 1048446,
-    0x0400, 0x040f, 1048656,
-    0x0410, 0x042f, 1048608,
-    0x0531, 0x0556, 1048624,
-    0x10a0, 0x10c5, 1055840,
-    0x1f08, 0x1f0f, 1048568,
-    0x1f18, 0x1f1d, 1048568,
-    0x1f28, 0x1f2f, 1048568,
-    0x1f38, 0x1f3f, 1048568,
-    0x1f48, 0x1f4d, 1048568,
-    0x1f68, 0x1f6f, 1048568,
-    0x1f88, 0x1f8f, 1048568,
-    0x1f98, 0x1f9f, 1048568,
-    0x1fa8, 0x1faf, 1048568,
-    0x1fb8, 0x1fb9, 1048568,
-    0x1fba, 0x1fbb, 1048502,
-    0x1fc8, 0x1fcb, 1048490,
-    0x1fd8, 0x1fd9, 1048568,
-    0x1fda, 0x1fdb, 1048476,
-    0x1fe8, 0x1fe9, 1048568,
-    0x1fea, 0x1feb, 1048464,
-    0x1ff8, 0x1ff9, 1048448,
-    0x1ffa, 0x1ffb, 1048450,
-    0x2160, 0x216f, 1048592,
-    0x24b6, 0x24cf, 1048602,
-    0x2c00, 0x2c2e, 1048624,
-    0x2c7e, 0x2c7f, 1037761,
-    0xff21, 0xff3a, 1048608,
-    0x10400, 0x10427, 1048616,
-    0x118a0, 0x118bf, 1048608,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t tolowers[] = {
-    0x0100, 1048577,
-    0x0102, 1048577,
-    0x0104, 1048577,
-    0x0106, 1048577,
-    0x0108, 1048577,
-    0x010a, 1048577,
-    0x010c, 1048577,
-    0x010e, 1048577,
-    0x0110, 1048577,
-    0x0112, 1048577,
-    0x0114, 1048577,
-    0x0116, 1048577,
-    0x0118, 1048577,
-    0x011a, 1048577,
-    0x011c, 1048577,
-    0x011e, 1048577,
-    0x0120, 1048577,
-    0x0122, 1048577,
-    0x0124, 1048577,
-    0x0126, 1048577,
-    0x0128, 1048577,
-    0x012a, 1048577,
-    0x012c, 1048577,
-    0x012e, 1048577,
-    0x0130, 1048377,
-    0x0132, 1048577,
-    0x0134, 1048577,
-    0x0136, 1048577,
-    0x0139, 1048577,
-    0x013b, 1048577,
-    0x013d, 1048577,
-    0x013f, 1048577,
-    0x0141, 1048577,
-    0x0143, 1048577,
-    0x0145, 1048577,
-    0x0147, 1048577,
-    0x014a, 1048577,
-    0x014c, 1048577,
-    0x014e, 1048577,
-    0x0150, 1048577,
-    0x0152, 1048577,
-    0x0154, 1048577,
-    0x0156, 1048577,
-    0x0158, 1048577,
-    0x015a, 1048577,
-    0x015c, 1048577,
-    0x015e, 1048577,
-    0x0160, 1048577,
-    0x0162, 1048577,
-    0x0164, 1048577,
-    0x0166, 1048577,
-    0x0168, 1048577,
-    0x016a, 1048577,
-    0x016c, 1048577,
-    0x016e, 1048577,
-    0x0170, 1048577,
-    0x0172, 1048577,
-    0x0174, 1048577,
-    0x0176, 1048577,
-    0x0178, 1048455,
-    0x0179, 1048577,
-    0x017b, 1048577,
-    0x017d, 1048577,
-    0x0181, 1048786,
-    0x0182, 1048577,
-    0x0184, 1048577,
-    0x0186, 1048782,
-    0x0187, 1048577,
-    0x018b, 1048577,
-    0x018e, 1048655,
-    0x018f, 1048778,
-    0x0190, 1048779,
-    0x0191, 1048577,
-    0x0193, 1048781,
-    0x0194, 1048783,
-    0x0196, 1048787,
-    0x0197, 1048785,
-    0x0198, 1048577,
-    0x019c, 1048787,
-    0x019d, 1048789,
-    0x019f, 1048790,
-    0x01a0, 1048577,
-    0x01a2, 1048577,
-    0x01a4, 1048577,
-    0x01a6, 1048794,
-    0x01a7, 1048577,
-    0x01a9, 1048794,
-    0x01ac, 1048577,
-    0x01ae, 1048794,
-    0x01af, 1048577,
-    0x01b3, 1048577,
-    0x01b5, 1048577,
-    0x01b7, 1048795,
-    0x01b8, 1048577,
-    0x01bc, 1048577,
-    0x01c4, 1048578,
-    0x01c5, 1048577,
-    0x01c7, 1048578,
-    0x01c8, 1048577,
-    0x01ca, 1048578,
-    0x01cb, 1048577,
-    0x01cd, 1048577,
-    0x01cf, 1048577,
-    0x01d1, 1048577,
-    0x01d3, 1048577,
-    0x01d5, 1048577,
-    0x01d7, 1048577,
-    0x01d9, 1048577,
-    0x01db, 1048577,
-    0x01de, 1048577,
-    0x01e0, 1048577,
-    0x01e2, 1048577,
-    0x01e4, 1048577,
-    0x01e6, 1048577,
-    0x01e8, 1048577,
-    0x01ea, 1048577,
-    0x01ec, 1048577,
-    0x01ee, 1048577,
-    0x01f1, 1048578,
-    0x01f2, 1048577,
-    0x01f4, 1048577,
-    0x01f6, 1048479,
-    0x01f7, 1048520,
-    0x01f8, 1048577,
-    0x01fa, 1048577,
-    0x01fc, 1048577,
-    0x01fe, 1048577,
-    0x0200, 1048577,
-    0x0202, 1048577,
-    0x0204, 1048577,
-    0x0206, 1048577,
-    0x0208, 1048577,
-    0x020a, 1048577,
-    0x020c, 1048577,
-    0x020e, 1048577,
-    0x0210, 1048577,
-    0x0212, 1048577,
-    0x0214, 1048577,
-    0x0216, 1048577,
-    0x0218, 1048577,
-    0x021a, 1048577,
-    0x021c, 1048577,
-    0x021e, 1048577,
-    0x0220, 1048446,
-    0x0222, 1048577,
-    0x0224, 1048577,
-    0x0226, 1048577,
-    0x0228, 1048577,
-    0x022a, 1048577,
-    0x022c, 1048577,
-    0x022e, 1048577,
-    0x0230, 1048577,
-    0x0232, 1048577,
-    0x023a, 1059371,
-    0x023b, 1048577,
-    0x023d, 1048413,
-    0x023e, 1059368,
-    0x0241, 1048577,
-    0x0243, 1048381,
-    0x0244, 1048645,
-    0x0245, 1048647,
-    0x0246, 1048577,
-    0x0248, 1048577,
-    0x024a, 1048577,
-    0x024c, 1048577,
-    0x024e, 1048577,
-    0x0370, 1048577,
-    0x0372, 1048577,
-    0x0376, 1048577,
-    0x037f, 1048692,
-    0x0386, 1048614,
-    0x038c, 1048640,
-    0x03cf, 1048584,
-    0x03d8, 1048577,
-    0x03da, 1048577,
-    0x03dc, 1048577,
-    0x03de, 1048577,
-    0x03e0, 1048577,
-    0x03e2, 1048577,
-    0x03e4, 1048577,
-    0x03e6, 1048577,
-    0x03e8, 1048577,
-    0x03ea, 1048577,
-    0x03ec, 1048577,
-    0x03ee, 1048577,
-    0x03f4, 1048516,
-    0x03f7, 1048577,
-    0x03f9, 1048569,
-    0x03fa, 1048577,
-    0x0460, 1048577,
-    0x0462, 1048577,
-    0x0464, 1048577,
-    0x0466, 1048577,
-    0x0468, 1048577,
-    0x046a, 1048577,
-    0x046c, 1048577,
-    0x046e, 1048577,
-    0x0470, 1048577,
-    0x0472, 1048577,
-    0x0474, 1048577,
-    0x0476, 1048577,
-    0x0478, 1048577,
-    0x047a, 1048577,
-    0x047c, 1048577,
-    0x047e, 1048577,
-    0x0480, 1048577,
-    0x048a, 1048577,
-    0x048c, 1048577,
-    0x048e, 1048577,
-    0x0490, 1048577,
-    0x0492, 1048577,
-    0x0494, 1048577,
-    0x0496, 1048577,
-    0x0498, 1048577,
-    0x049a, 1048577,
-    0x049c, 1048577,
-    0x049e, 1048577,
-    0x04a0, 1048577,
-    0x04a2, 1048577,
-    0x04a4, 1048577,
-    0x04a6, 1048577,
-    0x04a8, 1048577,
-    0x04aa, 1048577,
-    0x04ac, 1048577,
-    0x04ae, 1048577,
-    0x04b0, 1048577,
-    0x04b2, 1048577,
-    0x04b4, 1048577,
-    0x04b6, 1048577,
-    0x04b8, 1048577,
-    0x04ba, 1048577,
-    0x04bc, 1048577,
-    0x04be, 1048577,
-    0x04c0, 1048591,
-    0x04c1, 1048577,
-    0x04c3, 1048577,
-    0x04c5, 1048577,
-    0x04c7, 1048577,
-    0x04c9, 1048577,
-    0x04cb, 1048577,
-    0x04cd, 1048577,
-    0x04d0, 1048577,
-    0x04d2, 1048577,
-    0x04d4, 1048577,
-    0x04d6, 1048577,
-    0x04d8, 1048577,
-    0x04da, 1048577,
-    0x04dc, 1048577,
-    0x04de, 1048577,
-    0x04e0, 1048577,
-    0x04e2, 1048577,
-    0x04e4, 1048577,
-    0x04e6, 1048577,
-    0x04e8, 1048577,
-    0x04ea, 1048577,
-    0x04ec, 1048577,
-    0x04ee, 1048577,
-    0x04f0, 1048577,
-    0x04f2, 1048577,
-    0x04f4, 1048577,
-    0x04f6, 1048577,
-    0x04f8, 1048577,
-    0x04fa, 1048577,
-    0x04fc, 1048577,
-    0x04fe, 1048577,
-    0x0500, 1048577,
-    0x0502, 1048577,
-    0x0504, 1048577,
-    0x0506, 1048577,
-    0x0508, 1048577,
-    0x050a, 1048577,
-    0x050c, 1048577,
-    0x050e, 1048577,
-    0x0510, 1048577,
-    0x0512, 1048577,
-    0x0514, 1048577,
-    0x0516, 1048577,
-    0x0518, 1048577,
-    0x051a, 1048577,
-    0x051c, 1048577,
-    0x051e, 1048577,
-    0x0520, 1048577,
-    0x0522, 1048577,
-    0x0524, 1048577,
-    0x0526, 1048577,
-    0x0528, 1048577,
-    0x052a, 1048577,
-    0x052c, 1048577,
-    0x052e, 1048577,
-    0x10c7, 1055840,
-    0x10cd, 1055840,
-    0x1e00, 1048577,
-    0x1e02, 1048577,
-    0x1e04, 1048577,
-    0x1e06, 1048577,
-    0x1e08, 1048577,
-    0x1e0a, 1048577,
-    0x1e0c, 1048577,
-    0x1e0e, 1048577,
-    0x1e10, 1048577,
-    0x1e12, 1048577,
-    0x1e14, 1048577,
-    0x1e16, 1048577,
-    0x1e18, 1048577,
-    0x1e1a, 1048577,
-    0x1e1c, 1048577,
-    0x1e1e, 1048577,
-    0x1e20, 1048577,
-    0x1e22, 1048577,
-    0x1e24, 1048577,
-    0x1e26, 1048577,
-    0x1e28, 1048577,
-    0x1e2a, 1048577,
-    0x1e2c, 1048577,
-    0x1e2e, 1048577,
-    0x1e30, 1048577,
-    0x1e32, 1048577,
-    0x1e34, 1048577,
-    0x1e36, 1048577,
-    0x1e38, 1048577,
-    0x1e3a, 1048577,
-    0x1e3c, 1048577,
-    0x1e3e, 1048577,
-    0x1e40, 1048577,
-    0x1e42, 1048577,
-    0x1e44, 1048577,
-    0x1e46, 1048577,
-    0x1e48, 1048577,
-    0x1e4a, 1048577,
-    0x1e4c, 1048577,
-    0x1e4e, 1048577,
-    0x1e50, 1048577,
-    0x1e52, 1048577,
-    0x1e54, 1048577,
-    0x1e56, 1048577,
-    0x1e58, 1048577,
-    0x1e5a, 1048577,
-    0x1e5c, 1048577,
-    0x1e5e, 1048577,
-    0x1e60, 1048577,
-    0x1e62, 1048577,
-    0x1e64, 1048577,
-    0x1e66, 1048577,
-    0x1e68, 1048577,
-    0x1e6a, 1048577,
-    0x1e6c, 1048577,
-    0x1e6e, 1048577,
-    0x1e70, 1048577,
-    0x1e72, 1048577,
-    0x1e74, 1048577,
-    0x1e76, 1048577,
-    0x1e78, 1048577,
-    0x1e7a, 1048577,
-    0x1e7c, 1048577,
-    0x1e7e, 1048577,
-    0x1e80, 1048577,
-    0x1e82, 1048577,
-    0x1e84, 1048577,
-    0x1e86, 1048577,
-    0x1e88, 1048577,
-    0x1e8a, 1048577,
-    0x1e8c, 1048577,
-    0x1e8e, 1048577,
-    0x1e90, 1048577,
-    0x1e92, 1048577,
-    0x1e94, 1048577,
-    0x1e9e, 1040961,
-    0x1ea0, 1048577,
-    0x1ea2, 1048577,
-    0x1ea4, 1048577,
-    0x1ea6, 1048577,
-    0x1ea8, 1048577,
-    0x1eaa, 1048577,
-    0x1eac, 1048577,
-    0x1eae, 1048577,
-    0x1eb0, 1048577,
-    0x1eb2, 1048577,
-    0x1eb4, 1048577,
-    0x1eb6, 1048577,
-    0x1eb8, 1048577,
-    0x1eba, 1048577,
-    0x1ebc, 1048577,
-    0x1ebe, 1048577,
-    0x1ec0, 1048577,
-    0x1ec2, 1048577,
-    0x1ec4, 1048577,
-    0x1ec6, 1048577,
-    0x1ec8, 1048577,
-    0x1eca, 1048577,
-    0x1ecc, 1048577,
-    0x1ece, 1048577,
-    0x1ed0, 1048577,
-    0x1ed2, 1048577,
-    0x1ed4, 1048577,
-    0x1ed6, 1048577,
-    0x1ed8, 1048577,
-    0x1eda, 1048577,
-    0x1edc, 1048577,
-    0x1ede, 1048577,
-    0x1ee0, 1048577,
-    0x1ee2, 1048577,
-    0x1ee4, 1048577,
-    0x1ee6, 1048577,
-    0x1ee8, 1048577,
-    0x1eea, 1048577,
-    0x1eec, 1048577,
-    0x1eee, 1048577,
-    0x1ef0, 1048577,
-    0x1ef2, 1048577,
-    0x1ef4, 1048577,
-    0x1ef6, 1048577,
-    0x1ef8, 1048577,
-    0x1efa, 1048577,
-    0x1efc, 1048577,
-    0x1efe, 1048577,
-    0x1f59, 1048568,
-    0x1f5b, 1048568,
-    0x1f5d, 1048568,
-    0x1f5f, 1048568,
-    0x1fbc, 1048567,
-    0x1fcc, 1048567,
-    0x1fec, 1048569,
-    0x1ffc, 1048567,
-    0x2126, 1041059,
-    0x212a, 1040193,
-    0x212b, 1040314,
-    0x2132, 1048604,
-    0x2183, 1048577,
-    0x2c60, 1048577,
-    0x2c62, 1037833,
-    0x2c63, 1044762,
-    0x2c64, 1037849,
-    0x2c67, 1048577,
-    0x2c69, 1048577,
-    0x2c6b, 1048577,
-    0x2c6d, 1037796,
-    0x2c6e, 1037827,
-    0x2c6f, 1037793,
-    0x2c70, 1037794,
-    0x2c72, 1048577,
-    0x2c75, 1048577,
-    0x2c80, 1048577,
-    0x2c82, 1048577,
-    0x2c84, 1048577,
-    0x2c86, 1048577,
-    0x2c88, 1048577,
-    0x2c8a, 1048577,
-    0x2c8c, 1048577,
-    0x2c8e, 1048577,
-    0x2c90, 1048577,
-    0x2c92, 1048577,
-    0x2c94, 1048577,
-    0x2c96, 1048577,
-    0x2c98, 1048577,
-    0x2c9a, 1048577,
-    0x2c9c, 1048577,
-    0x2c9e, 1048577,
-    0x2ca0, 1048577,
-    0x2ca2, 1048577,
-    0x2ca4, 1048577,
-    0x2ca6, 1048577,
-    0x2ca8, 1048577,
-    0x2caa, 1048577,
-    0x2cac, 1048577,
-    0x2cae, 1048577,
-    0x2cb0, 1048577,
-    0x2cb2, 1048577,
-    0x2cb4, 1048577,
-    0x2cb6, 1048577,
-    0x2cb8, 1048577,
-    0x2cba, 1048577,
-    0x2cbc, 1048577,
-    0x2cbe, 1048577,
-    0x2cc0, 1048577,
-    0x2cc2, 1048577,
-    0x2cc4, 1048577,
-    0x2cc6, 1048577,
-    0x2cc8, 1048577,
-    0x2cca, 1048577,
-    0x2ccc, 1048577,
-    0x2cce, 1048577,
-    0x2cd0, 1048577,
-    0x2cd2, 1048577,
-    0x2cd4, 1048577,
-    0x2cd6, 1048577,
-    0x2cd8, 1048577,
-    0x2cda, 1048577,
-    0x2cdc, 1048577,
-    0x2cde, 1048577,
-    0x2ce0, 1048577,
-    0x2ce2, 1048577,
-    0x2ceb, 1048577,
-    0x2ced, 1048577,
-    0x2cf2, 1048577,
-    0xa640, 1048577,
-    0xa642, 1048577,
-    0xa644, 1048577,
-    0xa646, 1048577,
-    0xa648, 1048577,
-    0xa64a, 1048577,
-    0xa64c, 1048577,
-    0xa64e, 1048577,
-    0xa650, 1048577,
-    0xa652, 1048577,
-    0xa654, 1048577,
-    0xa656, 1048577,
-    0xa658, 1048577,
-    0xa65a, 1048577,
-    0xa65c, 1048577,
-    0xa65e, 1048577,
-    0xa660, 1048577,
-    0xa662, 1048577,
-    0xa664, 1048577,
-    0xa666, 1048577,
-    0xa668, 1048577,
-    0xa66a, 1048577,
-    0xa66c, 1048577,
-    0xa680, 1048577,
-    0xa682, 1048577,
-    0xa684, 1048577,
-    0xa686, 1048577,
-    0xa688, 1048577,
-    0xa68a, 1048577,
-    0xa68c, 1048577,
-    0xa68e, 1048577,
-    0xa690, 1048577,
-    0xa692, 1048577,
-    0xa694, 1048577,
-    0xa696, 1048577,
-    0xa698, 1048577,
-    0xa69a, 1048577,
-    0xa722, 1048577,
-    0xa724, 1048577,
-    0xa726, 1048577,
-    0xa728, 1048577,
-    0xa72a, 1048577,
-    0xa72c, 1048577,
-    0xa72e, 1048577,
-    0xa732, 1048577,
-    0xa734, 1048577,
-    0xa736, 1048577,
-    0xa738, 1048577,
-    0xa73a, 1048577,
-    0xa73c, 1048577,
-    0xa73e, 1048577,
-    0xa740, 1048577,
-    0xa742, 1048577,
-    0xa744, 1048577,
-    0xa746, 1048577,
-    0xa748, 1048577,
-    0xa74a, 1048577,
-    0xa74c, 1048577,
-    0xa74e, 1048577,
-    0xa750, 1048577,
-    0xa752, 1048577,
-    0xa754, 1048577,
-    0xa756, 1048577,
-    0xa758, 1048577,
-    0xa75a, 1048577,
-    0xa75c, 1048577,
-    0xa75e, 1048577,
-    0xa760, 1048577,
-    0xa762, 1048577,
-    0xa764, 1048577,
-    0xa766, 1048577,
-    0xa768, 1048577,
-    0xa76a, 1048577,
-    0xa76c, 1048577,
-    0xa76e, 1048577,
-    0xa779, 1048577,
-    0xa77b, 1048577,
-    0xa77d, 1013244,
-    0xa77e, 1048577,
-    0xa780, 1048577,
-    0xa782, 1048577,
-    0xa784, 1048577,
-    0xa786, 1048577,
-    0xa78b, 1048577,
-    0xa78d, 1006296,
-    0xa790, 1048577,
-    0xa792, 1048577,
-    0xa796, 1048577,
-    0xa798, 1048577,
-    0xa79a, 1048577,
-    0xa79c, 1048577,
-    0xa79e, 1048577,
-    0xa7a0, 1048577,
-    0xa7a2, 1048577,
-    0xa7a4, 1048577,
-    0xa7a6, 1048577,
-    0xa7a8, 1048577,
-    0xa7aa, 1006268,
-    0xa7ab, 1006257,
-    0xa7ac, 1006261,
-    0xa7ad, 1006271,
-    0xa7b0, 1006318,
-    0xa7b1, 1006294,
-};
-
-} // !namespace
-
-char32_t tolower(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, tolowerr, nelem (tolowerr) / 3, 3);
-
-    if (p && c >= p[0] && c <= p[1])
-        return c + p[2] - 1048576;
-
-    p = rbsearch(c, tolowers, nelem (tolowers) / 2, 2);
-
-    if (p && c == p[0])
-        return c + p[1] - 1048576;
-
-    return c;
-}
-
-namespace {
-
-const char32_t totitler[] = {
-    0x0061, 0x007a, 1048544,
-    0x00e0, 0x00f6, 1048544,
-    0x00f8, 0x00fe, 1048544,
-    0x023f, 0x0240, 1059391,
-    0x0256, 0x0257, 1048371,
-    0x028a, 0x028b, 1048359,
-    0x037b, 0x037d, 1048706,
-    0x03ad, 0x03af, 1048539,
-    0x03b1, 0x03c1, 1048544,
-    0x03c3, 0x03cb, 1048544,
-    0x03cd, 0x03ce, 1048513,
-    0x0430, 0x044f, 1048544,
-    0x0450, 0x045f, 1048496,
-    0x0561, 0x0586, 1048528,
-    0x1f00, 0x1f07, 1048584,
-    0x1f10, 0x1f15, 1048584,
-    0x1f20, 0x1f27, 1048584,
-    0x1f30, 0x1f37, 1048584,
-    0x1f40, 0x1f45, 1048584,
-    0x1f60, 0x1f67, 1048584,
-    0x1f70, 0x1f71, 1048650,
-    0x1f72, 0x1f75, 1048662,
-    0x1f76, 0x1f77, 1048676,
-    0x1f78, 0x1f79, 1048704,
-    0x1f7a, 0x1f7b, 1048688,
-    0x1f7c, 0x1f7d, 1048702,
-    0x1f80, 0x1f87, 1048584,
-    0x1f90, 0x1f97, 1048584,
-    0x1fa0, 0x1fa7, 1048584,
-    0x1fb0, 0x1fb1, 1048584,
-    0x1fd0, 0x1fd1, 1048584,
-    0x1fe0, 0x1fe1, 1048584,
-    0x2170, 0x217f, 1048560,
-    0x24d0, 0x24e9, 1048550,
-    0x2c30, 0x2c5e, 1048528,
-    0x2d00, 0x2d25, 1041312,
-    0xff41, 0xff5a, 1048544,
-    0x10428, 0x1044f, 1048536,
-    0x118c0, 0x118df, 1048544,
-};
-
-} // !namespace
-
-namespace {
-
-const char32_t totitles[] = {
-    0x00b5, 1049319,
-    0x00ff, 1048697,
-    0x0101, 1048575,
-    0x0103, 1048575,
-    0x0105, 1048575,
-    0x0107, 1048575,
-    0x0109, 1048575,
-    0x010b, 1048575,
-    0x010d, 1048575,
-    0x010f, 1048575,
-    0x0111, 1048575,
-    0x0113, 1048575,
-    0x0115, 1048575,
-    0x0117, 1048575,
-    0x0119, 1048575,
-    0x011b, 1048575,
-    0x011d, 1048575,
-    0x011f, 1048575,
-    0x0121, 1048575,
-    0x0123, 1048575,
-    0x0125, 1048575,
-    0x0127, 1048575,
-    0x0129, 1048575,
-    0x012b, 1048575,
-    0x012d, 1048575,
-    0x012f, 1048575,
-    0x0131, 1048344,
-    0x0133, 1048575,
-    0x0135, 1048575,
-    0x0137, 1048575,
-    0x013a, 1048575,
-    0x013c, 1048575,
-    0x013e, 1048575,
-    0x0140, 1048575,
-    0x0142, 1048575,
-    0x0144, 1048575,
-    0x0146, 1048575,
-    0x0148, 1048575,
-    0x014b, 1048575,
-    0x014d, 1048575,
-    0x014f, 1048575,
-    0x0151, 1048575,
-    0x0153, 1048575,
-    0x0155, 1048575,
-    0x0157, 1048575,
-    0x0159, 1048575,
-    0x015b, 1048575,
-    0x015d, 1048575,
-    0x015f, 1048575,
-    0x0161, 1048575,
-    0x0163, 1048575,
-    0x0165, 1048575,
-    0x0167, 1048575,
-    0x0169, 1048575,
-    0x016b, 1048575,
-    0x016d, 1048575,
-    0x016f, 1048575,
-    0x0171, 1048575,
-    0x0173, 1048575,
-    0x0175, 1048575,
-    0x0177, 1048575,
-    0x017a, 1048575,
-    0x017c, 1048575,
-    0x017e, 1048575,
-    0x017f, 1048276,
-    0x0180, 1048771,
-    0x0183, 1048575,
-    0x0185, 1048575,
-    0x0188, 1048575,
-    0x018c, 1048575,
-    0x0192, 1048575,
-    0x0195, 1048673,
-    0x0199, 1048575,
-    0x019a, 1048739,
-    0x019e, 1048706,
-    0x01a1, 1048575,
-    0x01a3, 1048575,
-    0x01a5, 1048575,
-    0x01a8, 1048575,
-    0x01ad, 1048575,
-    0x01b0, 1048575,
-    0x01b4, 1048575,
-    0x01b6, 1048575,
-    0x01b9, 1048575,
-    0x01bd, 1048575,
-    0x01bf, 1048632,
-    0x01c4, 1048577,
-    0x01c6, 1048575,
-    0x01c7, 1048577,
-    0x01c9, 1048575,
-    0x01ca, 1048577,
-    0x01cc, 1048575,
-    0x01ce, 1048575,
-    0x01d0, 1048575,
-    0x01d2, 1048575,
-    0x01d4, 1048575,
-    0x01d6, 1048575,
-    0x01d8, 1048575,
-    0x01da, 1048575,
-    0x01dc, 1048575,
-    0x01dd, 1048497,
-    0x01df, 1048575,
-    0x01e1, 1048575,
-    0x01e3, 1048575,
-    0x01e5, 1048575,
-    0x01e7, 1048575,
-    0x01e9, 1048575,
-    0x01eb, 1048575,
-    0x01ed, 1048575,
-    0x01ef, 1048575,
-    0x01f1, 1048577,
-    0x01f3, 1048575,
-    0x01f5, 1048575,
-    0x01f9, 1048575,
-    0x01fb, 1048575,
-    0x01fd, 1048575,
-    0x01ff, 1048575,
-    0x0201, 1048575,
-    0x0203, 1048575,
-    0x0205, 1048575,
-    0x0207, 1048575,
-    0x0209, 1048575,
-    0x020b, 1048575,
-    0x020d, 1048575,
-    0x020f, 1048575,
-    0x0211, 1048575,
-    0x0213, 1048575,
-    0x0215, 1048575,
-    0x0217, 1048575,
-    0x0219, 1048575,
-    0x021b, 1048575,
-    0x021d, 1048575,
-    0x021f, 1048575,
-    0x0223, 1048575,
-    0x0225, 1048575,
-    0x0227, 1048575,
-    0x0229, 1048575,
-    0x022b, 1048575,
-    0x022d, 1048575,
-    0x022f, 1048575,
-    0x0231, 1048575,
-    0x0233, 1048575,
-    0x023c, 1048575,
-    0x0242, 1048575,
-    0x0247, 1048575,
-    0x0249, 1048575,
-    0x024b, 1048575,
-    0x024d, 1048575,
-    0x024f, 1048575,
-    0x0250, 1059359,
-    0x0251, 1059356,
-    0x0252, 1059358,
-    0x0253, 1048366,
-    0x0254, 1048370,
-    0x0259, 1048374,
-    0x025b, 1048373,
-    0x025c, 1090895,
-    0x0260, 1048371,
-    0x0261, 1090891,
-    0x0263, 1048369,
-    0x0265, 1090856,
-    0x0266, 1090884,
-    0x0268, 1048367,
-    0x0269, 1048365,
-    0x026b, 1059319,
-    0x026c, 1090881,
-    0x026f, 1048365,
-    0x0271, 1059325,
-    0x0272, 1048363,
-    0x0275, 1048362,
-    0x027d, 1059303,
-    0x0280, 1048358,
-    0x0283, 1048358,
-    0x0287, 1090858,
-    0x0288, 1048358,
-    0x0289, 1048507,
-    0x028c, 1048505,
-    0x0292, 1048357,
-    0x029e, 1090834,
-    0x0345, 1048660,
-    0x0371, 1048575,
-    0x0373, 1048575,
-    0x0377, 1048575,
-    0x03ac, 1048538,
-    0x03c2, 1048545,
-    0x03cc, 1048512,
-    0x03d0, 1048514,
-    0x03d1, 1048519,
-    0x03d5, 1048529,
-    0x03d6, 1048522,
-    0x03d7, 1048568,
-    0x03d9, 1048575,
-    0x03db, 1048575,
-    0x03dd, 1048575,
-    0x03df, 1048575,
-    0x03e1, 1048575,
-    0x03e3, 1048575,
-    0x03e5, 1048575,
-    0x03e7, 1048575,
-    0x03e9, 1048575,
-    0x03eb, 1048575,
-    0x03ed, 1048575,
-    0x03ef, 1048575,
-    0x03f0, 1048490,
-    0x03f1, 1048496,
-    0x03f2, 1048583,
-    0x03f3, 1048460,
-    0x03f5, 1048480,
-    0x03f8, 1048575,
-    0x03fb, 1048575,
-    0x0461, 1048575,
-    0x0463, 1048575,
-    0x0465, 1048575,
-    0x0467, 1048575,
-    0x0469, 1048575,
-    0x046b, 1048575,
-    0x046d, 1048575,
-    0x046f, 1048575,
-    0x0471, 1048575,
-    0x0473, 1048575,
-    0x0475, 1048575,
-    0x0477, 1048575,
-    0x0479, 1048575,
-    0x047b, 1048575,
-    0x047d, 1048575,
-    0x047f, 1048575,
-    0x0481, 1048575,
-    0x048b, 1048575,
-    0x048d, 1048575,
-    0x048f, 1048575,
-    0x0491, 1048575,
-    0x0493, 1048575,
-    0x0495, 1048575,
-    0x0497, 1048575,
-    0x0499, 1048575,
-    0x049b, 1048575,
-    0x049d, 1048575,
-    0x049f, 1048575,
-    0x04a1, 1048575,
-    0x04a3, 1048575,
-    0x04a5, 1048575,
-    0x04a7, 1048575,
-    0x04a9, 1048575,
-    0x04ab, 1048575,
-    0x04ad, 1048575,
-    0x04af, 1048575,
-    0x04b1, 1048575,
-    0x04b3, 1048575,
-    0x04b5, 1048575,
-    0x04b7, 1048575,
-    0x04b9, 1048575,
-    0x04bb, 1048575,
-    0x04bd, 1048575,
-    0x04bf, 1048575,
-    0x04c2, 1048575,
-    0x04c4, 1048575,
-    0x04c6, 1048575,
-    0x04c8, 1048575,
-    0x04ca, 1048575,
-    0x04cc, 1048575,
-    0x04ce, 1048575,
-    0x04cf, 1048561,
-    0x04d1, 1048575,
-    0x04d3, 1048575,
-    0x04d5, 1048575,
-    0x04d7, 1048575,
-    0x04d9, 1048575,
-    0x04db, 1048575,
-    0x04dd, 1048575,
-    0x04df, 1048575,
-    0x04e1, 1048575,
-    0x04e3, 1048575,
-    0x04e5, 1048575,
-    0x04e7, 1048575,
-    0x04e9, 1048575,
-    0x04eb, 1048575,
-    0x04ed, 1048575,
-    0x04ef, 1048575,
-    0x04f1, 1048575,
-    0x04f3, 1048575,
-    0x04f5, 1048575,
-    0x04f7, 1048575,
-    0x04f9, 1048575,
-    0x04fb, 1048575,
-    0x04fd, 1048575,
-    0x04ff, 1048575,
-    0x0501, 1048575,
-    0x0503, 1048575,
-    0x0505, 1048575,
-    0x0507, 1048575,
-    0x0509, 1048575,
-    0x050b, 1048575,
-    0x050d, 1048575,
-    0x050f, 1048575,
-    0x0511, 1048575,
-    0x0513, 1048575,
-    0x0515, 1048575,
-    0x0517, 1048575,
-    0x0519, 1048575,
-    0x051b, 1048575,
-    0x051d, 1048575,
-    0x051f, 1048575,
-    0x0521, 1048575,
-    0x0523, 1048575,
-    0x0525, 1048575,
-    0x0527, 1048575,
-    0x0529, 1048575,
-    0x052b, 1048575,
-    0x052d, 1048575,
-    0x052f, 1048575,
-    0x1d79, 1083908,
-    0x1d7d, 1052390,
-    0x1e01, 1048575,
-    0x1e03, 1048575,
-    0x1e05, 1048575,
-    0x1e07, 1048575,
-    0x1e09, 1048575,
-    0x1e0b, 1048575,
-    0x1e0d, 1048575,
-    0x1e0f, 1048575,
-    0x1e11, 1048575,
-    0x1e13, 1048575,
-    0x1e15, 1048575,
-    0x1e17, 1048575,
-    0x1e19, 1048575,
-    0x1e1b, 1048575,
-    0x1e1d, 1048575,
-    0x1e1f, 1048575,
-    0x1e21, 1048575,
-    0x1e23, 1048575,
-    0x1e25, 1048575,
-    0x1e27, 1048575,
-    0x1e29, 1048575,
-    0x1e2b, 1048575,
-    0x1e2d, 1048575,
-    0x1e2f, 1048575,
-    0x1e31, 1048575,
-    0x1e33, 1048575,
-    0x1e35, 1048575,
-    0x1e37, 1048575,
-    0x1e39, 1048575,
-    0x1e3b, 1048575,
-    0x1e3d, 1048575,
-    0x1e3f, 1048575,
-    0x1e41, 1048575,
-    0x1e43, 1048575,
-    0x1e45, 1048575,
-    0x1e47, 1048575,
-    0x1e49, 1048575,
-    0x1e4b, 1048575,
-    0x1e4d, 1048575,
-    0x1e4f, 1048575,
-    0x1e51, 1048575,
-    0x1e53, 1048575,
-    0x1e55, 1048575,
-    0x1e57, 1048575,
-    0x1e59, 1048575,
-    0x1e5b, 1048575,
-    0x1e5d, 1048575,
-    0x1e5f, 1048575,
-    0x1e61, 1048575,
-    0x1e63, 1048575,
-    0x1e65, 1048575,
-    0x1e67, 1048575,
-    0x1e69, 1048575,
-    0x1e6b, 1048575,
-    0x1e6d, 1048575,
-    0x1e6f, 1048575,
-    0x1e71, 1048575,
-    0x1e73, 1048575,
-    0x1e75, 1048575,
-    0x1e77, 1048575,
-    0x1e79, 1048575,
-    0x1e7b, 1048575,
-    0x1e7d, 1048575,
-    0x1e7f, 1048575,
-    0x1e81, 1048575,
-    0x1e83, 1048575,
-    0x1e85, 1048575,
-    0x1e87, 1048575,
-    0x1e89, 1048575,
-    0x1e8b, 1048575,
-    0x1e8d, 1048575,
-    0x1e8f, 1048575,
-    0x1e91, 1048575,
-    0x1e93, 1048575,
-    0x1e95, 1048575,
-    0x1e9b, 1048517,
-    0x1ea1, 1048575,
-    0x1ea3, 1048575,
-    0x1ea5, 1048575,
-    0x1ea7, 1048575,
-    0x1ea9, 1048575,
-    0x1eab, 1048575,
-    0x1ead, 1048575,
-    0x1eaf, 1048575,
-    0x1eb1, 1048575,
-    0x1eb3, 1048575,
-    0x1eb5, 1048575,
-    0x1eb7, 1048575,
-    0x1eb9, 1048575,
-    0x1ebb, 1048575,
-    0x1ebd, 1048575,
-    0x1ebf, 1048575,
-    0x1ec1, 1048575,
-    0x1ec3, 1048575,
-    0x1ec5, 1048575,
-    0x1ec7, 1048575,
-    0x1ec9, 1048575,
-    0x1ecb, 1048575,
-    0x1ecd, 1048575,
-    0x1ecf, 1048575,
-    0x1ed1, 1048575,
-    0x1ed3, 1048575,
-    0x1ed5, 1048575,
-    0x1ed7, 1048575,
-    0x1ed9, 1048575,
-    0x1edb, 1048575,
-    0x1edd, 1048575,
-    0x1edf, 1048575,
-    0x1ee1, 1048575,
-    0x1ee3, 1048575,
-    0x1ee5, 1048575,
-    0x1ee7, 1048575,
-    0x1ee9, 1048575,
-    0x1eeb, 1048575,
-    0x1eed, 1048575,
-    0x1eef, 1048575,
-    0x1ef1, 1048575,
-    0x1ef3, 1048575,
-    0x1ef5, 1048575,
-    0x1ef7, 1048575,
-    0x1ef9, 1048575,
-    0x1efb, 1048575,
-    0x1efd, 1048575,
-    0x1eff, 1048575,
-    0x1f51, 1048584,
-    0x1f53, 1048584,
-    0x1f55, 1048584,
-    0x1f57, 1048584,
-    0x1fb3, 1048585,
-    0x1fbe, 1041371,
-    0x1fc3, 1048585,
-    0x1fe5, 1048583,
-    0x1ff3, 1048585,
-    0x214e, 1048548,
-    0x2184, 1048575,
-    0x2c61, 1048575,
-    0x2c65, 1037781,
-    0x2c66, 1037784,
-    0x2c68, 1048575,
-    0x2c6a, 1048575,
-    0x2c6c, 1048575,
-    0x2c73, 1048575,
-    0x2c76, 1048575,
-    0x2c81, 1048575,
-    0x2c83, 1048575,
-    0x2c85, 1048575,
-    0x2c87, 1048575,
-    0x2c89, 1048575,
-    0x2c8b, 1048575,
-    0x2c8d, 1048575,
-    0x2c8f, 1048575,
-    0x2c91, 1048575,
-    0x2c93, 1048575,
-    0x2c95, 1048575,
-    0x2c97, 1048575,
-    0x2c99, 1048575,
-    0x2c9b, 1048575,
-    0x2c9d, 1048575,
-    0x2c9f, 1048575,
-    0x2ca1, 1048575,
-    0x2ca3, 1048575,
-    0x2ca5, 1048575,
-    0x2ca7, 1048575,
-    0x2ca9, 1048575,
-    0x2cab, 1048575,
-    0x2cad, 1048575,
-    0x2caf, 1048575,
-    0x2cb1, 1048575,
-    0x2cb3, 1048575,
-    0x2cb5, 1048575,
-    0x2cb7, 1048575,
-    0x2cb9, 1048575,
-    0x2cbb, 1048575,
-    0x2cbd, 1048575,
-    0x2cbf, 1048575,
-    0x2cc1, 1048575,
-    0x2cc3, 1048575,
-    0x2cc5, 1048575,
-    0x2cc7, 1048575,
-    0x2cc9, 1048575,
-    0x2ccb, 1048575,
-    0x2ccd, 1048575,
-    0x2ccf, 1048575,
-    0x2cd1, 1048575,
-    0x2cd3, 1048575,
-    0x2cd5, 1048575,
-    0x2cd7, 1048575,
-    0x2cd9, 1048575,
-    0x2cdb, 1048575,
-    0x2cdd, 1048575,
-    0x2cdf, 1048575,
-    0x2ce1, 1048575,
-    0x2ce3, 1048575,
-    0x2cec, 1048575,
-    0x2cee, 1048575,
-    0x2cf3, 1048575,
-    0x2d27, 1041312,
-    0x2d2d, 1041312,
-    0xa641, 1048575,
-    0xa643, 1048575,
-    0xa645, 1048575,
-    0xa647, 1048575,
-    0xa649, 1048575,
-    0xa64b, 1048575,
-    0xa64d, 1048575,
-    0xa64f, 1048575,
-    0xa651, 1048575,
-    0xa653, 1048575,
-    0xa655, 1048575,
-    0xa657, 1048575,
-    0xa659, 1048575,
-    0xa65b, 1048575,
-    0xa65d, 1048575,
-    0xa65f, 1048575,
-    0xa661, 1048575,
-    0xa663, 1048575,
-    0xa665, 1048575,
-    0xa667, 1048575,
-    0xa669, 1048575,
-    0xa66b, 1048575,
-    0xa66d, 1048575,
-    0xa681, 1048575,
-    0xa683, 1048575,
-    0xa685, 1048575,
-    0xa687, 1048575,
-    0xa689, 1048575,
-    0xa68b, 1048575,
-    0xa68d, 1048575,
-    0xa68f, 1048575,
-    0xa691, 1048575,
-    0xa693, 1048575,
-    0xa695, 1048575,
-    0xa697, 1048575,
-    0xa699, 1048575,
-    0xa69b, 1048575,
-    0xa723, 1048575,
-    0xa725, 1048575,
-    0xa727, 1048575,
-    0xa729, 1048575,
-    0xa72b, 1048575,
-    0xa72d, 1048575,
-    0xa72f, 1048575,
-    0xa733, 1048575,
-    0xa735, 1048575,
-    0xa737, 1048575,
-    0xa739, 1048575,
-    0xa73b, 1048575,
-    0xa73d, 1048575,
-    0xa73f, 1048575,
-    0xa741, 1048575,
-    0xa743, 1048575,
-    0xa745, 1048575,
-    0xa747, 1048575,
-    0xa749, 1048575,
-    0xa74b, 1048575,
-    0xa74d, 1048575,
-    0xa74f, 1048575,
-    0xa751, 1048575,
-    0xa753, 1048575,
-    0xa755, 1048575,
-    0xa757, 1048575,
-    0xa759, 1048575,
-    0xa75b, 1048575,
-    0xa75d, 1048575,
-    0xa75f, 1048575,
-    0xa761, 1048575,
-    0xa763, 1048575,
-    0xa765, 1048575,
-    0xa767, 1048575,
-    0xa769, 1048575,
-    0xa76b, 1048575,
-    0xa76d, 1048575,
-    0xa76f, 1048575,
-    0xa77a, 1048575,
-    0xa77c, 1048575,
-    0xa77f, 1048575,
-    0xa781, 1048575,
-    0xa783, 1048575,
-    0xa785, 1048575,
-    0xa787, 1048575,
-    0xa78c, 1048575,
-    0xa791, 1048575,
-    0xa793, 1048575,
-    0xa797, 1048575,
-    0xa799, 1048575,
-    0xa79b, 1048575,
-    0xa79d, 1048575,
-    0xa79f, 1048575,
-    0xa7a1, 1048575,
-    0xa7a3, 1048575,
-    0xa7a5, 1048575,
-    0xa7a7, 1048575,
-    0xa7a9, 1048575,
-};
-
-} // !namespace
-
-char32_t totitle(char32_t c) noexcept
-{
-    const char32_t* p;
-
-    p = rbsearch(c, totitler, nelem (totitler) / 3, 3);
-
-    if (p && c >= p[0] && c <= p[1])
-        return c + p[2] - 1048576;
-
-    p = rbsearch(c, totitles, nelem (totitles) / 2, 2);
-
-    if (p && c == p[0])
-        return c + p[1] - 1048576;
-
-    return c;
-}
-
-void encode(char32_t c, char res[5]) noexcept
-{
-    switch (nbytes_point(c)) {
-    case 1:
-        res[0] = static_cast<char>(c);
-        res[1] = '\0';
-        break;
-    case 2:
-        res[0] = 0xC0 | ((c >> 6)  & 0x1F);
-        res[1] = 0x80 | (c & 0x3F);
-        res[2] = '\0';
-        break;
-    case 3:
-        res[0] = 0xE0 | ((c >> 12) & 0xF );
-        res[1] = 0x80 | ((c >> 6)  & 0x3F);
-        res[2] = 0x80 | (c & 0x3F);
-        res[3] = '\0';
-        break;
-    case 4:
-        res[0] = 0xF0 | ((c >> 18) & 0x7 );
-        res[1] = 0x80 | ((c >> 12) & 0x3F);
-        res[2] = 0x80 | ((c >> 6)  & 0x3F);
-        res[3] = 0x80 | (c & 0x3F);
-        res[4] = '\0';
-        break;
-    default:
-        break;
-    }
-}
-
-void decode(char32_t& c, const char* res) noexcept
-{
-    c = 0;
-
-    switch (nbytes_utf8(res[0])) {
-    case 1:
-        c = res[0];
-        break;
-    case 2:
-        c =  (res[0] & 0x1f) << 6;
-        c |= (res[1] & 0x3f);
-        break;
-    case 3:
-        c =  (res[0] & 0x0f) << 12;
-        c |= (res[1] & 0x3f) << 6;
-        c |= (res[2] & 0x3f);
-        break;
-    case 4:
-        c =  (res[0] & 0x07) << 16;
-        c |= (res[1] & 0x3f) << 12;
-        c |= (res[2] & 0x3f) << 6;
-        c |= (res[3] & 0x3f);
-    default:
-        break;
-    }
-}
-
-int nbytes_utf8(char c) noexcept
-{
-    if (static_cast<unsigned char>(c) <= 127)
-        return 1;
-    if ((c & 0xE0) == 0xC0)
-        return 2;
-    if ((c & 0xF0) == 0xE0)
-        return 3;
-    if ((c & 0xF8) == 0xF0)
-        return 4;
-
-    return -1;
-}
-
-int nbytes_point(char32_t c) noexcept
-{
-    if (c <= 0x7F)
-        return 1;
-    if (c <= 0x7FF)
-        return 2;
-    if (c <= 0xFFFF)
-        return 3;
-    if (c <= 0x1FFFFF)
-        return 4;
-
-    return -1;
-}
-
-unsigned length(const std::string& str)
-{
-    unsigned total = 0;
-
-    for_each(str, [&] (char32_t) {
-        ++ total;
-    });
-
-    return total;
-}
-
-std::string to_utf8(const std::u32string& array)
-{
-    std::string res;
-
-    for (size_t i = 0; i < array.size(); ++i) {
-        char tmp[5];
-        int size = nbytes_point(array[i]);
-
-        if (size < 0)
-            throw std::invalid_argument("invalid sequence");
-
-        encode(array[i], tmp);
-        res.insert(res.length(), tmp);
-    }
-
-    return res;
-}
-
-std::u32string to_utf32(const std::string& str)
-{
-    std::u32string res;
-
-    for_each(str, [&] (char32_t code) {
-        res.push_back(code);
-    });
-
-    return res;
-}
-
-} // !unicode
-
-} // !mlk
--- a/libcommon/malikania/unicode.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,273 +0,0 @@
-/*
- * unicode.hpp -- UTF-8 to UTF-32 conversions and various operations
- *
- * Copyright (c) 2013-2018 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 UNICODE_HPP
-#define UNICODE_HPP
-
-/**
- * \file unicode.hpp
- * \brief UTF-8 to UTF-32 conversions
- * \author David Demelier <markand@malikania.fr>
- * \warning These files are auto-generated!
- */
-
-#include <stdexcept>
-#include <string>
-
-namespace mlk {
-
-/**
- * \brief Unicode namespace.
- */
-namespace unicode {
-
-/**
- * Encode the unicode code point into multibyte string.
- *
- * \param point the unicode code point
- * \param res the output buffer
- */
-void encode(char32_t point, char res[5]) noexcept;
-
-/**
- * Decode the multibyte buffer into an unicode code point.
- *
- * \param c the code point destination
- * \param res the multibyte string.
- */
-void decode(char32_t& c, const char* res) noexcept;
-
-/**
- * Get the number of bytes for the first multi byte character from a
- * utf-8 string.
- *
- * This can be used to iterate a valid UTF-8 string to jump to the next
- * real character.
- *
- * \param c the first multi byte character
- * \return the number of bytes [1-4] or -1 if invalid
- */
-int nbytes_utf8(char c) noexcept;
-
-/**
- * Get the number of bytes for the unicode point.
- *
- * \param point the unicode point
- * \return the number of bytes [1-4] or -1 if invalid
- */
-int nbytes_point(char32_t point) noexcept;
-
-/**
- * Get real number of character in a string.
- *
- * \param str the string
- * \return the length
- * \throw std::invalid_argument on invalid sequence
- */
-unsigned length(const std::string& str);
-
-/**
- * Iterate over all real characters in the UTF-8 string.
- *
- * The function must have the following signature:
- *  void f(char ch)
- *
- * \param str the UTF-8 string
- * \param function the function callback
- * \throw std::invalid_argument on invalid sequence
- */
-template <typename Func>
-void for_each(const std::string& str, Func function)
-{
-    for (size_t i = 0; i < str.size(); ) {
-        char32_t point = 0;
-        int size = nbytes_utf8(str[i]);
-
-        if (size < 0)
-            throw std::invalid_argument("invalid sequence");
-
-        decode(point, str.data() + i);
-        function(point);
-
-        i += size;
-    }
-}
-
-/**
- * Convert a UTF-32 string to UTF-8 string.
- *
- * \param array the UTF-32 string
- * \return the UTF-8 string
- * \throw std::invalid_argument on invalid sequence
- */
-std::string to_utf8(const std::u32string& array);
-
-/**
- * Convert a UTF-8 string to UTF-32 string.
- *
- * \param str the UTF-8 string
- * \return the UTF-32 string
- * \throw std::invalid_argument on invalid sequence
- */
-std::u32string to_utf32(const std::string& str);
-
-/**
- * Check if the unicode character is space.
- *
- * \param c the character
- * \return true if space
- */
-bool isspace(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is digit.
- *
- * \param c the character
- * \return true if digit
- */
-bool isdigit(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is alpha category.
- *
- * \param c the character
- * \return true if alpha
- */
-bool isalpha(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is upper case.
- *
- * \param c the character
- * \return true if upper case
- */
-bool isupper(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is lower case.
- *
- * \param c the character
- * \return true if lower case
- */
-bool islower(char32_t c) noexcept;
-
-/**
- * Check if the unicode character is title case.
- *
- * \param c the character
- * \return true if title case
- */
-bool istitle(char32_t c) noexcept;
-
-/**
- * Convert to upper case.
- *
- * \param c the character
- * \return the upper case character
- */
-char32_t toupper(char32_t c) noexcept;
-
-/**
- * Convert to lower case.
- *
- * \param c the character
- * \return the lower case character
- */
-char32_t tolower(char32_t c) noexcept;
-
-/**
- * Convert to title case.
- *
- * \param c the character
- * \return the title case character
- */
-char32_t totitle(char32_t c) noexcept;
-
-/**
- * Convert the UTF-32 string to upper case.
- *
- * \param str the str
- * \return the upper case string
- */
-inline std::u32string toupper(std::u32string str)
-{
-    for (size_t i = 0; i < str.size(); ++i)
-        str[i] = toupper(str[i]);
-
-    return str;
-}
-
-/**
- * Convert the UTF-8 string to upper case.
- *
- * \param str the str
- * \return the upper case string
- * \warning very slow at the moment
- */
-inline std::string toupper(const std::string& str)
-{
-    std::string result;
-    char buffer[5];
-
-    for_each(str, [&] (char32_t code) {
-        encode(toupper(code), buffer);
-        result += buffer;
-    });
-
-    return result;
-}
-
-/**
- * Convert the UTF-32 string to lower case.
- *
- * \param str the str
- * \return the lower case string
- */
-inline std::u32string tolower(std::u32string str)
-{
-    for (size_t i = 0; i < str.size(); ++i)
-        str[i] = tolower(str[i]);
-
-    return str;
-}
-
-/**
- * Convert the UTF-8 string to lower case.
- *
- * \param str the str
- * \return the lower case string
- * \warning very slow at the moment
- */
-inline std::string tolower(const std::string& str)
-{
-    std::string result;
-    char buffer[5];
-
-    for_each(str, [&] (char32_t code) {
-        encode(tolower(code), buffer);
-        result += buffer;
-    });
-
-    return result;
-}
-
-} // !unicode
-
-} // !mlk
-
-#endif // !UNICODE_HPP
--- a/libcommon/malikania/util.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,443 +0,0 @@
-/*
- * util.cpp -- malikania utilities
- *
- * Copyright (c) 2013-2018 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 <cassert>
-#include <climits>
-#include <string>
-#include <stdexcept>
-
-#if defined(__linux__)
-#  include <unistd.h>
-
-#  include <cerrno>
-#  include <cstring>
-#  include <stdexcept>
-#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
-#  if defined(__NetBSD__)
-#    include <sys/param.h>
-#  else
-#    include <sys/types.h>
-#  endif
-
-#  if defined(__OpenBSD__)
-#    include <unistd.h>
-#  endif
-
-#  include <sys/sysctl.h>
-
-#  include <array>
-#  include <cerrno>
-#  include <climits>
-#  include <cstddef>
-#  include <cstdlib>
-#  include <cstring>
-#  include <stdexcept>
-#elif defined(__APPLE__)
-#  include <cerrno>
-#  include <cstring>
-#  include <libproc.h>
-#  include <unistd.h>
-#elif defined(_WIN32)
-#  include <Windows.h>
-#endif
-
-#include <boost/filesystem.hpp>
-
-#include "util.hpp"
-#include "size.hpp"
-
-namespace {
-
-#if defined(__linux__)
-
-std::string executable_path()
-{
-    std::string result;
-
-    result.resize(2048, '\0');
-
-    auto size = readlink("/proc/self/exe", &result[0], 2048);
-
-    if (size < 0) {
-        throw std::runtime_error(std::strerror(errno));
-    }
-
-    result.resize(size, '\0');
-
-    return result;
-}
-
-#elif defined(__FreeBSD__) || defined(__DragonFly__)
-
-std::string executable_path()
-{
-    std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } };
-    std::string result;
-    std::size_t size = PATH_MAX + 1;
-
-    result.resize(size, '\0');
-
-    if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) {
-        throw std::runtime_error(std::strerror(errno));
-    }
-
-    result.resize(size, '\0');
-
-    return result;
-}
-
-#elif defined(__APPLE__)
-
-std::string executable_path()
-{
-    std::string result;
-    std::size_t size = PROC_PIDPATHINFO_MAXSIZE;
-
-    result.resize(size, '\0');
-
-    if ((size = proc_pidpath(getpid(), &result[0], size)) == 0) {
-        throw std::runtime_error(std::strerror(errno));
-    }
-
-    result.resize(size, '\0');
-
-    return result;
-}
-
-#elif defined(_WIN32)
-
-std::string executable_path()
-{
-    std::string result;
-    std::size_t size = PATH_MAX;
-
-    result.resize(size, '\0');
-
-    if (!(size = GetModuleFileNameA(nullptr, &result[0], size))) {
-        throw std::runtime_error("GetModuleFileName error");
-    }
-
-    result.resize(size, '\0');
-
-    return result;
-}
-
-#elif defined(__NetBSD__)
-
-std::string executable_path()
-{
-        std::string result;
-
-#if defined(KERN_PROC_PATHNAME)
-        std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
-        std::size_t size = MAXPATHLEN;
-
-        result.resize(size, '\0');
-
-        if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) {
-                throw std::runtime_error(std::strerror(errno));
-        }
-
-        result.resize(size, '\0');
-#else
-        result.resize(2048, '\0');
-
-        auto size = readlink("/proc/curproc/exe", &result[0], 2048);
-
-        if (size < 0) {
-                throw std::runtime_error(std::strerror(errno));
-        }
-
-        result.resize(size, '\0');
-#endif
-
-        return result;
-}
-
-#elif defined(__OpenBSD__)
-
-std::string executable_path()
-{
-    char **paths, *path;
-    std::string result;
-    std::size_t len;
-    std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
-
-    if (sysctl(mib.data(), 4, nullptr, &len, nullptr, 0) == -1) {
-        throw std::runtime_error(std::strerror(errno));
-    }
-    if ((paths = static_cast<char **>(malloc(len))) == nullptr) {
-        throw std::runtime_error(std::strerror(errno));
-    }
-
-    sysctl(mib.data(), 4, paths, &len, nullptr, 0);
-
-    if ((path = static_cast<char *>(std::malloc(PATH_MAX + 1))) == nullptr) {
-        std::free(paths);
-        throw std::runtime_error(std::strerror(errno));
-    }
-
-    realpath(paths[0], path);
-    result = path;
-
-    std::free(paths);
-    std::free(path);
-
-    return result;
-}
-
-#else
-
-std::string executable_path()
-{
-    // Not supported.
-    return "";
-}
-
-#endif
-
-} // !namespace
-
-namespace mlk {
-
-namespace util {
-
-std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max)
-{
-    std::vector<std::string> result;
-    std::size_t next = -1, current;
-    int count = 1;
-    bool finished = false;
-
-    if (list.empty()) {
-        return result;
-    }
-
-    do {
-        std::string val;
-
-        current = next + 1;
-        next = list.find_first_of(delimiters, current);
-
-        // split max, get until the end
-        if (max >= 0 && count++ >= max) {
-            val = list.substr(current, std::string::npos);
-            finished = true;
-        } else {
-            val = list.substr(current, next - current);
-            finished = next == std::string::npos;
-        }
-
-        result.push_back(val);
-    } while (!finished);
-
-    return result;
-}
-
-std::string basedir() noexcept
-{
-    std::string result = "./";
-
-    try {
-        boost::filesystem::path tmp = executable_path();
-
-        tmp = tmp.parent_path();    // remove executable name
-        tmp = tmp.parent_path();    // remove "bin"
-        result = tmp.string();
-    } catch (...) {
-        // TODO: add log.
-    }
-
-    return result;
-}
-
-/*
- * json utilities
- * ------------------------------------------------------------------
- */
-
-namespace json {
-
-/*
- * XXX: until json get a non-throwing function to check if a pointer exists,
- * use this to check for a value at the given pointer or throw a
- * property_error.
- */
-nlohmann::json require(const nlohmann::json& object,
-                       const nlohmann::json::json_pointer& pointer,
-                       const nlohmann::json::value_t expected)
-{
-    assert(object.is_object());
-
-    nlohmann::json value;
-
-    try {
-        value = object.at(pointer);
-    } catch (...) {
-        throw property_error(object, pointer, nlohmann::json::value_t::null, expected);
-    }
-
-    if (value.type() != expected) {
-        throw property_error(object, pointer, value.type(), expected);
-    }
-
-    return value;
-}
-
-nlohmann::json get(const nlohmann::json& object,
-                   const nlohmann::json::json_pointer& pointer,
-                   const nlohmann::json::value_t expected,
-                   const nlohmann::json def)
-{
-    assert(object.is_object());
-
-    nlohmann::json value;
-
-    try {
-        value = object.at(pointer);
-    } catch (...) {
-    }
-
-    if (value.type() != expected)
-        value = def;
-
-    return value;
-}
-
-nlohmann::json require_array(const nlohmann::json& object,
-                             const nlohmann::json::json_pointer& pointer)
-{
-    return require(object, pointer, nlohmann::json::value_t::array);
-}
-
-bool require_bool(const nlohmann::json& object,
-                  const nlohmann::json::json_pointer& pointer)
-{
-    return require(object, pointer, nlohmann::json::value_t::boolean);
-}
-
-int require_int(const nlohmann::json& object,
-                const nlohmann::json::json_pointer& pointer)
-{
-    nlohmann::json v;
-
-    try {
-        v = object.at(pointer);
-
-        if (v.is_number_integer())
-            return v.get<int>();
-        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
-            return static_cast<int>(v.get<unsigned>());
-    } catch (...) {
-    }
-
-    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_integer);
-}
-
-nlohmann::json require_object(const nlohmann::json& object,
-                              const nlohmann::json::json_pointer& pointer)
-{
-    return require(object, pointer, nlohmann::json::value_t::object);
-}
-
-std::string require_string(const nlohmann::json& object,
-                           const nlohmann::json::json_pointer& pointer)
-{
-    return require(object, pointer, nlohmann::json::value_t::string);
-}
-
-unsigned require_uint(const nlohmann::json& object,
-                      const nlohmann::json::json_pointer& pointer)
-{
-    nlohmann::json v;
-
-    try {
-        v = object.at(pointer);
-
-        if (v.is_number_unsigned())
-            return v.get<unsigned>();
-        else if (v.is_number_integer() && v.get<int>() >= 0)
-            return static_cast<unsigned>(v.get<int>());
-    } catch (...) {
-    }
-
-    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_unsigned);
-}
-
-mlk::size require_size(const nlohmann::json& object,
-                       const nlohmann::json::json_pointer& index)
-{
-    using pointer = nlohmann::json::json_pointer;
-
-    return {
-        require_uint(object, pointer(index.to_string() + "/width")),
-        require_uint(object, pointer(index.to_string() + "/height"))
-    };
-}
-
-int get_int(const nlohmann::json &object,
-            const nlohmann::json::json_pointer &pointer,
-            int value) noexcept
-{
-    try {
-        auto v = object.at(pointer);
-
-        if (v.is_number_integer())
-            value = v.get<int>();
-        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
-            value = static_cast<int>(v.get<unsigned>());
-    } catch (...) {
-    }
-
-    return value;
-}
-
-unsigned get_uint(const nlohmann::json &object,
-                  const nlohmann::json::json_pointer& pointer,
-                  unsigned value) noexcept
-{
-    try {
-        auto v = object.at(pointer);
-
-        if (v.is_number_unsigned())
-            value = v.get<unsigned>();
-        else if (v.is_number_integer() && v.get<int>() >= 0)
-            value = static_cast<unsigned>(v.get<int>());
-    } catch (...) {
-    }
-
-    return value;
-}
-
-mlk::size get_size(const nlohmann::json& object,
-                   const nlohmann::json::json_pointer& pointer,
-                   const mlk::size& def) noexcept
-{
-    using json_pointer = nlohmann::json::json_pointer;
-
-    return {
-        get_uint(object, json_pointer(pointer.to_string() + "/width"), def.width),
-        get_uint(object, json_pointer(pointer.to_string() + "/height"), def.height)
-    };
-}
-
-} // !json
-
-} // !util
-
-} // !mlk
--- a/libcommon/malikania/util.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,331 +0,0 @@
-/*
- * util.hpp -- malikania utilities
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_UTIL_HPP
-#define MALIKANIA_UTIL_HPP
-
-/**
- * \file util.hpp
- * \brief Some utilities
- */
-
-#include <algorithm>
-#include <exception>
-#include <string>
-#include <vector>
-
-#include "json.hpp"
-#include "size.hpp"
-
-namespace mlk {
-
-/**
- * \brief Utilities.
- */
-namespace util {
-
-/**
- * Split a line into a list of tokens.
- *
- * If max is less than 0, split as much as possible, otherwise split at most
- * the number of specified tokens.
- *
- * \param line the line to split
- * \param delim the list of delimiters allowed
- * \param max the maximum number of tokens
- * \return the list
- */
-std::vector<std::string> split(const std::string& line, const std::string& delim, int max = -1);
-
-/**
- * Get the base directory of the current executable.
- *
- * \return the base directory or empty if impossible to get
- */
-std::string basedir() noexcept;
-
-/**
- * Clamp the value between low and high.
- *
- * \param value the value
- * \param low the minimum value
- * \param high the maximum value
- * \return the value between minimum and maximum
- */
-template <typename T>
-constexpr T clamp(T value, T low, T high) noexcept
-{
-    return (value < high) ? std::max(value, low) : std::min(value, high);
-}
-
-/**
- * \brief Utilities for networking.
- */
-namespace net {
-
-/**
- * Shortcut for util::split(line, " ", max);
- *
- * \param line the line
- * \param max the maximum of tokens to split
- * \return the list
- */
-inline std::vector<std::string> split(const std::string& line, int max = -1)
-{
-    return util::split(line, " ", max);
-}
-
-} // !net
-
-namespace json {
-
-/**
- * \brief Indicates a property error in an object.
- */
-class property_error : public std::exception {
-private:
-    nlohmann::json m_object;
-    nlohmann::json::json_pointer m_pointer;
-    nlohmann::json::value_t m_type;
-    nlohmann::json::value_t m_expected;
-    std::string m_message;
-
-public:
-    /**
-     * Construct the property error.
-     *
-     * \param object the faulty object
-     * \param pointer the property path
-     * \param type the found type
-     * \param expected the expected type
-     */
-    inline property_error(nlohmann::json object,
-                          nlohmann::json::json_pointer pointer,
-                          nlohmann::json::value_t type,
-                          nlohmann::json::value_t expected) noexcept
-        : m_object(std::move(object))
-        , m_pointer(std::move(pointer))
-        , m_type(type)
-        , m_expected(expected)
-    {
-        m_message += "invalid '" + m_pointer.to_string() + "' property ";
-        m_message += "(" + nlohmann::json(expected).type_name();
-        m_message += " expected, got ";
-        m_message += nlohmann::json(type).type_name() + ")";
-    }
-
-    /**
-     * Get the faulty object.
-     *
-     * return the object
-     */
-    inline const nlohmann::json& object() const noexcept
-    {
-        return m_object;
-    }
-
-    /**
-     * Get the pointer to the property.
-     *
-     * \return the pointer
-     */
-    inline const nlohmann::json::json_pointer& pointer() const noexcept
-    {
-        return m_pointer;
-    }
-
-    /**
-     * Get the actual type.
-     *
-     * \return the type
-     */
-    inline nlohmann::json::value_t type() const noexcept
-    {
-        return m_type;
-    }
-
-    /**
-     * Get the expected type.
-     *
-     * \return the type
-     */
-    inline nlohmann::json::value_t expected() const noexcept
-    {
-        return m_expected;
-    }
-
-    /**
-     * Get a human formatted message.
-     *
-     * \return the message
-     */
-    const char* what() const noexcept
-    {
-        return m_message.c_str();
-    }
-};
-
-/**
- * Require a value at the given pointer, if the value does not match the
- * expected type, an exception is thrown.
- *
- * If the property does not exists, a property_error with a null found type is
- * thrown.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \param expected the expected type
- * \throw property_error on errors
- */
-nlohmann::json require(const nlohmann::json& object,
-                       const nlohmann::json::json_pointer& pointer,
-                       const nlohmann::json::value_t expected);
-
-/**
- * Require an array at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-nlohmann::json require_array(const nlohmann::json& object,
-                             const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require a boolean at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-bool require_bool(const nlohmann::json& object,
-                  const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require an integer at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-int require_int(const nlohmann::json& object,
-                const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require an object at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-nlohmann::json require_object(const nlohmann::json& object,
-                              const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require a string at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-std::string require_string(const nlohmann::json& object,
-                           const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require an unsigned integer at the given pointer.
- *
- * \pre object.is_object()
- * \param object the object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-unsigned require_uint(const nlohmann::json& object,
-                      const nlohmann::json::json_pointer& pointer);
-
-/**
- * Require a size object at the given pointer.
- *
- * \pre object.is_object
- * \param object the json object
- * \param pointer the pointer to the property
- * \return the value
- * \throw property_error on errors
- */
-mlk::size require_size(const nlohmann::json& object,
-                       const nlohmann::json::json_pointer& pointer);
-
-/**
- * Get an integer at the given pointer or default value if not present or
- * invalid.
- *
- * \pre object.is_object
- * \param object the json object
- * \param pointer the pointer to the property
- * \param def the default value
- * \return an integer
- */
-int get_int(const nlohmann::json& object,
-            const nlohmann::json::json_pointer& pointer,
-            int def = 0) noexcept;
-
-/**
- * Get an unsigned integer at the given pointer or default value if not present
- * or invalid.
- *
- * \pre object.is_object
- * \param object the json object
- * \param pointer the pointer to the property
- * \param def the default value
- * \return an integer
- */
-unsigned get_uint(const nlohmann::json& object,
-                  const nlohmann::json::json_pointer& pointer,
-                  unsigned def = 0) noexcept;
-
-/**
- * Get a size object at the given pointer or a default value.
- *
- * \pre object.is_object
- * \param object the json object
- * \param pointer the pointer to the property
- * \param def the default value to return in case of error
- * \return a size or a default value
- */
-mlk::size get_size(const nlohmann::json& object,
-                   const nlohmann::json::json_pointer& pointer,
-                   const mlk::size& def = {}) noexcept;
-
-} // !json
-
-} // !util
-
-} // !mlk
-
-#endif // !MALIKANIA_UTIL_HPP
--- a/libdb/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-db)
-
-find_package(PostgreSQL REQUIRED)
-
-set(
-	HEADERS
-	${libmlk-db_SOURCE_DIR}/malikania/db/account.hpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/character.hpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/database.hpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/model.hpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/spell.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-db_SOURCE_DIR}/malikania/db/account.cpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/character.cpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/database.cpp
-	${libmlk-db_SOURCE_DIR}/malikania/db/spell.cpp
-)
-
-malikania_define_library(
-	TARGET libmlk-db
-	SOURCES ${HEADERS} ${SOURCES}
-	LIBRARIES
-		${PostgreSQL_LIBRARIES}
-	PUBLIC_INCLUDES
-		${Boost_INCLUDE_DIRS}
-		${libmlk-db_SOURCE_DIR}/malikania
-	PRIVATE_INCLUDES
-		${PostgreSQL_INCLUDE_DIRS}
-)
--- a/libdb/malikania/db/account.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +0,0 @@
-/*
- * account.cpp -- database account object
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "account.hpp"
-
-namespace mlk::db {
-
-auto account::load(const result& res) -> account
-{
-	account a(PQgetvalue(res.get(), 0, 1), PQgetvalue(res.get(), 0, 2));
-
-	a.id_ = std::stoi(PQgetvalue(res.get(), 0, 0));
-	a.firstname_ = PQgetvalue(res.get(), 0, 3);
-	a.lastname_ = PQgetvalue(res.get(), 0, 4);
-	a.email_ = PQgetvalue(res.get(), 0, 5);
-	a.characters_ = character::load(a);
-
-	return a;
-}
-
-void account::clear()
-{
-	id_ = 0U;
-}
-
-account::account(std::string login, std::string password) noexcept
-	: login_(std::move(login))
-	, password_(std::move(password))
-{
-	assert(!login_.empty());
-	assert(!password_.empty());
-}
-
-auto account::get_login() const noexcept -> const std::string&
-{
-	return login_;
-}
-
-void account::set_password(std::string password)
-{
-	static const std::string sql(
-		"UPDATE account"
-		"   SET password = $1"
-		" WHERE id = $2"
-	);
-
-	assert(!password.empty());
-
-	if (is_published() && password_ != password)
-		db::exec(sql, { password, id_ });
-
-	password_ = std::move(password);
-}
-
-auto account::get_email() const noexcept -> const std::string&
-{
-	return email_;
-}
-
-void account::set_email(std::string email)
-{
-	static const std::string sql(
-		"UPDATE account"
-		"   SET email = $1"
-		" WHERE id = $2"
-	);
-
-	assert(!email.empty());
-
-	if (is_published() && email_ != email)
-		db::exec(sql, { email, id_ });
-
-	email_ = std::move(email);
-}
-
-auto account::get_firstname() const noexcept -> const std::string&
-{
-	return firstname_;
-}
-
-void account::set_firstname(std::string name)
-{
-	static const std::string sql(
-		"UPDATE account"
-		"   SET firstname = $1"
-		" WHERE id = $2"
-	);
-
-	if (is_published() && firstname_ != name)
-		db::exec(sql, { name, id_ });
-
-	firstname_ = std::move(name);
-}
-
-auto account::get_lastname() const noexcept -> const std::string&
-{
-	return lastname_;
-}
-
-void account::set_lastname(std::string name)
-{
-	static const std::string sql(
-		"UPDATE account"
-		"   SET lastname = $1"
-		" WHERE id = $2"
-	);
-
-	if (is_published() && lastname_ != name)
-		db::exec(sql, { name, id_ });
-
-	lastname_ = std::move(name);
-}
-
-auto account::get_characters() const noexcept -> const std::vector<character>&
-{
-	return characters_;
-}
-
-void account::add(character ch)
-{
-	assert(ch.is_draft());
-
-	characters_.reserve(characters_.size() + 1);
-
-	if (is_published())
-		ch.publish(*this);
-
-	characters_.push_back(std::move(ch));
-}
-
-void account::remove(std::vector<character>::iterator it)
-{
-	// TODO: assert 'it' is in vector.
-	it->unpublish();
-	characters_.erase(it);
-}
-
-void account::publish()
-{
-	static const std::string sql(
-		"INSERT INTO account("
-		"  login,"
-		"  password,"
-		"  firstname,"
-		"  lastname,"
-		"  email"
-		") "
-		"VALUES ($1, $2, $3, $4, $5) "
-		"RETURNING id"
-	);
-
-	assert(is_draft());
-
-	/*
-	 * Recursively save the account, its characters and spells of those
-	 * characters.
-	 */
-	transaction txn([this] {
-		clear();
-
-		for (auto& c : characters_)
-			c.clear();
-	});
-
-	const auto r = select(sql, { login_, password_, firstname_, lastname_, email_ });
-
-	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
-
-	for (auto& c : characters_)
-		c.publish(*this);
-
-	txn.commit();
-
-	assert(is_published());
-}
-
-void account::unpublish()
-{
-	static const std::string sql(
-		"DELETE"
-		"  FROM account"
-		" WHERE id = $1"
-	);
-
-	assert(is_published());
-
-	id_ = 0;
-
-	/*
-	 * Recursively make all characters and their spells draft.
-	 */
-	for (auto& c : characters_)
-		c.clear();
-
-	assert(is_draft());
-}
-
-auto account::find_by_login(const std::string& login) -> std::optional<account>
-{
-	static const std::string sql(
-		"SELECT *"
-		"  FROM account"
-		" WHERE login = $1"
-	);
-
-	const auto r = select(sql, { login });
-
-	if (PQntuples(r.get()) == 0)
-		return std::nullopt;
-
-	return load(r);
-}
-
-auto account::authenticate(const std::string& login,
-                           const std::string& password) -> std::optional<account>
-{
-	auto ac = find_by_login(login);
-
-	if (!ac || ac->password_ != password)
-		return std::nullopt;
-
-	return ac;
-}
-
-} // !mlk::db
--- a/libdb/malikania/db/account.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-/*
- * account.hpp -- database account object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_DB_ACCOUNT_HPP
-#define MALIKANIA_DB_ACCOUNT_HPP
-
-/**
- * \file account.hpp
- * \brief Database account object.
- */
-
-#include <optional>
-
-#include "character.hpp"
-#include "database.hpp"
-#include "model.hpp"
-
-namespace mlk::db {
-
-/**
- * \brief Database account object.
- */
-class account : public model {
-private:
-	std::string login_;
-	std::string password_;
-	std::string email_;
-	std::string firstname_;
-	std::string lastname_;
-	std::vector<character> characters_;
-
-	static auto load(const result&) -> account;
-
-	void clear();
-
-public:
-	/**
-	 * Create a draft account.
-	 *
-	 * \pre !login.empty()
-	 * \pre !password.empty()
-	 * \param login the login name
-	 * \param password the password
-	 * \warning the password is saved as-is and **must** be hashed by the caller.
-	 */
-	account(std::string login, std::string password) noexcept;
-
-	/**
-	 * Get the account login.
-	 *
-	 * \return the login
-	 */
-	auto get_login() const noexcept -> const std::string&;
-
-	/**
-	 * Set the password.
-	 *
-	 * \pre !password.empty()
-	 * \warning the password is saved as-is and **must** be hashed by the caller.
-	 */
-	void set_password(std::string password);
-
-	/**
-	 * Get the account email.
-	 *
-	 * \return the email
-	 */
-	auto get_email() const noexcept -> const std::string&;
-
-	/**
-	 * Set the account email.
-	 *
-	 * \pre !email.empty()
-	 * \param email the new email
-	 */
-	void set_email(std::string email);
-
-	/**
-	 * Get the account first name.
-	 *
-	 * \return the name
-	 */
-	auto get_firstname() const noexcept -> const std::string&;
-
-	/**
-	 * Set the account firstname.
-	 *
-	 * \param name the new name
-	 */
-	void set_firstname(std::string name);
-
-	/**
-	 * Get the account last name.
-	 *
-	 * \return the name
-	 */
-	auto get_lastname() const noexcept -> const std::string&;
-
-	/**
-	 * Set the account last name.
-	 *
-	 * \param name the new name
-	 */
-	void set_lastname(std::string name);
-
-	/**
-	 * Get the caracter list.
-	 *
-	 * \return the associated characters.
-	 */
-	auto get_characters() const noexcept -> const std::vector<character>&;
-
-	/**
-	 * Add the character to the account.
-	 *
-	 * Account takes ownership of character.
-	 *
-	 * \pre ch.is_draft()
-	 * \param ch the character
-	 * \post ch.is_published()
-	 */
-	void add(character ch);
-
-	/**
-	 * Remove the character from the account.
-	 *
-	 * \pre it is in the the account character list
-	 * \param ch the character
-	 * \post ch == nullptr
-	 */
-	void remove(std::vector<character>::iterator it);
-
-	/**
-	 * Save the account, does nothing if is_published().
-	 *
-	 * \throw std::exception on errors
-	 * \post is_published()
-	 */
-	void publish();
-
-	/**
-	 * Destroy the account.
-	 *
-	 * The account will contains no characters anymore.
-	 *
-	 * \throw std::exception on errors
-	 * \post is_draft()
-	 */
-	void unpublish();
-
-	/**
-	 * Find an account by login.
-	 *
-	 * \param login the login
-	 * \return the account or none if not found
-	 * \throw std::runtime_error on database errors
-	 */
-	static auto find_by_login(const std::string& login) -> std::optional<account>;
-
-	/**
-	 * Find and authenticate a user.
-	 *
-	 * \param login the login
-	 * \param password the password
-	 * \return the account or none if not found
-	 * \throw std::runtime_error on database errors
-	 */
-	static auto authenticate(const std::string& login,
-                                 const std::string& password) -> std::optional<account>;
-};
-
-} // !mlk::db
-
-#endif // !MALIKANIA_DB_ACCOUNT_HPP
--- a/libdb/malikania/db/character.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,266 +0,0 @@
-/*
- * character.cpp -- database character object
- *
- * Copyright (c) 2013-2018 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 <cassert>
-#include <numeric>
-
-#include "account.hpp"
-#include "character.hpp"
-#include "database.hpp"
-
-namespace mlk::db {
-
-auto character::load(const result& result, int row) -> character
-{
-	character ch(PQgetvalue(result.get(), row, 2), PQgetvalue(result.get(), row, 3));
-
-	ch.levels_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 4));
-	ch.factors_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 5));
-	ch.exp_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 6));
-
-	ch.levels_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 7));
-	ch.factors_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 8));
-	ch.exp_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 9));
-
-	ch.levels_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 10));
-	ch.factors_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 11));
-	ch.exp_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 12));
-
-	ch.levels_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 13));
-	ch.factors_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 14));
-	ch.exp_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 15));
-
-	ch.levels_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 16));
-	ch.factors_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 17));
-	ch.exp_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 18));
-
-	ch.id_ = std::stoi(PQgetvalue(result.get(), row, 0));
-	ch.spells_ = spell::load(ch);
-
-	return ch;
-}
-
-auto character::load(const account& parent) -> std::vector<character>
-{
-	static const std::string sql(
-		"SELECT *"
-		"  FROM character"
-		" WHERE account_id = $1"
-	);
-
-	const auto r = select(sql, { parent.get_id() });
-
-	if (PQntuples(r.get()) == 0)
-		throw std::runtime_error("failed to load characters");
-
-	std::vector<character> characters;
-
-	for (int i = 0; i < PQntuples(r.get()); ++i)
-		characters.push_back(load(r, i));
-
-	return characters;
-}
-
-void character::clear()
-{
-	id_ = 0U;
-
-	for (auto& s : spells_)
-		s.clear();
-}
-
-void character::publish(account& parent)
-{
-	static const std::string sql(
-		"INSERT INTO character ("
-		"  account_id,"
-		"  nickname,"
-		"  type,"
-		"  hp_level,"
-		"  hp_factor,"
-		"  hp_exp,"
-		"  force_level,"
-		"  force_factor,"
-		"  force_exp,"
-		"  defense_level,"
-		"  defense_factor,"
-		"  defense_exp,"
-		"  agility_level,"
-		"  agility_factor,"
-		"  agility_exp,"
-		"  luck_level,"
-		"  luck_factor,"
-		"  luck_exp"
-		") "
-		"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) "
-		"RETURNING id"
-	);
-
-	const auto r = select(sql, {
-		parent.get_id(),
-		nickname_,
-		type_,
-		levels_[stat::hp],
-		factors_[stat::hp],
-		exp_[stat::hp],
-		levels_[stat::force],
-		factors_[stat::force],
-		exp_[stat::force],
-		levels_[stat::defense],
-		factors_[stat::defense],
-		exp_[stat::defense],
-		levels_[stat::agility],
-		factors_[stat::agility],
-		exp_[stat::agility],
-		levels_[stat::luck],
-		factors_[stat::luck],
-		exp_[stat::luck]
-	});
-
-	if (PQntuples(r.get()) == 0)
-		throw std::runtime_error("failed to save character");
-
-	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
-
-	for (auto& s : spells_)
-		s.publish(*this);
-}
-
-void character::unpublish()
-{
-	static const std::string sql(
-		"DELETE"
-		"  FROM character"
-		" WHERE id = $1"
-	);
-
-	exec(sql, { id_ });
-	clear();
-}
-
-character::character(std::string nickname, std::string type) noexcept
-	: nickname_(std::move(nickname))
-	, type_(std::move(type))
-{
-	assert(!nickname_.empty());
-	assert(!type_.empty());
-}
-
-auto character::get_nickname() const noexcept -> const std::string&
-{
-	return nickname_;
-}
-
-auto character::get_type() const noexcept -> const std::string&
-{
-	return type_;
-}
-
-auto character::get_spells() const noexcept -> const std::vector<spell>&
-{
-	return spells_;
-}
-
-auto character::get_levels() const noexcept -> const std::array<std::uint8_t, 5>&
-{
-	return levels_;
-}
-
-void character::set_levels(std::array<std::uint8_t, 5> levels)
-{
-	static const std::string sql(
-		"UPDATE character"
-		"   SET hp_level = $1"
-		"	 , force_level = $2"
-		"	 , defense_level = $3"
-		"	 , agility_level = $4"
-		"	 , luck_level = $5"
-	);
-
-	if (is_published())
-		exec(sql, { id_, levels[0], levels[1], levels[2], levels[3], levels[4] });
-
-	levels_ = std::move(levels);
-}
-
-auto character::get_factors() const noexcept -> const std::array<std::uint8_t, 5>&
-{
-	return factors_;
-}
-
-void character::set_factors(std::array<std::uint8_t, 5> factors)
-{
-	static const std::string sql(
-		"UPDATE character"
-		"   SET hp_factor = $1"
-		"	 , force_factor = $2"
-		"	 , defense_factor = $3"
-		"	 , agility_factor = $4"
-		"	 , luck_factor = $5"
-	);
-
-	assert(std::accumulate(factors.begin(), factors.end(), 0U) == 100U);
-
-	if (is_published())
-		exec(sql, { id_, factors[0], factors[1], factors[2], factors[3], factors[4] });
-
-	factors_ = std::move(factors);
-}
-
-auto character::get_experience() const noexcept -> const std::array<std::uint32_t, 5>&
-{
-	return exp_;
-}
-
-void character::set_experience(std::array<std::uint32_t, 5> experience)
-{
-	static const std::string sql(
-		"UPDATE character"
-		"   SET hp_experience = $1"
-		"	 , force_experience = $2"
-		"	 , defense_experience = $3"
-		"	 , agility_experience = $4"
-		"	 , luck_experience = $5"
-	);
-
-	if (is_published())
-		exec(sql, { id_, experience[0], experience[1], experience[2], experience[3], experience[4] });
-
-	exp_ = std::move(experience);
-}
-
-void character::add(spell sp)
-{
-	assert(sp.is_draft());
-
-	spells_.reserve(spells_.size() + 1);
-
-	if (is_published())
-		sp.publish(*this);
-
-	spells_.push_back(std::move(sp));
-}
-
-void character::remove(std::vector<spell>::iterator it)
-{
-	// TODO: assert 'it' is in vector.
-	it->unpublish();
-	spells_.erase(it);
-}
-
-} // !mlk::db
--- a/libdb/malikania/db/character.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-/*
- * character.hpp -- database character object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_DB_CHARACTER_HPP
-#define MALIKANIA_DB_CHARACTER_HPP
-
-/*
- * \file character.hpp
- * \brief Database character object.
- */
-
-#include <cstdint>
-#include <array>
-#include <vector>
-
-#include "database.hpp"
-#include "model.hpp"
-#include "spell.hpp"
-
-namespace mlk::db {
-
-class account;
-
-/*
- * \brief Database character object.
- */
-class character : public model {
-public:
-	friend class account;
-
-	/**
-	 * \brief Stats index in arrays
-	 * \see set_levels
-	 * \see set_factors
-	 * \see set_experience
-	 */
-	enum stat {
-		hp = 0,                 //!< hp index
-		force,                  //!< force index
-		defense,                //!< defense index
-		agility,                //!< agility index
-		luck                    //!< luck index
-	};
-
-private:
-	std::string nickname_;
-	std::string type_;
-	std::vector<spell> spells_;
-	std::array<std::uint8_t, 5> levels_{1U, 1U, 1U, 1U, 1U};
-	std::array<std::uint8_t, 5> factors_{20U, 20U, 20U, 20U, 20U};
-	std::array<std::uint32_t, 5> exp_{0U, 0U, 0U, 0U, 0U};
-
-	static auto load(const result&, int) -> character;
-	static auto load(const account&) -> std::vector<character>;
-
-	void clear();
-	void publish(account&);
-	void unpublish();
-
-public:
-	/**
-	 * Construct a character, no database is modified yet.
-	 *
-	 * \pre !nickname.empty()
-	 * \pre !type.empty()
-	 * \param nickname the nickname
-	 * \param type the type
-	 */
-	character(std::string nickname, std::string type) noexcept;
-
-	/**
-	 * Get the character nickname.
-	 *
-	 * \return the name
-	 */
-	auto get_nickname() const noexcept -> const std::string&;
-
-	/**
-	 * Get the character type name.
-	 *
-	 * \return the type name
-	 */
-	auto get_type() const noexcept -> const std::string&;
-
-	/**
-	 * Get the list of spells.
-	 *
-	 * \return the spells
-	 */
-	auto get_spells() const noexcept -> const std::vector<spell>&;
-
-	/**
-	 * Get the list of levels.
-	 *
-	 * \return the levels
-	 */
-	auto get_levels() const noexcept -> const std::array<std::uint8_t, 5>&;
-
-	/**
-	 * Set the new levels.
-	 *
-	 * \param levels the levels
-	 */
-	void set_levels(std::array<std::uint8_t, 5> levels);
-
-	/**
-	 * Get the list of factors.
-	 *
-	 * \return the factors
-	 */
-	auto get_factors() const noexcept -> const std::array<std::uint8_t, 5>&;
-
-	/**
-	 * Set progression factors.
-	 *
-	 * \pre factors sum must be 100
-	 * \param factors the factors
-	 */
-	void set_factors(std::array<std::uint8_t, 5> factors);
-
-	/**
-	 * Get the stats experience.
-	 *
-	 * \return the experience
-	 */
-	auto get_experience() const noexcept -> const std::array<std::uint32_t, 5>&;
-
-	/**
-	 * Set stats experience.
-	 *
-	 * \param experience the experience
-	 */
-	void set_experience(std::array<std::uint32_t, 5> experience);
-
-	/**
-	 * Add a spell.
-	 *
-	 * Character takes ownership of spell.
-	 *
-	 * \pre sp.is_draft()
-	 * \param sp the spell (moved)
-	 */
-	void add(spell sp);
-
-	/**
-	 * Remove the spell from the character.
-	 *
-	 * \pre it must be valid
-	 * \param it the spell iterator
-	 */
-	void remove(std::vector<spell>::iterator it);
-};
-
-} // !mlk::db
-
-#endif // !MALIKANIA_DB_CHARACTER_HPP
--- a/libdb/malikania/db/database.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/*
- * database.cpp -- connection to postgresql database
- *
- * Copyright (c) 2013-2018 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 <cassert>
-#include <sstream>
-
-#include "database.hpp"
-
-namespace mlk::db {
-
-namespace {
-
-struct strify {
-	template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
-	auto operator()(T i) const -> std::string
-	{
-		return std::to_string(i);
-	}
-
-	auto operator()(bool v) const -> std::string
-	{
-		return v ? "t" : "f";
-	}
-
-	auto operator()(std::string s) const -> std::string
-	{
-		return s;
-	}
-};
-
-std::unique_ptr<PGconn, void (*)(PGconn*)> connection{nullptr, nullptr};
-
-auto run(const std::string& sql, std::vector<arg> args = {}) -> result
-{
-	std::vector<const char*> list;
-
-	/*
-	 * PQexecParams requires an array of C strings, convert all arguments in the
-	 * args vector to string and then keep the C string value into a temporary
-	 * array.
-	 */
-	for (auto& arg : args) {
-		arg = std::visit(strify(), arg);
-		list.push_back(std::get<std::string>(arg).c_str());
-	}
-
-	return {
-		PQexecParams(connection.get(), sql.c_str(), list.size(), nullptr,
-			list.data(), nullptr, nullptr, 0
-		),
-		PQclear
-	};
-}
-
-} // !namespace
-
-void open(const std::string& host,
-          const std::string& port,
-          const std::string& user,
-          const std::string& database,
-          const std::string& password)
-{
-	assert(!connection);
-
-	std::ostringstream oss;
-
-	if (!host.empty())
-		oss << "host=" << host << " ";
-	if (!port.empty())
-		oss << "port=" << port << " ";
-	if (!user.empty())
-		oss << "user=" << user << " ";
-	if (!database.empty())
-		oss << "dbname=" << database << " ";
-	if (!password.empty())
-		oss << "password=" << password;
-
-	connection = { PQconnectdb(oss.str().c_str()), PQfinish };
-
-	if (PQstatus(connection.get()) != CONNECTION_OK)
-		throw std::runtime_error(PQerrorMessage(connection.get()));
-}
-
-const std::string init_account(
-	"CREATE TABLE IF NOT EXISTS account("
-	"  id SERIAL,"
-	"  login TEXT NOT NULL,"
-	"  password TEXT NOT NULL,"
-	"  firstname TEXT,"
-	"  lastname TEXT,"
-	"  email TEXT,"
-	"  PRIMARY KEY(id)"
-	")"
-);
-
-const std::string init_character(
-	"CREATE TABLE IF NOT EXISTS character("
-	"  id SERIAL,"
-	"  account_id INTEGER NOT NULL,"
-	"  nickname TEXT NOT NULL,"
-	"  type TEXT NOT NULL,"
-	"  hp_level SMALLINT NOT NULL DEFAULT 1,"
-	"  hp_factor SMALLINT NOT NULL,"
-	"  hp_exp INT NOT NULL,"
-	"  force_level SMALLINT NOT NULL DEFAULT 1,"
-	"  force_factor SMALLINT NOT NULL,"
-	"  force_exp INT NOT NULL,"
-	"  defense_level SMALLINT NOT NULL DEFAULT 1,"
-	"  defense_factor SMALLINT NOT NULL,"
-	"  defense_exp INT NOT NULL,"
-	"  agility_level SMALLINT NOT NULL DEFAULT 1,"
-	"  agility_factor SMALLINT NOT NULL,"
-	"  agility_exp INT NOT NULL,"
-	"  luck_level SMALLINT NOT NULL DEFAULT 1,"
-	"  luck_factor SMALLINT NOT NULL,"
-	"  luck_exp INT NOT NULL,"
-	"  PRIMARY KEY(id),"
-	"  FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE"
-	")"
-);
-
-const std::string init_spell(
-	"CREATE TABLE IF NOT EXISTS spell("
-	"  id SERIAL,"
-	"  character_id INTEGER NOT NULL,"
-	"  type TEXT NOT NULL,"
-	"  level SMALLINT NOT NULL,"
-	"  PRIMARY KEY(id),"
-	"  FOREIGN KEY (character_id) REFERENCES character(id) ON DELETE CASCADE"
-	")"
-);
-
-void init()
-{
-	assert(connection);
-
-	exec(init_account);
-	exec(init_character);
-	exec(init_spell);
-}
-
-auto select(const std::string& sql, const std::vector<arg>& args) -> result
-{
-	assert(connection);
-
-	auto result = run(sql, args);
-
-	switch (PQresultStatus(result.get())) {
-	case PGRES_COMMAND_OK:
-	case PGRES_TUPLES_OK:
-		break;
-	default:
-		throw std::runtime_error(PQerrorMessage(connection.get()));
-	}
-
-	return result;
-}
-
-void exec(const std::string& sql, const std::vector<arg>& args)
-{
-	assert(connection);
-
-	switch (const auto result = run(sql, args); PQresultStatus(result.get())) {
-	case PGRES_COMMAND_OK:
-		break;
-	default:
-		throw std::runtime_error(PQerrorMessage(connection.get()));
-	}
-}
-
-transaction::transaction(rollback fn)
-	: rollback_(std::move(fn))
-{
-	assert(rollback_);
-
-	exec("BEGIN");
-}
-
-transaction::~transaction()
-{
-	if (!commit_) {
-		rollback_();
-		exec("ROLLBACK");
-	}
-}
-
-void transaction::commit()
-{
-	if (!commit_) {
-		exec("COMMIT");
-		commit_ = true;
-	}
-}
-
-} // !db::mlk
--- a/libdb/malikania/db/database.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-/*
- * database.hpp -- connection to postgresql database
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_DB_DATABASE_HPP
-#define MALIKANIA_DB_DATABASE_HPP
-
-/**
- * \file database.hpp
- * \brief Abstract database interface.
- */
-
-#include <cstdint>
-#include <functional>
-#include <memory>
-#include <stdexcept>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include <libpq-fe.h>
-
-namespace mlk::db {
-
-/**
- * \brief Statement argument.
- *
- * When using prepared statement, you may pass any data you like using this
- * variant.
- */
-using arg = std::variant<
-	bool,
-	double,
-	std::int8_t,
-	std::uint8_t,
-	std::int16_t,
-	std::uint16_t,
-	std::int32_t,
-	std::uint32_t,
-	std::int64_t,
-	std::uint64_t,
-	std::string
->;
-
-/**
- * \brief Convenient result type.
- *
- * This unique pointer holds the PostgreSQL result and free it automatically on
- * out of scope.
- */
-using result = std::unique_ptr<PGresult, void (*)(PGresult*)>;
-
-/**
- * Open the connection to the database.
- *
- * All arguments are optional.
- *
- * \pre connection must be closed
- * \param host the hostname
- * \param port the port number
- * \param user the user name
- * \param database the database name
- * \param password the user password
- * \throw std::runtime_error on errors
- */
-void open(const std::string& host,
-          const std::string& port,
-          const std::string& user,
-          const std::string& database,
-          const std::string& password);
-
-/**
- * Unconditionally init the database.
- *
- * \throw std::runtime_error on errors
- */
-void init();
-
-/**
- * Execute a SELECT statement.
- *
- * \param sql the SQL statement
- * \param args the optional arguments to bind
- * \return the result value
- * \throw std::runtime_error on errors
- */
-auto select(const std::string& sql, const std::vector<arg>& args = {}) -> result;
-
-/**
- * Execute an UPDATE statement.
- *
- * \param sql the SQL statement
- * \param args the optional arguments to bind
- * \throw std::runtime_error on errors
- */
-void exec(const std::string& sql, const std::vector<arg>& args = {});
-
-class transaction {
-public:
-	/**
-	 * \brief Rollback function.
-	 */
-	using rollback = std::function<void ()>;
-
-private:
-	rollback rollback_;
-	bool commit_{false};
-
-public:
-	/**
-	 * Start a transaction.
-	 *
-	 * If there is already a transaction, the returned object is no-op.
-	 *
-	 * \param rollback the rollback function
-	 * \return the transaction object
-	 */
-	transaction(rollback fn);
-
-	/**
-	 * Rollback the transaction if commit() has not been called.
-	 */
-	~transaction();
-
-	/**
-	 * Commit the transaction.
-	 *
-	 * Does nothing if already committed.
-	 *
-	 * \throw std::exception on errors
-	 */
-	void commit();
-};
-
-} // !mlk::db
-
-#endif // !MALIKANIA_DB_DATABASE_HPP
--- a/libdb/malikania/db/model.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,120 +0,0 @@
-/*
- * model.hpp -- abstract database object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_DB_MODEL_HPP
-#define MALIKANIA_DB_MODEL_HPP
-
-/**
- * \file model.hpp
- * \brief Abstract database object.
- */
-
-#include <cstdint>
-
-namespace mlk::db {
-
-/**
- * \brief Abstract database object.
- */
-class model {
-public:
-	/**
-	 * Id type.
-	 */
-	using id_type = std::uint64_t;
-
-private:
-	model(const model&) = delete;
-	model& operator=(const model&) = delete;
-
-protected:
-	/**
-	 * Object id.
-	 */
-	id_type id_{0U};
-
-public:
-	/**
-	 * Default constructor.
-	 */
-	model() = default;
-
-	/**
-	 * Move constructor.
-	 *
-	 * \param other the original value (id resets to 0)
-	 */
-	inline model(model&& other) noexcept
-		: id_(other.id_)
-	{
-		other.id_ = 0U;
-	}
-
-	/**
-	 * Virtual destructor defaulted.
-	 */
-	virtual ~model() noexcept = default;
-
-	/**
-	 * Get the id.
-	 *
-	 * \return the id
-	 */
-	auto get_id() const noexcept -> id_type
-	{
-		return id_;
-	}
-
-	/**
-	 * Tells if the object is not persistent.
-	 *
-	 * \return true if object was never saved
-	 */
-	auto is_draft() const noexcept -> bool
-	{
-		return id_ == 0U;
-	}
-
-	/**
-	 * Tells if the object is present in database
-	 *
-	 * \return true if object is saved
-	 */
-	auto is_published() const noexcept -> bool
-	{
-		return id_ > 0U;
-	}
-
-	/**
-	 * Move operator.
-	 *
-	 * \param other the original value (id resets to 0)
-	 * \return *this
-	 */
-	auto operator=(model&& other) noexcept -> model&
-	{
-		id_ = other.id_;
-		other.id_ = 0U;
-
-		return *this;
-	}
-};
-
-} // !mlk::db
-
-#endif // !MALIKANIA_DB_MODEL_HPP
--- a/libdb/malikania/db/spell.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * spell.cpp -- database spell object
- *
- * Copyright (c) 2013-2018 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 <cassert>
-
-#include "character.hpp"
-#include "spell.hpp"
-
-namespace mlk::db {
-
-auto spell::load(const result& result, int row) -> spell
-{
-	spell s(PQgetvalue(result.get(), row, 2));
-
-	s.id_ = std::stoi(PQgetvalue(result.get(), row, 0));
-	s.level_ = std::stoi(PQgetvalue(result.get(), row, 3));
-
-	return s;
-}
-
-auto spell::load(const character& parent) -> std::vector<spell>
-{
-	static const std::string sql(
-		"SELECT *"
-		"  FROM spell"
-		" WHERE id = $1"
-	);
-
-	const auto r = select(sql, { parent.get_id() });
-
-	if (PQntuples(r.get()) == 0)
-		throw std::runtime_error("failed to load spells");
-
-	std::vector<spell> spells;
-
-	for (int i = 0; i < PQntuples(r.get()); ++i)
-		spells.push_back(load(r, i));
-
-	return spells;
-}
-
-void spell::clear()
-{
-	id_ = 0U;
-}
-
-void spell::unpublish()
-{
-	static const std::string sql(
-		"DELETE"
-		"  FROM spell"
-		" WHERE id = $1"
-	);
-
-	exec(sql, { id_ });
-	clear();
-}
-
-void spell::publish(character& parent)
-{
-	static const std::string sql(
-		"INSERT INTO spell("
-		"  character_id,"
-		"  type,"
-		"  level"
-		") "
-		"VALUES ($1, $2, $3) "
-		"RETURNING id"
-	);
-
-	const auto r = select(sql, { parent.get_id(), type_, level_ });
-
-	if (PQntuples(r.get()) == 0)
-		throw std::runtime_error("failed to save spell");
-
-	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
-}
-
-spell::spell(std::string type) noexcept
-	: type_(std::move(type))
-{
-	assert(!type_.empty());
-}
-
-auto spell::get_type() const noexcept -> const std::string&
-{
-	return type_;
-}
-
-auto spell::get_level() const noexcept -> std::uint8_t
-{
-	return level_;
-}
-
-void spell::set_level(std::uint8_t level)
-{
-	static const std::string sql(
-		"UPDATE spell"
-		"   SET level = $1"
-		" WHERE id = $2"
-	);
-
-	if (is_published() && level_ != level)
-		db::exec(sql, { level_, id_ });
-
-	level_ = level;
-}
-
-} // !mlk::db
--- a/libdb/malikania/db/spell.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * spell.hpp -- database spell object
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_DB_SPELL_HPP
-#define MALIKANIA_DB_SPELL_HPP
-
-/**
- * \file spell.hpp
- * \brief Database spell object.
- */
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include "database.hpp"
-#include "model.hpp"
-
-namespace mlk::db {
-
-class character;
-
-/**
- * \brief Describe a spell.
- */
-class spell : public model {
-private:
-	friend class character;
-
-	std::string type_;
-	std::uint8_t level_{1U};
-
-	static auto load(const result&, int) -> spell;
-	static auto load(const character&) -> std::vector<spell>;
-
-	void clear();
-	void unpublish();
-	void publish(character&);
-
-public:
-	/**
-	 * Constructor.
-	 *
-	 * \pre !type.empty()
-	 * \param type the type name
-	 */
-	spell(std::string type) noexcept;
-
-	/**
-	 * Get the type.
-	 *
-	 * \return the type
-	 */
-	auto get_type() const noexcept -> const std::string&;
-
-	/**
-	 * Get the level.
-	 *
-	 * \return the level
-	 */
-	auto get_level() const noexcept -> std::uint8_t;
-
-	/**
-	 * Set the spell level.
-	 *
-	 * \param level the level
-	 */
-	void set_level(std::uint8_t level);
-};
-
-} // !mlk::db
-
-#endif // !MALIKANIA_DB_SPELL_HPP
--- a/libmlk-client-js-test/malikania/client/js/test/js_api_fixture.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/libmlk-client-js-test/malikania/client/js/test/js_api_fixture.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -26,7 +26,7 @@
 
 #include <malikania/client/color.hpp>
 
-#include <malikania/js_color.hpp>
+#include <malikania/client/js/color_js_api.hpp>
 
 #include <malikania/js/test/js_api_fixture.hpp>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,52 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk-client-js)
+
+set(
+	HEADERS
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/animation_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/animator_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/color_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/font_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/image_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/loader_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/sprite_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/window_js_api.hpp
+)
+
+set(
+	SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/animation_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/animator_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/color_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/font_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/image_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/loader_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/sprite_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/js/window_js_api.cpp
+)
+
+malikania_define_library(
+	PROJECT libmlk-client-js
+	TARGET libmlk-client-js
+	SOURCES ${HEADERS} ${SOURCES}
+	LIBRARIES libmlk-client libmlk-common-js
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/animation_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,100 @@
+/*
+ * animation_js_api.cpp -- animation description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/client/animation.hpp>
+#include <malikania/client/loader.hpp>
+
+#include "animation_js_api.hpp"
+#include "loader_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string_view signature("\xff""\xff""Malikania.Animation.self");
+
+auto Animation_constructor(duk_context* ctx) -> duk_ret_t
+{
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "animation must be new-constructed");
+
+	try {
+		auto anim = mlk::js::duk::require<client::loader>(ctx, 0).load_animation(mlk::js::duk::require<std::string_view>(ctx, 0));
+
+		duk_push_this(ctx);
+		duk_push_pointer(ctx, new animation(std::move(anim)));
+		duk_put_prop_string(ctx, -2, signature.data());
+		duk_pop(ctx);
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Animation_destructor(duk_context* ctx) -> duk_ret_t
+{
+	duk_get_prop_string(ctx, 0, signature.data());
+	delete static_cast<animation*>(duk_to_pointer(ctx, -1));
+	duk_pop(ctx);
+	duk_del_prop_string(ctx, 0, signature.data());
+
+	return 0;
+}
+
+} // !namespace
+
+void load_animation_api(duk_context* ctx)
+{
+	assert(ctx);
+
+	mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Animation_constructor, 1);
+	duk_push_object(ctx);
+	duk_push_c_function(ctx, Animation_destructor, 1);
+	duk_set_finalizer(ctx, -2);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Animation");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
+
+namespace mlk::js::duk {
+
+auto type_traits<mlk::client::animation>::require(duk_context* ctx, duk_idx_t index) -> mlk::client::animation&
+{
+	assert(ctx);
+
+	duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, mlk::client::js::signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Animation object");
+
+	return *static_cast<mlk::client::animation*>(ptr);
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/animation_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,64 @@
+/*
+ * animation_js_api.hpp -- animation description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_ANIMATION_HPP
+#define MALIKANIA_CLIENT_JS_ANIMATION_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+struct animation;
+
+namespace js {
+
+/**
+ * Load Malikania.Animation API into the context.
+ *
+ * \warning you need to put a mlk::client::loader before use
+ * \param ctx the context
+ */
+void load_animation_api(duk_context* ctx);
+
+} // !js
+
+} // !client
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+template <>
+struct type_traits<client::animation> {
+	/**
+	 * Get an animation object at the given index or raise a Javascript
+	 * error.
+	 *
+	 * \pre ctx != nullptr
+	 * \param ctx the context
+	 * \param index the value index
+	 * \return the Animation object
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> client::animation&;
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_JS_ANIMATION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/animator_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,121 @@
+/*
+ * animator_js_api.cpp -- animation drawing object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+#include <string>
+
+#include <malikania/client/animator.hpp>
+
+#include <malikania/js/point_js_api.hpp>
+
+#include "animation_js_api.hpp"
+#include "animator_js_api.hpp"
+#include "window_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string_view signature("\xff""\xff""Malikania.Animator.self");
+
+auto self(duk_context* ctx) -> animator&
+{
+	mlk::js::duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Animator object");
+
+	return *static_cast<client::animator*>(ptr);
+}
+
+auto Animator_constructor(duk_context* ctx) -> duk_ret_t
+{
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "Animator must be new-constructed");
+
+	duk_push_this(ctx);
+	duk_push_pointer(ctx, new animator(mlk::js::duk::require<animation>(ctx, 0)));
+	duk_put_prop_string(ctx, -2, signature.data());
+
+	// Be sure animation get not collected before.
+	duk_dup(ctx, 0);
+	duk_put_prop_string(ctx, -2, "\xff""\xff""animation");
+	duk_pop(ctx);
+
+	return 0;
+}
+
+auto Animator_destructor(duk_context* ctx) -> duk_ret_t
+{
+	duk_get_prop_string(ctx, 0, signature.data());
+	delete static_cast<animator*>(duk_to_pointer(ctx, -1));
+	duk_pop(ctx);
+	duk_del_prop_string(ctx, 0, signature.data());
+
+	return 0;
+}
+
+auto Animator_draw(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).draw(mlk::js::duk::require<window>(ctx, 0), mlk::js::duk::get<point>(ctx, 1));
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Animator_update(duk_context* ctx) -> duk_ret_t
+{
+	self(ctx).update();
+
+	return 0;
+}
+
+const duk_function_list_entry methods[] = {
+	{ "draw",       Animator_draw,          2 },
+	{ "update",     Animator_update,        0 },
+	{ nullptr,      nullptr,                0 }
+};
+
+} // !namespace
+
+void load_animator_api(duk_context* ctx)
+{
+	assert(ctx);
+
+	mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Animator_constructor, 1);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_push_c_function(ctx, Animator_destructor, 1);
+	duk_set_finalizer(ctx, -2);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Animator");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/animator_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,35 @@
+/*
+ * animator_js_api.hpp -- animation drawing object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_ANIMATOR_HPP
+#define MALIKANIA_CLIENT_JS_ANIMATOR_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk::client::js {
+
+/**
+ * Load Malikania.ElapsedTimer API into the context.
+ *
+ * \param ctx the context
+ */
+void load_animator_api(duk_context* ctx);
+
+} // !mlk::client:;js
+
+#endif // !MALIKANIA_CLIENT_JS_ANIMATOR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/color_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,193 @@
+/*
+ * color_js_api.cpp -- color description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/client/color.hpp>
+
+#include "color_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+auto parse_string(duk_context* ctx, duk_idx_t index, bool required) -> color
+{
+	assert(duk_is_string(ctx, index));
+
+	color ret;
+
+	try {
+		ret = color::from_name(duk_get_string(ctx, index));
+	} catch (const std::exception &ex) {
+		if (required)
+			duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return ret;
+}
+
+auto parse_object(duk_context* ctx, duk_idx_t index, bool required) -> color
+{
+	assert(duk_is_object(ctx, index));
+
+	duk_get_prop_string(ctx, index, "red");
+	const auto red = mlk::js::duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "green");
+	const auto green = mlk::js::duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "blue");
+	const auto blue = mlk::js::duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "alpha");
+	auto alpha = mlk::js::duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+
+        if (required) {
+                if (!red)
+                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'red' property");
+                if (!green)
+                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'green' property");
+                if (!blue)
+                        duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'blue' property");
+        }
+
+        if (!alpha)
+                alpha = 255;
+
+	return {
+                static_cast<std::uint8_t>(*red),
+                static_cast<std::uint8_t>(*green),
+                static_cast<std::uint8_t>(*blue),
+                static_cast<std::uint8_t>(*alpha)
+        };
+}
+
+auto parse(duk_context* ctx, duk_idx_t index, bool required, color ret = {}) -> color
+{
+	switch (duk_get_type(ctx, index)) {
+	case DUK_TYPE_STRING:
+		ret = parse_string(ctx, index, required);
+		break;
+	case DUK_TYPE_OBJECT:
+		ret = parse_object(ctx, index, required);
+		break;
+	default:
+		if (required)
+			duk_error(ctx, DUK_ERR_TYPE_ERROR, "Color (string, object) expected");
+
+		break;
+	}
+
+	return ret;
+}
+
+auto Color_constructor(duk_context* ctx) -> duk_ret_t
+{
+	color obj;
+
+	/*
+	 * The constructor allows an additional signature that takes 4 number
+	 * arguments, otherwise use the literal parsing functions.
+	 */
+	if (duk_get_top(ctx) >= 3) {
+		// Alpha is optional.
+                obj.red = mlk::js::duk::require<unsigned>(ctx, 0);
+                obj.green = mlk::js::duk::require<unsigned>(ctx, 1);
+                obj.blue = mlk::js::duk::require<unsigned>(ctx, 2);
+		obj.alpha = duk_is_number(ctx, 3) ? mlk::js::duk::get<unsigned>(ctx, 3) : 255;
+	} else if (duk_get_top(ctx) == 1)
+		obj = mlk::js::duk::require<color>(ctx, 0);
+
+	duk_ret_t ret;
+
+	// Allow both constructor and non constructor calls.
+	if (duk_is_constructor_call(ctx)) {
+		duk_push_this(ctx);
+                mlk::js::duk::put(ctx, obj);
+		duk_pop(ctx);
+		ret = 0;
+	} else {
+                mlk::js::duk::push(ctx, obj);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+} // !namespace
+
+void load_color_api(duk_context* ctx)
+{
+        mlk::js::duk::stack_guard sa(ctx, 0);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Color_constructor, DUK_VARARGS);
+	duk_put_prop_string(ctx, -2, "Color");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
+
+namespace mlk::js::duk {
+
+auto type_traits<mlk::client::color>::get(duk_context* ctx, duk_idx_t index) -> mlk::client::color
+{
+        return mlk::client::js::parse(ctx, index, false);
+}
+
+auto type_traits<mlk::client::color>::require(duk_context* ctx, duk_idx_t index) -> mlk::client::color
+{
+        return mlk::client::js::parse(ctx, index, true);
+}
+
+auto type_traits<mlk::client::color>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<mlk::client::color>
+{
+        try {
+                return mlk::client::js::parse(ctx, index, false);
+        } catch (...) {
+                return mlk::client::color();
+        }
+}
+
+void type_traits<mlk::client::color>::push(duk_context* ctx, const mlk::client::color& color)
+{
+        mlk::js::duk::stack_guard sa(ctx, 1);
+
+	duk_push_object(ctx);
+	put(ctx, color);
+}
+
+void type_traits<mlk::client::color>::put(duk_context* ctx, const mlk::client::color& color)
+{
+	assert(duk_is_object(ctx, -1));
+
+        mlk::js::duk::stack_guard sa(ctx, 0);
+
+        mlk::js::duk::push<unsigned>(ctx, color.red);
+	duk_put_prop_string(ctx, -2, "red");
+        mlk::js::duk::push<unsigned>(ctx, color.green);
+	duk_put_prop_string(ctx, -2, "green");
+        mlk::js::duk::push<unsigned>(ctx, color.blue);
+	duk_put_prop_string(ctx, -2, "blue");
+        mlk::js::duk::push<unsigned>(ctx, color.alpha);
+	duk_put_prop_string(ctx, -2, "alpha");
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/color_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,116 @@
+/*
+ * color_js_api.hpp -- color description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_COLOR_HPP
+#define MALIKANIA_CLIENT_JS_COLOR_HPP
+
+/**
+ * \file js_color.hpp
+ * \brief JavaScript binding for color.
+ *
+ * Colors can be created from plain JavaScript object.
+ *
+ * ````
+ * {
+ *   red: 0,
+ *   green: 255,
+ *   blue: 255,
+ *   alpha: 255
+ * }
+ * ````
+ *
+ * It can also takes strings like "#rrggbbaa" and SVG names.
+ */
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+struct color;
+
+namespace js {
+
+/**
+ * Load Malikania.ElapsedTimer API into the context.
+ *
+ * \param ctx the context
+ */
+void load_color_api(duk_context* ctx);
+
+} // !js
+
+} // !client
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+template <>
+struct type_traits<mlk::client::color> {
+        /**
+         * Get a color.
+         *
+         * May return a default value or a color with adjusted components.
+         *
+         * \param ctx the context
+         * \param index the index
+         */
+        static auto get(duk_context* ctx, duk_idx_t index) -> mlk::client::color;
+
+        /**
+         * Require a color.
+         *
+         * If the color has any invalid component, raise a JavaScript error.
+         *
+         * \param ctx the context
+         * \param index the index
+         */
+        static auto require(duk_context* ctx, duk_idx_t index) -> mlk::client::color;
+
+        /**
+         * Like get, but return the default value only if the value at the given
+         * index is not an object or not a string, otherwise, adjust invalid values.
+         *
+         * \param ctx the context
+         * \param index the index
+         */
+        static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<mlk::client::color>;
+
+        /**
+         * Push the color as object.
+         *
+         * \param ctx the context
+         * \param color the color
+         */
+        static void push(duk_context* ctx, const mlk::client::color& color);
+
+        /**
+         * Put the color properties into the object at the top of the stack.
+         *
+         * \pre the top value must be an object
+         * \param ctx the context
+         * \param color the color
+         */
+        static void put(duk_context* ctx, const mlk::client::color& color);
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_CLIENT_JS_COLOR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/font_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,125 @@
+/*
+ * font_js_api.cpp -- font object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 SOTWARE 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 <cassert>
+
+#include <malikania/size.hpp>
+
+#include <malikania/client/loader.hpp>
+#include <malikania/client/font.hpp>
+
+#include <malikania/js/size_js_api.hpp>
+
+#include "font_js_api.hpp"
+#include "loader_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string signature("\xff""\xff""Malikania.Font.self");
+
+auto self(duk_context* ctx) -> font&
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.c_str());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a font object");
+
+	return *static_cast<font*>(ptr);
+}
+
+auto Font_constructor(duk_context* ctx) -> duk_ret_t
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "font must be new-constructed");
+
+	try {
+		auto id = mlk::js::duk::require<std::string_view>(ctx, 0);
+		auto size = mlk::js::duk::require<unsigned>(ctx, 1);
+
+		duk_push_this(ctx);
+		duk_push_pointer(ctx, new font(mlk::js::duk::require<loader>(ctx, 0).load_font(id, size)));
+		duk_put_prop_string(ctx, -2, signature.c_str());
+		duk_pop(ctx);
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Font_prototype_clip(duk_context* ctx) -> duk_ret_t
+{
+	try {
+                mlk::js::duk::push(ctx, self(ctx).clip(mlk::js::duk::require<std::string>(ctx, 0)));
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 1;
+}
+
+const duk_function_list_entry methods[] = {
+	{ "clip",       Font_prototype_clip,    1 },
+	{ nullptr,      nullptr,                0 }
+};
+
+} // !namespace
+
+void load_font_api(duk_context* ctx)
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Font_constructor, 2);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Font");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
+
+namespace mlk::js::duk {
+
+auto type_traits<mlk::client::font>::require(duk_context* ctx, duk_idx_t index) -> mlk::client::font&
+{
+	assert(ctx);
+
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, mlk::client::js::signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Font object");
+
+	return *static_cast<mlk::client::font*>(ptr);
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/font_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,54 @@
+/*
+ * font_js_api.hpp -- font object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_FONT_HPP
+#define MALIKANIA_CLIENT_JS_FONT_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class font;
+
+namespace js {
+
+/**
+ * Load Malikania.Font API into the context.
+ *
+ * \param ctx the context
+ */
+void load_font_api(duk_context* ctx);
+
+} // !js
+
+} // !client
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+template <>
+struct type_traits<mlk::client::font> {
+        static auto require(duk_context* ctx, duk_idx_t index) -> mlk::client::font&;
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_CLIENT_JS_FONT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/image_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,125 @@
+/*
+ * image_js_api.cpp -- image object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/client/image.hpp>
+#include <malikania/client/loader.hpp>
+#include <malikania/client/window.hpp>
+
+#include <malikania/js/point_js_api.hpp>
+#include <malikania/js/rectangle_js_api.hpp>
+#include <malikania/js/size_js_api.hpp>
+
+#include "image_js_api.hpp"
+#include "loader_js_api.hpp"
+#include "window_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string_view signature("\xff""\xff""Malikania.Image.self");
+
+auto self(duk_context* ctx) -> image&
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Image object");
+
+	return *static_cast<image*>(ptr);
+}
+
+auto Image_property_get_size(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		mlk::js::duk::push(ctx, self(ctx).get_size());
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 1;
+}
+
+auto Image_prototype_draw(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		auto& image = self(ctx);
+		auto& win= mlk::js::duk::require<window>(ctx, 0);
+
+		if (duk_get_top(ctx) == 2)
+			image.draw(win, mlk::js::duk::get<point>(ctx, 1));
+		else if (duk_get_top(ctx) == 3)
+			image.draw(win, mlk::js::duk::get<rectangle>(ctx, 1), mlk::js::duk::get<rectangle>(ctx, 2));
+		else
+			duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid number of arguments: #%d", duk_get_top(ctx));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Image_constructor(duk_context* ctx) -> duk_ret_t
+{
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "Image must be new-constructed");
+
+	try {
+		duk_push_this(ctx);
+		duk_push_pointer(ctx, new image(mlk::js::duk::require<loader>(ctx, 0)
+                        .load_image(mlk::js::duk::require<std::string_view>(ctx, 0))));
+		duk_put_prop_string(ctx, -2, signature.data());
+		duk_push_string(ctx, "size");
+		duk_push_c_function(ctx, Image_property_get_size, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+		duk_pop(ctx);
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+const duk_function_list_entry methods[] = {
+	{ "draw",       Image_prototype_draw,   DUK_VARARGS     },
+	{ nullptr,      nullptr,                0               }
+};
+
+} // !namespace
+
+void load_image_api(duk_context* ctx)
+{
+	mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Image_constructor, 1);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Image");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/image_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,35 @@
+/*
+ * image_js_api.hpp -- image object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_IMAGE_HPP
+#define MALIKANIA_CLIENT_JS_IMAGE_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk::client::js {
+
+/**
+ * Load Malikania.Image API into the context.
+ *
+ * \param ctx the context
+ */
+void load_image_api(duk_context* ctx);
+
+} // !mlk::client::js
+
+#endif // !MALIKANIA_CLIENT_JS_IMAGE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/loader_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,61 @@
+/*
+ * loader_js_api.cpp -- client resources loader (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/client/loader.hpp>
+
+#include <malikania/js/loader_js_api.hpp>
+
+#include "loader_js_api.hpp"
+
+namespace mlk::js::duk {
+
+namespace {
+
+const std::string_view variable("\xff""\xff""Malikania.ClientLoader");
+
+} // !namespace
+
+void type_traits<mlk::client::loader>::put(duk_context* ctx, mlk::client::loader& loader)
+{
+	assert(ctx);
+
+	// Also store as parent.
+        type_traits<mlk::loader>::put(ctx, loader);
+
+        duk::stack_guard sa(ctx);
+
+	duk_push_pointer(ctx, &loader);
+	duk_put_global_string(ctx, variable.data());
+}
+
+auto type_traits<mlk::client::loader>::require(duk_context* ctx, duk_idx_t) -> mlk::client::loader&
+{
+	assert(ctx);
+
+        duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, variable.data());
+	auto ptr = static_cast<mlk::client::loader*>(duk_to_pointer(ctx, -1));
+	duk_pop(ctx);
+
+	return *static_cast<mlk::client::loader*>(ptr);
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/loader_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,45 @@
+/*
+ * loader_js_api.hpp -- client resources loader (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_LOADER_HPP
+#define MALIKANIA_CLIENT_JS_LOADER_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class loader;
+
+} // !client
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+template <>
+struct type_traits<mlk::client::loader> {
+        static void put(duk_context* ctx, mlk::client::loader& loader);
+
+        static auto require(duk_context* ctx, duk_idx_t) -> mlk::client::loader&;
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_CLIENT_JS_LOADER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/sprite_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,160 @@
+/*
+ * sprite_js_api.cpp -- sprite object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <malikania/client/loader.hpp>
+#include <malikania/client/sprite.hpp>
+#include <malikania/client/window.hpp>
+
+#include <malikania/js/point_js_api.hpp>
+#include <malikania/js/size_js_api.hpp>
+
+#include "loader_js_api.hpp"
+#include "sprite_js_api.hpp"
+#include "window_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string signature("\xff""\xff""Malikania.Sprite.self");
+
+auto self(duk_context *ctx) -> sprite&
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.c_str());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Sprite object");
+
+	return *static_cast<sprite*>(ptr);
+}
+
+auto Sprite_property_get_cell(duk_context* ctx) -> duk_ret_t
+{
+	return mlk::js::duk::push(ctx, self(ctx).get_cell());
+}
+
+auto Sprite_property_get_columns(duk_context* ctx) -> duk_ret_t
+{
+	return mlk::js::duk::push(ctx, self(ctx).get_columns());
+}
+
+auto Sprite_property_get_margins(duk_context* ctx) -> duk_ret_t
+{
+	return mlk::js::duk::push(ctx, self(ctx).get_margin());
+}
+
+auto Sprite_property_get_rows(duk_context* ctx) -> duk_ret_t
+{
+	return mlk::js::duk::push(ctx, self(ctx).get_rows());
+}
+
+auto Sprite_property_get_space(duk_context *ctx) -> duk_ret_t
+{
+	return mlk::js::duk::push(ctx, self(ctx).get_space());
+}
+
+auto Sprite_constructor(duk_context *ctx) -> duk_ret_t
+{
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "Sprite must be new-constructed");
+
+	try {
+		auto sp = mlk::js::duk::require<loader>(ctx, 0).load_sprite(duk_require_string(ctx, 0));
+
+		duk_push_this(ctx);
+		duk_push_pointer(ctx, new sprite(std::move(sp)));
+		duk_put_prop_string(ctx, -2, signature.data());
+
+		// Cell.
+		duk_push_string(ctx, "cell");
+		duk_push_c_function(ctx, Sprite_property_get_cell, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+
+		// Columns.
+		duk_push_string(ctx, "columns");
+		duk_push_c_function(ctx, Sprite_property_get_columns, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+
+		// Margins.
+		duk_push_string(ctx, "margins");
+		duk_push_c_function(ctx, Sprite_property_get_margins, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+
+		// Rows.
+		duk_push_string(ctx, "rows");
+		duk_push_c_function(ctx, Sprite_property_get_rows, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+
+		// Space.
+		duk_push_string(ctx, "space");
+		duk_push_c_function(ctx, Sprite_property_get_space, 0);
+		duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER);
+
+		duk_pop(ctx);
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Sprite_prototype_draw(duk_context *ctx) -> duk_ret_t
+{
+	try {
+		auto& sprite = self(ctx);
+		auto& win = mlk::js::duk::require<window>(ctx, 0);
+		auto cell = mlk::js::duk::require<unsigned>(ctx, 1);
+		auto position = mlk::js::duk::require<point>(ctx, 2);
+
+		if (cell >= sprite.get_total())
+			duk_error(ctx, DUK_ERR_RANGE_ERROR, "cell %d is out of range", cell);
+
+		sprite.draw(win, cell, position);
+	} catch (const std::exception &ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+const duk_function_list_entry methods[] = {
+	{ "draw",       Sprite_prototype_draw,  3 },
+	{ nullptr,      nullptr,                0 }
+};
+
+} // !namespace
+
+void load_sprite_api(duk_context* ctx)
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Sprite_constructor, 1);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Sprite");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/sprite_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,35 @@
+/*
+ * sprite_js_api.hpp -- sprite object (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_SPRITE_HPP
+#define MALIKANIA_CLIENT_JS_SPRITE_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk::client::js {
+
+/**
+ * Load Malikania.Sprite API into the context.
+ *
+ * \param ctx the context
+ */
+void load_sprite_api(duk_context* ctx);
+
+} // !mlk::client::js
+
+#endif // !MALIKANIA_CLIENT_JS_SPRITE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/window_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,232 @@
+/*
+ * window_js_api.cpp -- window management (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/line.hpp>
+#include <malikania/rectangle.hpp>
+
+#include <malikania/client/color.hpp>
+#include <malikania/client/window.hpp>
+
+#include <malikania/js/line_js_api.hpp>
+#include <malikania/js/point_js_api.hpp>
+#include <malikania/js/rectangle_js_api.hpp>
+
+#include "color_js_api.hpp"
+#include "font_js_api.hpp"
+#include "window_js_api.hpp"
+
+namespace mlk::client::js {
+
+namespace {
+
+const std::string_view signature("\xff""\xff""Malikania.Window.self");
+const std::string_view prototype("\xff""\xff""Malikania.Window.prototype");
+
+auto self(duk_context* ctx) -> window&
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an Window object");
+
+	return *static_cast<window*>(ptr);
+}
+
+auto Window_constructor(duk_context* ctx) -> duk_ret_t
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	if (!duk_is_constructor_call(ctx))
+		duk_error(ctx, DUK_ERR_ERROR, "window must be new-constructed");
+
+	// TODO: add parameters.
+	try {
+		duk_push_this(ctx);
+		duk_push_pointer(ctx, new window);
+		duk_put_prop_string(ctx, -2, signature.data());
+		duk_pop(ctx);
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_clear(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).clear();
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_present(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).present();
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_drawingColor(duk_context* ctx) -> duk_ret_t
+{
+	try {
+                mlk::js::duk::push(ctx, self(ctx).get_drawing_color());
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 1;
+}
+
+auto Window_prototype_drawLine(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).draw_line(mlk::js::duk::get<line>(ctx, 0));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_drawPoint(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).draw_point(mlk::js::duk::get<point>(ctx, 0));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_drawRectangle(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).draw_rectangle(mlk::js::duk::get<rectangle>(ctx, 0));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_drawText(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		auto& win = self(ctx);
+		auto text = mlk::js::duk::require<std::string>(ctx, 0);
+		auto& fnt = mlk::js::duk::require<font>(ctx, 1);
+		auto rect = mlk::js::duk::get<rectangle>(ctx, 2);
+
+		if (!rect.is_null())
+			win.draw_text(text, fnt, rect);
+		else
+			win.draw_text(text, fnt, point{rect.x, rect.y});
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_fillRectangle(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).fill_rectangle(mlk::js::duk::get<rectangle>(ctx, 0));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+auto Window_prototype_setDrawingColor(duk_context* ctx) -> duk_ret_t
+{
+	try {
+		self(ctx).set_drawing_color(mlk::js::duk::get<color>(ctx, 0));
+	} catch (const std::exception& ex) {
+		duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+	}
+
+	return 0;
+}
+
+const duk_function_list_entry methods[] = {
+	{ "clear",              Window_prototype_clear,                 0 },
+	{ "drawingColor",       Window_prototype_drawingColor,          0 },
+	{ "drawLine",           Window_prototype_drawLine,              1 },
+	{ "drawPoint",          Window_prototype_drawPoint,             1 },
+	{ "drawRectangle",      Window_prototype_drawRectangle,         1 },
+	{ "drawText",           Window_prototype_drawText,              3 },
+	{ "fillRectangle",      Window_prototype_fillRectangle,         1 },
+	{ "present",            Window_prototype_present,               0 },
+	{ "setDrawingColor",    Window_prototype_setDrawingColor,       1 },
+	{ nullptr,              nullptr,                                0 }
+};
+
+} // !namespace
+
+void load_window_api(duk_context* ctx)
+{
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Window_constructor, 0);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "Window");
+	duk_pop(ctx);
+}
+
+} // !mlk::client::js
+
+namespace mlk::js::duk {
+
+auto type_traits<mlk::client::window>::require(duk_context* ctx, duk_idx_t index) -> mlk::client::window&
+{
+	assert(ctx);
+
+        mlk::js::duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, mlk::client::js::signature.data());
+	auto ptr = duk_to_pointer(ctx, -1);
+	duk_pop(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a Window object");
+
+	return *static_cast<mlk::client::window*>(ptr);
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client-js/malikania/client/js/window_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,54 @@
+/*
+ * window_js_api.hpp -- window management (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_JS_WINDOW_HPP
+#define MALIKANIA_CLIENT_JS_WINDOW_HPP
+
+#include <malikania/js/duk.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class window;
+
+namespace js {
+
+/**
+ * Load Malikania.Window API into the context.
+ *
+ * \param ctx the context
+ */
+void load_window_api(duk_context* ctx);
+
+} // !client
+
+} // !js
+
+} // !mlk
+
+namespace mlk::js::duk {
+
+template <>
+struct type_traits<client::window> {
+        static auto require(duk_context* ctx, duk_idx_t index) -> client::window&;
+};
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_CLIENT_JS_WINDOW_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,94 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk-client)
+
+find_package(Boost REQUIRED COMPONENTS timer)
+
+set(
+	HEADERS
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animation.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animator.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/button.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/client.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/color.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/connection.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/dispatcher.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/font.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/image.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/key.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/label.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/loader.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/mouse.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sdl_util.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sprite.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/lobby_state.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/login_state.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/map_state.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/theme.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/widget.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/window.hpp
+)
+
+set(
+	SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/animator.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/button.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/client.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/color.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/connection.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/font.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/image.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/label.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/loader.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sdl_util.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/sprite.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/lobby_state.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/login_state.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/state/map_state.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/theme.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/widget.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/client/window.cpp
+)
+
+find_package(OpenSSL REQUIRED)
+find_package(SDL2 REQUIRED)
+find_package(SDL2_image REQUIRED)
+find_package(SDL2_ttf REQUIRED)
+
+malikania_define_library(
+	PROJECT libmlk-client
+	TARGET libmlk-client
+	SOURCES ${HEADERS} ${SOURCES}
+	ASSETS ${libmlk-client_SOURCE_DIR}/assets/dejavu_sans.ttf
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+		${SDL2_INCLUDE_DIRS}
+		${SDL2_IMAGE_INCLUDE_DIRS}
+		${SDL2_TTF_INCLUDE_DIRS}
+	LIBRARIES
+		${SDL2_LIBRARIES}
+		${SDL2_IMAGE_LIBRARIES}
+		${SDL2_TTF_LIBRARIES}
+		libmlk
+		Boost::boost
+		Boost::timer
+		OpenSSL::SSL
+		OpenSSL::Crypto
+)
Binary file libmlk-client/assets/dejavu_sans.ttf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/animation.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,64 @@
+/*
+ * animation.hpp -- animation description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_ANIMATION_HPP
+#define MALIKANIA_CLIENT_ANIMATION_HPP
+
+/**
+ * \file animation.hpp
+ * \brief Describe an animation.
+ */
+
+#include <cstdint>
+#include <vector>
+#include <utility>
+
+#include "sprite.hpp"
+
+namespace mlk::client {
+
+/**
+ * \brief Animation description.
+ *
+ * An animation is composed of a sprite and a set of frames. For example, if
+ * your sprite has several images, you can select only the one you want by
+ * specifying the index in each frame.
+ *
+ * \see animator
+ */
+struct animation {
+	/**
+	 * \brief Delay between each frames.
+	 */
+	struct frame {
+		std::uint64_t cell{0U};                 //!< sprite cell index
+		std::uint64_t delay{50U};               //!< delay in milliseconds
+	};
+
+	/**
+	 * \brief List of frames.
+	 */
+	using frame_list = std::vector<frame>;
+
+	mlk::client::sprite sprite;                     //!< sprite used
+	mlk::client::animation::frame_list frames;      //!< list of frames
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_ANIMATION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/animator.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,55 @@
+/*
+ * animator.cpp -- animation drawing object
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include "animation.hpp"
+#include "animator.hpp"
+
+namespace mlk::client {
+
+animator::animator(animation& animation) noexcept
+	 : animation_(animation)
+	 , current_(animation_.frames.begin())
+{
+}
+
+void animator::reset() noexcept
+{
+	timer_.start();
+	current_ = animation_.frames.begin();
+}
+
+void animator::update() noexcept
+{
+	if (current_ == animation_.frames.end())
+		return;
+
+	if (timer_.elapsed().wall / 1000000LL >= static_cast<long long>(current_->delay)) {
+	         current_ ++;
+	         timer_.start();
+	}
+}
+
+void animator::draw(window& window, const point& point)
+{
+	if (current_ != animation_.frames.end())
+		animation_.sprite.draw(window, current_->cell, point);
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/animator.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,85 @@
+/*
+ * animator.hpp -- animation drawing object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_ANIMATOR_HPP
+#define MALIKANIA_CLIENT_ANIMATOR_HPP
+
+/**
+ * \file animator.hpp
+ * \brief Draw animations.
+ */
+
+#include <boost/timer/timer.hpp>
+
+#include "animation.hpp"
+
+namespace mlk {
+
+struct point;
+
+namespace client {
+
+class window;
+
+/**
+ * \brief Object for drawing animations.
+ *
+ * The animator contains an animation and a state.
+ */
+class animator {
+private:
+	boost::timer::cpu_timer timer_;
+	animation& animation_;
+	animation::frame_list::const_iterator current_;
+
+public:
+	/**
+	 * Construct an animator.
+	 *
+	 * \pre animation must not be null
+	 * \param animation the animation
+	 */
+	animator(animation& animation) noexcept;
+	
+	/**
+	 * Reset the animator to the beginning.
+	 */
+	void reset() noexcept;
+
+	/**
+	 * Update the animator state.
+	 *
+	 * This function should be called in the main loop to update the cell
+	 * to draw before calling draw().
+	 */
+	void update() noexcept;
+
+	/**
+	 * Draw the animation.
+	 *
+	 * \param window the window
+	 * \param position the position in the window
+	 */
+	void draw(window& window, const point& position);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_ANIMATOR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/button.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,54 @@
+/*
+ * button.cpp -- GUI button element
+ *
+ * Copyright (c) 2013-2018 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 "button.hpp"
+#include "theme.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+button::button(std::string text)
+	: text_(std::move(text))
+{
+}
+
+auto button::get_text() const noexcept -> const std::string&
+{
+	return text_;
+}
+
+void button::set_text(std::string text) noexcept
+{
+	text_ = std::move(text);
+}
+
+void button::handle_mouse_down(const mouse_click_event& ev)
+{
+	if (ev.button == mouse::left && on_press_)
+		on_press_();
+
+	// TODO: implement release.
+}
+
+void button::draw(window& w, const size& size)
+{
+	(void)w;
+	(void)size;
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/button.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,88 @@
+/*
+ * button.hpp -- GUI button element
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_BUTTON_HPP
+#define MALIKANIA_CLIENT_BUTTON_HPP
+
+/**
+ * \file button.hpp
+ * \brief GUI button element.
+ */
+
+#include <functional>
+#include <string>
+
+#include "widget.hpp"
+
+namespace mlk::client {
+
+/**
+ * \brief Basic button element.
+ */
+class button : public widget {
+public:
+	using on_press_func = std::function<void ()>;
+	using on_release_func = std::function<void ()>;
+
+private:
+	on_press_func on_press_;
+	on_release_func on_released_;
+
+	std::string text_;
+
+public:
+	/**
+	 * Default constructor with optional text.
+	 *
+	 * \param text the text
+	 */
+	button(std::string text = "");
+
+	/**
+	 * Get the button text.
+	 *
+	 * \return the text
+	 */
+	auto get_text() const noexcept -> const std::string&;
+
+	/**
+	 * Set the button text.
+	 *
+	 * \param text the text
+	 */
+	void set_text(std::string text) noexcept;
+
+	/**
+	 * \copydoc widget::handle_mouse_down
+	 */
+	void handle_mouse_down(const mouse_click_event& ev) override;
+
+	/**
+	 * Bring back other functions.
+	 */
+	using widget::draw;
+
+	/**
+	 * \copydoc widget::draw
+	 */
+	void draw(window& w, const size& size) override;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_BUTTON_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/client.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,176 @@
+/*
+ * client.cpp -- main client game clas
+ *
+ * Copyright (c) 2013-2018 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 <iostream>
+#include <thread>
+
+#include "client.hpp"
+#include "connection.hpp"
+#include "state.hpp"
+#include "window.hpp"
+
+namespace mlk {
+
+namespace client {
+
+client::client(boost::asio::io_service& service,
+               mlk::client::connection& connection,
+               mlk::client::window& window) noexcept
+	: service_(service)
+	, connection_(connection)
+	, window_(window)
+{
+}
+
+client::~client() noexcept = default;
+
+void client::recv()
+{
+	// Network thread space.
+	connection_.recv([this] (auto code, auto message) {
+		if (!code)
+			recv();
+
+		std::lock_guard<std::mutex> lock(nmutex_);
+
+		nqueue_.push_back(std::bind(&client::handle_message, this, code, message));
+	});
+}
+
+void client::connect(std::string host, std::uint16_t port)
+{
+	service_.post([this, host, port] () {
+		connection_.connect(host, port, [this] (auto code) {
+			// Start receiveing on success.
+			if (!code)
+				recv();
+
+			// Push the result to the main loop queue.
+			std::lock_guard<std::mutex> lock(nmutex_);
+
+			nqueue_.push_back(std::bind(&client::handle_connect, this, code));
+		});
+	});
+}
+
+void client::send(nlohmann::json message)
+{
+	service_.post([this, message] () {
+		// TODO: error checking.
+		connection_.send(std::move(message), nullptr);
+	});
+}
+
+void client::set_state(std::unique_ptr<state> state)
+{
+	state_next_ = std::move(state);
+}
+
+void client::handle_connect(const std::error_code& code)
+{
+	if (state_)
+		state_->handle_connect(code);
+}
+
+void client::handle_message(const std::error_code& code, const nlohmann::json& msg)
+{
+	if (state_)
+		state_->handle_message(code, msg);
+}
+
+void client::handle_key_down(const key_event& ev)
+{
+	if (state_)
+		state_->handle_key_down(ev);
+}
+
+void client::handle_key_up(const key_event& ev)
+{
+	if (state_)
+		state_->handle_key_up(ev);
+}
+
+void client::handle_mouse_down(const mouse_click_event& ev)
+{
+	if (state_)
+		state_->handle_mouse_down(ev);
+}
+
+void client::handle_mouse_up(const mouse_click_event& ev)
+{
+	if (state_)
+		state_->handle_mouse_up(ev);
+}
+
+void client::handle_mouse_wheel(const mouse_wheel_event& ev)
+{
+	if (state_)
+		state_->handle_mouse_wheel(ev);
+}
+
+void client::handle_text(const text_event& ev)
+{
+	if (state_)
+		state_->handle_text(ev);
+}
+
+void client::handle_quit()
+{
+	window_.close();
+	service_.stop();
+}
+
+void client::run()
+{
+	std::thread t([this] () {
+		// TODO: change to an atomic member variable.
+		while (window_.is_open()) {
+			service_.reset();
+			service_.run();
+		}
+	});
+
+	while (window_.is_open()) {
+		// Network events.
+		{
+			std::lock_guard<std::mutex> lock(nmutex_);
+
+			for (auto& ev : nqueue_)
+				ev();
+
+			nqueue_.clear();
+		}
+
+		// State and input events.
+		if (state_next_)
+			state_ = std::move(state_next_);
+
+		window_.poll(*this);
+
+		if (state_) {
+			state_->update(*this);
+			state_->draw(*this);
+		}
+	}
+
+	t.join();
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/client.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,180 @@
+/*
+ * client.hpp -- main client game clas
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_CLIENT_HPP
+#define MALIKANIA_CLIENT_CLIENT_HPP
+
+/**
+ * \file client.hpp
+ * \brief Main client game class.
+ */
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <vector>
+#include <system_error>
+
+#include <boost/asio.hpp>
+
+#include <json.hpp>
+
+#include "dispatcher.hpp"
+
+namespace mlk {
+
+namespace client {
+
+class connection;
+class state;
+class window;
+
+/**
+ * \brief Main client game class
+ */
+class client : public dispatcher {
+private:
+	boost::asio::io_service& service_;
+
+	mlk::client::connection& connection_;
+	mlk::client::window& window_;
+
+	std::unique_ptr<state> state_;
+	std::unique_ptr<state> state_next_;
+	std::mutex nmutex_;
+	std::vector<std::function<void ()>> nqueue_;
+
+	void recv();
+
+public:
+	/**
+	 * Construct the client, no connection attempt will be made yet.
+	 *
+	 * \param service the I/O service
+	 * \param connection the connection object
+	 * \param window the window
+	 */
+	client(boost::asio::io_service& service,
+	       mlk::client::connection& connection,
+	       mlk::client::window& window) noexcept;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~client() noexcept;
+
+	/**
+	 * Get the window.
+	 *
+	 * \return the window
+	 */
+	inline const mlk::client::window& window() const noexcept
+	{
+		return window_;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the window
+	 */
+	inline mlk::client::window& window() noexcept
+	{
+		return window_;
+	}
+
+	/**
+	 * Connect to the server.
+	 *
+	 * This is an alias of connection_.connect(*this, host, port);
+	 *
+	 * \param host the hostname
+	 * \param port the port number
+	 */
+	void connect(std::string host, std::uint16_t port);
+
+	/**
+	 * Send a message.
+	 *
+	 * \param message the message
+	 */
+	void send(nlohmann::json message);
+
+	/**
+	 * Set the client state.
+	 *
+	 * \param state the state
+	 */
+	void set_state(std::unique_ptr<state>);
+
+	/**
+	 * Run the client until the game window is closed.
+	 */
+	void run();
+
+	/**
+	 * \copydoc dispatcher::handle_connect
+	 */
+	void handle_connect(const std::error_code& code) override;
+
+	/**
+	 * \copydoc dispatcher::handle_message
+	 */
+	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
+
+	/**
+	 * \copydoc dispatcher::handle_key_down
+	 */
+	void handle_key_down(const key_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_key_up
+	 */
+	void handle_key_up(const key_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_mouse_down
+	 */
+	void handle_mouse_down(const mouse_click_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_mouse_up
+	 */
+	void handle_mouse_up(const mouse_click_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_mouse_wheel
+	 */
+	void handle_mouse_wheel(const mouse_wheel_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_text
+	 */
+	void handle_text(const text_event& ev) override;
+
+	/**
+	 * \copydoc dispatcher::handle_quit
+	 */
+	void handle_quit() override;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_CLIENT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/color.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,247 @@
+/*
+ * color.cpp -- color description
+ *
+ * Copyright (c) 2013-2018 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 <cctype>
+#include <locale>
+#include <unordered_map>
+
+#include "color.hpp"
+
+namespace mlk::client {
+
+namespace {
+
+constexpr auto rgb(std::uint8_t r, std::uint8_t g, std::uint8_t b) noexcept -> unsigned
+{
+	return (0xff000000 | (r << 16) | (g << 8) | b);
+}
+
+/*
+ * Convert hexadecimal value to the correct number.
+ */
+auto value(char digit) -> std::uint8_t
+{
+	if (std::isdigit(digit, {}))
+		return digit - '0';
+	if ((digit = std::toupper(digit, {})) < 'A' || digit > 'F')
+		throw std::invalid_argument("invalid hexadecimal value: " + std::to_string(digit));
+
+	return digit - 55;
+}
+
+auto value(char digit1, char digit2) -> std::uint8_t
+{
+	return ((value(digit1) << 4) & 0xf0) | value(digit2);
+}
+
+const std::unordered_map<std::string_view, std::uint32_t> colors{
+	{ "aliceblue",                  rgb(240, 248, 255)      },
+	{ "antiquewhite",               rgb(250, 235, 215)      },
+	{ "aqua",                       rgb( 0, 255, 255)       },
+	{ "aquamarine",                 rgb(127, 255, 212)      },
+	{ "azure",                      rgb(240, 255, 255)      },
+	{ "beige",                      rgb(245, 245, 220)      },
+	{ "bisque",                     rgb(255, 228, 196)      },
+	{ "black",                      rgb( 0, 0, 0)           },
+	{ "blanchedalmond",             rgb(255, 235, 205)      },
+	{ "blue",                       rgb( 0, 0, 255)         },
+	{ "blueviolet",                 rgb(138, 43, 226)       },
+	{ "brown",                      rgb(165, 42, 42)        },
+	{ "burlywood",                  rgb(222, 184, 135)      },
+	{ "cadetblue",                  rgb( 95, 158, 160)      },
+	{ "chartreuse",                 rgb(127, 255, 0)        },
+	{ "chocolate",                  rgb(210, 105, 30)       },
+	{ "coral",                      rgb(255, 127, 80)       },
+	{ "cornflowerblue",             rgb(100, 149, 237)      },
+	{ "cornsilk",                   rgb(255, 248, 220)      },
+	{ "crimson",                    rgb(220, 20, 60)        },
+	{ "cyan",                       rgb( 0, 255, 255)       },
+	{ "darkblue",                   rgb( 0, 0, 139)         },
+	{ "darkcyan",                   rgb( 0, 139, 139)       },
+	{ "darkgoldenrod",              rgb(184, 134, 11)       },
+	{ "darkgray",                   rgb(169, 169, 169)      },
+	{ "darkgreen",                  rgb( 0, 100, 0)         },
+	{ "darkgrey",                   rgb(169, 169, 169)      },
+	{ "darkkhaki",                  rgb(189, 183, 107)      },
+	{ "darkmagenta",                rgb(139, 0, 139)        },
+	{ "darkolivegreen",             rgb( 85, 107, 47)       },
+	{ "darkorange",                 rgb(255, 140, 0)        },
+	{ "darkorchid",                 rgb(153, 50, 204)       },
+	{ "darkred",                    rgb(139, 0, 0)          },
+	{ "darksalmon",                 rgb(233, 150, 122)      },
+	{ "darkseagreen",               rgb(143, 188, 143)      },
+	{ "darkslateblue",              rgb( 72, 61, 139)       },
+	{ "darkslategray",              rgb( 47, 79, 79)        },
+	{ "darkslategrey",              rgb( 47, 79, 79)        },
+	{ "darkturquoise",              rgb( 0, 206, 209)       },
+	{ "darkviolet",                 rgb(148, 0, 211)        },
+	{ "deeppink",                   rgb(255, 20, 147)       },
+	{ "deepskyblue",                rgb( 0, 191, 255)       },
+	{ "dimgray",                    rgb(105, 105, 105)      },
+	{ "dimgrey",                    rgb(105, 105, 105)      },
+	{ "dodgerblue",                 rgb( 30, 144, 255)      },
+	{ "firebrick",                  rgb(178, 34, 34)        },
+	{ "floralwhite",                rgb(255, 250, 240)      },
+	{ "forestgreen",                rgb( 34, 139, 34)       },
+	{ "fuchsia",                    rgb(255, 0, 255)        },
+	{ "gainsboro",                  rgb(220, 220, 220)      },
+	{ "ghostwhite",                 rgb(248, 248, 255)      },
+	{ "gold",                       rgb(255, 215, 0)        },
+	{ "goldenrod",                  rgb(218, 165, 32)       },
+	{ "gray",                       rgb(128, 128, 128)      },
+	{ "green",                      rgb( 0, 128, 0)         },
+	{ "greenyellow",                rgb(173, 255, 47)       },
+	{ "grey",                       rgb(128, 128, 128)      },
+	{ "honeydew",                   rgb(240, 255, 240)      },
+	{ "hotpink",                    rgb(255, 105, 180)      },
+	{ "indianred",                  rgb(205, 92, 92)        },
+	{ "indigo",                     rgb( 75, 0, 130)        },
+	{ "ivory",                      rgb(255, 255, 240)      },
+	{ "khaki",                      rgb(240, 230, 140)      },
+	{ "lavender",                   rgb(230, 230, 250)      },
+	{ "lavenderblush",              rgb(255, 240, 245)      },
+	{ "lawngreen",                  rgb(124, 252, 0)        },
+	{ "lemonchiffon",               rgb(255, 250, 205)      },
+	{ "lightblue",                  rgb(173, 216, 230)      },
+	{ "lightcoral",                 rgb(240, 128, 128)      },
+	{ "lightcyan",                  rgb(224, 255, 255)      },
+	{ "lightgoldenrodyellow",       rgb(250, 250, 210)      },
+	{ "lightgray",                  rgb(211, 211, 211)      },
+	{ "lightgreen",                 rgb(144, 238, 144)      },
+	{ "lightgrey",                  rgb(211, 211, 211)      },
+	{ "lightpink",                  rgb(255, 182, 193)      },
+	{ "lightsalmon",                rgb(255, 160, 122)      },
+	{ "lightseagreen",              rgb( 32, 178, 170)      },
+	{ "lightskyblue",               rgb(135, 206, 250)      },
+	{ "lightslategray",             rgb(119, 136, 153)      },
+	{ "lightslategrey",             rgb(119, 136, 153)      },
+	{ "lightsteelblue",             rgb(176, 196, 222)      },
+	{ "lightyellow",                rgb(255, 255, 224)      },
+	{ "lime",                       rgb( 0, 255, 0)         },
+	{ "limegreen",                  rgb( 50, 205, 50)       },
+	{ "linen",                      rgb(250, 240, 230)      },
+	{ "magenta",                    rgb(255, 0, 255)        },
+	{ "maroon",                     rgb(128, 0, 0)          },
+	{ "mediumaquamarine",           rgb(102, 205, 170)      },
+	{ "mediumblue",                 rgb( 0, 0, 205)         },
+	{ "mediumorchid",               rgb(186, 85, 211)       },
+	{ "mediumpurple",               rgb(147, 112, 219)      },
+	{ "mediumseagreen",             rgb( 60, 179, 113)      },
+	{ "mediumslateblue",            rgb(123, 104, 238)      },
+	{ "mediumspringgreen",          rgb( 0, 250, 154)       },
+	{ "mediumturquoise",            rgb( 72, 209, 204)      },
+	{ "mediumvioletred",            rgb(199, 21, 133)       },
+	{ "midnightblue",               rgb( 25, 25, 112)       },
+	{ "mintcream",                  rgb(245, 255, 250)      },
+	{ "mistyrose",                  rgb(255, 228, 225)      },
+	{ "moccasin",                   rgb(255, 228, 181)      },
+	{ "navajowhite",                rgb(255, 222, 173)      },
+	{ "navy",                       rgb( 0, 0, 128)         },
+	{ "oldlace",                    rgb(253, 245, 230)      },
+	{ "olive",                      rgb(128, 128, 0)        },
+	{ "olivedrab",                  rgb(107, 142, 35)       },
+	{ "orange",                     rgb(255, 165, 0)        },
+	{ "orangered",                  rgb(255, 69, 0)         },
+	{ "orchid",                     rgb(218, 112, 214)      },
+	{ "palegoldenrod",              rgb(238, 232, 170)      },
+	{ "palegreen",                  rgb(152, 251, 152)      },
+	{ "paleturquoise",              rgb(175, 238, 238)      },
+	{ "palevioletred",              rgb(219, 112, 147)      },
+	{ "papayawhip",                 rgb(255, 239, 213)      },
+	{ "peachpuff",                  rgb(255, 218, 185)      },
+	{ "peru",                       rgb(205, 133, 63)       },
+	{ "pink",                       rgb(255, 192, 203)      },
+	{ "plum",                       rgb(221, 160, 221)      },
+	{ "powderblue",                 rgb(176, 224, 230)      },
+	{ "purple",                     rgb(128, 0, 128)        },
+	{ "red",                        rgb(255, 0, 0)          },
+	{ "rosybrown",                  rgb(188, 143, 143)      },
+	{ "royalblue",                  rgb( 65, 105, 225)      },
+	{ "saddlebrown",                rgb(139, 69, 19)        },
+	{ "salmon",                     rgb(250, 128, 114)      },
+	{ "sandybrown",                 rgb(244, 164, 96)       },
+	{ "seagreen",                   rgb( 46, 139, 87)       },
+	{ "seashell",                   rgb(255, 245, 238)      },
+	{ "sienna",                     rgb(160, 82, 45)        },
+	{ "silver",                     rgb(192, 192, 192)      },
+	{ "skyblue",                    rgb(135, 206, 235)      },
+	{ "slateblue",                  rgb(106, 90, 205)       },
+	{ "slategray",                  rgb(112, 128, 144)      },
+	{ "slategrey",                  rgb(112, 128, 144)      },
+	{ "snow",                       rgb(255, 250, 250)      },
+	{ "springgreen",                rgb( 0, 255, 127)       },
+	{ "steelblue",                  rgb( 70, 130, 180)      },
+	{ "tan",                        rgb(210, 180, 140)      },
+	{ "teal",                       rgb( 0, 128, 128)       },
+	{ "thistle",                    rgb(216, 191, 216)      },
+	{ "tomato",                     rgb(255, 99, 71)        },
+	{ "transparent",                0                       },
+	{ "turquoise",                  rgb( 64, 224, 208)      },
+	{ "violet",                     rgb(238, 130, 238)      },
+	{ "wheat",                      rgb(245, 222, 179)      },
+	{ "white",                      rgb(255, 255, 255)      },
+	{ "whitesmoke",                 rgb(245, 245, 245)      },
+	{ "yellow",                     rgb(255, 255, 0)        },
+	{ "yellowgreen",                rgb(154, 205, 50)       }
+};
+
+} // !namespace
+
+auto color::from_hex(std::uint32_t hex) noexcept -> color
+{
+	return {
+		static_cast<std::uint8_t>((hex >> 16) & 0xff),
+		static_cast<std::uint8_t>((hex >> 8) & 0xff),
+		static_cast<std::uint8_t>((hex & 0xff)),
+		static_cast<std::uint8_t>((hex >> 24) & 0xff)
+	};
+}
+
+auto color::from_name(std::string_view name) -> color
+{
+	if (name.empty())
+		return color();
+
+	color result;
+
+	// Parse #rrggbb or #rgb.
+	if (name[0] == '#') {
+		if (name.length() == 7) {
+			result.red   = value(name[1], name[2]);
+			result.green = value(name[3], name[4]);
+			result.blue  = value(name[5], name[6]);
+		} else if (name.length() == 4) {
+			result.red   = value(name[1], name[1]);
+			result.green = value(name[2], name[2]);
+			result.blue  = value(name[3], name[3]);
+		} else
+			throw std::invalid_argument("invalid format");
+	} else {
+		// Name lookup.
+		const auto it = colors.find(name);
+
+		if (it == colors.end())
+			throw std::invalid_argument("invalid color");
+
+		result = from_hex(it->second);
+	}
+
+	return result;
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/color.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,61 @@
+/*
+ * color.hpp -- color description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_COLOR_HPP
+#define MALIKANIA_CLIENT_COLOR_HPP
+
+/**
+ * \file color.hpp
+ * \brief Colors.
+ */
+
+#include <cstdint>
+#include <string_view>
+
+namespace mlk::client {
+
+/**
+ * \brief color description
+ */
+struct color {
+	std::uint8_t red{0};
+	std::uint8_t green{0};
+	std::uint8_t blue{0};
+	std::uint8_t alpha{255};
+
+	/**
+	 * Constructor with an hexadecimal value.
+	 *
+	 * \param hex the color
+	 */
+	static auto from_hex(std::uint32_t hex) noexcept -> color;
+
+	/**
+	 * Construct a color from #rrggbb or name.
+	 *
+	 * See the SVG this [list](http://www.december.com/html/spec/colorsvg.html) for supported names.
+	 *
+	 * \param name the color name
+	 * \throw std::invalid_argument if the color does not exist or is invalid
+	 */
+	static auto from_name(std::string_view name) -> color;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_COLOR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/connection.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,159 @@
+/*
+ * connection.hpp -- connection to server
+ *
+ * Copyright (c) 2013-2018 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 "connection.hpp"
+
+namespace mlk {
+
+namespace client {
+
+/*
+ * connection::connection
+ * ------------------------------------------------------------------
+ */
+connection::connection(boost::asio::io_service& service)
+	: service_(service)
+	, context_(boost::asio::ssl::context::sslv23)
+	, resolver_(service_)
+{
+}
+
+/*
+ * connection::handshake
+ * ------------------------------------------------------------------
+ */
+void connection::handshake(connect_t handler)
+{
+	socket_->async_handshake(boost::asio::ssl::stream_base::client, std::move(handler));
+}
+
+/*
+ * connection::flush
+ * ------------------------------------------------------------------
+ */
+void connection::flush()
+{
+	if (output_.empty())
+		return;
+
+	auto buffer = boost::asio::buffer(std::get<1>(output_[0]));
+
+	boost::asio::async_write(*socket_, buffer, [this] (auto code, auto xfer) {
+		auto handler = std::get<2>(output_[0]);
+
+		output_.pop_front();
+
+		if (handler)
+			handler(std::move(code), std::get<0>(output_[0]));
+
+		// TODO: precise error code.
+		if (!code && xfer != 0)
+			flush();
+	});
+}
+
+/*
+ * connection::connect
+ * ------------------------------------------------------------------
+ */
+void connection::connect(const std::string& host, std::uint16_t port, connect_t handler)
+{
+	socket_ = std::make_unique<socket_t>(service_, context_);
+
+	using tcp = boost::asio::ip::tcp;
+
+	auto str = std::to_string(port);
+
+	resolver_.async_resolve(tcp::resolver::query(host, str), [this, handler] (auto code, auto ep) {
+		if (code)
+			handler(std::move(code));
+		else {
+			boost::asio::async_connect(socket_->lowest_layer(), ep, [this, handler] (auto code, auto) {
+				if (code)
+					handler(std::move(code));
+				else
+					handshake(std::move(handler));
+			});
+		}
+	});
+}
+
+/*
+ * connection::send
+ * ------------------------------------------------------------------
+ */
+void connection::send(nlohmann::json message, send_t handler)
+{
+	assert(socket_);
+	assert(message.is_object());
+
+	auto in_progress = !output_.empty();
+	auto str = message.dump() + "\r\n\r\n";
+
+	output_.push_back(std::make_tuple(std::move(message), std::move(str), std::move(handler)));
+
+	if (!in_progress)
+		flush();
+}
+
+/*
+ * connection::recv
+ * ------------------------------------------------------------------
+ */
+void connection::recv(recv_t handler)
+{
+	assert(socket_);
+
+#if !defined(NDEBUG)
+	assert(!is_reading);
+
+	is_reading = true;
+#endif
+
+	boost::asio::async_read_until(*socket_, input_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
+#if !defined(NDEBUG)
+		is_reading = false;
+#endif
+		if (code)
+			handler(std::move(code), nullptr);
+		else if (xfer == 0)
+			handler(make_error_code(boost::system::errc::network_down), nullptr);
+		else {
+			std::string command{
+				boost::asio::buffers_begin(input_.data()),
+				boost::asio::buffers_begin(input_.data()) + xfer - /* \r\n\r\n */ 4
+			};
+
+			input_.consume(xfer);
+
+			nlohmann::json msg;
+
+			try {
+				msg = nlohmann::json::parse(command);
+			} catch (const std::exception&) {
+				// TODO: add custom error code.
+			}
+
+			handler(std::move(code), std::move(msg));
+		}
+	});
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/connection.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,123 @@
+/*
+ * connection.hpp -- connection to server
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_CONNECTION_HPP
+#define MALIKANIA_CLIENT_CONNECTION_HPP
+
+/**
+ * \file connection.hpp
+ * \brief Connection to server.
+ */
+
+#include <deque>
+#include <functional>
+#include <utility>
+
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+#include <json.hpp>
+
+namespace mlk {
+
+namespace client {
+
+class client;
+
+/**
+ * \brief connection to server.
+ */
+class connection {
+public:
+	/**
+	 * Connection handler.
+	 */
+	using connect_t = std::function<void (boost::system::error_code)>;
+
+	/**
+	 * Receive handler.
+	 */
+	using recv_t = std::function<void (boost::system::error_code, nlohmann::json)>;
+
+	/**
+	 * Send handler.
+	 */
+	using send_t = std::function<void (boost::system::error_code, nlohmann::json)>;
+
+private:
+	/**
+	 * Wrap an handler, the JSON message and its dump string.
+	 */
+	using output_item_t = std::tuple<nlohmann::json, std::string, send_t>;
+
+	using socket_t = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
+
+	boost::asio::io_service& service_;
+	boost::asio::ssl::context context_;
+	boost::asio::ip::tcp::resolver resolver_;
+	std::unique_ptr<socket_t> socket_;
+	boost::asio::streambuf input_;
+	std::deque<output_item_t> output_;
+
+#if !defined(NDEBUG)
+	bool is_reading{false};
+#endif
+
+	void handshake(connect_t);
+	void flush();
+
+public:
+	/**
+	 * Create a connection object.
+	 */
+	connection(boost::asio::io_service& service);
+
+	/**
+	 * Connect to the given end point.
+	 *
+	 * \pre handler != nullptr
+	 * \param host the hostname
+	 * \param port the port number
+	 * \param handler the handler
+	 */
+	virtual void connect(const std::string& host, std::uint16_t port, connect_t handler);
+
+	/**
+	 * Send the given message to the server.
+	 *
+	 * \pre message.is_object()
+	 * \param message the message object
+	 * \param handler the handler (may be null)
+	 */
+	virtual void send(nlohmann::json message, send_t handler);
+
+	/**
+	 * Request for a recv operation.
+	 *
+	 * \pre no reading operation must be pending
+	 * \pre handler != nullptr
+	 * \param handler the handler
+	 */
+	virtual void recv(recv_t handler);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_CONNECTION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/dispatcher.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,183 @@
+/*
+ * dispatcher.hpp -- client event dispatcher
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_DISPATCHER_HPP
+#define MALIKANIA_CLIENT_DISPATCHER_HPP
+
+/**
+ * \file dispatcher.hpp
+ * \brief Client event dispatcher.
+ */
+
+#include <system_error>
+
+#include <json.hpp>
+
+#include <malikania/point.hpp>
+
+#include "mouse.hpp"
+#include "key.hpp"
+
+namespace mlk::client {
+
+/**
+ * \brief Describe key event.
+ */
+struct key_event {
+	key keycode{key::unknown};      //!< layout-dependant key
+	key scancode{key::unknown};     //!< physical key
+	mod modifiers{mod::none};       //!< optional modifiers
+};
+
+/**
+ * \brief Describe mouse click event.
+ */
+struct mouse_click_event {
+	mouse button{mouse::none};      //!< which mouse button
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe a mouse motion event.
+ */
+struct mouse_motion_event {
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe mouse wheel (up/down) event.
+ */
+struct mouse_wheel_event {
+	point pos;                      //!< current mouse position
+};
+
+/**
+ * \brief Describe text edition event.
+ */
+struct text_event {
+	std::u32string text;            //!< text added from this event
+};
+
+/**
+ * \brief Client event dispatcher.
+ */
+class dispatcher {
+public:
+	/**
+	 * Default constructor.
+	 */
+	dispatcher() noexcept = default;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~dispatcher() noexcept = default;
+
+	/**
+	 * Connection event.
+	 *
+	 * \param code the result code
+	 */
+	virtual void handle_connect(const std::error_code& code)
+	{
+		(void)code;
+	}
+
+	/**
+	 * Message event.
+	 *
+	 * \param code the result code
+	 * \param msg the network message
+	 */
+	virtual void handle_message(const std::error_code& code, const nlohmann::json& msg)
+	{
+		(void)code;
+		(void)msg;
+	}
+
+	/**
+	 * Key down event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_key_down(const key_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Key released event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_key_up(const key_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Mouse click event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_mouse_down(const mouse_click_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Mouse click release event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_mouse_up(const mouse_click_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Mouse wheel event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_mouse_wheel(const mouse_wheel_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Text editing event.
+	 *
+	 * \param ev the event
+	 */
+	virtual void handle_text(const text_event& ev)
+	{
+		(void)ev;
+	}
+
+	/**
+	 * Quit request.
+	 */
+	virtual void handle_quit()
+	{
+	}
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_DISPATCHER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/font.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,72 @@
+/*
+ * font.cpp -- font object
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/size.hpp>
+
+#include "font.hpp"
+#include "sdl_util.hpp"
+
+namespace mlk::client {
+
+font::font(std::string data, std::size_t size)
+	: size_(size)
+{
+	assert(size > 0);
+
+	auto rw = SDLx_RWFromBinary(std::move(data));
+
+	if (rw == nullptr)
+		throw std::runtime_error(SDL_GetError());
+
+	font_ = { TTF_OpenFontRW(rw, true, size), TTF_CloseFont };
+
+	if (font_ == nullptr)
+		throw std::runtime_error(TTF_GetError());
+}
+
+auto font::get_handle() const -> TTF_Font*
+{
+	return font_.get();
+}
+
+auto font::get_handle() -> TTF_Font*
+{
+	return font_.get();
+}
+
+auto font::get_size() const noexcept -> std::size_t
+{
+	return size_;
+}
+
+auto font::clip(const std::string& text) const -> size
+{
+	int width, height;
+
+	if (TTF_SizeUTF8(font_.get(), text.c_str(), &width, &height) != 0)
+		throw std::runtime_error(SDL_GetError());
+
+	return {
+		static_cast<unsigned>(width),
+		static_cast<unsigned>(height)
+	};
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/font.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,91 @@
+/*
+ * font.hpp -- font object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_FONT_HPP
+#define MALIKANIA_CLIENT_FONT_HPP
+
+/**
+ * \file font.hpp
+ * \brief fonts.
+ */
+
+#include <memory>
+#include <string>
+
+#include <SDL.h>
+#include <SDL_ttf.h>
+
+namespace mlk {
+
+struct size;
+
+namespace client {
+
+/**
+ * \brief font object.
+ */
+class font {
+private:
+	std::unique_ptr<TTF_Font, void (*)(TTF_Font*)> font_{nullptr, nullptr};
+	std::size_t size_;
+
+public:
+	/**
+	 * Construct a font from binary data.
+	 *
+	 * \pre size > 0
+	 * \param data the raw data
+	 * \param size the size
+	 */
+	font(std::string data, std::size_t size);
+
+	/**
+	 * Get the underlying SDL TTF_Font.
+	 *
+	 * \return the font
+	 */
+	auto get_handle() const -> TTF_Font*;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the font
+	 */
+	auto get_handle() -> TTF_Font*;
+
+	/**
+	 * Get the font size.
+	 *
+	 * \return the font size
+	 */
+	auto get_size() const noexcept -> std::size_t;
+
+	/**
+	 * Get the clipping size required to draw the given text.
+	 *
+	 * \param text the text to clip
+	 * \return the required size
+	 */
+	auto clip(const std::string& text) const -> size;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // MALIKANIA_CLIENT_FONT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/image.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,112 @@
+/*
+ * image.cpp -- image object
+ *
+ * Copyright (c) 2013-2018 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_image.h>
+
+#include "image.hpp"
+#include "sdl_util.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+void image::create_texture(window& w)
+{
+	texture_ = {
+		SDL_CreateTextureFromSurface(w.get_renderer(), surface_.get()),
+		SDL_DestroyTexture
+	};
+
+	if (texture_ == nullptr)
+		throw std::runtime_error(SDL_GetError());
+}
+
+image::image(std::string data)
+{
+	auto rw = SDLx_RWFromBinary(std::move(data));
+
+	if (rw == nullptr)
+		throw std::runtime_error(SDL_GetError());
+
+	surface_ = { IMG_Load_RW(rw, true), SDL_FreeSurface };
+
+	if (!surface_)
+		throw std::runtime_error(SDL_GetError());
+
+	size_ = {
+		static_cast<unsigned>(surface_->w),
+		static_cast<unsigned>(surface_->h)
+	};
+}
+
+auto image::get_size() const noexcept -> const mlk::size&
+{
+	return size_;
+}
+
+void image::draw(window& window, const point& point)
+{
+	/*
+	 * Create texture at this step so the image constructor does not need the
+	 * window.
+	 */
+	if (!texture_)
+		create_texture(window);
+
+	SDL_Rect target;
+
+	target.x = static_cast<int>(point.x);
+	target.y = static_cast<int>(point.y);
+	target.w = static_cast<int>(size_.width);
+	target.h = static_cast<int>(size_.height);
+
+	if (SDL_RenderCopy(window.get_renderer(), texture_.get(), nullptr, &target) < 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void image::draw(window& window, const rectangle& source, const rectangle& target)
+{
+	if (!texture_)
+		create_texture(window);
+
+	SDL_Rect sr, st;
+
+	sr.x = source.x;
+	sr.y = source.y;
+	sr.w = static_cast<int>(source.width);
+	sr.h = static_cast<int>(source.height);
+
+	st.x = target.x;
+	st.y = target.y;
+	st.w = static_cast<int>(target.width);
+	st.h = static_cast<int>(target.height);
+
+	// Readjust .w, .h if null.
+	if (source.is_null()) {
+		sr.w = static_cast<int>(size_.width);
+		sr.h = static_cast<int>(size_.height);
+	}
+	if (target.is_null()) {
+		st.w = static_cast<int>(size_.width);
+		st.h = static_cast<int>(size_.height);
+	}
+
+	if (SDL_RenderCopy(window.get_renderer(), texture_.get(), &sr, &st) < 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/image.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,88 @@
+/*
+ * image.hpp -- image object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_IMAGE_HPP
+#define MALIKANIA_CLIENT_IMAGE_HPP
+
+/**
+ * \file image.hpp
+ * \brief Images.
+ */
+
+#include <memory>
+#include <string>
+
+#include <SDL.h>
+
+#include <malikania/point.hpp>
+#include <malikania/rectangle.hpp>
+#include <malikania/size.hpp>
+
+namespace mlk::client {
+
+class window;
+
+/**
+ * \brief Image object.
+ */
+class image {
+private:
+	std::unique_ptr<SDL_Surface, void (*)(SDL_Surface *)> surface_{nullptr, nullptr};
+	std::unique_ptr<SDL_Texture, void (*)(SDL_Texture *)> texture_{nullptr, nullptr};
+
+	size size_;
+
+	void create_texture(window& window);
+
+public:
+	/**
+	 * Construct an image from the binary data.
+	 *
+	 * \param window the window
+	 * \param data the data
+	 */
+	image(std::string data);
+
+	/**
+	 * Get the image size.
+	 *
+	 * \return the size
+	 */
+	auto get_size() const noexcept -> const mlk::size&;
+
+	/**
+	 * Draw the image to the window.
+	 *
+	 * \param window the window
+	 * \param position the position
+	 */
+	void draw(window& window, const point& position = {0, 0});
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \param window the window
+	 * \param source the source to clip
+	 * \param target the target destination
+	 */
+	void draw(window& window, const rectangle& source, const rectangle& target);
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_IMAGE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/key.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,256 @@
+/*
+ * key.hpp -- key definitions
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_KEY_HPP
+#define MALIKANIA_CLIENT_KEY_HPP
+
+namespace mlk::client {
+
+/**
+ * \brief Describe keyboard modifiers
+ *
+ * This enumeration is a satisfied the Bitmask concept.
+ */
+enum class mod {
+	none            = 0,                    //!< no modifiers
+	left_control    = (1 << 1),             //!< left control
+	left_alt        = (1 << 2),             //!< left alt
+	left_shift      = (1 << 3),             //!< left shift
+	left_super      = (1 << 4),             //!< left logo
+	right_control   = (1 << 5),             //!< right control
+	right_alt       = (1 << 6),             //!< right alt
+	right_shift     = (1 << 7),             //!< right shift
+	right_super     = (1 << 8),             //!< right logo
+	num_lock        = (1 << 9),             //!< num lock is on
+	caps_lock       = (1 << 10),            //!< caps lock is on
+
+	/**
+	 * Left or right control.
+	 */
+	control         = left_control | right_control,
+
+        /**
+         * Left or right shift.
+         */
+	shift           = left_shift | right_shift,
+
+        /**
+         * Left or right alt.
+         */
+	alt             = left_alt | right_alt,
+
+        /**
+         * Left or right super.
+         */
+	super           = left_super | right_super
+};
+
+/**
+ * \cond ENUM_HIDDEN_SYMBOLS
+ */
+
+inline auto operator^(mod v1, mod v2) noexcept -> mod
+{
+	return static_cast<mod>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2));
+}
+
+inline auto operator&(mod v1, mod v2) noexcept -> mod
+{
+	return static_cast<mod>(static_cast<unsigned>(v1)&  static_cast<unsigned>(v2));
+}
+
+inline auto operator|(mod v1, mod v2) noexcept -> mod
+{
+	return static_cast<mod>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2));
+}
+
+inline auto operator~(mod v) noexcept -> mod
+{
+	return static_cast<mod>(~static_cast<unsigned>(v));
+}
+
+inline auto operator|=(mod& v1, mod v2) noexcept -> mod&
+{
+	v1 = static_cast<mod>(static_cast<unsigned>(v1) | static_cast<unsigned>(v2));
+
+	return v1;
+}
+
+inline auto operator&=(mod& v1, mod v2) noexcept -> mod&
+{
+	v1 = static_cast<mod>(static_cast<unsigned>(v1)&  static_cast<unsigned>(v2));
+
+	return v1;
+}
+
+inline auto operator^=(mod& v1, mod v2) noexcept -> mod&
+{
+	v1 = static_cast<mod>(static_cast<unsigned>(v1) ^ static_cast<unsigned>(v2));
+
+	return v1;
+}
+
+/**
+ * \endcond
+ */
+
+/**
+ * \brief Key description.
+ */
+enum class key {
+	unknown,
+	a,
+	b,
+	c,
+	d,
+	e,
+	f,
+	g,
+	h,
+	i,
+	j,
+	k,
+	l,
+	m,
+	n,
+	o,
+	p,
+	q,
+	r,
+	s,
+	t,
+	u,
+	v,
+	w,
+	x,
+	y,
+	z,
+	exclaim,
+	double_quote,
+	percent,
+	dollar,
+	ampersand,
+	left_parenthese,
+	right_parenthese,
+	asterisk,
+	plus,
+	colon,
+	less,
+	greater,
+	question,
+	at,
+	caret,
+	underscore,
+	back_quote,
+	quote,
+	one,
+	two,
+	three,
+	four,
+	five,
+	six,
+	seven,
+	eight,
+	nine,
+	zero,
+	enter,
+	escape,
+	backspace,
+	tab,
+	space,
+	minus,
+	equals,
+	left_bracket,
+	right_bracket,
+	backslash,
+	hash,
+	semicolon,
+	apostrophe,
+	grave,
+	comma,
+	period,
+	slash,
+	caps_lock,
+	f1,
+	f2,
+	f3,
+	f4,
+	f5,
+	f6,
+	f7,
+	f8,
+	f9,
+	f10,
+	f11,
+	f12,
+	f13,
+	f14,
+	f15,
+	f16,
+	f17,
+	f18,
+	f19,
+	f20,
+	f21,
+	f22,
+	f23,
+	f24,
+	print_screen,
+	scroll_lock,
+	pause,
+	insert,
+	home,
+	page_up,
+	page_down,
+	del,
+	end,
+	right,
+	left,
+	down,
+	up,
+	kp_divide,
+	kp_multiply,
+	kp_minus,
+	kp_plus,
+	kp_enter,
+	kp_one,
+	kp_two,
+	kp_three,
+	kp_four,
+	kp_five,
+	kp_six,
+	kp_seven,
+	kp_eight,
+	kp_nine,
+	kp_zero,
+	mute,
+	volume_up,
+	volume_down,
+	left_control,
+	left_alt,
+	left_shift,
+	left_super,
+	right_control,
+	right_alt,
+	right_shift,
+	right_super
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_KEY_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/label.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,49 @@
+/*
+ * label.cpp -- GUI label element
+ *
+ * Copyright (c) 2013-2018 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 <malikania/size.hpp>
+
+#include "label.hpp"
+#include "theme.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+label::label(std::string text) noexcept
+	: text_(std::move(text))
+{
+}
+
+auto label::get_text() const noexcept -> const std::string&
+{
+	return text_;
+}
+
+void label::set_text(std::string text) noexcept
+{
+	text_ = std::move(text);
+}
+
+void label::draw(window& w, const size& size)
+{
+	// TODO: implement.
+	(void)w;
+	(void)size;
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/label.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,70 @@
+/*
+ * label.hpp -- GUI label element
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LABEL_HPP
+#define MALIKANIA_CLIENT_LABEL_HPP
+
+/**
+ * \file label.hpp
+ * \brief GUI label element.
+ */
+
+#include <string>
+
+#include "widget.hpp"
+
+namespace mlk::client {
+
+/**
+ * \brief Basic label for displaying test.
+ */
+class label : public widget {
+private:
+	std::string text_;
+
+public:
+	/**
+	 * Create a label with an optional text.
+	 *
+	 * \param text the text
+	 */
+	label(std::string text = "") noexcept;
+
+	/**
+	 * Get the text.
+	 *
+	 * \return the label text
+	 */
+	auto get_text() const noexcept -> const std::string&;
+
+	/**
+	 * Set the label text.
+	 *
+	 * \param text the text
+	 */
+	void set_text(std::string text) noexcept;
+
+	/**
+	 * \copydoc widget::draw
+	 */
+	void draw(window& w, const size& size) override;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_LABEL_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/loader.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,108 @@
+/*
+ * client_resources_loader.cpp -- load shared resources files for the client
+ *
+ * Copyright (c) 2013-2018 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 <json.hpp>
+
+#include <malikania/locator.hpp>
+#include <malikania/size.hpp>
+#include <malikania/util.hpp>
+
+#include "animation.hpp"
+#include "font.hpp"
+#include "image.hpp"
+#include "loader.hpp"
+#include "sprite.hpp"
+
+using namespace nlohmann;
+
+namespace mlk::client {
+
+namespace {
+
+auto load_animation_frames(const json& value)
+{
+	animation::frame_list frames;
+
+	for (const auto& v : value) {
+		if (!v.is_object())
+			throw std::runtime_error("not a JSON object");
+
+		const auto cell = v.find("cell");
+		const auto delay = v.find("delay");
+
+		if (cell == v.end() || !cell->is_number_unsigned())
+			throw std::runtime_error("invalid cell");
+		if (delay == v.end() || !delay->is_number_unsigned())
+			throw std::runtime_error("invalid delay");
+
+		// TODO: range check.
+		frames.push_back({cell->get<unsigned>(), delay->get<unsigned>()});
+	}
+
+	return frames;
+}
+
+} // !namespace
+
+loader::loader(locator& locator) noexcept
+	: mlk::loader(locator)
+{
+}
+
+auto loader::load_font(std::string_view id, unsigned size) -> font
+{
+	return font(get_locator().read(id), size);
+}
+
+auto loader::load_image(std::string_view id) -> image
+{
+	return image(get_locator().read(id));
+}
+
+auto loader::load_sprite(std::string_view id) -> sprite
+{
+	auto value = json::parse(get_locator().read(id));
+
+	if (!value.is_object())
+		throw std::runtime_error(": not a JSON object");
+
+	return sprite(
+		load_image(util::json::require_string(value, "/image"_json_pointer)),
+		util::json::require_size(value, "/cell"_json_pointer),
+		util::json::get_size(value, "/margin"_json_pointer),
+		util::json::get_size(value, "/space"_json_pointer),
+		util::json::get_size(value, "/size"_json_pointer)
+	);
+}
+
+auto loader::load_animation(std::string_view id) -> animation
+{
+	const auto value = json::parse(get_locator().read(id));
+
+	if (!value.is_object())
+		throw std::runtime_error("not a JSON object");
+
+	const auto s = load_sprite(util::json::require_string(value, "/sprite"_json_pointer));
+
+	return {
+		load_sprite(util::json::require_string(value, "/sprite"_json_pointer)),
+		load_animation_frames(util::json::require_array(value, "/frames"_json_pointer))
+	};
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/loader.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,99 @@
+/*
+ * client_resources_loader.hpp -- load shared resources files for the client
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOADER_HPP
+#define MALIKANIA_CLIENT_LOADER_HPP
+
+/*
+ * \file loader.hpp
+ * \brief Load client assets.
+ */
+
+#include <malikania/loader.hpp>
+
+#include <string>
+
+namespace mlk {
+
+struct size;
+
+namespace client {
+
+struct animation;
+
+class font;
+class image;
+class sprite;
+
+/**
+ * \brief Load client resources.
+ */
+class loader : public mlk::loader {
+public:
+	/**
+	 * Client resources loader constructor.
+	 *
+	 * The window is required because some of the resources require it.
+	 *
+	 * \param locator the resources locator
+	 */
+	loader(locator& locator) noexcept;
+
+	/**
+	 * Load a font.
+	 *
+	 * \param id the resource id
+	 * \param size the desired size
+	 * \return the font
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_font(std::string_view id, unsigned size) -> font;
+
+	/**
+	 * Load an image.
+	 *
+	 * \param id the resource id
+	 * \return the image
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_image(std::string_view id) -> image;
+
+	/**
+	 * Load a sprite.
+	 *
+	 * \param id the resource id
+	 * \return the sprite
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_sprite(std::string_view id) -> sprite;
+
+	/**
+	 * Load an animation.
+	 *
+	 * \param id the resource id
+	 * \return the animation
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_animation(std::string_view id) -> animation;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_LOADER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/mouse.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,41 @@
+/*
+ * mouse.hpp -- mouse definitions
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_MOUSE_HPP
+#define MALIKANIA_CLIENT_MOUSE_HPP
+
+/**
+ * \file mouse.hpp
+ * \brief Mouse definitions.
+ */
+
+namespace mlk::client {
+
+/**
+ * \brief Describe mouse button
+ */
+enum class mouse {
+	none,                   //!< no buttons are pressed
+	left,                   //!< left click is pressed
+	right,                  //!< right click is pressed
+	middle                  //!< middle click is pressed
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_MOUSE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/sdl_util.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,142 @@
+/*
+ * sdl_util.cpp -- common SDL2 related code
+ *
+ * Copyright (c) 2013-2018 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 <cerrno>
+#include <cstdint>
+#include <cstring>
+#include <new>
+
+#include "sdl_util.hpp"
+
+namespace mlk::client {
+
+namespace {
+
+/*
+ * RWFromBinary implementation
+ * ------------------------------------------------------------------
+ *
+ * A little bit inspired by official SDL_RWFromMem implementation, largely
+ * modified to match our conventions and the C++ code.
+ */
+
+struct buffer {
+	std::string data;
+	std::uint64_t position;
+	std::uint64_t length;
+
+	buffer(std::string input) noexcept
+		: data(std::move(input))
+		, position(0ULL)
+		, length(data.length())
+	{
+	}
+};
+
+auto size(SDL_RWops* ops) noexcept -> Sint64
+{
+	return reinterpret_cast<buffer*>(ops->hidden.unknown.data1)->length;
+}
+
+auto seek(SDL_RWops* ops, Sint64 offset, int whence) noexcept -> Sint64
+{
+	auto data = reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
+	auto position = data->position;
+
+	switch (whence) {
+	case RW_SEEK_SET:
+		position = offset;
+		break;
+	case RW_SEEK_CUR:
+		position = data->position + offset;
+		break;
+	case RW_SEEK_END:
+		position = data->length + offset;
+		break;
+	default:
+		break;
+	}
+
+	if (static_cast<std::uint64_t>(position) > data->length)
+		position = data->length;
+
+	return (data->position = position);
+}
+
+auto read(SDL_RWops* ops, void* dst, std::size_t size, std::size_t number) noexcept -> std::size_t
+{
+	auto data = reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
+	auto total = number * size;
+	auto avail = data->length - data->position;
+
+	if (number <= 0U || size <= 0U || ((total / number) != static_cast<std::size_t>(size)))
+		return 0;
+	if (total > avail)
+		total = avail;
+
+	SDL_memcpy(dst, &data->data[data->position], total);
+	data->position += total;
+
+	return total / size;
+}
+
+auto write(SDL_RWops*, const void*, std::size_t, std::size_t) noexcept -> std::size_t
+{
+	SDL_SetError("write not supported");
+	return -1;
+}
+
+auto close(SDL_RWops* ops) noexcept -> int
+{
+	if (ops != nullptr) {
+		delete reinterpret_cast<buffer*>(ops->hidden.unknown.data1);
+		SDL_FreeRW(ops);
+	}
+
+	return 0;
+}
+
+} // !namespace
+
+auto SDLx_RWFromBinary(std::string data) noexcept -> SDL_RWops*
+{
+	SDL_RWops* ops = SDL_AllocRW();
+
+	if (ops == nullptr)
+		return nullptr;
+
+	ops->hidden.unknown.data1 = new (std::nothrow) buffer(std::move(data));
+
+	if (ops->hidden.unknown.data1 == nullptr) {
+		SDL_SetError("%s", std::strerror(errno));
+		SDL_FreeRW(ops);
+
+		return nullptr;
+	}
+
+	ops->type = SDL_RWOPS_UNKNOWN;
+	ops->seek = seek;
+	ops->size = size;
+	ops->read = read;
+	ops->write = write;
+	ops->close = close;
+
+	return ops;
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/sdl_util.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,51 @@
+/*
+ * sdl_util.hpp -- common SDL2 related code
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_SDL_UTIL_HPP
+#define MALIKANIA_SDL_UTIL_HPP
+
+/**
+ * \file sdl_util.hpp
+ * \brief Utilities for SDL backend.
+ */
+
+#include <SDL.h>
+
+#include <string>
+
+namespace mlk::client {
+
+/**
+ * Create a SDL_RWops that owns the binary data.
+ *
+ * This is a safe alternative to SDL_RWFromMem because it owns the memory
+ * pointed by data until it is closed. The data is moved so there are no copies.
+ *
+ * The stream has read-only support and can not write.
+ *
+ * Seeking past-the-end or past-the-begin readjust the position to the end or
+ * begin respectively.
+ *
+ * \param data the data
+ * \return the object or nullptr on errors
+ */
+auto SDLx_RWFromBinary(std::string data) noexcept -> SDL_RWops*;
+
+} // !mlk::client
+
+#endif // !MALIKANIA_SDL_UTIL_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/sprite.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,106 @@
+/*
+ * sprite.cpp -- image sprite
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include "sprite.hpp"
+
+namespace mlk::client {
+
+sprite::sprite(image image,
+               size cell,
+               size margin,
+               size space,
+               size dimension) noexcept
+	: image_(std::move(image))
+	, cell_(std::move(cell))
+	, margin_(std::move(margin))
+	, space_(std::move(space))
+	, size_(std::move(dimension))
+{
+	assert(cell_.width > 0);
+	assert(cell_.height > 0);
+
+	// If size is not specified, take from image.
+	if (size_.is_null())
+		size_ = image_.get_size();
+
+	// Compute number of cells.
+	rows_ = (size_.height - (margin_.height * 2) + space_.height) / (cell_.height + space_.height);
+	columns_ = (size_.width - (margin_.width * 2) + space_.width) / (cell_.width + space_.width);
+}
+
+auto sprite::get_image() const noexcept -> const image&
+{
+        return image_;
+}
+
+auto sprite::get_image() noexcept -> image&
+{
+        return image_;
+}
+
+auto sprite::get_cell() const noexcept -> const size&
+{
+        return cell_;
+}
+
+auto sprite::get_margin() const noexcept -> const size&
+{
+        return margin_;
+}
+
+auto sprite::get_space() const noexcept -> const size&
+{
+        return space_;
+}
+
+auto sprite::get_rows() const noexcept -> unsigned
+{
+        return rows_;
+}
+
+auto sprite::get_columns() const noexcept -> unsigned
+{
+        return columns_;
+}
+
+auto sprite::get_total() const noexcept -> unsigned
+{
+        return columns_ * rows_;
+}
+
+void sprite::draw(window& window, unsigned cell, const point& point)
+{
+	assert(cell < rows_ * columns_);
+
+	// Compute index in the grid.
+	unsigned hindex = (cell % columns_);
+	unsigned vindex = (cell / columns_);
+
+	// Compute the pixel boundaries.
+	int x = margin_.width + (hindex * space_.width) + (hindex * cell_.width);
+	int y = margin_.height + (vindex * space_.height) + (vindex * cell_.height);
+
+	rectangle source{x, y, cell_.width, cell_.height};
+	rectangle target{point.x, point.y, cell_.width, cell_.height};
+
+	image_.draw(window, source, target);
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/sprite.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,136 @@
+/*
+ * sprite.hpp -- image sprite
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_SPRITE_HPP
+#define MALIKANIA_CLIENT_SPRITE_HPP
+
+/**
+ * \file sprite.hpp
+ * \brief Sprite description.
+ */
+
+#include "image.hpp"
+
+namespace mlk {
+
+struct point;
+
+namespace client {
+
+/**
+ * \brief A Sprite is an image divided into cells.
+ */
+class sprite {
+private:
+	image image_;
+	size cell_;
+	size margin_;
+	size space_;
+	size size_;
+	unsigned rows_;
+	unsigned columns_;
+
+public:
+	/**
+	 * Construct a sprite.
+	 *
+	 * \pre cell must not have height or width null
+	 * \param image the image to use
+	 * \param cell size of cell in the image
+	 * \param margin the optional space from borders
+	 * \param space the optional space between cells
+	 * \param dimension the sprite size (if 0, taken from the image)
+	 */
+	sprite(image image,
+	       size cell,
+	       size margin = { 0, 0 },
+	       size space = { 0, 0 },
+	       size dimension = { 0, 0 }) noexcept;
+
+	/**
+	 * Get the underlying image.
+	 *
+	 * \return the image
+	 */
+	auto get_image() const noexcept -> const image&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the image
+	 */
+	auto get_image() noexcept -> image&;
+
+	/**
+	 * Get the cell size.
+	 *
+	 * \return the cell size
+	 */
+	auto get_cell() const noexcept -> const size&;
+
+	/**
+	 * Get the margin size.
+	 *
+	 * \return the margin size
+	 */
+	auto get_margin() const noexcept -> const size&;
+
+	/**
+	 * Get the space size.
+	 *
+	 * \return the space size
+	 */
+	auto get_space() const noexcept -> const size&;
+
+	/**
+	 * Get the number of rows in the grid.
+	 *
+	 * \return the number of rows
+	 */
+	auto get_rows() const noexcept -> unsigned;
+
+	/**
+	 * Get the number of columns in the grid.
+	 *
+	 * \return the number of columns
+	 */
+	auto get_columns() const noexcept -> unsigned;
+
+	/**
+	 * Get the max number of image in the sprite.
+	 *
+	 * \return the total
+	 */
+	auto get_total() const noexcept -> unsigned;
+
+	/**
+	 * Draw the sprite into the window at the specified position.
+	 *
+	 * \pre cell < rows() * columns()
+	 * \param window the window
+	 * \param cell the cell index
+	 * \param position the position in the window
+	 */
+	void draw(window& window, unsigned cell, const point& position);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_SPRITE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,71 @@
+/*
+ * state.hpp -- game client state
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_STATE_HPP
+#define MALIKANIA_CLIENT_STATE_HPP
+
+/**
+ * \file state.hpp
+ * \brief Game client state.
+ */
+
+#include "dispatcher.hpp"
+
+namespace mlk::client {
+
+class client;
+
+/**
+ * \brief Game client state.
+ */
+class state : public dispatcher {
+public:
+	/**
+	 * Default constructor.
+	 */
+	state() noexcept = default;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~state() noexcept = default;
+
+	/**
+	 * Update the state.
+	 *
+	 * \param clt the client
+	 */
+	virtual void update(client& clt)
+	{
+		(void)clt;
+	}
+
+	/**
+	 * Draw the state.
+	 *
+	 * \param clt the client
+	 */
+	virtual void draw(client& clt)
+	{
+		(void)clt;
+	}
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_STATE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/lobby_state.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,76 @@
+/*
+ * lobby_state.cpp -- character management state
+ *
+ * Copyright (c) 2013-2018 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 <malikania/client/client.hpp>
+#include <malikania/client/color.hpp>
+#include <malikania/client/theme.hpp>
+#include <malikania/client/window.hpp>
+
+#include "lobby_state.hpp"
+#include "map_state.hpp"
+
+namespace mlk {
+
+namespace client {
+
+lobby_state::lobby_state(client& client)
+	: client_(client)
+	, names_{"erekin", "luna"}
+{
+}
+
+void lobby_state::handle_key_down(const key_event& ev)
+{
+	if (ev.keycode == key::down)
+		index_ = index_ == names_.size() - 1 ? 0 : index_ + 1;
+	else if (ev.keycode == key::up)
+		index_ = index_ == 0 ? names_.size() - 1 : index_ - 1;
+	else
+		client_.set_state(std::make_unique<map_state>());
+}
+
+void lobby_state::update(client&)
+{
+}
+
+void lobby_state::draw(client& clt)
+{
+	// TODO: lobby_window.
+	auto& win = clt.window();
+
+	win.set_drawing_color(color::from_hex(0xffffffff));
+	win.clear();
+	win.set_drawing_color(color::from_hex(0xff000000));
+	win.draw_text("Select your player", theme::get_default().get_font(), {20, 10});
+
+	std::size_t i = 0U;
+	for (const auto& n : names_) {
+		int y = 30 + (10 * i);
+
+		if (i++ == index_)
+			win.draw_text("»", theme::get_default().get_font(), {10, y});
+
+		win.draw_text(n, theme::get_default().get_font(), {20, y});
+	}
+
+	win.present();
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/lobby_state.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,55 @@
+/*
+ * lobby_state.hpp -- character management state
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOBBY_STATE_HPP
+#define MALIKANIA_CLIENT_LOBBY_STATE_HPP
+
+/**
+ * \file lobby_state.hpp
+ * \brief Character management state.
+ */
+
+#include <string>
+#include <vector>
+
+#include <malikania/client/state.hpp>
+
+namespace mlk::client {
+
+/**
+ * \brief Character management state.
+ */
+class lobby_state : public state {
+private:
+	client& client_;
+	std::vector<std::string> names_;
+	std::size_t index_{0};
+
+public:
+	lobby_state(client& client);
+
+	void handle_key_down(const key_event& ev) override;
+
+	void update(client&) override;
+
+	void draw(client& clt) override;
+};
+
+} // !mlk::client
+
+#endif // !MALIKANIA_CLIENT_LOBBY_STATE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/login_state.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,133 @@
+/*
+ * login_state.cpp -- login state
+ *
+ * Copyright (c) 2013-2018 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 <malikania/unicode.hpp>
+
+#include <malikania/client/color.hpp>
+#include <malikania/client/client.hpp>
+#include <malikania/client/theme.hpp>
+#include <malikania/client/window.hpp>
+
+#include "login_state.hpp"
+#include "lobby_state.hpp"
+
+namespace mlk {
+
+namespace client {
+
+void login_state::do_connect()
+{
+	state_ = state_t::connecting;
+	status_ = "Connecting...";
+	client_.connect("localhost", 3320);
+}
+
+void login_state::do_auth()
+{
+	status_ = "Authenticating";
+	state_ = state_t::authenticating;
+	client_.send({
+		{ "command",    "auth"                          },
+		{ "login",      unicode::to_utf8(login_)        },
+		{ "password",   unicode::to_utf8(password_)     }
+	});
+}
+
+login_state::login_state(client& clt)
+	: client_(clt)
+{
+	client_.window().start_edit();
+}
+
+void login_state::handle_text(const text_event& ev)
+{
+	if (index_ == 0)
+		login_ += ev.text;
+	else
+		password_ += ev.text;
+}
+
+void login_state::update(client&)
+{
+}
+
+void login_state::handle_connect(const std::error_code& code)
+{
+	if (code)
+		status_ = code.message();
+	else
+		do_auth();
+}
+
+void login_state::handle_message(const std::error_code& code, const nlohmann::json& msg)
+{
+	if (code)
+		status_ = code.message();
+	else
+		status_ = msg["error"].get<std::string>();
+}
+
+void login_state::handle_key_down(const key_event& ev)
+{
+	switch (ev.keycode) {
+	case key::tab:
+		index_ ++;
+
+		if (index_ == 2)
+			index_ = 0;
+		break;
+	case key::backspace:
+		if (index_ == 0)
+			login_.pop_back();
+		else
+			password_.pop_back();
+		break;
+	case key::enter:
+		if (index_ == 1 && state_ == state_t::disconnected)
+			do_connect();
+		break;
+	default:
+		break;
+	}
+}
+
+void login_state::draw(client& clt)
+{
+	auto& win = clt.window();
+	const auto& font = theme::get_default().get_font();
+
+	win.set_drawing_color(color::from_hex(0xffffffff));
+	win.clear();
+	win.set_drawing_color(color::from_hex(0xff000000));
+	win.draw_text("Please log in", font, {10, 10});
+	win.draw_text("login: ", font, {10, 50});
+	win.draw_text("password: ", font, {10, 70});
+
+	if (!login_.empty())
+		win.draw_text(unicode::to_utf8(login_), font, {70, 50});
+	if (!password_.empty())
+		win.draw_text(std::string(password_.length(), '*'), font, {70, 70});
+	if (!status_.empty())
+		win.draw_text(status_, font, {70, 90});
+
+	win.present();
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/login_state.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,69 @@
+/*
+ * login_state.cpp -- login state
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_LOGIN_STATE_HPP
+#define MALIKANIA_CLIENT_LOGIN_STATE_HPP
+
+/**
+ * \file login_state.hpp
+ * \brief Login state.
+ */
+
+#include <malikania/client/state.hpp>
+
+namespace mlk {
+
+namespace client {
+
+/**
+ * \brief Login state.
+ */
+class login_state : public state {
+private:
+	enum class state_t {
+		disconnected,
+		connecting,
+		authenticating
+	};
+
+	client& client_;
+	state_t state_{state_t::disconnected};
+	std::size_t index_{0};
+	std::u32string login_;
+	std::u32string password_;
+	std::string status_;
+
+	void do_connect();
+	void do_auth();
+
+public:
+	login_state(client& clt);
+
+	void update(client&) override;
+	void draw(client& clt) override;
+	void handle_connect(const std::error_code& code) override;
+	void handle_message(const std::error_code& code, const nlohmann::json& msg) override;
+	void handle_key_down(const key_event& ev) override;
+	void handle_text(const text_event& ev) override;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_LOGIN_STATE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/map_state.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,66 @@
+#include <malikania/point.hpp>
+#include <malikania/rectangle.hpp>
+
+#include <malikania/client/client.hpp>
+#include <malikania/client/color.hpp>
+#include <malikania/client/theme.hpp>
+#include <malikania/client/window.hpp>
+
+#include "map_state.hpp"
+
+namespace mlk {
+
+namespace client {
+
+void map_state::handle_key_down(const key_event& ev)
+{
+	if (ev.keycode == key::down)
+		delta_ = {delta_.x, 1};
+	else if (ev.keycode == key::up)
+		delta_ = {delta_.x, -1};
+	else if (ev.keycode == key::left)
+		delta_ = {-1, delta_.y};
+	else if (ev.keycode == key::right)
+		delta_ = {1, delta_.y};
+}
+
+void map_state::handle_key_up(const key_event& ev)
+{
+	if (ev.keycode == key::down || ev.keycode == key::up)
+		delta_ = {delta_.x, 0};
+	else if (ev.keycode == key::left || ev.keycode == key::right)
+		delta_ = {0, delta_.y};
+}
+
+void map_state::update(client&)
+{
+	position_ = {
+		position_.x + delta_.x,
+		position_.y + delta_.y
+	};
+}
+
+void map_state::draw(client& clt)
+{
+	auto& win = clt.window();
+
+	win.set_drawing_color(color::from_hex(0xffffffff));
+	win.clear();
+	win.set_drawing_color(color::from_hex(0xff000000));
+
+	// Draw a little square as user position.
+	win.fill_rectangle({ position_.x, position_.y, 10, 10 });
+
+	// Draw nickname on top of it.
+	auto clip = theme::get_default().get_font().clip("molko");
+
+	win.draw_text("molko", theme::get_default().get_font(), point{
+		static_cast<int>(position_.x - (clip.width / 2)),
+		static_cast<int>(position_.y - clip.height - 10)
+	});
+	win.present();
+}
+
+} // !client
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/state/map_state.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,54 @@
+/*
+ * map_state.hpp -- main game state
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_MAP_STATE_HPP
+#define MALIKANIA_CLIENT_MAP_STATE_HPP
+
+/**
+ * \file map_state.hpp
+ * \brief Main game state.
+ */
+
+#include <malikania/point.hpp>
+
+#include <malikania/client/state.hpp>
+
+namespace mlk {
+
+namespace client {
+
+/**
+ * \brief Main game state.
+ */
+class map_state : public state {
+private:
+	point position_{320, 240};
+	point delta_;
+
+public:
+	void update(client&) override;
+	void draw(client& clt) override;
+	void handle_key_down(const key_event& ev) override;
+	void handle_key_up(const key_event& ev) override;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_MAP_STATE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/theme.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,106 @@
+/*
+ * theme.cpp -- theming support for gui elements
+ *
+ * Copyright (c) 2013-2018 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 <malikania/point.hpp>
+#include <malikania/rectangle.hpp>
+
+#include "button.hpp"
+#include "color.hpp"
+#include "label.hpp"
+#include "theme.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+namespace {
+
+#include <dejavu_sans.hpp>
+
+} // !namespace
+
+std::unique_ptr<theme> theme::global;
+
+auto theme::get_default() noexcept -> theme&
+{
+	if (!global)
+		global = std::make_unique<theme>();
+
+	return *global;
+}
+
+void theme::set_default(std::unique_ptr<theme> theme) noexcept
+{
+	global = std::move(theme);
+}
+
+theme::theme()
+	: font_(std::string(dejavu_sans, sizeof (dejavu_sans)), 10)
+	, background_color_{228, 228, 228, 255}
+	, border_color_{102, 102, 102, 255}
+	, text_color_{50, 50, 50, 255}
+{
+}
+
+auto theme::get_font() const noexcept -> const font&
+{
+	return font_;
+}
+
+auto theme::get_font() noexcept -> font&
+{
+	return font_;
+}
+
+auto theme::get_background_color() const noexcept -> const color&
+{
+	return background_color_;
+}
+
+auto theme::get_border_color() const noexcept -> const color&
+{
+	return border_color_;
+}
+
+auto theme::get_text_color() const noexcept -> const color&
+{
+	return text_color_;
+}
+
+void theme::draw_button(window& w, const button& b, const rectangle& rect)
+{
+	// Border.
+	w.set_drawing_color(border_color_);
+	w.draw_rectangle({ rect.x, rect.y, rect.width, rect.height });
+
+	// Box content.
+	w.set_drawing_color(background_color_);
+	w.fill_rectangle({ rect.x + 2, rect.y + 2, rect.width - 2, rect.height - 2 });
+
+	// Text.
+	// TODO: alignment.
+	w.set_drawing_color(text_color_);
+	w.draw_text(b.get_text(), font_, point{rect.x + 5, rect.y + 5});
+}
+
+void theme::draw_label(window& w, const label& label, const rectangle& rect)
+{
+	w.set_drawing_color(text_color_);
+	w.draw_text(label.get_text(), font_, point{rect.x, rect.y});
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/theme.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,128 @@
+/*
+ * theme.hpp -- theming support for gui elements
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_THEME_HPP
+#define MALIKANIA_CLIENT_THEME_HPP
+
+/**
+ * \file theme.hpp
+ * \brief Theming support for gui elements.
+ */
+
+#include <memory>
+
+#include <malikania/size.hpp>
+
+#include "color.hpp"
+#include "font.hpp"
+
+namespace mlk {
+
+struct rectangle;
+
+namespace client {
+
+class button;
+class label;
+class window;
+
+/**
+ * \brief Reimplement this class to provide your own theme.
+ */
+class theme {
+private:
+	static std::unique_ptr<theme> global;
+
+	font font_;
+	color background_color_;
+	color border_color_;
+	color text_color_;
+
+public:
+	static auto get_default() noexcept -> theme&;
+
+	static void set_default(std::unique_ptr<theme> theme) noexcept;
+
+	/**
+	 * Default constructor.
+	 */
+	theme();
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~theme() noexcept = default;
+
+	/**
+	 * Get the font.
+	 *
+	 * \return the font
+	 */
+	virtual auto get_font() const noexcept -> const font&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the font
+	 */
+	virtual auto get_font() noexcept -> font&;
+
+	/**
+	 * Get the background color for basic widgets.
+	 *
+	 * \return the color
+	 */
+	virtual auto get_background_color() const noexcept -> const color&;
+
+	/**
+	 * Get the border color for basic widgets.
+	 *
+	 * \return the color
+	 */
+	virtual auto get_border_color() const noexcept -> const color&;
+
+	/**
+	 * Get the text color for basic widgets.
+	 *
+	 * \return the color
+	 */
+	virtual auto get_text_color() const noexcept -> const color&;
+
+	/**
+	 * Draw a button.
+	 *
+	 * \param w the main window
+	 * \param b the button
+	 */
+	virtual void draw_button(window& w, const button& b, const rectangle& rect);
+
+	/**
+	 * Draw a label.
+	 *
+	 * \param w the main window
+	 * \param l the label
+	 * \param rect the bounding rectangle.
+	 */
+	virtual void draw_label(window& w, const label& l, const rectangle& rect);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_THEME_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/widget.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,54 @@
+/*
+ * widget.cpp -- basic abstract widget for GUI
+ *
+ * Copyright (c) 2013-2018 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 "theme.hpp"
+#include "widget.hpp"
+
+namespace mlk::client {
+
+widget::widget() noexcept
+	: theme_(std::addressof(theme::get_default()))
+{
+}
+
+auto widget::get_position() const noexcept -> const point&
+{
+	return position_;
+}
+
+void widget::set_position(point position) noexcept
+{
+	position_ = std::move(position);
+}
+
+auto widget::get_theme() const noexcept -> const theme&
+{
+	return *theme_;
+}
+
+auto widget::get_theme() noexcept -> theme&
+{
+	return *theme_;
+}
+
+void widget::set_theme(theme& theme) noexcept
+{
+	theme_ = std::addressof(theme);
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/widget.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,110 @@
+/*
+ * widget.hpp -- basic abstract widget for GUI
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_WIDGET_HPP
+#define MALIKANIA_CLIENT_WIDGET_HPP
+
+/**
+ * \file widget.hpp
+ * \brief Basic abstract widget for GUI
+ */
+
+#include <malikania/point.hpp>
+
+#include "dispatcher.hpp"
+
+namespace mlk {
+
+struct rectangle;
+struct size;
+
+namespace client {
+
+class window;
+class theme;
+
+/**
+ * \brief Abstract widget
+ */
+class widget : public dispatcher {
+protected:
+	theme* theme_;                  //!< widget theme
+	point position_;                //!< widget position
+
+public:
+	/**
+	 * Constructor.
+	 *
+	 * Initialize the theme with the global one.
+	 */
+	widget() noexcept;
+
+	/**
+	 * Default destructor.
+	 */
+	virtual ~widget() noexcept = default;
+
+	/**
+	 * Get the widget position.
+	 *
+	 * \return the position
+	 */
+	auto get_position() const noexcept -> const point&;
+
+	/**
+	 * Set the widget position.
+	 *
+	 * \param position the new position
+	 */
+	void set_position(point position) noexcept;
+
+	/**
+	 * Get the theme.
+	 *
+	 * \return the theme
+	 */
+	auto get_theme() const noexcept -> const theme&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the theme
+	 */
+	auto get_theme() noexcept -> theme&;
+
+	/**
+	 * Change the widget theme.
+	 *
+	 * \param theme the new theme
+	 */
+	void set_theme(theme& theme) noexcept;
+
+	/**
+	 * Draw the widget at the specified position.
+	 *
+	 * \param w the window
+	 * \param size the maximum size allowed
+	 */
+	virtual void draw(window& w, const size& size) = 0;
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_WIDGET_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/window.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,588 @@
+/*
+ * window.cpp -- main window and basic drawing
+ *
+ * Copyright (c) 2013-2018 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 <iostream>
+#include <stdexcept>
+
+#include <malikania/line.hpp>
+#include <malikania/rectangle.hpp>
+#include <malikania/unicode.hpp>
+
+#include "color.hpp"
+#include "theme.hpp"
+#include "widget.hpp"
+#include "window.hpp"
+
+namespace mlk::client {
+
+namespace {
+
+/*
+ * conversion maps
+ * ------------------------------------------------------------------
+ *
+ * There is a difference between scancode and key codes. The scancode is the
+ * key located on the keyboard which is always the same, the key code is the
+ * translated to the user layout.
+ *
+ * For example, on azerty, hitting the 'a' key makes a key code of 'a' but a
+ * scancode of 'q'.
+ *
+ * - scancodes_map	: convert SDL scancode do key
+ * - keycodes_map	: convert SDL key code to key
+ * - modifiers_map	: convert masks
+ * - button_map		: convert SDL mouse buttons
+ */
+
+const std::unordered_map<SDL_Keycode, key> scancodes_map{
+	{ SDL_SCANCODE_A,                       key::a                  },
+	{ SDL_SCANCODE_B,                       key::b                  },
+	{ SDL_SCANCODE_C,                       key::c                  },
+	{ SDL_SCANCODE_D,                       key::d                  },
+	{ SDL_SCANCODE_E,                       key::e                  },
+	{ SDL_SCANCODE_F,                       key::f                  },
+	{ SDL_SCANCODE_G,                       key::g                  },
+	{ SDL_SCANCODE_H,                       key::h                  },
+	{ SDL_SCANCODE_I,                       key::i                  },
+	{ SDL_SCANCODE_J,                       key::j                  },
+	{ SDL_SCANCODE_K,                       key::k                  },
+	{ SDL_SCANCODE_L,                       key::l                  },
+	{ SDL_SCANCODE_M,                       key::m                  },
+	{ SDL_SCANCODE_N,                       key::n                  },
+	{ SDL_SCANCODE_O,                       key::o                  },
+	{ SDL_SCANCODE_P,                       key::p                  },
+	{ SDL_SCANCODE_Q,                       key::q                  },
+	{ SDL_SCANCODE_R,                       key::r                  },
+	{ SDL_SCANCODE_S,                       key::s                  },
+	{ SDL_SCANCODE_T,                       key::t                  },
+	{ SDL_SCANCODE_U,                       key::u                  },
+	{ SDL_SCANCODE_V,                       key::v                  },
+	{ SDL_SCANCODE_W,                       key::w                  },
+	{ SDL_SCANCODE_X,                       key::x                  },
+	{ SDL_SCANCODE_Y,                       key::y                  },
+	{ SDL_SCANCODE_Z,                       key::z                  },
+	{ SDL_SCANCODE_1,                       key::one                },
+	{ SDL_SCANCODE_2,                       key::two                },
+	{ SDL_SCANCODE_3,                       key::three              },
+	{ SDL_SCANCODE_4,                       key::four               },
+	{ SDL_SCANCODE_5,                       key::five               },
+	{ SDL_SCANCODE_6,                       key::six                },
+	{ SDL_SCANCODE_7,                       key::seven              },
+	{ SDL_SCANCODE_8,                       key::eight              },
+	{ SDL_SCANCODE_9,                       key::nine               },
+	{ SDL_SCANCODE_0,                       key::zero               },
+	{ SDL_SCANCODE_RETURN,                  key::enter              },
+	{ SDL_SCANCODE_ESCAPE,                  key::escape             },
+	{ SDL_SCANCODE_BACKSPACE,               key::backspace          },
+	{ SDL_SCANCODE_TAB,                     key::tab                },
+	{ SDL_SCANCODE_SPACE,                   key::space              },
+	{ SDL_SCANCODE_MINUS,                   key::minus              },
+	{ SDL_SCANCODE_EQUALS,                  key::equals             },
+	{ SDL_SCANCODE_LEFTBRACKET,             key::left_bracket       },
+	{ SDL_SCANCODE_RIGHTBRACKET,            key::right_bracket      },
+	{ SDL_SCANCODE_BACKSLASH,               key::backslash          },
+	{ SDL_SCANCODE_NONUSHASH,               key::hash               },
+	{ SDL_SCANCODE_SEMICOLON,               key::semicolon          },
+	{ SDL_SCANCODE_APOSTROPHE,              key::apostrophe         },
+	{ SDL_SCANCODE_GRAVE,                   key::grave              },
+	{ SDL_SCANCODE_COMMA,                   key::comma              },
+	{ SDL_SCANCODE_PERIOD,                  key::period             },
+	{ SDL_SCANCODE_SLASH,                   key::slash              },
+	{ SDL_SCANCODE_CAPSLOCK,                key::caps_lock          },
+	{ SDL_SCANCODE_F1,                      key::f1                 },
+	{ SDL_SCANCODE_F2,                      key::f2                 },
+	{ SDL_SCANCODE_F3,                      key::f3                 },
+	{ SDL_SCANCODE_F4,                      key::f4                 },
+	{ SDL_SCANCODE_F5,                      key::f5                 },
+	{ SDL_SCANCODE_F6,                      key::f6                 },
+	{ SDL_SCANCODE_F7,                      key::f7                 },
+	{ SDL_SCANCODE_F8,                      key::f8                 },
+	{ SDL_SCANCODE_F9,                      key::f9                 },
+	{ SDL_SCANCODE_F10,                     key::f10                },
+	{ SDL_SCANCODE_F11,                     key::f11                },
+	{ SDL_SCANCODE_F12,                     key::f12                },
+	{ SDL_SCANCODE_F13,                     key::f13                },
+	{ SDL_SCANCODE_F14,                     key::f14                },
+	{ SDL_SCANCODE_F15,                     key::f15                },
+	{ SDL_SCANCODE_F16,                     key::f16                },
+	{ SDL_SCANCODE_F17,                     key::f17                },
+	{ SDL_SCANCODE_F18,                     key::f18                },
+	{ SDL_SCANCODE_F19,                     key::f19                },
+	{ SDL_SCANCODE_F20,                     key::f20                },
+	{ SDL_SCANCODE_F21,                     key::f21                },
+	{ SDL_SCANCODE_F22,                     key::f22                },
+	{ SDL_SCANCODE_F23,                     key::f23                },
+	{ SDL_SCANCODE_F24,                     key::f24                },
+	{ SDL_SCANCODE_PRINTSCREEN,             key::print_screen       },
+	{ SDL_SCANCODE_SCROLLLOCK,              key::scroll_lock        },
+	{ SDL_SCANCODE_PAUSE,                   key::pause              },
+	{ SDL_SCANCODE_INSERT,                  key::insert             },
+	{ SDL_SCANCODE_HOME,                    key::home               },
+	{ SDL_SCANCODE_PAGEUP,                  key::page_up            },
+	{ SDL_SCANCODE_PAGEDOWN,                key::page_down          },
+	{ SDL_SCANCODE_DELETE,                  key::del                },
+	{ SDL_SCANCODE_END,                     key::end                },
+	{ SDL_SCANCODE_RIGHT,                   key::right              },
+	{ SDL_SCANCODE_LEFT,                    key::left               },
+	{ SDL_SCANCODE_DOWN,                    key::down               },
+	{ SDL_SCANCODE_UP,                      key::up	                },
+	{ SDL_SCANCODE_KP_DIVIDE,               key::kp_divide          },
+	{ SDL_SCANCODE_KP_MULTIPLY,             key::kp_multiply        },
+	{ SDL_SCANCODE_KP_MINUS,                key::kp_minus           },
+	{ SDL_SCANCODE_KP_PLUS,                 key::kp_plus            },
+	{ SDL_SCANCODE_KP_ENTER,                key::kp_enter           },
+	{ SDL_SCANCODE_KP_1,                    key::kp_one             },
+	{ SDL_SCANCODE_KP_2,                    key::kp_two             },
+	{ SDL_SCANCODE_KP_3,                    key::kp_three           },
+	{ SDL_SCANCODE_KP_4,                    key::kp_four            },
+	{ SDL_SCANCODE_KP_5,                    key::kp_five            },
+	{ SDL_SCANCODE_KP_6,                    key::kp_six             },
+	{ SDL_SCANCODE_KP_7,                    key::kp_seven           },
+	{ SDL_SCANCODE_KP_8,                    key::kp_eight           },
+	{ SDL_SCANCODE_KP_9,                    key::kp_nine            },
+	{ SDL_SCANCODE_KP_0,                    key::kp_zero            },
+	{ SDL_SCANCODE_MUTE,                    key::mute               },
+	{ SDL_SCANCODE_VOLUMEUP,                key::volume_up          },
+	{ SDL_SCANCODE_VOLUMEDOWN,              key::volume_down        },
+	{ SDL_SCANCODE_LCTRL,                   key::left_control       },
+	{ SDL_SCANCODE_LALT,                    key::left_alt           },
+	{ SDL_SCANCODE_LSHIFT,                  key::left_shift         },
+	{ SDL_SCANCODE_LGUI,                    key::left_super         },
+	{ SDL_SCANCODE_RCTRL,                   key::right_control      },
+	{ SDL_SCANCODE_RALT,                    key::right_alt          },
+	{ SDL_SCANCODE_RSHIFT,                  key::right_shift        },
+	{ SDL_SCANCODE_RGUI,                    key::right_super        }
+};
+
+const std::unordered_map<SDL_Keycode, key> keycodes_map{
+	{ 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_parenthese    },
+	{ SDLK_RIGHTPAREN,                      key::right_parenthese   },
+	{ 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::zero               },
+	{ SDLK_1,                               key::one                },
+	{ SDLK_2,                               key::two                },
+	{ SDLK_3,                               key::three              },
+	{ SDLK_4,                               key::four               },
+	{ SDLK_5,                               key::five               },
+	{ SDLK_6,                               key::six                },
+	{ SDLK_7,                               key::seven              },
+	{ SDLK_8,                               key::eight              },
+	{ SDLK_9,                               key::nine               },
+	{ 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::back_quote         },
+	{ 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::caps_lock          },
+	{ 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::print_screen       },
+	{ SDLK_SCROLLLOCK,                      key::scroll_lock        },
+	{ SDLK_PAUSE,                           key::pause              },
+	{ SDLK_INSERT,                          key::insert             },
+	{ SDLK_HOME,                            key::home               },
+	{ SDLK_PAGEUP,                          key::page_up            },
+	{ SDLK_PAGEDOWN,                        key::page_down          },
+	{ SDLK_DELETE,                          key::del                },
+	{ SDLK_END,                             key::end                },
+	{ 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_one             },
+	{ SDLK_KP_2,                            key::kp_two             },
+	{ SDLK_KP_3,                            key::kp_three           },
+	{ SDLK_KP_4,                            key::kp_four            },
+	{ SDLK_KP_5,                            key::kp_five            },
+	{ SDLK_KP_6,                            key::kp_six             },
+	{ SDLK_KP_7,                            key::kp_seven           },
+	{ SDLK_KP_8,                            key::kp_eight           },
+	{ SDLK_KP_9,                            key::kp_nine            },
+	{ SDLK_KP_0,                            key::kp_zero            },
+	{ SDLK_MUTE,                            key::mute               },
+	{ SDLK_VOLUMEUP,                        key::volume_up          },
+	{ SDLK_VOLUMEDOWN,                      key::volume_down        },
+	{ SDLK_LCTRL,                           key::left_control       },
+	{ SDLK_LALT,                            key::left_alt           },
+	{ SDLK_LSHIFT,                          key::left_shift         },
+	{ SDLK_LGUI,                            key::left_super         },
+	{ SDLK_RCTRL,                           key::right_control      },
+	{ SDLK_RALT,                            key::right_alt          },
+	{ SDLK_RSHIFT,                          key::right_shift        },
+	{ SDLK_RGUI,                            key::right_super        }
+};
+
+const std::unordered_map<SDL_Keymod, mod> modifiers_map{
+	{ KMOD_LCTRL,                           mod::left_control       },
+	{ KMOD_LALT,                            mod::left_alt           },
+	{ KMOD_LSHIFT,                          mod::left_shift         },
+	{ KMOD_LGUI,                            mod::left_super         },
+	{ KMOD_RCTRL,                           mod::right_control      },
+	{ KMOD_RALT,                            mod::right_alt          },
+	{ KMOD_RSHIFT,                          mod::right_shift        },
+	{ KMOD_RGUI,                            mod::right_super        },
+	{ KMOD_NUM,                             mod::num_lock           },
+	{ KMOD_CAPS,                            mod::caps_lock          }
+};
+
+const std::unordered_map<Uint32, mouse> button_map{
+	{ SDL_BUTTON_LEFT,                      mouse::left             },
+	{ SDL_BUTTON_RIGHT,                     mouse::right            },
+	{ SDL_BUTTON_MIDDLE,                    mouse::middle           }
+};
+
+} // !namespace
+
+void window::dispatch_key_event(dispatcher& dp, const SDL_Event& ev) const
+{
+	assert(ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP);
+
+	// Key and scan code.
+	const auto kc = keycodes_map.find(ev.key.keysym.sym);
+	const auto sc = scancodes_map.find(ev.key.keysym.scancode);
+
+	if (kc == keycodes_map.end() || sc == scancodes_map.end())
+		return;
+
+	key_event kev{kc->second, sc->second, mod::none};
+
+	// Modifier is a mask.
+	for (const auto& pair : modifiers_map)
+		if (ev.key.keysym.mod & pair.first)
+			kev.modifiers |= pair.second;
+
+	if (ev.type == SDL_KEYDOWN)
+		dp.handle_key_down(kev);
+	else
+		dp.handle_key_up(kev);
+}
+
+void window::dispatch_mouse_event(dispatcher& dp, const SDL_Event& ev) const
+{
+	assert(ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP);
+
+	const auto which = button_map.find(ev.button.button);
+
+	if (which == button_map.end())
+		return;
+
+	mouse_click_event mev{
+		which->second,
+		point{ev.button.x, ev.button.y}
+	};
+
+	if (ev.type == SDL_MOUSEBUTTONDOWN)
+		dp.handle_mouse_down(mev);
+	else
+		dp.handle_mouse_up(mev);
+}
+
+void window::dispatch_mouse_wheel_event(dispatcher& dp, const SDL_Event& ev) const
+{
+	assert(ev.type == SDL_MOUSEWHEEL);
+
+	dp.handle_mouse_wheel({{ev.wheel.x, ev.wheel.y}});
+}
+
+void window::dispatch_text_event(dispatcher& dp, const SDL_Event& ev) const
+{
+	assert(ev.type == SDL_TEXTINPUT);
+
+	dp.handle_text({unicode::to_utf32(ev.text.text)});
+}
+
+
+void window::init()
+{
+	static bool is_initialized{false};
+
+	if (!is_initialized) {
+		if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
+			throw std::runtime_error(SDL_GetError());
+		if (TTF_Init() < 0)
+			throw std::runtime_error(TTF_GetError());
+
+		is_initialized = true;
+	}
+}
+
+window::window(unsigned width, unsigned height, const std::string& title)
+{
+	init();
+
+	window_ = {
+		SDL_CreateWindow(
+			title.c_str(),
+			SDL_WINDOWPOS_UNDEFINED,
+			SDL_WINDOWPOS_UNDEFINED,
+			width, height,
+			SDL_WINDOW_OPENGL
+		),
+		SDL_DestroyWindow
+	};
+
+	if (window_ == nullptr)
+		throw std::runtime_error(SDL_GetError());
+
+	// Create renderer.
+	renderer_ = {
+		SDL_CreateRenderer(window_.get(), -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC),
+		SDL_DestroyRenderer
+	};
+
+	if (renderer_ == nullptr)
+		throw std::runtime_error(SDL_GetError());
+}
+
+auto window::get_renderer() const noexcept -> const SDL_Renderer*
+{
+	assert(is_open_);
+
+	return renderer_.get();
+}
+
+auto window::get_renderer() noexcept -> SDL_Renderer*
+{
+	assert(is_open_);
+
+	return renderer_.get();
+}
+
+auto window::is_open() const noexcept -> bool
+{
+	return is_open_;
+}
+
+void window::start_edit()
+{
+	if (!is_editing_) {
+		SDL_StartTextInput();
+		is_editing_ = true;
+	}
+}
+
+void window::stop_edit()
+{
+	if (is_editing_) {
+		SDL_StopTextInput();
+		is_editing_ = false;
+	}
+}
+
+void window::clear()
+{
+	SDL_RenderClear(renderer_.get());
+}
+
+void window::present()
+{
+	SDL_RenderPresent(renderer_.get());
+}
+
+void window::close() noexcept
+{
+	is_open_ = false;
+	renderer_ = nullptr;
+	window_ = nullptr;
+}
+
+auto window::get_drawing_color() const -> color
+{
+	SDL_Color color;
+
+	if (SDL_GetRenderDrawColor(renderer_.get(), &color.r, &color.g, &color.b, &color.a) < 0)
+		throw std::runtime_error(SDL_GetError());
+
+	return { color.r, color.g, color.b, color.a };
+}
+
+void window::set_drawing_color(const color& color)
+{
+	if (SDL_SetRenderDrawColor(renderer_.get(), color.red, color.green, color.blue, color.alpha) < 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void window::draw_line(const line& line)
+{
+	if (SDL_RenderDrawLine(renderer_.get(), line.x1, line.y1, line.x2, line.y2) != 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void window::draw_point(const point& point)
+{
+	if (SDL_RenderDrawPoint(renderer_.get(), point.x, point.y) != 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void window::draw_rectangle(const rectangle& rectangle)
+{
+	SDL_Rect rect{
+		rectangle.x,
+		rectangle.y,
+		static_cast<int>(rectangle.width),
+		static_cast<int>(rectangle.height)
+	};
+
+	if (SDL_RenderDrawRect(renderer_.get(), &rect) < 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void window::fill_rectangle(const rectangle& rectangle)
+{
+	SDL_Rect rect{
+		rectangle.x,
+		rectangle.y,
+		static_cast<int>(rectangle.width),
+		static_cast<int>(rectangle.height)
+	};
+
+	if (SDL_RenderFillRect(renderer_.get(), &rect) < 0)
+		throw std::runtime_error(SDL_GetError());
+}
+
+void window::draw_text(const std::string& text, const font& font, const rectangle& rectangle)
+{
+	SDL_Color color = { 0, 0, 0, 255 };
+	SDL_Surface* message = TTF_RenderUTF8_Blended(font.get_handle(), text.c_str(), color);
+	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get(), message);
+	SDL_Rect rect = { rectangle.x, rectangle.y, (int)rectangle.width, (int)rectangle.height };
+	SDL_RenderCopy(renderer_.get(), texture, nullptr, &rect);
+	SDL_FreeSurface(message);
+	SDL_DestroyTexture(texture);
+}
+
+void window::draw_text(const std::string& text, const font& font, const point& point)
+{
+	SDL_Color color = { 0, 0, 0, 0 };
+	SDL_GetRenderDrawColor(renderer_.get(), &color.r, &color.g, &color.b, &color.a);
+	SDL_Surface* message = TTF_RenderUTF8_Blended(font.get_handle(), text.c_str(), color);
+	SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer_.get(), message);
+	SDL_Rect rect = { point.x, point.y, message->w, message->h };
+	SDL_RenderCopy(renderer_.get(), texture, nullptr, &rect);
+	SDL_FreeSurface(message);
+	SDL_DestroyTexture(texture);
+}
+
+void window::poll(dispatcher& dp)
+{
+	SDL_Event event;
+
+	while (SDL_PollEvent(&event)) {
+		switch (event.type) {
+		case SDL_KEYUP:
+		case SDL_KEYDOWN:
+			dispatch_key_event(dp, event);
+			break;
+		case SDL_MOUSEBUTTONDOWN:
+		case SDL_MOUSEBUTTONUP:
+			dispatch_mouse_event(dp, event);
+			break;
+		case SDL_MOUSEWHEEL:
+			dispatch_mouse_wheel_event(dp, event);
+			break;
+		case SDL_TEXTINPUT:
+			dispatch_text_event(dp, event);
+			break;
+		case SDL_QUIT:
+			dp.handle_quit();
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+} // !mlk::client
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-client/malikania/client/window.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,212 @@
+/*
+ * window.hpp -- main window and basic drawing
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_CLIENT_WINDOW_HPP
+#define MALIKANIA_CLIENT_WINDOW_HPP
+
+/**
+ * \file window.hpp
+ * \brief Window and drawing.
+ */
+
+#include <memory>
+#include <string>
+
+#include <SDL.h>
+
+#include "dispatcher.hpp"
+
+namespace mlk {
+
+struct line;
+struct point;
+struct rectangle;
+
+namespace client {
+
+struct color;
+
+class font;
+
+/**
+ * \brief Main window class and drawing.
+ */
+class window {
+private:
+	std::unique_ptr<SDL_Window, void (*)(SDL_Window *)> window_{nullptr, nullptr};
+	std::unique_ptr<SDL_Renderer, void (*)(SDL_Renderer *)> renderer_{nullptr, nullptr};
+
+	bool is_open_{true};
+	bool is_editing_{false};
+
+	void dispatch_key_event(dispatcher&, const SDL_Event&) const;
+	void dispatch_mouse_event(dispatcher&, const SDL_Event&) const;
+	void dispatch_mouse_wheel_event(dispatcher&, const SDL_Event&) const;
+	void dispatch_text_event(dispatcher&, const SDL_Event&) const;
+
+	void init();
+
+public:
+	/**
+	 * Create a window.
+	 *
+	 * \param width the initial width
+	 * \param height the initial height
+	 * \param title the optional title
+	 * \throw std::runtime_error on errors
+	 */
+	window(unsigned width = 640, unsigned height = 480, const std::string& title = "Game");
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~window() noexcept = default;
+
+	/**
+	 * Get the renderer.
+	 *
+	 * \pre is_open()
+	 * \return the renderer
+	 */
+	auto get_renderer() const noexcept -> const SDL_Renderer*;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \pre is_open()
+	 * \return the renderer
+	 */
+	auto get_renderer() noexcept -> SDL_Renderer*;
+
+	/**
+	 * Tells if the window is open.
+	 *
+	 * \return true if open
+	 */
+	auto is_open() const noexcept -> bool;
+
+	/**
+	 * Start editing input.
+	 *
+	 * \note may open a visual keyboard on some platforms
+	 * \note can safely be called multiple times
+	 */
+	void start_edit();
+
+	/**
+	 * Stop editing input.
+	 *
+	 * \note may close the visual keyboard on some platforms
+	 * \note can safely be called multiple times
+	 */
+	void stop_edit();
+
+	/**
+	 * Clear the window content with the current drawing color.
+	 */
+	void clear();
+
+	/**
+	 * Render the content of the window to the screen.
+	 */
+	void present();
+
+	/**
+	 * Close the window.
+	 */
+	void close() noexcept;
+
+	/**
+	 * Get the current drawing color.
+	 *
+	 * \return the color
+	 */
+	auto get_drawing_color() const -> color;
+
+	/**
+	 * Set the drawing color.
+	 *
+	 * \param color the color
+	 */
+	void set_drawing_color(const color& color);
+
+	/**
+	 * Draw a line.
+	 *
+	 * \param line the line
+	 */
+	void draw_line(const line& line);
+
+	/**
+	 * Draw a point.
+	 *
+	 * \param point the point
+	 */
+	void draw_point(const point& point);
+
+	/**
+	 * Draw a rectangle (only borders).
+	 *
+	 * \param rect the rectangle
+	 * \see fillRectangle
+	 */
+	void draw_rectangle(const rectangle& rect);
+
+	/**
+	 * Fill the given rectangle with the current color.
+	 *
+	 * \param rect the rectangle
+	 * \see drawRectangle
+	 */
+	void fill_rectangle(const rectangle& rect);
+
+	/**
+	 * Draw some text.
+	 *
+	 * This function may stretch the text texture.
+	 *
+	 * \param text the text (UTF-8)
+	 * \param font the font
+	 * \param rectangle the rectangle target
+	 */
+	void draw_text(const std::string& text, const font& font, const rectangle& rectangle);
+
+	/**
+	 * Overloaded function.
+	 *
+	 * Draw the text at the given position.
+	 *
+	 * \param text the text (UTF-8)
+	 * \param font the font
+	 * \param point the text position
+	 */
+	void draw_text(const std::string& text, const font& font, const point& point);
+
+	/**
+	 * Poll all pending events and call appropriate functions.
+	 *
+	 * \param dispatcher the event dispatcher
+	 */
+	void poll(dispatcher& dp);
+};
+
+} // !client
+
+} // !mlk
+
+#endif // !MALIKANIA_CLIENT_WINDOW_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,50 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk-db)
+
+find_package(PostgreSQL REQUIRED)
+
+set(
+	HEADERS
+	${libmlk-db_SOURCE_DIR}/malikania/db/account.hpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/character.hpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/database.hpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/model.hpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/spell.hpp
+)
+
+set(
+	SOURCES
+	${libmlk-db_SOURCE_DIR}/malikania/db/account.cpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/character.cpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/database.cpp
+	${libmlk-db_SOURCE_DIR}/malikania/db/spell.cpp
+)
+
+malikania_define_library(
+	TARGET libmlk-db
+	SOURCES ${HEADERS} ${SOURCES}
+	LIBRARIES
+		${PostgreSQL_LIBRARIES}
+	PUBLIC_INCLUDES
+		${Boost_INCLUDE_DIRS}
+		${libmlk-db_SOURCE_DIR}/malikania
+	PRIVATE_INCLUDES
+		${PostgreSQL_INCLUDE_DIRS}
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/account.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,242 @@
+/*
+ * account.cpp -- database account object
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include "account.hpp"
+
+namespace mlk::db {
+
+auto account::load(const result& res) -> account
+{
+	account a(PQgetvalue(res.get(), 0, 1), PQgetvalue(res.get(), 0, 2));
+
+	a.id_ = std::stoi(PQgetvalue(res.get(), 0, 0));
+	a.firstname_ = PQgetvalue(res.get(), 0, 3);
+	a.lastname_ = PQgetvalue(res.get(), 0, 4);
+	a.email_ = PQgetvalue(res.get(), 0, 5);
+	a.characters_ = character::load(a);
+
+	return a;
+}
+
+void account::clear()
+{
+	id_ = 0U;
+}
+
+account::account(std::string login, std::string password) noexcept
+	: login_(std::move(login))
+	, password_(std::move(password))
+{
+	assert(!login_.empty());
+	assert(!password_.empty());
+}
+
+auto account::get_login() const noexcept -> const std::string&
+{
+	return login_;
+}
+
+void account::set_password(std::string password)
+{
+	static const std::string sql(
+		"UPDATE account"
+		"   SET password = $1"
+		" WHERE id = $2"
+	);
+
+	assert(!password.empty());
+
+	if (is_published() && password_ != password)
+		db::exec(sql, { password, id_ });
+
+	password_ = std::move(password);
+}
+
+auto account::get_email() const noexcept -> const std::string&
+{
+	return email_;
+}
+
+void account::set_email(std::string email)
+{
+	static const std::string sql(
+		"UPDATE account"
+		"   SET email = $1"
+		" WHERE id = $2"
+	);
+
+	assert(!email.empty());
+
+	if (is_published() && email_ != email)
+		db::exec(sql, { email, id_ });
+
+	email_ = std::move(email);
+}
+
+auto account::get_firstname() const noexcept -> const std::string&
+{
+	return firstname_;
+}
+
+void account::set_firstname(std::string name)
+{
+	static const std::string sql(
+		"UPDATE account"
+		"   SET firstname = $1"
+		" WHERE id = $2"
+	);
+
+	if (is_published() && firstname_ != name)
+		db::exec(sql, { name, id_ });
+
+	firstname_ = std::move(name);
+}
+
+auto account::get_lastname() const noexcept -> const std::string&
+{
+	return lastname_;
+}
+
+void account::set_lastname(std::string name)
+{
+	static const std::string sql(
+		"UPDATE account"
+		"   SET lastname = $1"
+		" WHERE id = $2"
+	);
+
+	if (is_published() && lastname_ != name)
+		db::exec(sql, { name, id_ });
+
+	lastname_ = std::move(name);
+}
+
+auto account::get_characters() const noexcept -> const std::vector<character>&
+{
+	return characters_;
+}
+
+void account::add(character ch)
+{
+	assert(ch.is_draft());
+
+	characters_.reserve(characters_.size() + 1);
+
+	if (is_published())
+		ch.publish(*this);
+
+	characters_.push_back(std::move(ch));
+}
+
+void account::remove(std::vector<character>::iterator it)
+{
+	// TODO: assert 'it' is in vector.
+	it->unpublish();
+	characters_.erase(it);
+}
+
+void account::publish()
+{
+	static const std::string sql(
+		"INSERT INTO account("
+		"  login,"
+		"  password,"
+		"  firstname,"
+		"  lastname,"
+		"  email"
+		") "
+		"VALUES ($1, $2, $3, $4, $5) "
+		"RETURNING id"
+	);
+
+	assert(is_draft());
+
+	/*
+	 * Recursively save the account, its characters and spells of those
+	 * characters.
+	 */
+	transaction txn([this] {
+		clear();
+
+		for (auto& c : characters_)
+			c.clear();
+	});
+
+	const auto r = select(sql, { login_, password_, firstname_, lastname_, email_ });
+
+	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
+
+	for (auto& c : characters_)
+		c.publish(*this);
+
+	txn.commit();
+
+	assert(is_published());
+}
+
+void account::unpublish()
+{
+	static const std::string sql(
+		"DELETE"
+		"  FROM account"
+		" WHERE id = $1"
+	);
+
+	assert(is_published());
+
+	id_ = 0;
+
+	/*
+	 * Recursively make all characters and their spells draft.
+	 */
+	for (auto& c : characters_)
+		c.clear();
+
+	assert(is_draft());
+}
+
+auto account::find_by_login(const std::string& login) -> std::optional<account>
+{
+	static const std::string sql(
+		"SELECT *"
+		"  FROM account"
+		" WHERE login = $1"
+	);
+
+	const auto r = select(sql, { login });
+
+	if (PQntuples(r.get()) == 0)
+		return std::nullopt;
+
+	return load(r);
+}
+
+auto account::authenticate(const std::string& login,
+                           const std::string& password) -> std::optional<account>
+{
+	auto ac = find_by_login(login);
+
+	if (!ac || ac->password_ != password)
+		return std::nullopt;
+
+	return ac;
+}
+
+} // !mlk::db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/account.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,189 @@
+/*
+ * account.hpp -- database account object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_DB_ACCOUNT_HPP
+#define MALIKANIA_DB_ACCOUNT_HPP
+
+/**
+ * \file account.hpp
+ * \brief Database account object.
+ */
+
+#include <optional>
+
+#include "character.hpp"
+#include "database.hpp"
+#include "model.hpp"
+
+namespace mlk::db {
+
+/**
+ * \brief Database account object.
+ */
+class account : public model {
+private:
+	std::string login_;
+	std::string password_;
+	std::string email_;
+	std::string firstname_;
+	std::string lastname_;
+	std::vector<character> characters_;
+
+	static auto load(const result&) -> account;
+
+	void clear();
+
+public:
+	/**
+	 * Create a draft account.
+	 *
+	 * \pre !login.empty()
+	 * \pre !password.empty()
+	 * \param login the login name
+	 * \param password the password
+	 * \warning the password is saved as-is and **must** be hashed by the caller.
+	 */
+	account(std::string login, std::string password) noexcept;
+
+	/**
+	 * Get the account login.
+	 *
+	 * \return the login
+	 */
+	auto get_login() const noexcept -> const std::string&;
+
+	/**
+	 * Set the password.
+	 *
+	 * \pre !password.empty()
+	 * \warning the password is saved as-is and **must** be hashed by the caller.
+	 */
+	void set_password(std::string password);
+
+	/**
+	 * Get the account email.
+	 *
+	 * \return the email
+	 */
+	auto get_email() const noexcept -> const std::string&;
+
+	/**
+	 * Set the account email.
+	 *
+	 * \pre !email.empty()
+	 * \param email the new email
+	 */
+	void set_email(std::string email);
+
+	/**
+	 * Get the account first name.
+	 *
+	 * \return the name
+	 */
+	auto get_firstname() const noexcept -> const std::string&;
+
+	/**
+	 * Set the account firstname.
+	 *
+	 * \param name the new name
+	 */
+	void set_firstname(std::string name);
+
+	/**
+	 * Get the account last name.
+	 *
+	 * \return the name
+	 */
+	auto get_lastname() const noexcept -> const std::string&;
+
+	/**
+	 * Set the account last name.
+	 *
+	 * \param name the new name
+	 */
+	void set_lastname(std::string name);
+
+	/**
+	 * Get the caracter list.
+	 *
+	 * \return the associated characters.
+	 */
+	auto get_characters() const noexcept -> const std::vector<character>&;
+
+	/**
+	 * Add the character to the account.
+	 *
+	 * Account takes ownership of character.
+	 *
+	 * \pre ch.is_draft()
+	 * \param ch the character
+	 * \post ch.is_published()
+	 */
+	void add(character ch);
+
+	/**
+	 * Remove the character from the account.
+	 *
+	 * \pre it is in the the account character list
+	 * \param ch the character
+	 * \post ch == nullptr
+	 */
+	void remove(std::vector<character>::iterator it);
+
+	/**
+	 * Save the account, does nothing if is_published().
+	 *
+	 * \throw std::exception on errors
+	 * \post is_published()
+	 */
+	void publish();
+
+	/**
+	 * Destroy the account.
+	 *
+	 * The account will contains no characters anymore.
+	 *
+	 * \throw std::exception on errors
+	 * \post is_draft()
+	 */
+	void unpublish();
+
+	/**
+	 * Find an account by login.
+	 *
+	 * \param login the login
+	 * \return the account or none if not found
+	 * \throw std::runtime_error on database errors
+	 */
+	static auto find_by_login(const std::string& login) -> std::optional<account>;
+
+	/**
+	 * Find and authenticate a user.
+	 *
+	 * \param login the login
+	 * \param password the password
+	 * \return the account or none if not found
+	 * \throw std::runtime_error on database errors
+	 */
+	static auto authenticate(const std::string& login,
+                                 const std::string& password) -> std::optional<account>;
+};
+
+} // !mlk::db
+
+#endif // !MALIKANIA_DB_ACCOUNT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/character.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,266 @@
+/*
+ * character.cpp -- database character object
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+#include <numeric>
+
+#include "account.hpp"
+#include "character.hpp"
+#include "database.hpp"
+
+namespace mlk::db {
+
+auto character::load(const result& result, int row) -> character
+{
+	character ch(PQgetvalue(result.get(), row, 2), PQgetvalue(result.get(), row, 3));
+
+	ch.levels_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 4));
+	ch.factors_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 5));
+	ch.exp_[stat::hp] = std::stoi(PQgetvalue(result.get(), row, 6));
+
+	ch.levels_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 7));
+	ch.factors_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 8));
+	ch.exp_[stat::force] = std::stoi(PQgetvalue(result.get(), row, 9));
+
+	ch.levels_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 10));
+	ch.factors_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 11));
+	ch.exp_[stat::defense] = std::stoi(PQgetvalue(result.get(), row, 12));
+
+	ch.levels_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 13));
+	ch.factors_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 14));
+	ch.exp_[stat::agility] = std::stoi(PQgetvalue(result.get(), row, 15));
+
+	ch.levels_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 16));
+	ch.factors_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 17));
+	ch.exp_[stat::luck] = std::stoi(PQgetvalue(result.get(), row, 18));
+
+	ch.id_ = std::stoi(PQgetvalue(result.get(), row, 0));
+	ch.spells_ = spell::load(ch);
+
+	return ch;
+}
+
+auto character::load(const account& parent) -> std::vector<character>
+{
+	static const std::string sql(
+		"SELECT *"
+		"  FROM character"
+		" WHERE account_id = $1"
+	);
+
+	const auto r = select(sql, { parent.get_id() });
+
+	if (PQntuples(r.get()) == 0)
+		throw std::runtime_error("failed to load characters");
+
+	std::vector<character> characters;
+
+	for (int i = 0; i < PQntuples(r.get()); ++i)
+		characters.push_back(load(r, i));
+
+	return characters;
+}
+
+void character::clear()
+{
+	id_ = 0U;
+
+	for (auto& s : spells_)
+		s.clear();
+}
+
+void character::publish(account& parent)
+{
+	static const std::string sql(
+		"INSERT INTO character ("
+		"  account_id,"
+		"  nickname,"
+		"  type,"
+		"  hp_level,"
+		"  hp_factor,"
+		"  hp_exp,"
+		"  force_level,"
+		"  force_factor,"
+		"  force_exp,"
+		"  defense_level,"
+		"  defense_factor,"
+		"  defense_exp,"
+		"  agility_level,"
+		"  agility_factor,"
+		"  agility_exp,"
+		"  luck_level,"
+		"  luck_factor,"
+		"  luck_exp"
+		") "
+		"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) "
+		"RETURNING id"
+	);
+
+	const auto r = select(sql, {
+		parent.get_id(),
+		nickname_,
+		type_,
+		levels_[stat::hp],
+		factors_[stat::hp],
+		exp_[stat::hp],
+		levels_[stat::force],
+		factors_[stat::force],
+		exp_[stat::force],
+		levels_[stat::defense],
+		factors_[stat::defense],
+		exp_[stat::defense],
+		levels_[stat::agility],
+		factors_[stat::agility],
+		exp_[stat::agility],
+		levels_[stat::luck],
+		factors_[stat::luck],
+		exp_[stat::luck]
+	});
+
+	if (PQntuples(r.get()) == 0)
+		throw std::runtime_error("failed to save character");
+
+	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
+
+	for (auto& s : spells_)
+		s.publish(*this);
+}
+
+void character::unpublish()
+{
+	static const std::string sql(
+		"DELETE"
+		"  FROM character"
+		" WHERE id = $1"
+	);
+
+	exec(sql, { id_ });
+	clear();
+}
+
+character::character(std::string nickname, std::string type) noexcept
+	: nickname_(std::move(nickname))
+	, type_(std::move(type))
+{
+	assert(!nickname_.empty());
+	assert(!type_.empty());
+}
+
+auto character::get_nickname() const noexcept -> const std::string&
+{
+	return nickname_;
+}
+
+auto character::get_type() const noexcept -> const std::string&
+{
+	return type_;
+}
+
+auto character::get_spells() const noexcept -> const std::vector<spell>&
+{
+	return spells_;
+}
+
+auto character::get_levels() const noexcept -> const std::array<std::uint8_t, 5>&
+{
+	return levels_;
+}
+
+void character::set_levels(std::array<std::uint8_t, 5> levels)
+{
+	static const std::string sql(
+		"UPDATE character"
+		"   SET hp_level = $1"
+		"	 , force_level = $2"
+		"	 , defense_level = $3"
+		"	 , agility_level = $4"
+		"	 , luck_level = $5"
+	);
+
+	if (is_published())
+		exec(sql, { id_, levels[0], levels[1], levels[2], levels[3], levels[4] });
+
+	levels_ = std::move(levels);
+}
+
+auto character::get_factors() const noexcept -> const std::array<std::uint8_t, 5>&
+{
+	return factors_;
+}
+
+void character::set_factors(std::array<std::uint8_t, 5> factors)
+{
+	static const std::string sql(
+		"UPDATE character"
+		"   SET hp_factor = $1"
+		"	 , force_factor = $2"
+		"	 , defense_factor = $3"
+		"	 , agility_factor = $4"
+		"	 , luck_factor = $5"
+	);
+
+	assert(std::accumulate(factors.begin(), factors.end(), 0U) == 100U);
+
+	if (is_published())
+		exec(sql, { id_, factors[0], factors[1], factors[2], factors[3], factors[4] });
+
+	factors_ = std::move(factors);
+}
+
+auto character::get_experience() const noexcept -> const std::array<std::uint32_t, 5>&
+{
+	return exp_;
+}
+
+void character::set_experience(std::array<std::uint32_t, 5> experience)
+{
+	static const std::string sql(
+		"UPDATE character"
+		"   SET hp_experience = $1"
+		"	 , force_experience = $2"
+		"	 , defense_experience = $3"
+		"	 , agility_experience = $4"
+		"	 , luck_experience = $5"
+	);
+
+	if (is_published())
+		exec(sql, { id_, experience[0], experience[1], experience[2], experience[3], experience[4] });
+
+	exp_ = std::move(experience);
+}
+
+void character::add(spell sp)
+{
+	assert(sp.is_draft());
+
+	spells_.reserve(spells_.size() + 1);
+
+	if (is_published())
+		sp.publish(*this);
+
+	spells_.push_back(std::move(sp));
+}
+
+void character::remove(std::vector<spell>::iterator it)
+{
+	// TODO: assert 'it' is in vector.
+	it->unpublish();
+	spells_.erase(it);
+}
+
+} // !mlk::db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/character.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,171 @@
+/*
+ * character.hpp -- database character object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_DB_CHARACTER_HPP
+#define MALIKANIA_DB_CHARACTER_HPP
+
+/*
+ * \file character.hpp
+ * \brief Database character object.
+ */
+
+#include <cstdint>
+#include <array>
+#include <vector>
+
+#include "database.hpp"
+#include "model.hpp"
+#include "spell.hpp"
+
+namespace mlk::db {
+
+class account;
+
+/*
+ * \brief Database character object.
+ */
+class character : public model {
+public:
+	friend class account;
+
+	/**
+	 * \brief Stats index in arrays
+	 * \see set_levels
+	 * \see set_factors
+	 * \see set_experience
+	 */
+	enum stat {
+		hp = 0,                 //!< hp index
+		force,                  //!< force index
+		defense,                //!< defense index
+		agility,                //!< agility index
+		luck                    //!< luck index
+	};
+
+private:
+	std::string nickname_;
+	std::string type_;
+	std::vector<spell> spells_;
+	std::array<std::uint8_t, 5> levels_{1U, 1U, 1U, 1U, 1U};
+	std::array<std::uint8_t, 5> factors_{20U, 20U, 20U, 20U, 20U};
+	std::array<std::uint32_t, 5> exp_{0U, 0U, 0U, 0U, 0U};
+
+	static auto load(const result&, int) -> character;
+	static auto load(const account&) -> std::vector<character>;
+
+	void clear();
+	void publish(account&);
+	void unpublish();
+
+public:
+	/**
+	 * Construct a character, no database is modified yet.
+	 *
+	 * \pre !nickname.empty()
+	 * \pre !type.empty()
+	 * \param nickname the nickname
+	 * \param type the type
+	 */
+	character(std::string nickname, std::string type) noexcept;
+
+	/**
+	 * Get the character nickname.
+	 *
+	 * \return the name
+	 */
+	auto get_nickname() const noexcept -> const std::string&;
+
+	/**
+	 * Get the character type name.
+	 *
+	 * \return the type name
+	 */
+	auto get_type() const noexcept -> const std::string&;
+
+	/**
+	 * Get the list of spells.
+	 *
+	 * \return the spells
+	 */
+	auto get_spells() const noexcept -> const std::vector<spell>&;
+
+	/**
+	 * Get the list of levels.
+	 *
+	 * \return the levels
+	 */
+	auto get_levels() const noexcept -> const std::array<std::uint8_t, 5>&;
+
+	/**
+	 * Set the new levels.
+	 *
+	 * \param levels the levels
+	 */
+	void set_levels(std::array<std::uint8_t, 5> levels);
+
+	/**
+	 * Get the list of factors.
+	 *
+	 * \return the factors
+	 */
+	auto get_factors() const noexcept -> const std::array<std::uint8_t, 5>&;
+
+	/**
+	 * Set progression factors.
+	 *
+	 * \pre factors sum must be 100
+	 * \param factors the factors
+	 */
+	void set_factors(std::array<std::uint8_t, 5> factors);
+
+	/**
+	 * Get the stats experience.
+	 *
+	 * \return the experience
+	 */
+	auto get_experience() const noexcept -> const std::array<std::uint32_t, 5>&;
+
+	/**
+	 * Set stats experience.
+	 *
+	 * \param experience the experience
+	 */
+	void set_experience(std::array<std::uint32_t, 5> experience);
+
+	/**
+	 * Add a spell.
+	 *
+	 * Character takes ownership of spell.
+	 *
+	 * \pre sp.is_draft()
+	 * \param sp the spell (moved)
+	 */
+	void add(spell sp);
+
+	/**
+	 * Remove the spell from the character.
+	 *
+	 * \pre it must be valid
+	 * \param it the spell iterator
+	 */
+	void remove(std::vector<spell>::iterator it);
+};
+
+} // !mlk::db
+
+#endif // !MALIKANIA_DB_CHARACTER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/database.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,210 @@
+/*
+ * database.cpp -- connection to postgresql database
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+#include <sstream>
+
+#include "database.hpp"
+
+namespace mlk::db {
+
+namespace {
+
+struct strify {
+	template <typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
+	auto operator()(T i) const -> std::string
+	{
+		return std::to_string(i);
+	}
+
+	auto operator()(bool v) const -> std::string
+	{
+		return v ? "t" : "f";
+	}
+
+	auto operator()(std::string s) const -> std::string
+	{
+		return s;
+	}
+};
+
+std::unique_ptr<PGconn, void (*)(PGconn*)> connection{nullptr, nullptr};
+
+auto run(const std::string& sql, std::vector<arg> args = {}) -> result
+{
+	std::vector<const char*> list;
+
+	/*
+	 * PQexecParams requires an array of C strings, convert all arguments in the
+	 * args vector to string and then keep the C string value into a temporary
+	 * array.
+	 */
+	for (auto& arg : args) {
+		arg = std::visit(strify(), arg);
+		list.push_back(std::get<std::string>(arg).c_str());
+	}
+
+	return {
+		PQexecParams(connection.get(), sql.c_str(), list.size(), nullptr,
+			list.data(), nullptr, nullptr, 0
+		),
+		PQclear
+	};
+}
+
+} // !namespace
+
+void open(const std::string& host,
+          const std::string& port,
+          const std::string& user,
+          const std::string& database,
+          const std::string& password)
+{
+	assert(!connection);
+
+	std::ostringstream oss;
+
+	if (!host.empty())
+		oss << "host=" << host << " ";
+	if (!port.empty())
+		oss << "port=" << port << " ";
+	if (!user.empty())
+		oss << "user=" << user << " ";
+	if (!database.empty())
+		oss << "dbname=" << database << " ";
+	if (!password.empty())
+		oss << "password=" << password;
+
+	connection = { PQconnectdb(oss.str().c_str()), PQfinish };
+
+	if (PQstatus(connection.get()) != CONNECTION_OK)
+		throw std::runtime_error(PQerrorMessage(connection.get()));
+}
+
+const std::string init_account(
+	"CREATE TABLE IF NOT EXISTS account("
+	"  id SERIAL,"
+	"  login TEXT NOT NULL,"
+	"  password TEXT NOT NULL,"
+	"  firstname TEXT,"
+	"  lastname TEXT,"
+	"  email TEXT,"
+	"  PRIMARY KEY(id)"
+	")"
+);
+
+const std::string init_character(
+	"CREATE TABLE IF NOT EXISTS character("
+	"  id SERIAL,"
+	"  account_id INTEGER NOT NULL,"
+	"  nickname TEXT NOT NULL,"
+	"  type TEXT NOT NULL,"
+	"  hp_level SMALLINT NOT NULL DEFAULT 1,"
+	"  hp_factor SMALLINT NOT NULL,"
+	"  hp_exp INT NOT NULL,"
+	"  force_level SMALLINT NOT NULL DEFAULT 1,"
+	"  force_factor SMALLINT NOT NULL,"
+	"  force_exp INT NOT NULL,"
+	"  defense_level SMALLINT NOT NULL DEFAULT 1,"
+	"  defense_factor SMALLINT NOT NULL,"
+	"  defense_exp INT NOT NULL,"
+	"  agility_level SMALLINT NOT NULL DEFAULT 1,"
+	"  agility_factor SMALLINT NOT NULL,"
+	"  agility_exp INT NOT NULL,"
+	"  luck_level SMALLINT NOT NULL DEFAULT 1,"
+	"  luck_factor SMALLINT NOT NULL,"
+	"  luck_exp INT NOT NULL,"
+	"  PRIMARY KEY(id),"
+	"  FOREIGN KEY (account_id) REFERENCES account(id) ON DELETE CASCADE"
+	")"
+);
+
+const std::string init_spell(
+	"CREATE TABLE IF NOT EXISTS spell("
+	"  id SERIAL,"
+	"  character_id INTEGER NOT NULL,"
+	"  type TEXT NOT NULL,"
+	"  level SMALLINT NOT NULL,"
+	"  PRIMARY KEY(id),"
+	"  FOREIGN KEY (character_id) REFERENCES character(id) ON DELETE CASCADE"
+	")"
+);
+
+void init()
+{
+	assert(connection);
+
+	exec(init_account);
+	exec(init_character);
+	exec(init_spell);
+}
+
+auto select(const std::string& sql, const std::vector<arg>& args) -> result
+{
+	assert(connection);
+
+	auto result = run(sql, args);
+
+	switch (PQresultStatus(result.get())) {
+	case PGRES_COMMAND_OK:
+	case PGRES_TUPLES_OK:
+		break;
+	default:
+		throw std::runtime_error(PQerrorMessage(connection.get()));
+	}
+
+	return result;
+}
+
+void exec(const std::string& sql, const std::vector<arg>& args)
+{
+	assert(connection);
+
+	switch (const auto result = run(sql, args); PQresultStatus(result.get())) {
+	case PGRES_COMMAND_OK:
+		break;
+	default:
+		throw std::runtime_error(PQerrorMessage(connection.get()));
+	}
+}
+
+transaction::transaction(rollback fn)
+	: rollback_(std::move(fn))
+{
+	assert(rollback_);
+
+	exec("BEGIN");
+}
+
+transaction::~transaction()
+{
+	if (!commit_) {
+		rollback_();
+		exec("ROLLBACK");
+	}
+}
+
+void transaction::commit()
+{
+	if (!commit_) {
+		exec("COMMIT");
+		commit_ = true;
+	}
+}
+
+} // !db::mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/database.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,151 @@
+/*
+ * database.hpp -- connection to postgresql database
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_DB_DATABASE_HPP
+#define MALIKANIA_DB_DATABASE_HPP
+
+/**
+ * \file database.hpp
+ * \brief Abstract database interface.
+ */
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <variant>
+#include <vector>
+
+#include <libpq-fe.h>
+
+namespace mlk::db {
+
+/**
+ * \brief Statement argument.
+ *
+ * When using prepared statement, you may pass any data you like using this
+ * variant.
+ */
+using arg = std::variant<
+	bool,
+	double,
+	std::int8_t,
+	std::uint8_t,
+	std::int16_t,
+	std::uint16_t,
+	std::int32_t,
+	std::uint32_t,
+	std::int64_t,
+	std::uint64_t,
+	std::string
+>;
+
+/**
+ * \brief Convenient result type.
+ *
+ * This unique pointer holds the PostgreSQL result and free it automatically on
+ * out of scope.
+ */
+using result = std::unique_ptr<PGresult, void (*)(PGresult*)>;
+
+/**
+ * Open the connection to the database.
+ *
+ * All arguments are optional.
+ *
+ * \pre connection must be closed
+ * \param host the hostname
+ * \param port the port number
+ * \param user the user name
+ * \param database the database name
+ * \param password the user password
+ * \throw std::runtime_error on errors
+ */
+void open(const std::string& host,
+          const std::string& port,
+          const std::string& user,
+          const std::string& database,
+          const std::string& password);
+
+/**
+ * Unconditionally init the database.
+ *
+ * \throw std::runtime_error on errors
+ */
+void init();
+
+/**
+ * Execute a SELECT statement.
+ *
+ * \param sql the SQL statement
+ * \param args the optional arguments to bind
+ * \return the result value
+ * \throw std::runtime_error on errors
+ */
+auto select(const std::string& sql, const std::vector<arg>& args = {}) -> result;
+
+/**
+ * Execute an UPDATE statement.
+ *
+ * \param sql the SQL statement
+ * \param args the optional arguments to bind
+ * \throw std::runtime_error on errors
+ */
+void exec(const std::string& sql, const std::vector<arg>& args = {});
+
+class transaction {
+public:
+	/**
+	 * \brief Rollback function.
+	 */
+	using rollback = std::function<void ()>;
+
+private:
+	rollback rollback_;
+	bool commit_{false};
+
+public:
+	/**
+	 * Start a transaction.
+	 *
+	 * If there is already a transaction, the returned object is no-op.
+	 *
+	 * \param rollback the rollback function
+	 * \return the transaction object
+	 */
+	transaction(rollback fn);
+
+	/**
+	 * Rollback the transaction if commit() has not been called.
+	 */
+	~transaction();
+
+	/**
+	 * Commit the transaction.
+	 *
+	 * Does nothing if already committed.
+	 *
+	 * \throw std::exception on errors
+	 */
+	void commit();
+};
+
+} // !mlk::db
+
+#endif // !MALIKANIA_DB_DATABASE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/model.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,120 @@
+/*
+ * model.hpp -- abstract database object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_DB_MODEL_HPP
+#define MALIKANIA_DB_MODEL_HPP
+
+/**
+ * \file model.hpp
+ * \brief Abstract database object.
+ */
+
+#include <cstdint>
+
+namespace mlk::db {
+
+/**
+ * \brief Abstract database object.
+ */
+class model {
+public:
+	/**
+	 * Id type.
+	 */
+	using id_type = std::uint64_t;
+
+private:
+	model(const model&) = delete;
+	model& operator=(const model&) = delete;
+
+protected:
+	/**
+	 * Object id.
+	 */
+	id_type id_{0U};
+
+public:
+	/**
+	 * Default constructor.
+	 */
+	model() = default;
+
+	/**
+	 * Move constructor.
+	 *
+	 * \param other the original value (id resets to 0)
+	 */
+	inline model(model&& other) noexcept
+		: id_(other.id_)
+	{
+		other.id_ = 0U;
+	}
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~model() noexcept = default;
+
+	/**
+	 * Get the id.
+	 *
+	 * \return the id
+	 */
+	auto get_id() const noexcept -> id_type
+	{
+		return id_;
+	}
+
+	/**
+	 * Tells if the object is not persistent.
+	 *
+	 * \return true if object was never saved
+	 */
+	auto is_draft() const noexcept -> bool
+	{
+		return id_ == 0U;
+	}
+
+	/**
+	 * Tells if the object is present in database
+	 *
+	 * \return true if object is saved
+	 */
+	auto is_published() const noexcept -> bool
+	{
+		return id_ > 0U;
+	}
+
+	/**
+	 * Move operator.
+	 *
+	 * \param other the original value (id resets to 0)
+	 * \return *this
+	 */
+	auto operator=(model&& other) noexcept -> model&
+	{
+		id_ = other.id_;
+		other.id_ = 0U;
+
+		return *this;
+	}
+};
+
+} // !mlk::db
+
+#endif // !MALIKANIA_DB_MODEL_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/spell.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,124 @@
+/*
+ * spell.cpp -- database spell object
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include "character.hpp"
+#include "spell.hpp"
+
+namespace mlk::db {
+
+auto spell::load(const result& result, int row) -> spell
+{
+	spell s(PQgetvalue(result.get(), row, 2));
+
+	s.id_ = std::stoi(PQgetvalue(result.get(), row, 0));
+	s.level_ = std::stoi(PQgetvalue(result.get(), row, 3));
+
+	return s;
+}
+
+auto spell::load(const character& parent) -> std::vector<spell>
+{
+	static const std::string sql(
+		"SELECT *"
+		"  FROM spell"
+		" WHERE id = $1"
+	);
+
+	const auto r = select(sql, { parent.get_id() });
+
+	if (PQntuples(r.get()) == 0)
+		throw std::runtime_error("failed to load spells");
+
+	std::vector<spell> spells;
+
+	for (int i = 0; i < PQntuples(r.get()); ++i)
+		spells.push_back(load(r, i));
+
+	return spells;
+}
+
+void spell::clear()
+{
+	id_ = 0U;
+}
+
+void spell::unpublish()
+{
+	static const std::string sql(
+		"DELETE"
+		"  FROM spell"
+		" WHERE id = $1"
+	);
+
+	exec(sql, { id_ });
+	clear();
+}
+
+void spell::publish(character& parent)
+{
+	static const std::string sql(
+		"INSERT INTO spell("
+		"  character_id,"
+		"  type,"
+		"  level"
+		") "
+		"VALUES ($1, $2, $3) "
+		"RETURNING id"
+	);
+
+	const auto r = select(sql, { parent.get_id(), type_, level_ });
+
+	if (PQntuples(r.get()) == 0)
+		throw std::runtime_error("failed to save spell");
+
+	id_ = std::stoi(PQgetvalue(r.get(), 0, 0));
+}
+
+spell::spell(std::string type) noexcept
+	: type_(std::move(type))
+{
+	assert(!type_.empty());
+}
+
+auto spell::get_type() const noexcept -> const std::string&
+{
+	return type_;
+}
+
+auto spell::get_level() const noexcept -> std::uint8_t
+{
+	return level_;
+}
+
+void spell::set_level(std::uint8_t level)
+{
+	static const std::string sql(
+		"UPDATE spell"
+		"   SET level = $1"
+		" WHERE id = $2"
+	);
+
+	if (is_published() && level_ != level)
+		db::exec(sql, { level_, id_ });
+
+	level_ = level;
+}
+
+} // !mlk::db
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-db/malikania/db/spell.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,88 @@
+/*
+ * spell.hpp -- database spell object
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_DB_SPELL_HPP
+#define MALIKANIA_DB_SPELL_HPP
+
+/**
+ * \file spell.hpp
+ * \brief Database spell object.
+ */
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "database.hpp"
+#include "model.hpp"
+
+namespace mlk::db {
+
+class character;
+
+/**
+ * \brief Describe a spell.
+ */
+class spell : public model {
+private:
+	friend class character;
+
+	std::string type_;
+	std::uint8_t level_{1U};
+
+	static auto load(const result&, int) -> spell;
+	static auto load(const character&) -> std::vector<spell>;
+
+	void clear();
+	void unpublish();
+	void publish(character&);
+
+public:
+	/**
+	 * Constructor.
+	 *
+	 * \pre !type.empty()
+	 * \param type the type name
+	 */
+	spell(std::string type) noexcept;
+
+	/**
+	 * Get the type.
+	 *
+	 * \return the type
+	 */
+	auto get_type() const noexcept -> const std::string&;
+
+	/**
+	 * Get the level.
+	 *
+	 * \return the level
+	 */
+	auto get_level() const noexcept -> std::uint8_t;
+
+	/**
+	 * Set the spell level.
+	 *
+	 * \param level the level
+	 */
+	void set_level(std::uint8_t level);
+};
+
+} // !mlk::db
+
+#endif // !MALIKANIA_DB_SPELL_HPP
--- a/libmlk-js-test/malikania/js/test/js_api_fixture.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/libmlk-js-test/malikania/js/test/js_api_fixture.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -30,13 +30,13 @@
 #include <malikania/rectangle.hpp>
 #include <malikania/size.hpp>
 
-#include <malikania/duk.hpp>
-#include <malikania/js_elapsed_timer.hpp>
-#include <malikania/js_line.hpp>
-#include <malikania/js_point.hpp>
-#include <malikania/js_rectangle.hpp>
-#include <malikania/js_resources_loader.hpp>
-#include <malikania/js_size.hpp>
+#include <malikania/js/duk.hpp>
+#include <malikania/js/elapsed_timer_js_api.hpp>
+#include <malikania/js/line_js_api.hpp>
+#include <malikania/js/point_js_api.hpp>
+#include <malikania/js/rectangle_js_api.hpp>
+#include <malikania/js/loader_js_api.hpp>
+#include <malikania/js/size_js_api.hpp>
 
 namespace mlk::js::test {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,50 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk-js)
+
+set(
+	HEADERS
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/duk.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/elapsed_timer_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/line_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/loader_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/point_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/rectangle_js_api.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/size_js_api.hpp
+)
+
+set(
+	SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/duk.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/elapsed_timer_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/line_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/loader_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/point_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/rectangle_js_api.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/js/size_js_api.cpp
+)
+
+malikania_define_library(
+	PROJECT libmlk-common-js
+	TARGET libmlk-common-js
+	SOURCES ${HEADERS} ${SOURCES}
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+	LIBRARIES duktape json libmlk
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/duk.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,447 @@
+/*
+ * duk.cpp -- miscellaneous Duktape extras
+ *
+ * Copyright (c) 2017-2018 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 <cassert>
+#include <cstdio>
+
+#include "duk.hpp"
+
+namespace mlk::js::duk {
+
+// {{{ stack_guard
+
+stack_guard::stack_guard(duk_context* ctx, unsigned expected) noexcept
+#if !defined(NDEBUG)
+	: context_(ctx)
+	, expected_(expected)
+	, at_start_(duk_get_top(ctx))
+#endif
+{
+#if defined(NDEBUG)
+	(void)ctx;
+	(void)expected;
+#endif
+}
+
+stack_guard::~stack_guard() noexcept
+{
+#if !defined(NDEBUG)
+	auto result = duk_get_top(context_) - at_start_;
+
+	if (result != static_cast<int>(expected_)) {
+		std::fprintf(stderr, "Corrupt stack detection in stack_guard:\n");
+		std::fprintf(stderr, "  Size at start:          %d\n", at_start_);
+		std::fprintf(stderr, "  Size at end:            %d\n", duk_get_top(context_));
+		std::fprintf(stderr, "  Expected (user):        %u\n", expected_);
+		std::fprintf(stderr, "  Expected (adjusted):    %u\n", expected_ + at_start_);
+		std::fprintf(stderr, "  Difference count:       %+d\n", result - expected_);
+		std::abort();
+	}
+#endif
+}
+
+// }}}
+
+// {{{ context
+
+context::context() noexcept
+	: handle_(duk_create_heap_default(), duk_destroy_heap)
+{
+	duk_push_object(handle_.get());
+	duk_put_global_string(handle_.get(), "Malikania");
+}
+
+context::operator duk_context*() noexcept
+{
+	return handle_.get();
+}
+
+context::operator duk_context*() const noexcept
+{
+	return handle_.get();
+}
+
+// }}}
+
+// {{{ stack_info
+
+stack_info::stack_info(std::string name,
+                       std::string message,
+                       std::string stack,
+                       std::string file_name,
+                       unsigned line_number) noexcept
+	: name_(std::move(name))
+	, message_(std::move(message))
+	, stack_(std::move(stack))
+	, file_name_(std::move(file_name))
+	, line_number_(line_number)
+{
+}
+
+auto stack_info::get_name() const noexcept -> const std::string&
+{
+	return name_;
+}
+
+auto stack_info::get_message() const noexcept -> const std::string&
+{
+	return message_;
+}
+
+auto stack_info::get_stack() const noexcept -> const std::string&
+{
+	return stack_;
+}
+
+auto stack_info::get_file_name() const noexcept -> const std::string&
+{
+	return file_name_;
+}
+
+auto stack_info::get_line_number() const noexcept -> unsigned
+{
+	return line_number_;
+}
+
+auto stack_info::what() const noexcept -> const char*
+{
+	return message_.c_str();
+}
+
+// }}}
+
+// {{{ error
+
+error::error(int type, std::string message) noexcept
+	: type_(type)
+	, message_(std::move(message))
+{
+}
+
+error::error(std::string message) noexcept
+	: message_(std::move(message))
+{
+}
+
+void error::create(duk_context* ctx) const
+{
+	duk_push_error_object(ctx, type_, "%s", message_.c_str());
+}
+
+// }}}
+
+// {{{ eval_error
+
+eval_error::eval_error(std::string message) noexcept
+	: error(DUK_ERR_EVAL_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ range_error
+
+range_error::range_error(std::string message) noexcept
+	: error(DUK_ERR_RANGE_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ reference_error
+
+reference_error::reference_error(std::string message) noexcept
+	: error(DUK_ERR_REFERENCE_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ syntax_error
+
+syntax_error::syntax_error(std::string message) noexcept
+	: error(DUK_ERR_SYNTAX_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ type_error
+
+type_error::type_error(std::string message) noexcept
+	: error(DUK_ERR_TYPE_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ uri_error
+
+uri_error::uri_error(std::string message) noexcept
+	: error(DUK_ERR_URI_ERROR, std::move(message))
+{
+}
+
+// }}}
+
+// {{{ get_stack
+
+auto get_stack(duk_context* ctx, int index, bool pop) -> stack_info
+{
+	index = duk_normalize_index(ctx, index);
+
+	duk_get_prop_string(ctx, index, "name");
+	auto name = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "message");
+	auto message = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "fileName");
+	auto file_name = duk_to_string(ctx, -1);
+	duk_get_prop_string(ctx, index, "lineNumber");
+	auto line_number = duk_to_uint(ctx, -1);
+	duk_get_prop_string(ctx, index, "stack");
+	auto stack = duk_to_string(ctx, -1);
+	duk_pop_n(ctx, 5);
+
+	if (pop)
+		duk_remove(ctx, index);
+
+	return {
+		std::move(name),
+		std::move(message),
+		std::move(stack),
+		std::move(file_name),
+		line_number
+	};
+}
+
+// }}}
+
+// {{{ type_traits<std::exception>
+
+void type_traits<std::exception>::raise(duk_context* ctx, const std::exception& ex)
+{
+	duk_error(ctx, DUK_ERR_ERROR, "%s", ex.what());
+}
+
+// }}}
+
+// {{{ type_traits<std::system_error>
+
+void type_traits<std::system_error>::raise(duk_context* ctx, const std::system_error& ex)
+{
+	duk_push_error_object(ctx, DUK_ERR_ERROR, ex.what());
+	duk_push_int(ctx, ex.code().value());
+	duk_put_prop_string(ctx, -2, "code");
+	duk_push_string(ctx, ex.code().category().name());
+	duk_put_prop_string(ctx, -2, "category");
+	duk_throw(ctx);
+}
+
+// }}}
+
+// {{{ type_traits<error>
+
+void type_traits<error>::raise(duk_context* ctx, const error& ex)
+{
+	ex.create(ctx);
+	duk_throw(ctx);
+}
+
+// }}}
+
+// {{{ type_traits<bool>
+
+void type_traits<bool>::push(duk_context* ctx, bool value)
+{
+	duk_push_boolean(ctx, value);
+}
+
+auto type_traits<bool>::get(duk_context* ctx, duk_idx_t index) -> bool
+{
+	return duk_get_boolean(ctx, index);
+}
+
+auto type_traits<bool>::require(duk_context* ctx, duk_idx_t index) -> bool
+{
+	return duk_require_boolean(ctx, index);
+}
+
+auto type_traits<bool>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<bool>
+{
+	return duk_is_boolean(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<duk_double_t>
+
+void type_traits<duk_double_t>::push(duk_context* ctx, duk_double_t value)
+{
+	duk_push_number(ctx, value);
+}
+
+auto type_traits<duk_double_t>::get(duk_context* ctx, duk_idx_t index) -> duk_double_t
+{
+	return duk_get_number(ctx, index);
+}
+
+auto type_traits<duk_double_t>::require(duk_context* ctx, duk_idx_t index) -> duk_double_t
+{
+	return duk_require_number(ctx, index);
+}
+
+auto type_traits<duk_double_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_double_t>
+{
+	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<duk_int_t>
+
+void type_traits<duk_int_t>::push(duk_context* ctx, duk_int_t value)
+{
+	duk_push_int(ctx, value);
+}
+
+auto type_traits<duk_int_t>::get(duk_context* ctx, duk_idx_t index) -> duk_int_t
+{
+	return duk_get_int(ctx, index);
+}
+
+auto type_traits<duk_int_t>::require(duk_context* ctx, duk_idx_t index) -> duk_int_t
+{
+	return duk_require_int(ctx, index);
+}
+
+auto type_traits<duk_int_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_int_t>
+{
+	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<duk_uint_t>
+
+void type_traits<duk_uint_t>::push(duk_context* ctx, duk_uint_t value)
+{
+	duk_push_uint(ctx, value);
+}
+
+auto type_traits<duk_uint_t>::get(duk_context* ctx, duk_idx_t index) -> duk_uint_t
+{
+	return duk_get_uint(ctx, index);
+}
+
+auto type_traits<duk_uint_t>::require(duk_context* ctx, duk_idx_t index) -> duk_uint_t
+{
+	return duk_require_uint(ctx, index);
+}
+
+auto type_traits<duk_uint_t>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_uint_t>
+{
+	return duk_is_number(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<const char*>
+
+void type_traits<const char*>::push(duk_context* ctx, const char* value)
+{
+	duk_push_string(ctx, value);
+}
+
+auto type_traits<const char*>::get(duk_context* ctx, duk_idx_t index) -> const char*
+{
+	return duk_get_string(ctx, index);
+}
+
+auto type_traits<const char*>::require(duk_context* ctx, duk_idx_t index) -> const char*
+{
+	return duk_require_string(ctx, index);
+}
+
+auto type_traits<const char*>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<const char*>
+{
+	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<std::string>
+
+void type_traits<std::string>::push(duk_context* ctx, const std::string& value)
+{
+	duk_push_lstring(ctx, value.data(), value.size());
+}
+
+auto type_traits<std::string>::get(duk_context* ctx, duk_idx_t index) -> std::string
+{
+	duk_size_t length;
+	const char* str = duk_get_lstring(ctx, index, &length);
+
+	return { str, length };
+}
+
+auto type_traits<std::string>::require(duk_context* ctx, duk_idx_t index) -> std::string
+{
+	duk_size_t length;
+	const char* str = duk_require_lstring(ctx, index, &length);
+
+	return { str, length };
+}
+
+auto type_traits<std::string>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string>
+{
+	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+// {{{ type_traits<std::string_view>
+
+void type_traits<std::string_view>::push(duk_context* ctx, std::string_view value)
+{
+	duk_push_lstring(ctx, value.data(), value.size());
+}
+
+auto type_traits<std::string_view>::get(duk_context* ctx, duk_idx_t index) -> std::string_view
+{
+	duk_size_t length;
+	const char* str = duk_get_lstring(ctx, index, &length);
+
+	return { str, length };
+}
+
+auto type_traits<std::string_view>::require(duk_context* ctx, duk_idx_t index) -> std::string_view
+{
+	duk_size_t length;
+	const char* str = duk_require_lstring(ctx, index, &length);
+
+	return { str, length };
+}
+
+auto type_traits<std::string_view>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string_view>
+{
+	return duk_is_string(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+// }}}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/duk.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,988 @@
+/*
+ * duk.hpp -- miscellaneous Duktape extras
+ *
+ * Copyright (c) 2017-2018 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 MALIKANIA_JS_DUK_HPP
+#define MALIKANIA_JS_DUK_HPP
+
+/**
+ * \file duk.hpp
+ * \brief Miscellaneous Duktape extras
+ * \author David Demelier <markand@malikania.fr>
+ * \version 0.2.0
+ */
+
+#include <exception>
+#include <memory>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <system_error>
+
+#include "duktape.h"
+
+/**
+ * \brief Miscellaneous Duktape extras.
+ */
+namespace mlk::js::duk {
+
+// {{{ stack_guard
+
+/**
+ * \brief Stack sanity checker.
+ *
+ * Instanciate this class where you need to manipulate the Duktape stack outside
+ * a Duktape/C function, its destructor will examinate if the stack size matches
+ * the user expected size.
+ *
+ * When compiled with NDEBUG, this class does nothing.
+ *
+ * To use it, just declare an lvalue at the beginning of your function.
+ */
+class stack_guard {
+#if !defined(NDEBUG)
+private:
+	duk_context* context_;
+	unsigned expected_;
+	int at_start_;
+#endif
+
+public:
+	/**
+	 * Create the stack checker.
+	 *
+	 * No-op if NDEBUG is set.
+	 *
+	 * \param ctx the context
+	 * \param expected the size expected relative to the already existing values
+	 */
+	stack_guard(duk_context* ctx, unsigned expected = 0) noexcept;
+
+	/**
+	 * Verify the expected size.
+	 *
+	 * No-op if NDEBUG is set.
+	 */
+	~stack_guard() noexcept;
+};
+
+// }}}
+
+// {{{ context
+
+/**
+ * \brief RAII based Duktape handler.
+ *
+ * This class is implicitly convertible to duk_context for convenience.
+ */
+class context {
+private:
+	std::unique_ptr<duk_context, void (*)(duk_context*)> handle_;
+
+	context(const context&) = delete;
+	void operator=(const context&) = delete;
+
+public:
+	/**
+	 * Create default context.
+	 */
+	context() noexcept;
+
+	/**
+	 * Default move constructor.
+	 */
+	context(context&&) noexcept = default;
+
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * \return the duk_context
+	 */
+	operator duk_context*() noexcept;
+
+	/**
+	 * Convert the context to the native Duktape/C type.
+	 *
+	 * \return the duk_context
+	 */
+	operator duk_context*() const noexcept;
+
+	/**
+	 * Default move assignment operator.
+	 *
+	 * \return this
+	 */
+	auto operator=(context&&) noexcept -> context& = default;
+};
+
+// }}}
+
+// {{{ stack_info
+
+/**
+ * \brief Error description.
+ *
+ * This class fills the fields got in an Error object.
+ */
+class stack_info : public std::exception {
+private:
+	std::string name_;
+	std::string message_;
+	std::string stack_;
+	std::string file_name_;
+	unsigned line_number_;
+
+public:
+	/**
+	 * Construct the stack information.
+	 *
+	 * \param name the exception name (e.g. ReferenceError)
+	 * \param message the error message
+	 * \param stack the stack trace
+	 * \param file_name the optional filename
+	 * \param line_number the optional line number
+	 */
+	stack_info(std::string name,
+	           std::string message,
+	           std::string stack,
+	           std::string file_name,
+	           unsigned line_number = 0) noexcept;
+
+	/**
+	 * Get the exception name.
+	 *
+	 * \return the exception name (e.g. ReferenceError)
+	 */
+	auto get_name() const noexcept -> const std::string&;
+
+	/**
+	 * Get the error message.
+	 *
+	 * \return the message
+	 */
+	auto get_message() const noexcept -> const std::string&;
+
+	/**
+	 * Get the stack trace.
+	 *
+	 * \return the stack
+	 */
+	auto get_stack() const noexcept -> const std::string&;
+
+	/**
+	 * Get the optional file name.
+	 *
+	 * \return the file name
+	 */
+	auto get_file_name() const noexcept -> const std::string&;
+
+	/**
+	 * Get the line number.
+	 *
+	 * \return the line number
+	 */
+	auto get_line_number() const noexcept -> unsigned;
+
+	/**
+	 * Get the error message. This effectively returns message field.
+	 *
+	 * \return the message
+	 */
+	auto what() const noexcept -> const char* override;
+};
+
+// }}}
+
+// {{{ type_traits
+
+/**
+ * \brief Operations on different types.
+ *
+ * This class provides some functions for the given type, depending on the
+ * nature of the function.
+ *
+ * For example, push will call type_traits<T>::push static function
+ * if the type_traits is implemented for that given T type.
+ *
+ * This helps passing/getting function between Javascript and C++ code.
+ *
+ * Example:
+ *
+ * ```cpp
+ * push(ctx, 123);	 // Uses type_traits<int>
+ * push(ctx, true);	// Uses type_traits<bool>
+ * ```
+ *
+ * This class is specialized for the following types:
+ *
+ *   - `bool`,
+ *   - `duk_int_t`,
+ *   - `duk_uint_t`,
+ *   - `duk_double_t`,
+ *   - `const char*`,
+ *   - `std::string`,
+ *   - `std::string_view`.
+ *
+ * Regarding exceptions, this class is specialized for the following types:
+ *
+ *   - error,
+ *   - std::exception,
+ *   - std::system_error.
+ *
+ * \see push
+ * \see get
+ * \see require
+ * \see raise
+ */
+template <typename T>
+struct type_traits;
+
+// }}}
+
+// {{{ push
+
+/**
+ * Generic push function.
+ *
+ * This function calls type_traits<T>::push if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param value the forwarded value
+ * \return 1 for convenience
+ */
+template <typename T>
+auto push(duk_context* ctx, T&& value) -> int
+{
+	using Type = typename std::decay<T>::type;
+
+	type_traits<Type>::push(ctx, std::forward<T>(value));
+
+	return 1;
+}
+
+// }}}
+
+// {{{ put
+
+/**
+ * Generic put function.
+ *
+ * This function calls type_traits<T>::put if specialized.
+ *
+ * The purpose of this function is to inject the argument through the value at
+ * the top of the stack.
+ *
+ * \param ctx the Duktape context
+ * \param value the forwarded value
+ * \return 1 for convenience
+ */
+template <typename T>
+auto put(duk_context* ctx, T&& value) -> int
+{
+	using Type = typename std::decay<T>::type;
+
+	type_traits<Type>::put(ctx, std::forward<T>(value));
+
+	return 1;
+}
+
+// }}}
+
+// {{{ get
+
+/**
+ * Generic get function.
+ *
+ * This functions calls type_traits<T>::get if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param index the value index
+ * \return the converted value
+ */
+template <typename T>
+auto get(duk_context* ctx, duk_idx_t index) -> decltype(auto)
+{
+	using Type = typename std::decay<T>::type;
+
+	return type_traits<Type>::get(ctx, index);
+}
+
+// }}}
+
+// {{{ optional
+
+/**
+ * Generic optional function.
+ *
+ * This functions calls type_traits<T>::optional if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param index the value index
+ * \return the converted value
+ */
+template <typename T>
+auto optional(duk_context* ctx, duk_idx_t index) -> decltype(auto)
+{
+	using Type = typename std::decay<T>::type;
+
+	return type_traits<Type>::optional(ctx, index);
+}
+
+// }}}
+
+// {{{ require
+
+/**
+ * Generic require function.
+ *
+ * This functions calls type_traits<T>::require if specialized.
+ *
+ * \param ctx the Duktape context
+ * \param index the value index
+ * \return the converted value
+ */
+template <typename T>
+auto require(duk_context* ctx, duk_idx_t index) -> decltype(auto)
+{
+	using Type = typename std::decay<T>::type;
+
+	return type_traits<Type>::require(ctx, index);
+}
+
+// }}}
+
+// {{{ error
+
+/**
+ * \brief Base ECMAScript error class.
+ * \warning Override the function create for your own exceptions
+ */
+class error {
+private:
+	int type_{DUK_ERR_ERROR};
+	std::string message_;
+
+protected:
+	/**
+	 * Constructor with a type of error specified, specially designed for
+	 * derived errors.
+	 *
+	 * \param type of error (e.g. DUK_ERR_ERROR)
+	 * \param message the message
+	 */
+	error(int type, std::string message) noexcept;
+
+public:
+	/**
+	 * Constructor with a message.
+	 *
+	 * \param message the message
+	 */
+	error(std::string message) noexcept;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	~error() = default;
+
+	/**
+	 * Create the exception on the stack.
+	 *
+	 * \note the default implementation search for the global variables
+	 * \param ctx the context
+	 */
+	void create(duk_context* ctx) const;
+};
+
+// }}}
+
+// {{{ eval_error
+
+/**
+ * \brief Error in eval() function.
+ */
+class eval_error : public error {
+public:
+	/**
+	 * Construct an EvalError.
+	 *
+	 * \param message the message
+	 */
+	eval_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ range_error
+
+/**
+ * \brief Value is out of range.
+ */
+class range_error : public error {
+public:
+	/**
+	 * Construct an RangeError.
+	 *
+	 * \param message the message
+	 */
+	range_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ reference_error
+
+/**
+ * \brief Trying to use a variable that does not exist.
+ */
+class reference_error : public error {
+public:
+	/**
+	 * Construct an ReferenceError.
+	 *
+	 * \param message the message
+	 */
+	reference_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ syntax_error
+
+/**
+ * \brief Syntax error in the script.
+ */
+class syntax_error : public error {
+public:
+	/**
+	 * Construct an SyntaxError.
+	 *
+	 * \param message the message
+	 */
+	syntax_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ type_error
+
+/**
+ * \brief Invalid type given.
+ */
+class type_error : public error {
+public:
+	/**
+	 * Construct an TypeError.
+	 *
+	 * \param message the message
+	 */
+	type_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ uri_error
+
+/**
+ * \brief URI manipulation failure.
+ */
+class uri_error : public error {
+public:
+	/**
+	 * Construct an URIError.
+	 *
+	 * \param message the message
+	 */
+	uri_error(std::string message) noexcept;
+};
+
+// }}}
+
+// {{{ raise
+
+/**
+ * Create an exception into the stack and throws it.
+ *
+ * This function needs the following requirements in type_traits
+ *
+ * ```cpp
+ * static void raise(duk_context*, Error);
+ * ```
+ *
+ * Error can be any kind of value, it is forwarded.
+ *
+ * \param ctx the Duktape context
+ * \param error the error object
+ */
+template <typename Error>
+void raise(duk_context* ctx, Error&& error)
+{
+	using type = std::decay_t<Error>;
+
+	type_traits<type>::raise(ctx, std::forward<Error>(error));
+}
+
+// }}}
+
+// {{{ get_stack
+
+/**
+ * Get the error object when a JavaScript error has been thrown (e.g. eval
+ * failure).
+ *
+ * \param ctx the context
+ * \param index the index
+ * \param pop if true, also remove the exception from the stack
+ * \return the information
+ */
+auto get_stack(duk_context* ctx, int index, bool pop = true) -> stack_info;
+
+// }}}
+
+// {{{ type_traits<std::exception>
+
+/**
+ * \brief Specialization for std::exception.
+ */
+template <>
+struct type_traits<std::exception> {
+	/**
+	 * Raise a Error object.
+	 *
+	 * \param ctx the Duktape context
+	 * \param ex the exception
+	 */
+	static void raise(duk_context* ctx, const std::exception& ex);
+};
+
+// }}}
+
+// {{{ type_traits<std::system_error>
+
+/**
+ * \brief Specialization for std::system_error.
+ */
+template <>
+struct type_traits<std::system_error> {
+	/**
+	 * Raise a Error object.
+	 *
+	 * The error object has the following properties defined in it:
+	 *
+	 * - code: (int) error code,
+	 * - category: (string) error category string.
+	 *
+	 * \param ctx the Duktape context
+	 * \param ex the exception
+	 */
+	static void raise(duk_context* ctx, const std::system_error& ex);
+};
+
+// }}}
+
+// {{{ type_traits<error>
+
+/**
+ * \brief Specialization for error.
+ */
+template <>
+struct type_traits<error> {
+	/**
+	 * Raise a error.
+	 *
+	 * \param ctx the Duktape context
+	 * \param ex the exception
+	 */
+	static void raise(duk_context* ctx, const error& ex);
+};
+
+// }}}
+
+// {{{ type_traits<bool>
+
+/**
+ * \brief Specialization for bool.
+ */
+template <>
+struct type_traits<bool> {
+	/**
+	 * Push a boolean.
+	 *
+	 * Uses duk_push_boolean
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, bool value);
+
+	/**
+	 * Get a boolean.
+	 *
+	 * Uses duk_get_boolean.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> bool;
+
+	/**
+	 * Require a boolean.
+	 *
+	 * Uses duk_require_boolean.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> bool;
+
+	/**
+	 * Get a boolean if convertible.
+	 *
+	 * Uses duk_get_boolean.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<bool>;
+};
+
+// }}}
+
+// {{{ type_traits<duk_double_t>
+
+/**
+ * \brief Specialization for duk_double_t.
+ */
+template <>
+struct type_traits<duk_double_t> {
+	/**
+	 * Push a double.
+	 *
+	 * Uses duk_push_number
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, duk_double_t value);
+
+	/**
+	 * Get a double.
+	 *
+	 * Uses duk_get_number.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> duk_double_t;
+
+	/**
+	 * Require a double.
+	 *
+	 * Uses duk_require_double.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> duk_double_t;
+
+	/**
+	 * Get a double if convertible
+	 *
+	 * Uses duk_get_number.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_double_t>;
+};
+
+// }}}
+
+// {{{ type_traits<duk_int_t>
+
+/**
+ * \brief Specialization for duk_int_t.
+ */
+template <>
+struct type_traits<duk_int_t> {
+	/**
+	 * Push an int.
+	 *
+	 * Uses duk_push_int
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, duk_int_t value);
+
+	/**
+	 * Get an int.
+	 *
+	 * Uses duk_get_int.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> duk_int_t;
+
+	/**
+	 * Require an int.
+	 *
+	 * Uses duk_require_int.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> duk_int_t;
+
+	/**
+	 * Get an int if convertible.
+	 *
+	 * Uses duk_get_int.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_int_t>;
+};
+
+// }}}
+
+// {{{ type_traits<duk_uint_t>
+
+/**
+ * \brief Specialization for duk_uint_t.
+ */
+template <>
+struct type_traits<duk_uint_t> {
+	/**
+	 * Push an unsigned int.
+	 *
+	 * Uses duk_push_uint
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, duk_uint_t value);
+
+	/**
+	 * Get an unsigned int.
+	 *
+	 * Uses duk_get_uint.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> duk_uint_t;
+
+	/**
+	 * Require an unsigned int.
+	 *
+	 * Uses duk_require_uint.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> duk_uint_t;
+
+	/**
+	 * Get an unsigned int if convertible.
+	 *
+	 * Uses duk_get_uint.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<duk_uint_t>;
+};
+
+// }}}
+
+// {{{ type_traits<const char*>
+
+/**
+ * \brief Specialization for C strings.
+ */
+template <>
+struct type_traits<const char*> {
+	/**
+	 * Push a C string.
+	 *
+	 * Uses duk_push_string
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, const char* value);
+
+	/**
+	 * Get a C string.
+	 *
+	 * Uses duk_get_string.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> const char*;
+
+	/**
+	 * Require a C string.
+	 *
+	 * Uses duk_require_string.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> const char*;
+
+	/**
+	 * Get a C string if convertible.
+	 *
+	 * Uses duk_get_string.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<const char*>;
+};
+
+// }}}
+
+// {{{ type_traits<std::string>
+
+/**
+ * \brief Specialization for C++ std::strings.
+ */
+template <>
+struct type_traits<std::string> {
+	/**
+	 * Push a C++ std::string.
+	 *
+	 * Uses duk_push_lstring
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, const std::string& value);
+
+	/**
+	 * Get a C++ std::string.
+	 *
+	 * Uses duk_get_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> std::string;
+
+	/**
+	 * Require a C++ std::string.
+	 *
+	 * Uses duk_require_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> std::string;
+
+	/**
+	 * Get a C++ std::string if convertible.
+	 *
+	 * Uses duk_get_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string>;
+};
+
+// }}}
+
+// {{{ type_traits<std::string_view>
+
+/**
+ * \brief Specialization for C++ std::string_views.
+ */
+template <>
+struct type_traits<std::string_view> : public std::true_type {
+	/**
+	 * Push a C++ std::string_view.
+	 *
+	 * Uses duk_push_lstring
+	 *
+	 * \param ctx the Duktape context
+	 * \param value the value
+	 */
+	static void push(duk_context* ctx, std::string_view value);
+
+	/**
+	 * Get a C++ std::string_view.
+	 *
+	 * Uses duk_get_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> std::string_view;
+
+	/**
+	 * Require a C++ std::string_view.
+	 *
+	 * Uses duk_require_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> std::string_view;
+
+	/**
+	 * Get a C++ std::string_view if convertible.
+	 *
+	 * Uses duk_get_lstring.
+	 *
+	 * \param ctx the Duktape context
+	 * \param index the value index
+	 * \return the converted value
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<std::string_view>;
+};
+
+// }}}
+
+} // !mlk::js::duk
+
+#endif // !MALIKANIA_JS_DUK_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/elapsed_timer_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,170 @@
+/*
+ * elapsed_timer_js_api.cpp -- Malikania.ElapsedTimer API
+ *
+ * Copyright (c) 2013-2018 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 <boost/timer/timer.hpp>
+
+#include "elapsed_timer_js_api.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+const std::string_view signature("\xff""\xff""Malikania.ElapsedTimer");
+
+// {{{ self
+
+auto self(duk_context* ctx) -> boost::timer::cpu_timer*
+{
+	duk::stack_guard sa(ctx);
+
+	duk_push_this(ctx);
+	duk_get_prop_string(ctx, -1, signature.data());
+	const auto ptr = static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1));
+	duk_pop_2(ctx);
+
+	if (!ptr)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "not an ElapsedTimer object");
+
+	return ptr;
+}
+
+// }}}
+
+// {{{ Irccd.ElapsedTimer.prototype.pause
+
+/*
+ * Method: ElapsedTimer.prototype.pause
+ * ------------------------------------------------------------------
+ *
+ * Pause the timer, without resetting the current elapsed time stored.
+ */
+auto ElapsedTimer_prototype_pause(duk_context* ctx) -> duk_ret_t
+{
+	self(ctx)->stop();
+
+	return 0;
+}
+
+// }}}
+
+// {{{ Irccd.ElapsedTimer.prototype.restart
+
+/*
+ * Method: Irccd.ElapsedTimer.prototype.restart
+ * ------------------------------------------------------------------
+ *
+ * Restart the timer without resetting the current elapsed time.
+ */
+auto ElapsedTimer_prototype_restart(duk_context* ctx) -> duk_ret_t
+{
+	self(ctx)->resume();
+
+	return 0;
+}
+
+// }}}
+
+// {{{ Irccd.ElapsedTimer.prototype.elapsed
+
+/*
+ * Method: ElapsedTimer.prototype.elapsed
+ * ------------------------------------------------------------------
+ *
+ * Get the number of elapsed milliseconds.
+ *
+ * Returns:
+ *   The time elapsed.
+ */
+auto ElapsedTimer_prototype_elapsed(duk_context* ctx) -> duk_ret_t
+{
+	duk_push_uint(ctx, self(ctx)->elapsed().wall / 1000000LL);
+
+	return 1;
+}
+
+// }}}
+
+// {{{ Irccd.ElapsedTimer [constructor]
+
+/*
+ * Function: Irccd.ElapsedTimer [constructor]
+ * ------------------------------------------------------------------
+ *
+ * Construct a new ElapsedTimer object.
+ */
+auto ElapsedTimer_constructor(duk_context* ctx) -> duk_ret_t
+{
+	duk_push_this(ctx);
+	duk_push_pointer(ctx, new boost::timer::cpu_timer);
+	duk_put_prop_string(ctx, -2, signature.data());
+	duk_pop(ctx);
+
+	return 0;
+}
+
+// }}}
+
+// {{{ Irccd.ElapsedTimer [destructor]
+
+/*
+ * Function: Irccd.ElapsedTimer [destructor]
+ * ------------------------------------------------------------------
+ *
+ * Delete the property.
+ */
+auto ElapsedTimer_destructor(duk_context* ctx) -> duk_ret_t
+{
+	duk_get_prop_string(ctx, 0, signature.data());
+	delete static_cast<boost::timer::cpu_timer*>(duk_to_pointer(ctx, -1));
+	duk_pop(ctx);
+	duk_del_prop_string(ctx, 0, signature.data());
+
+	return 0;
+}
+
+// }}}
+
+// {{{ definitions
+
+const duk_function_list_entry methods[] = {
+	{ "elapsed",    ElapsedTimer_prototype_elapsed, 0 },
+	{ "pause",      ElapsedTimer_prototype_pause,   0 },
+	{ "restart",    ElapsedTimer_prototype_restart, 0 },
+	{ nullptr,      nullptr,                        0 }
+};
+
+// }}}
+
+} // !namespace
+
+void load_elapsed_timer_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, ElapsedTimer_constructor, 0);
+	duk_push_object(ctx);
+	duk_put_function_list(ctx, -1, methods);
+	duk_push_c_function(ctx, ElapsedTimer_destructor, 1);
+	duk_set_finalizer(ctx, -2);
+	duk_put_prop_string(ctx, -2, "prototype");
+	duk_put_prop_string(ctx, -2, "ElapsedTimer");
+	duk_pop(ctx);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/elapsed_timer_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,40 @@
+/*
+ * elapsed_timer_js_api.hpp -- Malikania.ElapsedTimer API
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_ELAPSED_TIMER_HPP
+#define MALIKANIA_JS_ELAPSED_TIMER_HPP
+
+/**
+ * \file js_elapsed_timer.hpp
+ * \brief Malikania.ElapsedTimer Javascript API.
+ */
+
+#include "duk.hpp"
+
+namespace mlk::js {
+
+/**
+ * Load Malikania.ElapsedTimer API into the context.
+ *
+ * \param ctx the context
+ */
+void load_elapsed_timer_api(duk_context* ctx);
+
+} // !mlk::js
+
+#endif // !MALIKANIA_JS_ELAPSED_TIMER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/line_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,155 @@
+/*
+ * line_js_api.cpp -- line description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/line.hpp>
+
+#include "line_js_api.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+auto Line_constructor(duk_context* ctx) -> duk_ret_t
+{
+	line obj;
+
+	if (duk_get_top(ctx) == 4)
+		obj = line{
+			duk::require<int>(ctx, 0),
+			duk::require<int>(ctx, 1),
+			duk::require<int>(ctx, 2),
+			duk::require<int>(ctx, 3)
+		};
+	else if (duk_get_top(ctx) == 1)
+		obj = duk::require<line>(ctx, 0);
+
+	duk_ret_t ret;
+
+	// Allow both constructor and non constructor calls.
+	if (duk_is_constructor_call(ctx)) {
+		duk_push_this(ctx);
+		duk::put(ctx, obj);
+		duk_pop(ctx);
+		ret = 0;
+	} else {
+		duk::push(ctx, obj);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+} // !namespace
+
+namespace duk {
+
+auto type_traits<line>::get(duk_context* ctx, duk_idx_t index) -> line
+{
+	duk::stack_guard sa(ctx);
+
+	line ret;
+
+	duk_get_prop_string(ctx, index, "x1");
+	ret.x1 = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y1");
+	ret.y1 = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "x2");
+	ret.x2 = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y2");
+	ret.y2 = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+
+	return ret;
+}
+
+auto type_traits<line>::require(duk_context* ctx, duk_idx_t index) -> line
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, "x1");
+	const auto x1 = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y1");
+	const auto y1 = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "x2");
+	const auto x2 = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y2");
+	const auto y2 = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+
+	if (!x1)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x1' property");
+	if (!y1)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y1' property");
+	if (!x2)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x2' property");
+	if (!y2)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y2' property");
+
+	return { *x1, *y1, *x2, *y2 };
+}
+
+auto type_traits<line>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<line>
+{
+	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+void type_traits<line>::push(duk_context* ctx, const line& line)
+{
+	duk::stack_guard sa(ctx, 1);
+
+	duk_push_object(ctx);
+	put(ctx, line);
+}
+
+void type_traits<line>::put(duk_context* ctx, const line& line)
+{
+	assert(duk_is_object(ctx, -1));
+
+	duk::stack_guard sa(ctx);
+
+	duk::push(ctx, line.x1);
+	duk_put_prop_string(ctx, -2, "x1");
+	duk::push(ctx, line.y1);
+	duk_put_prop_string(ctx, -2, "y1");
+	duk::push(ctx, line.x2);
+	duk_put_prop_string(ctx, -2, "x2");
+	duk::push(ctx, line.y2);
+	duk_put_prop_string(ctx, -2, "y2");
+}
+
+} // !duk
+
+void load_line_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx, 0);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Line_constructor, DUK_VARARGS);
+	duk_put_prop_string(ctx, -2, "Line");
+	duk_pop(ctx);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/line_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,110 @@
+/*
+ * line_js_api.hpp -- line description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_LINE_HPP
+#define MALIKANIA_JS_LINE_HPP
+
+/**
+ * \file js_line.hpp
+ * \brief JavaScript binding for line.
+ *
+ * Lines are plain objects.
+ *
+ * ````
+ * {
+ *   x1: 10,
+ *   y1: 10,
+ *   x2: 50,
+ *   y2: 50
+ * }
+ * ````
+ */
+
+#include "duk.hpp"
+
+namespace mlk {
+
+struct line;
+
+namespace js {
+
+namespace duk {
+
+template <>
+struct type_traits<line> {
+	/**
+	 * Get a line.
+	 *
+	 * \param ctx the context
+	 * \param index the index
+	 * \return the line
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> line;
+
+	/**
+	 * Require a line.
+	 *
+	 * If value is not an object or any property is invalid, raise a
+	 * JavaScript error.
+	 *
+	 * \param ctx the context
+	 * \param index the index
+	 * \return the line
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> line;
+
+	/**
+	 * Like get but return def if the value at the given index is not an object.
+	 *
+	 * \param ctx the context
+	 * \param index the index
+	 * \return the line
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<line>;
+
+	/**
+	 * Push the line as object.
+	 *
+	 * \param ctx the context
+	 * \param line the line
+	 */
+	static void push(duk_context* ctx, const line& line);
+
+	/**
+	 * Put the line properties into the object at the top of the stack.
+	 *
+	 * \param ctx the context
+	 * \param line the line
+	 */
+	static void put(duk_context* ctx, const line& line);
+};
+
+} // !duk
+
+/**
+ * Load Malikania.Line API into the context.
+ *
+ * \param ctx the context
+ */
+void load_line_api(duk_context* ctx);
+
+} // !js
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_LINE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/loader_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,56 @@
+/*
+ * loader_js_api.cpp -- resources loader (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/loader.hpp>
+
+#include "loader_js_api.hpp"
+
+namespace mlk::js::duk {
+
+namespace {
+
+const std::string_view variable("\xff""\xff""Malikania.Loader");
+
+} // !namespace
+
+void type_traits<loader>::put(duk_context *ctx, loader& loader)
+{
+	assert(ctx);
+
+	duk::stack_guard sa(ctx);
+
+	duk_push_pointer(ctx, &loader);
+	duk_put_global_string(ctx, variable.data());
+}
+
+auto type_traits<loader>::require(duk_context* ctx, duk_idx_t) -> loader&
+{
+	assert(ctx);
+
+	duk::stack_guard sa(ctx);
+
+	duk_get_global_string(ctx, variable.data());
+	auto ptr = static_cast<loader*>(duk_to_pointer(ctx, -1));
+	duk_pop(ctx);
+
+	return *static_cast<loader*>(ptr);
+}
+
+} // !mlk::js::duk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/loader_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,45 @@
+/*
+ * loader_js_api.hpp -- resources loader (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_LOADER_HPP
+#define MALIKANIA_JS_LOADER_HPP
+
+#include "duk.hpp"
+
+namespace mlk {
+
+class loader;
+
+namespace js {
+
+namespace duk {
+
+template <>
+struct type_traits<loader> {
+	static void put(duk_context* ctx, loader& loader);
+
+	static auto require(duk_context* ctx, duk_idx_t index) -> loader&;
+};
+
+} // !duk
+
+} // !js
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_LOADER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/point_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,133 @@
+/*
+ * point_js_api.cpp -- point description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/point.hpp>
+
+#include "point_js_api.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+auto Point_constructor(duk_context* ctx) -> duk_ret_t
+{
+	point obj;
+
+	if (duk_get_top(ctx) == 2)
+		obj = point{
+			duk::require<int>(ctx, 0),
+			duk::require<int>(ctx, 1)
+		};
+	else if (duk_get_top(ctx) == 1)
+		obj = duk::require<point>(ctx, 0);
+
+	duk_ret_t ret;
+
+	// Allow both constructor and non constructor calls.
+	if (duk_is_constructor_call(ctx)) {
+		duk_push_this(ctx);
+		duk::put(ctx, obj);
+		duk_pop(ctx);
+		ret = 0;
+	} else {
+		duk::push(ctx, obj);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+} // !namespace
+
+namespace duk {
+
+auto type_traits<point>::get(duk_context* ctx, duk_idx_t index) -> point
+{
+	duk::stack_guard sa(ctx);
+
+	point ret;
+
+	duk_get_prop_string(ctx, index, "x");
+	ret.x = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y");
+	ret.y = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+
+	return ret;
+}
+
+auto type_traits<point>::require(duk_context* ctx, duk_idx_t index) -> point
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, "x");
+	const auto x = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y");
+	const auto y = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+
+	if (!x)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x' property");
+	if (!y)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y' property");
+
+	return { *x, *y };
+}
+
+auto type_traits<point>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<point>
+{
+	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+void type_traits<point>::push(duk_context* ctx, const point &point)
+{
+	duk::stack_guard sa(ctx, 1);
+
+	duk_push_object(ctx);
+	put(ctx, point);
+}
+
+void type_traits<point>::put(duk_context* ctx, const point& point)
+{
+	assert(duk_is_object(ctx, -1));
+
+	duk::stack_guard sa(ctx);
+
+	duk::push(ctx, point.x);
+	duk_put_prop_string(ctx, -2, "x");
+	duk::push(ctx, point.y);
+	duk_put_prop_string(ctx, -2, "y");
+}
+
+} // !duk
+
+void load_point_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx, 0);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Point_constructor, DUK_VARARGS);
+	duk_put_prop_string(ctx, -2, "Point");
+	duk_pop(ctx);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/point_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,72 @@
+/*
+ * point_js_api.hpp -- point description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_POINT_HPP
+#define MALIKANIA_JS_POINT_HPP
+
+/**
+ * \file js_point.hpp
+ * \brief JavaScript binding for line.
+ *
+ * Points are plain objects.
+ *
+ * ````
+ * {
+ *   x: 10,
+ *   y: 10,
+ * }
+ * ````
+ */
+
+#include "duk.hpp"
+
+namespace mlk {
+
+struct point;
+
+namespace js {
+
+namespace duk {
+
+template <>
+struct type_traits<point> {
+        static auto require(duk_context* ctx, duk_idx_t index) -> point;
+
+        static auto get(duk_context* ctx, duk_idx_t index) -> point;
+
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<point>;
+
+        static void push(duk_context* ctx, const point& point);
+
+        static void put(duk_context* ctx, const point& point);
+};
+
+} // !duk
+
+/**
+ * Load Malikania.Point API into the context.
+ *
+ * \param ctx the context
+ */
+void load_point_api(duk_context* ctx);
+
+} // !js
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_POINT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/rectangle_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,158 @@
+/*
+ * rectangle_js_api.cpp -- rectangle description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/rectangle.hpp>
+
+#include "rectangle_js_api.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+auto Rectangle_constructor(duk_context* ctx) -> duk_ret_t
+{
+	rectangle rect;
+
+	if (duk_get_top(ctx) == 4) {
+		rect = rectangle{
+			duk::require<int>(ctx, 0),
+			duk::require<int>(ctx, 1),
+			duk::require<unsigned>(ctx, 2),
+			duk::require<unsigned>(ctx, 3)
+		};
+	} else if (duk_get_top(ctx) == 1)
+		rect = duk::require<rectangle>(ctx, 0);
+
+	duk_ret_t ret;
+
+	// Allow both constructor and non constructor calls.
+	if (duk_is_constructor_call(ctx)) {
+		duk_push_this(ctx);
+		duk::put(ctx, rect);
+		duk_pop(ctx);
+		ret = 0;
+	} else {
+		duk::push(ctx, rect);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+} // !namespace
+
+namespace duk {
+
+auto type_traits<rectangle>::get(duk_context* ctx, duk_idx_t index) -> rectangle
+{
+	duk::stack_guard sa(ctx);
+
+	rectangle ret;
+
+	if (!duk_is_object(ctx, index))
+		return ret;
+
+	duk_get_prop_string(ctx, index, "x");
+	ret.x = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y");
+	ret.y = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "width");
+	ret.width = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "height");
+	ret.height = duk::get<int>(ctx, -1);
+	duk_pop(ctx);
+
+	return ret;
+}
+
+auto type_traits<rectangle>::require(duk_context* ctx, duk_idx_t index) -> rectangle
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, "x");
+	const auto x = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "y");
+	const auto y = duk::optional<int>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "width");
+	const auto width = duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "height");
+	const auto height = duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+
+	if (!x)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'x' property");
+	if (!y)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'y' property");
+	if (!width)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'width' property");
+	if (!height)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'height' property");
+
+	return { *x, *y, *width, *height };
+}
+
+auto type_traits<rectangle>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<rectangle>
+{
+	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;;
+}
+
+void type_traits<rectangle>::push(duk_context* ctx, const rectangle& rect)
+{
+	duk::stack_guard sa(ctx, 1);
+
+	duk_push_object(ctx);
+	put(ctx, rect);
+}
+
+void type_traits<rectangle>::put(duk_context* ctx, const rectangle& rect)
+{
+	assert(duk_is_object(ctx, -1));
+
+	duk::stack_guard sa(ctx);
+
+        duk::push(ctx, rect.x);
+	duk_put_prop_string(ctx, -2, "x");
+        duk::push(ctx, rect.y);
+	duk_put_prop_string(ctx, -2, "y");
+        duk::push(ctx, rect.width);
+	duk_put_prop_string(ctx, -2, "width");
+        duk::push(ctx, rect.height);
+	duk_put_prop_string(ctx, -2, "height");
+}
+
+} // !duk
+
+void load_rectangle_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx, 0);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Rectangle_constructor, DUK_VARARGS);
+	duk_put_prop_string(ctx, -2, "Rectangle");
+	duk_pop(ctx);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/rectangle_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,111 @@
+/*
+ * rectangle_js_api.hpp -- rectangle description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_RECTANGLE_HPP
+#define MALIKANIA_JS_RECTANGLE_HPP
+
+/**
+ * \file js_rectangle.hpp
+ * \brief JavaScript binding for rectangle.
+ *
+ * Rectangles are plain objects.
+ *
+ * ````
+ * {
+ *   x: 10,
+ *   y: 20,
+ *   width: 100,
+ *   height: 200
+ * }
+ * ````
+ */
+
+#include "duk.hpp"
+
+namespace mlk {
+
+struct rectangle;
+
+namespace js {
+
+namespace duk {
+
+template <>
+struct type_traits<rectangle> {
+	/**
+	 * Get a rectangle.
+	 *
+	 * The rectangle may be adjusted if any values are incorrect.
+	 *
+	 * \param ctx the context
+	 * \param index the value index
+	 * \return the rectangle
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> rectangle;
+
+	/**
+	 * Require a rectangle.
+	 *
+	 * If the object is not a rectangle or if width, height are invalid, raise a JavaScript error.
+	 *
+	 * \param ctx the context
+	 * \param index the index
+	 * \return the rectangle
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> rectangle;
+
+	/**
+	 * Like get but return the default value if the value at the given index is not an object or invalid
+	 *
+	 * \param ctx the context
+	 * \param index the idnex
+	 * \return the rectangle
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<rectangle>;
+
+	/**
+	 * Push the rectangle as object.
+	 *
+	 * \param ctx the context
+	 * \param rect the rectangle
+	 */
+	static void push(duk_context* ctx, const rectangle &rect);
+
+	/**
+	 * Put the rectangle properties into the object at the top of the stack.
+	 *
+	 * \param ctx the context
+	 * \param rect the rectangle
+	 */
+	static void put(duk_context* ctx, const rectangle& rect);
+};
+
+} // !duk
+
+/**
+ * Load Malikania.Rectangle API into the context.
+ *
+ * \param ctx the context
+ */
+void load_rectangle_api(duk_context* ctx);
+
+} // !js
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_RECTANGLE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/size_js_api.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,133 @@
+/*
+ * size_js_api.cpp -- size description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include <malikania/size.hpp>
+
+#include "size_js_api.hpp"
+
+namespace mlk::js {
+
+namespace {
+
+auto Size_constructor(duk_context* ctx) -> duk_ret_t
+{
+	size obj;
+
+	if (duk_get_top(ctx) == 2) {
+		obj = size{
+			duk::require<unsigned>(ctx, 0),
+			duk::require<unsigned>(ctx, 1)
+		};
+	} else if (duk_get_top(ctx) == 1)
+		obj = duk::require<size>(ctx, 0);
+
+	duk_ret_t ret;
+
+	// Allow both constructor and non constructor calls.
+	if (duk_is_constructor_call(ctx)) {
+		duk_push_this(ctx);
+		duk::put(ctx, obj);
+		duk_pop(ctx);
+		ret = 0;
+	} else {
+		duk::push(ctx, obj);
+		ret = 1;
+	}
+
+	return ret;
+}
+
+} // !namespace
+
+namespace duk {
+
+auto type_traits<size>::get(duk_context* ctx, duk_idx_t index) -> size
+{
+	duk::stack_guard sa(ctx);
+
+	size ret;
+
+	duk_get_prop_string(ctx, index, "width");
+	ret.width = duk::get<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "height");
+	ret.height = duk::get<unsigned>(ctx, -1);
+	duk_pop(ctx);
+
+	return ret;
+}
+
+auto type_traits<size>::require(duk_context* ctx, duk_idx_t index) -> size
+{
+	duk::stack_guard sa(ctx);
+
+	duk_get_prop_string(ctx, index, "width");
+	const auto width = duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+	duk_get_prop_string(ctx, index, "height");
+	const auto height = duk::optional<unsigned>(ctx, -1);
+	duk_pop(ctx);
+
+	if (!width)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'width' property");
+	if (!height)
+		duk_error(ctx, DUK_ERR_TYPE_ERROR, "invalid 'height' property");
+
+	return { *width, *height };
+}
+
+auto type_traits<size>::optional(duk_context* ctx, duk_idx_t index) -> std::optional<size>
+{
+	return duk_is_object(ctx, index) ? std::make_optional(get(ctx, index)) : std::nullopt;
+}
+
+void type_traits<size>::push(duk_context* ctx, const size& size)
+{
+	duk::stack_guard sa(ctx, 1);
+
+	duk_push_object(ctx);
+	put(ctx, size);
+}
+
+void type_traits<size>::put(duk_context* ctx, const size& size)
+{
+	assert(duk_is_object(ctx, -1));
+
+	duk::stack_guard sa(ctx, 0);
+
+	duk::push(ctx, size.width);
+	duk_put_prop_string(ctx, -2, "width");
+	duk::push(ctx, size.height);
+	duk_put_prop_string(ctx, -2, "height");
+}
+
+} // !duk
+
+void load_size_api(duk_context* ctx)
+{
+	duk::stack_guard sa(ctx, 0);
+
+	duk_get_global_string(ctx, "Malikania");
+	duk_push_c_function(ctx, Size_constructor, DUK_VARARGS);
+	duk_put_prop_string(ctx, -2, "Size");
+	duk_pop(ctx);
+}
+
+} // !mlk::js
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-js/malikania/js/size_js_api.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,108 @@
+/*
+ * size_js_api.hpp -- size description (JavaScript binding)
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_JS_SIZE_HPP
+#define MALIKANIA_JS_SIZE_HPP
+
+/**
+ * \file js_size.hpp
+ * \brief JavaScript binding for size.
+ *
+ * Sizes are plain objects.
+ *
+ * ````
+ * {
+ *   width: 1000,
+ *   height: 2000
+ * }
+ * ````
+ */
+
+#include "duk.hpp"
+
+namespace mlk {
+
+struct size;
+
+namespace js {
+
+namespace duk {
+
+template <>
+struct type_traits<size> {
+	/**
+	 * Get a size.
+	 *
+	 * The size may be adjusted if any values are incorrect.
+	 *
+	 * \param ctx the context
+	 * \param index the value index
+	 * \return the size
+	 */
+	static auto get(duk_context* ctx, duk_idx_t index) -> size;
+
+	/**
+	 * Require a size
+	 *
+	 * If the object is not a size, raise a JavaScript error.
+	 *
+	 * \param ctx the context
+	 * \param index the index
+	 * \return the size
+	 */
+	static auto require(duk_context* ctx, duk_idx_t index) -> size;
+
+	/**
+	 * \param ctx the context
+	 * \param index the idnex
+	 * \param def the default value
+	 * \return the size
+	 */
+	static auto optional(duk_context* ctx, duk_idx_t index) -> std::optional<size>;
+
+	/**
+	 * Push the size as object.
+	 *
+	 * \param ctx the context
+	 * \param size the size
+	 */
+	static void push(duk_context* ctx, const size& size);
+
+	/**
+	 * Put the size properties into the object at the top of the stack.
+	 *
+	 * \param ctx the context
+	 * \param size the size
+	 */
+	static void put(duk_context* ctx, const size& size);
+};
+
+} // !duk
+
+/**
+ * Load Malikania.Size API into the context.
+ *
+ * \param ctx the context
+ */
+void load_size_api(duk_context* ctx);
+
+} // !js
+
+} // !mlk
+
+#endif // !MALIKANIA_JS_SIZE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,59 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk-server)
+
+find_package(Threads REQUIRED)
+
+set(
+	HEADERS
+	${libmlk-server_SOURCE_DIR}/malikania/server/client.hpp
+	${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.hpp
+	${libmlk-server_SOURCE_DIR}/malikania/server/server.hpp
+)
+
+set(
+	SOURCES
+	${libmlk-server_SOURCE_DIR}/malikania/server/client.cpp
+	${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.cpp
+	${libmlk-server_SOURCE_DIR}/malikania/server/server.cpp
+)
+
+set(
+	LIBRARIES
+	OpenSSL::Crypto
+	OpenSSL::SSL
+	Threads::Threads
+	json
+	libmlk
+	libmlk-db
+)
+
+if (CMAKE_SYSTEM_NAME MATCHES Windows)
+	list(APPEND LIBRARIES mswsock)
+endif ()
+
+malikania_define_library(
+	TARGET libmlk-server
+	SOURCES ${HEADERS} ${SOURCES}
+	LIBRARIES ${LIBRARIES}
+	PUBLIC_INCLUDES
+		${libmlk-server_SOURCE_DIR}
+	PRIVATE_INCLUDES
+		${libmlk-server_SOURCE_DIR}/malikania/server
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/client.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,185 @@
+/*
+ * client.cpp -- client connected to the server
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+#include <iostream>
+
+#include "client.hpp"
+#include "server.hpp"
+
+namespace mlk::server {
+
+void client::handle_read(std::error_code code, std::size_t xfer)
+{
+	// TODO: use fixed size stream + verify exceed.
+	if (code)
+		server_.handle_disconnect(shared_from_this());
+	else {
+		std::string message(
+			boost::asio::buffers_begin(input_.data()),
+			boost::asio::buffers_begin(input_.data()) + xfer - 4
+		);
+
+		// Remove early in case of errors.
+		input_.consume(xfer);
+
+		try {
+			server_.handle_read(shared_from_this(), nlohmann::json::parse(message));
+		} catch (const std::exception& ex) {
+			std::cerr << ex.what() << std::endl;
+		}
+
+		do_read();
+	}
+}
+
+void client::handle_write(std::error_code code, std::size_t)
+{
+	if (code) {
+		server_.handle_disconnect(shared_from_this());
+	} else {
+		output_.pop_front();
+
+		if (!output_.empty()) {
+			do_write();
+		} else if (state_ == state::closing) {
+			server_.handle_disconnect(shared_from_this());
+		}
+	}
+}
+
+void client::do_read()
+{
+	const auto self = shared_from_this();
+
+	boost::asio::async_read_until(socket_, input_, "\r\n\r\n", [self] (auto code, auto xfer) {
+		self->handle_read(code, xfer);
+	});
+}
+
+void client::do_write()
+{
+	assert(!output_.empty());
+
+	const auto self = shared_from_this();
+
+	boost::asio::async_write(socket_, boost::asio::buffer(output_[0]), [self] (auto code, auto xfer) {
+		self->handle_write(code, xfer);
+	});
+}
+
+void client::handshake()
+{
+	const auto self = shared_from_this();
+
+	socket_.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) {
+		if (code) {
+			std::cerr << "handshake failure: " << code << std::endl;
+		} else {
+			self->read();
+		}
+	});
+}
+
+void client::read()
+{
+	do_read();
+}
+
+client::client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context)
+	: server_(server)
+	, socket_(service, context)
+{
+}
+
+auto client::get_state() const noexcept -> state
+{
+	return state_;
+}
+
+void client::set_state(state state) noexcept
+{
+	state_ = state;
+}
+
+auto client::get_account() const noexcept -> const db::account&
+{
+	assert(state_ == state::ready);
+
+	return *account_;
+}
+
+auto client::get_account() noexcept -> db::account&
+{
+	assert(state_ == state::ready);
+
+	return *account_;
+}
+
+void client::set_account(db::account account) noexcept
+{
+	account_ = std::move(account);
+}
+
+void client::send(nlohmann::json message)
+{
+	assert(message.is_object());
+
+	if (state_ == state::closing) {
+		return;
+	}
+
+	const auto in_progress = !output_.empty();
+
+	output_.push_back(message.dump() + "\r\n\r\n");
+
+	if (!in_progress) {
+		do_write();
+	}
+}
+
+void client::ok(std::string cmd, nlohmann::json message)
+{
+	assert(message.is_null() || message.is_object());
+
+	if (!message.is_object()) {
+		message = nlohmann::json::object();
+	}
+
+	message["command"] = std::move(cmd);
+	message["status"] = true;
+
+	send(std::move(message));
+}
+
+void client::error(std::string cmd, std::error_code code)
+{
+	send({
+		{ "command",	std::move(cmd)  },
+		{ "status",	 code.value()	},
+		{ "error",	  code.message()  }
+	});
+}
+
+void client::fatal(std::string cmd, std::error_code code)
+{
+	error(std::move(cmd), std::move(code));
+	state_ = state::closing;
+}
+
+} // !mlk::server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/client.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,184 @@
+/*
+ * client.hpp -- client connected to the server
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_CLIENT_HPP
+#define MALIKANIA_SERVER_CLIENT_HPP
+
+/**
+ * \file client.hpp
+ * \brief Client connected to the server.
+ */
+
+#include <cstdint>
+#include <deque>
+#include <memory>
+#include <optional>
+#include <sstream>
+#include <string>
+#include <system_error>
+
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+#include <json.hpp>
+
+#include "db/account.hpp"
+
+namespace mlk::server {
+
+class server;
+
+/**
+ * \brief Client connected to the server.
+ */
+class client : public std::enable_shared_from_this<client> {
+public:
+	friend class server;
+
+	/**
+	 * \brief Describe client current state
+	 */
+	enum class state : std::uint8_t {
+		/**
+		 * The client is authenticating.
+		 */
+		authenticating,
+
+		/**
+		 * The client is ready for I/O
+		 */
+		ready,
+
+		/**
+		 * The client is closing.
+		 *
+		 * No more messages are accepted for output, the pending queue is sent
+		 * to the client and then disconnected.
+		 */
+		closing
+	};
+
+private:
+	server& server_;
+	state state_{state::authenticating};
+
+	boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
+	boost::asio::streambuf input_;
+	std::deque<std::string> output_;
+	std::optional<db::account> account_;
+
+	void handle_read(std::error_code, std::size_t);
+	void handle_write(std::error_code, std::size_t);
+
+	void do_read();
+	void do_write();
+
+	void handshake();
+	void read();
+
+public:
+	/**
+	 * Create the client in the associated server.
+	 *
+	 * \param server the server object
+	 * \param service the main loop service
+	 * \param context the ssl context
+	 */
+	client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context);
+
+	/**
+	 * Get the client state.
+	 *
+	 * \return the state
+	 */
+	auto get_state() const noexcept -> state;
+
+	/**
+	 * Set the client state.
+	 *
+	 * \param new_state the state
+	 */
+	void set_state(state state) noexcept;
+
+	/**
+	 * Get the underlying account database object.
+	 *
+	 * \pre get_state() == ready
+	 * \return the account
+	 */
+	auto get_account() const noexcept -> const db::account&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \pre get_state() == ready
+	 * \return the account
+	 */
+	auto get_account() noexcept -> db::account&;
+
+	/**
+	 * Set the client account.
+	 *
+	 * \param account the account
+	 */
+	void set_account(db::account account) noexcept;
+
+	/**
+	 * Send a command and its arguments.
+	 *
+	 * \pre !cmd.empty()
+	 * \param message the message to send
+	 */
+	void send(nlohmann::json message);
+
+	/**
+	 * Send successful command result.
+	 *
+	 * \pre !cmd.empty()
+	 * \pre args.is_object() || args.is_object()
+	 * \param cmd the command name
+	 * \param args the extra data
+	 */
+	void ok(std::string cmd, nlohmann::json args = nullptr);
+
+	/**
+	 * Send a error command result.
+	 *
+	 * \pre !cmd.empty()
+	 * \pre code
+	 * \param cmd the command name
+	 * \param code the error code
+	 */
+	void error(std::string cmd, std::error_code code);
+
+	/**
+	 * Send a fatal error result.
+	 *
+	 * The client state is changed to closing.
+	 *
+	 * \pre !cmd.empty()
+	 * \pre code
+	 * \param cmd the command name
+	 * \param code the error code
+	 */
+	void fatal(std::string cmd, std::error_code code);
+};
+
+} // !mlk::server
+
+#endif // !MALIKANIA_SERVER_CLIENT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/hash.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,262 @@
+/*
+ * hash.hpp -- hash functions
+ *
+ * Copyright (c) 2013-2018 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_SERVER_HASH_HPP
+#define MLK_SERVER_HASH_HPP
+
+/**
+ * \file hash.hpp
+ * \brief Hash functions.
+ * \author David Demelier <markand@malikania.fr>
+ * \version 2.0.0-dev
+ */
+
+/**
+ * \brief Define buffer size.
+ */
+#if !defined(HASH_BUFFER_SIZE)
+#	define HASH_BUFFER_SIZE 2048
+#endif
+
+#include <cassert>
+#include <istream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <string_view>
+
+#include <openssl/sha.h>
+#include <openssl/md5.h>
+
+/**
+ * \brief Hash namespace.
+ */
+namespace mlk::server::hash {
+
+/**
+ * \cond HASH_HIDDEN_SYMBOLS
+ */
+
+namespace detail {
+
+template <typename Context>
+using init_func = int (*)(Context*);
+
+template <typename Context>
+using update_func = int (*)(Context*, const void*, size_t);
+
+template <typename Context>
+using final_func = int (*)(unsigned char*, Context*);
+
+template <typename Context, size_t Length, typename InputIt>
+inline auto convert(InputIt it,
+                    InputIt end,
+                    init_func<Context> init,
+                    update_func<Context> update,
+                    final_func<Context> finalize) -> std::string
+{
+	unsigned char digest[Length] = { 0 };
+	char hash[Length * 2 + 1];
+	char buf[HASH_BUFFER_SIZE];
+
+	Context ctx;
+	init(&ctx);
+
+	while (it != end) {
+		unsigned i;
+
+		for (i = 0; it != end && i < HASH_BUFFER_SIZE; ++i)
+			buf[i] = *it++;
+
+		update(&ctx, buf, i);
+	}
+
+	finalize(digest, &ctx);
+
+	for (unsigned long i = 0; i < Length; i++)
+		std::snprintf(&hash[i * 2], 2 + 1, "%02x", static_cast<unsigned>(digest[i]));
+
+	return std::string(hash);
+}
+
+} // !namespace
+
+/**
+ * \endcond
+ */
+
+/**
+ * \brief The scheme to use.
+ */
+enum class scheme {
+	md5,            //!< MD5
+	sha1,           //!< SHA-1
+	sha256,         //!< SHA-256
+	sha512          //!< SHA-512
+};
+
+/**
+ * Generic function.
+ *
+ * \param scheme the scheme to use
+ * \param it the first character
+ * \param end the last character
+ * \return the string
+ */
+template <typename InputIt>
+inline auto to_string(scheme scheme, InputIt it, InputIt end) -> std::string
+{
+	assert(scheme >= scheme::md5 && scheme <= scheme::sha512);
+
+	std::string result;
+
+	switch (scheme) {
+	case scheme::md5:
+		result = detail::convert<MD5_CTX, MD5_DIGEST_LENGTH>(it, end, MD5_Init, MD5_Update, MD5_Final);
+		break;
+	case scheme::sha1:
+		result = detail::convert<SHA_CTX, SHA_DIGEST_LENGTH>(it, end, SHA1_Init, SHA1_Update, SHA1_Final);;
+		break;
+	case scheme::sha256:
+		result = detail::convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(it, end, SHA256_Init, SHA256_Update, SHA256_Final);
+		break;
+	case scheme::sha512:
+		result = detail::convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(it, end, SHA512_Init, SHA512_Update, SHA512_Final);
+		break;
+	default:
+		break;
+	}
+
+	return result;
+}
+
+/**
+ * Overload for std::istream.
+ *
+ * \param scheme the scheme to use
+ * \param input the input stream
+ * \return the string
+ */
+inline auto to_string(scheme scheme, std::istream& input) -> std::string
+{
+	return to_string(scheme, std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>());
+}
+
+/**
+ * Overload for std::string_view.
+ *
+ * \param scheme the scheme to use
+ * \param input the input string
+ * \return the string
+ */
+inline auto to_string(scheme scheme, std::string_view input) -> std::string
+{
+	return to_string(scheme, input.begin(), input.end());
+}
+
+/**
+ * Hash using MD5.
+ *
+ * \param input the input string
+ * \return the hashed string
+ */
+inline auto md5(std::string_view input) -> std::string
+{
+	return to_string(scheme::md5, input);
+}
+
+/**
+ * Hash using MD5.
+ *
+ * \param input the input stream
+ * \return the hashed string
+ */
+inline auto md5(std::istream& input) -> std::string
+{
+	return to_string(scheme::md5, input);
+}
+
+/**
+ * Hash using SHA1.
+ *
+ * \param input the input string
+ * \return the hashed string
+ */
+inline auto sha1(std::string_view input) -> std::string
+{
+	return to_string(scheme::sha1, input);
+}
+
+/**
+ * Hash using SHA1.
+ *
+ * \param input the input stream
+ * \return the hashed string
+ */
+inline auto sha1(std::istream& input) -> std::string
+{
+	return to_string(scheme::sha1, input);
+}
+
+/**
+ * Hash using SHA256.
+ *
+ * \param input the input string
+ * \return the hashed string
+ */
+inline auto sha256(std::string_view input) -> std::string
+{
+	return to_string(scheme::sha256, input);
+}
+
+/**
+ * Hash using SHA256.
+ *
+ * \param input the input stream
+ * \return the hashed string
+ */
+inline auto sha256(std::istream& input) -> std::string
+{
+	return to_string(scheme::sha256, input);
+}
+
+/**
+ * Hash using SHA512.
+ *
+ * \param input the input string
+ * \return the hashed string
+ */
+inline auto sha512(std::string_view input) -> std::string
+{
+	return to_string(scheme::sha512, input);
+}
+
+/**
+ * Hash using SHA512.
+ *
+ * \param input the input stream
+ * \return the hashed string
+ */
+inline auto sha512(std::istream& input) -> std::string
+{
+	return to_string(scheme::sha512, input);
+}
+
+} // !mlk::hash
+
+#endif // !MLK_SERVER_HASH_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/net/auth_handler.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,66 @@
+/*
+ * auth_handler.cpp -- handle "auth" network command
+ *
+ * Copyright (c) 2013-2018 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 <malikania/util.hpp>
+
+#include <malikania/error/auth_error.hpp>
+
+#include "db/account.hpp"
+
+#include "auth_handler.hpp"
+#include "client.hpp"
+#include "server.hpp"
+
+namespace mlk::server {
+
+namespace {
+
+auto is_connected(const server& server, const db::account& account) noexcept -> bool
+{
+	const auto& clients = server.get_clients();
+	const auto it = std::find_if(clients.begin(), clients.end(), [&] (const auto& c) {
+		if (c->get_state() != client::state::ready)
+			return false;
+
+		return c->get_account().get_login() == account.get_login();
+	});
+
+	return it != clients.end();
+}
+
+} // !namespace
+
+void auth_handler::exec(server& server, client& clt, nlohmann::json object) noexcept
+{
+	auto ac = db::account::authenticate(
+		util::json::require_string(object, "/login"_json_pointer),
+		util::json::require_string(object, "/password"_json_pointer)
+	);
+
+	if (!ac)
+		clt.fatal("auth", auth_error::invalid_credentials);
+	else if (is_connected(server, *ac))
+		clt.fatal("auth", auth_error::already_connected);
+	else {
+		clt.set_account(std::move(*ac));
+		clt.set_state(client::state::ready);
+		clt.ok("auth");
+	}
+}
+
+} // !mlk::server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/net/auth_handler.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,44 @@
+/*
+ * auth_handler.hpp -- handle "auth" network command
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_AUTH_HANDLER_HPP
+#define MALIKANIA_SERVER_AUTH_HANDLER_HPP
+
+/**
+ * \file auth_handler.hpp
+ * \brief Handle "auth" network command.
+ */
+
+#include "handler.hpp"
+
+namespace mlk::server {
+
+/**
+ * \brief Handle "auth" network command.
+ */
+class auth_handler : public handler {
+public:
+	/**
+	 * \copydoc handler::exec
+	 */
+	void exec(server& server, client& clt, nlohmann::json object) noexcept override;
+};
+
+} // !mlk::server
+
+#endif // !MALIKANIA_SERVER_AUTH_HANDLER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/net/handler.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,63 @@
+/*
+ * handler.hpp -- server side remote command
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_HANDLER_HPP
+#define MALIKANIA_SERVER_HANDLER_HPP
+
+/**
+ * \file handler.hpp
+ * \brief Server side remote command.
+ */
+
+#include <memory>
+
+#include <json.hpp>
+
+namespace mlk::server {
+
+class server;
+class client;
+
+/**
+ * \brief Server side remote command.
+ */
+class handler {
+public:
+	/**
+	 * Default constructor.
+	 */
+	handler() noexcept = default;
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~handler() noexcept = default;
+
+	/**
+	 * Execute the function from the given client.
+	 *
+	 * \param server the global object server
+	 * \param client the client
+	 * \param message the network message
+	 */
+	virtual void exec(server& server, client& client, nlohmann::json message) noexcept = 0;
+};
+
+} // !mlk::server
+
+#endif // !MALIKANIA_SERVER_HANDLER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/server.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,124 @@
+/*
+ * server.cpp -- malikania basic server
+ *
+ * Copyright (c) 2016-2018 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 <malikania/util.hpp>
+
+#include "client.hpp"
+#include "server.hpp"
+
+#include "net/auth_handler.hpp"
+
+namespace mlk::server {
+
+namespace {
+
+auto endpoint(const settings& params) -> boost::asio::ip::tcp::endpoint
+{
+	// TODO: add more settings there.
+	return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port);
+}
+
+} // !namespace
+
+void server::load()
+{
+	handle("auth", std::make_unique<auth_handler>());
+}
+
+void server::start()
+{
+	auto clt = std::make_shared<client>(*this, service_, context_);
+
+	acceptor_.async_accept(clt->socket_.lowest_layer(), [this, clt] (auto code) {
+		handle_accept(std::move(clt), code);
+	});
+}
+
+server::server(boost::asio::io_service& service, const settings& settings)
+	: service_(service)
+	, acceptor_(service, endpoint(settings))
+	, context_(boost::asio::ssl::context::sslv23)
+{
+	context_.use_certificate_chain_file(settings.certificate);
+	context_.use_private_key_file(settings.key, boost::asio::ssl::context::pem);
+
+	load();
+	start();
+}
+
+auto server::get_clients() const noexcept -> const clients&
+{
+	return clients_;
+}
+
+auto server::get_clients() noexcept -> clients&
+{
+	return clients_;
+}
+
+void server::handle(std::string name, std::unique_ptr<handler> handler)
+{
+	assert(!name.empty());
+	assert(handler);
+
+	commands_.emplace(std::move(name), std::move(handler));
+}
+
+void server::handle_disconnect(std::shared_ptr<client> clt)
+{
+	clients_.erase(std::move(clt));
+}
+
+void server::handle_read(std::shared_ptr<client> clt, nlohmann::json message)
+{
+	assert(message.is_object());
+
+	const auto cmd = message.find("command");
+
+	if (cmd == message.end() || !cmd->is_string()) {
+		// TODO: log error.
+		return;
+	}
+
+	const auto it = commands_.find(*cmd);
+
+	if (it == commands_.end()) {
+		// TODO: log error.
+		return;
+	}
+
+	try {
+		it->second->exec(*this, *clt, std::move(message));
+	} catch (const std::exception&) {
+		// TODO: log error.
+	}
+}
+
+void server::handle_accept(std::shared_ptr<client> clt, std::error_code code)
+{
+	if (code) {
+		// TODO: log error.
+	} else {
+		clt->handshake();
+		clients_.insert(std::move(clt));
+	}
+
+	start();
+}
+
+} // !mlk::server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk-server/malikania/server/server.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,139 @@
+/*
+ * server.hpp -- malikania basic server
+ *
+ * Copyright (c) 2016-2018 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 MALIKANIA_SERVER_SERVER_HPP
+#define MALIKANIA_SERVER_SERVER_HPP
+
+/**
+ * \file server.hpp
+ * \brief Malikania basic server.
+ */
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <system_error>
+
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+#include <json.hpp>
+
+#include "net/handler.hpp"
+
+namespace mlk::server {
+
+class client;
+class database;
+
+/**
+ * \brief Server parameters
+ */
+struct settings {
+    std::uint16_t port{3320};
+    std::string certificate;
+    std::string key;
+};
+
+/**
+ * \file server.hpp
+ * \brief Malikania basic server.
+ */
+class server {
+public:
+    using clients = std::unordered_set<std::shared_ptr<client>>;
+    using commands = std::unordered_map<std::string, std::unique_ptr<handler>>;
+
+private:
+    boost::asio::io_service& service_;
+    boost::asio::ip::tcp::acceptor acceptor_;
+    boost::asio::ssl::context context_;
+
+    clients clients_;
+    commands commands_;
+
+    void load();
+    void start();
+
+public:
+    /**
+     * Construct a server object.
+     *
+     * \param ctx the IO context
+     * \param settings the settings
+     */
+    server(boost::asio::io_service& service, const settings& settings);
+
+    /**
+     * Get the set of connected clients.
+     *
+     * \return the clients
+     */
+    auto get_clients() const noexcept -> const clients&;
+
+    /**
+     * Overloaded function.
+     *
+     * \return the clients
+     */
+    auto get_clients() noexcept -> clients&;
+
+    /**
+     * Add network handler.
+     *
+     * If a handler is already present, it will be replaced with the new one.
+     *
+     * \pre handler != nullptr
+     * \pre !name.empty()
+     * \param handler the handler
+     */
+    void handle(std::string name, std::unique_ptr<handler> handler);
+
+    /**
+     * Handle client disconnection.
+     *
+     * \param client the client
+     */
+    virtual void handle_disconnect(std::shared_ptr<client> client);
+
+    /**
+     * Asynchronous function called once a message has been completely and
+     * successfully received from the client.
+     *
+     * The default implementation searches for a handler defined for this
+     * message.
+     *
+     * \param client the client
+     * \param message the message received
+     */
+    virtual void handle_read(std::shared_ptr<client>, nlohmann::json message);
+
+    /**
+     * Handle accept of a new client.
+     *
+     * \param client the newly received client
+     * \param code the error code if any
+     */
+    virtual void handle_accept(std::shared_ptr<client> client, std::error_code code);
+};
+
+} // !mlk
+
+#endif // !MALIKANIA_SERVER_SERVER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,64 @@
+#
+# CMakeLists.txt -- CMake build system for malikania
+#
+# Copyright (c) 2013-2018 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.
+#
+
+project(libmlk)
+
+find_package(Boost REQUIRED filesystem system)
+find_package(OpenSSL REQUIRED)
+
+set(
+	HEADERS
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/error/auth_error.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/error/error.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/game.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/line.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/loader.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/locator.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/point.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/rectangle.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/size.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/socket.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/tileset.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/unicode.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/util.hpp
+)
+
+set(
+	SOURCES
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/error/auth_error.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/loader.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/locator.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/socket.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/unicode.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/malikania/util.cpp
+)
+
+malikania_define_library(
+	PROJECT libmlk
+	TARGET libmlk
+	SOURCES ${HEADERS} ${SOURCES}
+	PUBLIC_INCLUDES
+		$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+	LIBRARIES
+		Boost::boost
+		Boost::filesystem
+		Boost::system
+		OpenSSL::Crypto
+		OpenSSL::SSL
+		json
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/error/auth_error.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,40 @@
+/*
+ * auth_error.hpp -- auth command error
+ *
+ * Copyright (c) 2013-2018 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 "auth_error.hpp"
+
+namespace mlk {
+
+auto auth_error::error_category() -> std::string
+{
+	return "auth";
+}
+
+auto auth_error::error_message(error code) -> std::string
+{
+	switch (code) {
+	case invalid_credentials:
+		return "invalid credentials";
+	case already_connected:
+		return "already connected";
+	default:
+		return "no error";
+	}
+}
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/error/auth_error.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,73 @@
+/*
+ * auth_error.hpp -- auth command error
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_AUTH_ERROR_HPP
+#define MALIKANIA_AUTH_ERROR_HPP
+
+/**
+ * \file auth_error.hpp
+ * \brief Auth command error.
+ */
+
+#include "error.hpp"
+
+namespace mlk {
+
+/**
+ * \brief Auth command error.
+ */
+struct auth_error {
+	/**
+	 * \brief Error description
+	 */
+	enum error {
+		/**
+		 * \brief No error.
+		 */
+		no_error = 0,
+
+		/**
+		 * \brief Invalid password or login.
+		 */
+		invalid_credentials,
+
+		/**
+		 * \brief User is already connected.
+		 */
+		already_connected
+	};
+
+	static auto error_category() -> std::string;
+
+	/**
+	 * Convert the code to a human message.
+	 *
+	 * \param code the code
+	 */
+	static auto error_message(error code) -> std::string;
+};
+
+} // !mlk
+
+MALIKANIA_ERROR_CODE(
+	mlk::auth_error::error,
+	mlk::auth_error::error_category,
+	mlk::auth_error::error_message
+)
+
+#endif // !MALIKANIA_AUTH_ERROR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/error/error.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,99 @@
+/*
+ * error.hpp -- error code creation
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_ERROR_HPP
+#define MALIKANIA_ERROR_HPP
+
+/**
+ * \file error.hpp
+ * \brief Error code creation.
+ */
+
+#include <string>
+#include <system_error>
+#include <type_traits>
+
+/**
+ * Generate a custom std::error_code with the given type. The macro must be
+ * called outside mlk namespace.
+ *
+ * See the following example.
+ *
+ * ```cpp
+ * namespace mlk {
+ *
+ * class foo {
+ * public:
+ *     enum class error {
+ *         no_error = 0,
+ *         error_kind_foo,
+ *         error_kind_bar
+ *     };
+ *
+ *     static auto error_category() -> std::string
+ *     {
+ *         return "sometext";
+ *     }
+ *
+ *     static auto error_message(error code) -> std::string
+ *     {
+ *         // convert code to a string.
+ *     }
+ * };
+ *
+ * } // !mlk
+ *
+ * MALIKANIA_ERROR_CODE(
+ *     my_error::error,
+ *     my_error::error_category,
+ *     my_error::error_message
+ * )
+ * ```
+ */
+#define MALIKANIA_ERROR_CODE(Enum, Category, Message)                       \
+namespace std {                                                             \
+                                                                            \
+template <>                                                                 \
+struct is_error_code_enum<Enum> : public std::true_type {                   \
+};                                                                          \
+                                                                            \
+}                                                                           \
+                                                                            \
+namespace mlk {                                                             \
+                                                                            \
+inline auto make_error_code(Enum code) noexcept -> std::error_code          \
+{                                                                           \
+        static const class : public std::error_category {                   \
+        public:                                                             \
+            auto name() const noexcept -> const char* override              \
+            {                                                               \
+                    return Category().c_str();                              \
+            }                                                               \
+                                                                            \
+            auto message(int e) const -> std::string override               \
+            {                                                               \
+                    return Message(static_cast<Enum>(e));                   \
+            }                                                               \
+        } category;                                                         \
+                                                                            \
+        return { static_cast<int>(code), category };                        \
+}                                                                           \
+                                                                            \
+}
+
+#endif // !MALIKANIA_ERROR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/game.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,44 @@
+/*
+ * game.hpp -- basic game class
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_GAME_HPP
+#define MALIKANIA_GAME_HPP
+
+/**
+ * \file game.hpp
+ * \brief Game description.
+ */
+
+#include <string>
+
+namespace mlk {
+
+/**
+ * \brief Basic game description.
+ */
+struct game {
+	std::string name;
+	std::string version;
+	std::string requires;
+	std::string license;
+	std::string author;
+};
+
+} // !mlk
+
+#endif // !MALIKANIA_GAME_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/line.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,68 @@
+/*
+ * line.hpp -- line description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_LINE_HPP
+#define MALIKANIA_LINE_HPP
+
+/**
+ * \file line.hpp
+ * \brief line description.
+ */
+
+namespace mlk {
+
+/**
+ * \brief line description.
+ *
+ * A line has an origin (x, y) and a destination (x, y).
+ */
+struct line {
+	int x1{0};
+	int y1{0};
+	int x2{0};
+	int y2{0};
+};
+
+/**
+ * Compare equality.
+ *
+ * \param l1 the first line
+ * \param l2 the second line
+ * \return true if they equal
+ */
+inline auto operator==(const line& l1, const line& l2) noexcept -> bool
+{
+	return l1.x1 == l2.x1 && l1.x2 == l2.x2 &&
+	       l1.y1 == l2.y1 && l1.y2 == l2.y2;
+}
+
+/**
+ * Compare equality.
+ *
+ * \param l1 the first line
+ * \param l2 the second line
+ * \return false if they equal
+ */
+inline auto operator!=(const line& l1, const line& l2) noexcept -> bool
+{
+	return !(l1 == l2);
+}
+
+} // !mlk
+
+#endif // !MALIKANIA_LINE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/loader.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,108 @@
+/*
+ * loader.cpp -- load shared resources files
+ *
+ * Copyright (c) 2013-2018 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 "game.hpp"
+#include "loader.hpp"
+#include "locator.hpp"
+#include "tileset.hpp"
+#include "util.hpp"
+
+namespace mlk {
+
+namespace {
+
+auto load_tileset_tile_properties(const nlohmann::json& json) -> tileset::properties_map
+{
+	auto it = json.find("properties");
+
+	if (it == json.end())
+		return {};
+
+	tileset::properties_map props;
+
+	for (auto pair = it->begin(); pair != it->end(); ++pair) {
+		// TODO: maxi convert
+		if (!pair->is_string())
+			continue;
+
+		props.emplace(pair.key(), pair->get<std::string>());
+	}
+
+	return props;
+}
+
+auto load_tileset_tiles_properties(const nlohmann::json& json) -> tileset::tile_properties_map
+{
+	auto tiles = json.find("tiles");
+
+	if (tiles == json.end())
+		return {};
+
+	tileset::tile_properties_map props;
+
+	for (auto it = tiles->begin(); it != tiles->end(); ++it)
+		props.emplace(std::stoi(it.key()), load_tileset_tile_properties(*it));
+
+	return props;
+}
+
+} // !namespace
+
+loader::loader(mlk::locator& locator)
+	: locator_(locator)
+{
+}
+
+auto loader::get_locator() noexcept -> locator&
+{
+	return locator_;
+}
+
+auto loader::load_game() const -> game
+{
+	auto value = nlohmann::json::parse(locator_.read("game.json"));
+
+	if (!value.is_object())
+		throw std::runtime_error("game.json: not a JSON object");
+
+	return game{
+		util::json::require_string(value, "/name"_json_pointer),
+		util::json::require_string(value, "/version"_json_pointer),
+		util::json::require_string(value, "/requires"_json_pointer),
+		value.count("license") > 0 ? value["license"] : "",
+		value.count("author") > 0 ? value["author"] : ""
+	};
+}
+
+auto loader::load_tileset(std::string_view id) const -> tileset
+{
+	auto value = nlohmann::json::parse(locator_.read(id));
+
+	if (!value.is_object())
+		throw std::runtime_error("not a valid tileset");
+
+	tileset tileset;
+
+	tileset.image = util::json::require_string(value, "/image"_json_pointer);
+	tileset.cell = util::json::require_size(value, "/cell"_json_pointer);
+	tileset.tile_properties = load_tileset_tiles_properties(value);
+
+	return tileset;
+}
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/loader.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,83 @@
+/*
+ * loader.hpp -- load shared resources files
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_LOADER_HPP
+#define MALIKANIA_LOADER_HPP
+
+#include <string>
+
+namespace mlk {
+
+struct game;
+struct tileset;
+
+class locator;
+
+/**
+ * \brief Open resources files using a locator.
+ *
+ * This class is used to load resources files that are common to the server and
+ * the client.
+ *
+ * \see client_loader
+ */
+class loader {
+private:
+	locator& locator_;
+
+public:
+	/**
+	 * Construct the resources loader.
+	 *
+	 * \param locator the locator
+	 */
+	loader(locator& locator);
+
+	/**
+	 * Virtual destructor defaulted.
+	 */
+	virtual ~loader() noexcept = default;
+
+	/**
+	 * Get the underlying locator.
+	 *
+	 * \return the locator
+	 */
+	auto get_locator() noexcept -> locator&;
+
+	/**
+	 * Load a game.
+	 *
+	 * \return the game
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_game() const -> game;
+
+	/**
+	 * Load a tileset.
+	 *
+	 * \param id the tileset id
+	 * \return a tileset ready to use
+	 * \throw std::runtime_error on errors
+	 */
+	virtual auto load_tileset(std::string_view id) const -> tileset;
+};
+
+} // !mlk
+
+#endif // !MALIKANIA_LOADER_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/locator.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,69 @@
+/*
+ * locator.cpp -- file and stream loader
+ *
+ * Copyright (c) 2013-2018 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 <cerrno>
+#include <cstring>
+#include <fstream>
+#include <iterator>
+#include <sstream>
+#include <stdexcept>
+
+#include "locator.hpp"
+
+namespace mlk {
+
+auto directory_locator::make_path(std::string_view id) -> std::string
+{
+	std::ostringstream oss;
+
+	oss << path_;
+	oss << "/";
+	oss << id;
+
+	return oss.str();
+}
+
+directory_locator::directory_locator(std::string path) noexcept
+	: path_(std::move(path))
+{
+}
+
+auto directory_locator::read(std::string_view id) -> std::string 
+{
+	std::ifstream in(make_path(id), std::ifstream::in | std::ifstream::binary);
+
+	if (!in)
+		throw std::runtime_error(std::strerror(errno));
+
+	return std::string(
+		std::istreambuf_iterator<char>(in.rdbuf()),
+		std::istreambuf_iterator<char>()
+	);
+}
+
+auto directory_locator::open(std::string_view id) -> std::unique_ptr<std::istream> 
+{
+	auto ptr = std::make_unique<std::ifstream>(make_path(id));
+
+	if (!(*ptr))
+		throw std::runtime_error(std::strerror(errno));
+
+	return std::move(ptr);
+}
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/locator.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,88 @@
+/*
+ * locator.hpp -- file and stream loader
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_LOCATOR_HPP
+#define MALIKANIA_LOCATOR_HPP
+
+#include <istream>
+#include <memory>
+#include <string>
+#include <string_view>
+
+namespace mlk {
+
+/**
+ * \brief Abstract assets locator.
+ */
+class locator {
+public:
+	/**
+	 * Default destructor.
+	 */
+	virtual ~locator() = default;
+
+	/**
+	 * Read a whole resource as a string.
+	 *
+	 * \param id the resource id
+	 * \return the string
+	 * \throw std::runtime_error on any errors
+	 */
+	virtual auto read(std::string_view id) -> std::string = 0;
+
+	/**
+	 * Open a resource as a stream.
+	 *
+	 * \param id the resource id
+	 * \return the stream
+	 * \throw std::runtime_error on any errors
+	 */
+	virtual auto open(std::string_view id) -> std::unique_ptr<std::istream> = 0;
+};
+
+/**
+ * \brief Load a game from a directory.
+ */
+class directory_locator : public locator {
+private:
+	std::string path_;
+
+	auto make_path(std::string_view) -> std::string;
+
+public:
+	/**
+	 * Load the game from the directory.
+	 *
+	 * \param path the base directory
+	 */
+	directory_locator(std::string path) noexcept;
+
+	/**
+	 * \copydoc locator::read
+	 */
+	auto read(std::string_view id) -> std::string override;
+
+	/**
+	 * \copydoc locator::open
+	 */
+	auto open(std::string_view id) -> std::unique_ptr<std::istream> override;
+};
+
+} // !mlk
+
+#endif // !MALIKANIA_LOCATOR_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/point.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,63 @@
+/*
+ * point.hpp -- point description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_POINT_HPP
+#define MALIKANIA_POINT_HPP
+
+/**
+ * \file point.hpp
+ * \brief point description.
+ */
+
+namespace mlk {
+
+/**
+ * \brief point coordinate.
+ */
+struct point {
+	int x{0};
+	int y{0};
+};
+
+/**
+ * Compare equality.
+ *
+ * \param p1 the first point
+ * \param p2 the second point
+ * \return true if they equal
+ */
+inline auto operator==(const point& p1, const point& p2) noexcept -> bool
+{
+	return p1.x == p2.x && p1.y == p2.y;
+}
+
+/**
+ * Compare equality.
+ *
+ * \param p1 the first point
+ * \param p2 the second point
+ * \return false if they equal
+ */
+inline auto operator!=(const point& p1, const point& p2) noexcept -> bool
+{
+	return !(p1 == p2);
+}
+
+} // !mlk
+
+#endif // !MALIKANIA_POINT_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/rectangle.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,79 @@
+/*
+ * rectangle.hpp -- rectangle description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_RECTANGLE_HPP
+#define MALIKANIA_RECTANGLE_HPP
+
+/**
+ * \file rectangle.hpp
+ * \brief rectangle description.
+ */
+
+namespace mlk {
+
+/**
+ * \brief rectangle description.
+ *
+ * A rectangle has coordinates (x, y) and dimensions (width, height).
+ *
+ * They are commonly used for clipping images into the window.
+ */
+struct rectangle {
+	int x{0};                       //!< x coordinate
+	int y{0};                       //!< y coordinate
+	unsigned width{0};              //!< width
+	unsigned height{0};             //!< height
+
+	/**
+	 * Check if the rectangle has null dimensions.
+	 *
+	 * \return true if weight and height are 0
+	 */
+	inline auto is_null() const noexcept -> bool
+	{
+		return width == 0 && height == 0;
+	}
+};
+
+/**
+ * Compare equality.
+ *
+ * \param r1 the first rectangle
+ * \param r2 the second rectangle
+ * \return true if they equal
+ */
+inline auto operator==(const rectangle& r1, const rectangle& r2) noexcept -> bool
+{
+	return r1.x == r2.x && r1.y == r2.y && r1.width == r2.width && r1.height == r2.height;
+}
+
+/**
+ * Compare equality.
+ *
+ * \param r1 the first rectangle
+ * \param r2 the second rectangle
+ * \return false if they equal
+ */
+inline auto operator!=(const rectangle& r1, const rectangle& r2) noexcept -> bool
+{
+	return !(r1 == r2);
+}
+
+} // !mlk
+
+#endif // !MALIKANIA_RECTANGLE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/size.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,91 @@
+/*
+ * size.hpp -- size description
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_SIZE_HPP
+#define MALIKANIA_SIZE_HPP
+
+#include <ostream>
+
+namespace mlk {
+
+/**
+ * \brief size description.
+ */
+struct size {
+	/**
+	 * \brief Width.
+	 */
+	unsigned width{0};
+
+	/**
+	 * \brief Height.
+	 */
+	unsigned height{0};
+
+	/**
+	 * Check if the size is 0, 0.
+	 *
+	 * \return true if height and width are 0
+	 */
+	inline auto is_null() const noexcept -> bool
+	{
+		return height == 0 && width == 0;
+	}
+};
+
+/**
+ * Compare equality.
+ *
+ * \param s1 the first size
+ * \param s2 the second size
+ * \return true if they equal
+ */
+inline auto operator==(const size& s1, const size& s2) noexcept -> bool
+{
+	return s1.width == s2.width && s1.height == s2.height;
+}
+
+/**
+ * Compare equality.
+ *
+ * \param s1 the first size
+ * \param s2 the second size
+ * \return false if they equal
+ */
+inline auto operator!=(const size& s1, const size& s2) noexcept -> bool
+{
+	return !(s1 == s2);
+}
+
+/**
+ * Prints the size object.
+ *
+ * \param out the output
+ * \param size the size object
+ * \return out
+ */
+inline auto operator<<(std::ostream& out, const size& size) -> std::ostream&
+{
+	out << "{" << size.width << ", " << size.height << "}";
+
+	return out;
+}
+
+} // !mlk
+
+#endif // !MALIKANIA_SIZE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/socket.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,154 @@
+/*
+ * socket.cpp -- SSL socket using JSON messages
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+
+#include "socket.hpp"
+
+namespace mlk {
+
+void socket::do_recv(recv_handler handler)
+{
+	boost::asio::async_read_until(socket_, rbuffer_, "\r\n\r\n", [this, handler] (auto code, auto xfer) {
+		if (code)
+			handler(std::move(code), nullptr);
+		else if (xfer == 0U)
+			handler(make_error_code(boost::system::errc::network_down), nullptr);
+		else {
+			std::string str(
+				boost::asio::buffers_begin(rbuffer_.data()),
+				boost::asio::buffers_begin(rbuffer_.data()) + xfer - 4
+			);
+
+			// Remove early in case of errors.
+			rbuffer_.consume(xfer);
+
+			// TODO: catch nlohmann::json::parse_error when 3.0.0 is released.
+			nlohmann::json message;
+
+			try {
+				message = nlohmann::json::parse(str);
+			} catch (...) {}
+
+			if (!message.is_object())
+				handler(make_error_code(boost::system::errc::invalid_argument), nullptr);
+			else
+				handler(code, std::move(message));
+		}
+	});
+}
+
+void socket::do_send(const std::string& str, send_handler handler)
+{
+	boost::asio::async_write(socket_, boost::asio::buffer(str), [handler] (auto code, auto xfer) {
+		if (xfer == 0U)
+			handler(make_error_code(boost::system::errc::network_down));
+		else
+			handler(code);
+	});
+}
+
+socket::socket(boost::asio::io_service& service,
+			   boost::asio::ssl::context& ctx)
+	: socket_(service, ctx)
+{
+}
+
+auto socket::get_socket() const noexcept -> const socket_t&
+{
+	return socket_;
+}
+
+auto socket::get_socket() noexcept -> socket_t&
+{
+	return socket_;
+}
+
+auto socket::is_receiving() const noexcept -> bool
+{
+	return !rqueue_.empty();
+}
+
+auto socket::is_sending() const noexcept -> bool
+{
+	return !squeue_.empty();
+}
+
+auto socket::is_active() const noexcept -> bool
+{
+	return is_receiving() || is_sending();
+}
+
+void socket::rflush()
+{
+	if (rqueue_.empty())
+		return;
+
+	do_recv([this] (auto code, auto json) {
+		auto handler = rqueue_.front();
+
+		rqueue_.pop_front();
+		handler(code, std::move(json));
+
+		if (!code)
+			rflush();
+	});
+}
+
+void socket::sflush()
+{
+	if (squeue_.empty())
+		return;
+
+	do_send(squeue_.front().first, [this] (auto code) {
+		auto handler = squeue_.front().second;
+
+		squeue_.pop_front();
+
+		if (handler)
+			handler(code);
+		if (!code)
+			sflush();
+	});
+}
+
+void socket::recv(recv_handler handler)
+{
+	assert(handler);
+
+	auto in_progress = !rqueue_.empty();
+
+	rqueue_.push_back(std::move(handler));
+
+	if (!in_progress)
+		rflush();
+}
+
+void socket::send(nlohmann::json json, send_handler handler)
+{
+	assert(json.is_object());
+
+	auto in_progress = !squeue_.empty();
+
+	squeue_.emplace_back(json.dump(0) + "\r\n\r\n", std::move(handler));
+
+	if (!in_progress)
+		sflush();
+}
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/socket.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,148 @@
+/*
+ * socket.hpp -- SSL socket using JSON messages
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_COMMON_SOCKET_HPP
+#define MALIKANIA_COMMON_SOCKET_HPP
+
+/**
+ * \file socket.hpp
+ * \brief SSL socket using JSON messages
+ */
+
+#include "sysconfig.hpp"
+
+#include <deque>
+#include <functional>
+#include <string>
+#include <system_error>
+#include <utility>
+
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+#include <json.hpp>
+
+namespace mlk {
+
+/**
+ * \brief SSL socket using JSON messages
+ *
+ * This class can be used to perform I/O over a networking socket, it is
+ * implemented as asynchronous operations over Boost.Asio.
+ *
+ * All recv/send operations are placed in a queue and performed when possible.
+ */
+class socket {
+public:
+	/**
+	 * Read handler.
+	 *
+	 * Call this function when a receive operation has finished on success or
+	 * failure.
+	 */
+	using recv_handler = std::function<void (std::error_code, nlohmann::json)>;
+
+	/**
+	 * Send handler.
+	 *
+	 * Call this function when a send operation has finished on success or failure.
+	 */
+	using send_handler = std::function<void (std::error_code)>;
+
+private:
+	using socket_t = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;
+	using rbuffer_t = boost::asio::streambuf;
+	using rqueue_t = std::deque<recv_handler>;
+	using squeue_t = std::deque<std::pair<std::string, send_handler>>;
+
+	socket_t socket_;
+	rbuffer_t rbuffer_;
+	rqueue_t rqueue_;
+	squeue_t squeue_;
+
+	void rflush();
+	void sflush();
+	void do_recv(recv_handler);
+	void do_send(const std::string&, send_handler);
+
+public:
+	/**
+	 * Construct the socket.
+	 *
+	 * \param service the IO service
+	 * \param ctx the SSL context
+	 */
+	socket(boost::asio::io_service& service,
+               boost::asio::ssl::context& ctx);
+
+	/**
+	 * Get the underlying socket.
+	 *
+	 * \return the socket
+	 */
+	auto get_socket() const noexcept -> const socket_t&;
+
+	/**
+	 * Overloaded function.
+	 *
+	 * \return the socket
+	 */
+	auto get_socket() noexcept -> socket_t&;
+
+	/**
+	 * Tells if receive operations are pending.
+	 *
+	 * \return true if receiving is in progress
+	 */
+	auto is_receiving() const noexcept -> bool;
+
+	/**
+	 * Tells if send operations are pending.
+	 *
+	 * \return true if sending is in progress
+	 */
+	auto is_sending() const noexcept -> bool;
+
+	/**
+	 * Tells if there are any I/O pending.
+	 *
+	 * \return true if sending is in progress
+	 */
+	auto is_active() const noexcept -> bool;
+
+	/**
+	 * Request a receive operation.
+	 *
+	 * \pre handler != nullptr
+	 * \param handler the handler
+	 */
+	void recv(recv_handler);
+
+	/**
+	 * Request a send operation.
+	 *
+	 * \pre json.is_object()
+	 * \param json the json message
+	 * \param handler the optional handler
+	 */
+	void send(nlohmann::json json, send_handler = nullptr);
+};
+
+} // !mlk
+
+#endif // MALIKANIA_COMMON_SOCKET_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/tileset.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,58 @@
+/*
+ * tileset.hpp -- map tileset definition
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_COMMON_TILESET_HPP
+#define MALIKANIA_COMMON_TILESET_HPP
+
+/**
+ * \file tileset.hpp
+ * \brief Map tileset definition.
+ */
+
+#include <boost/variant.hpp>
+
+#include <string>
+#include <unordered_map>
+
+#include "size.hpp"
+
+namespace mlk {
+
+/**
+ * \brief Map tileset definition.
+ */
+struct tileset {
+	/**
+	 * Map of general properties.
+	 */
+	using properties_map = std::unordered_map<std::string, std::string>;
+
+	/**
+	 * Per tile properties.
+	 */
+	using tile_properties_map = std::unordered_map<int, properties_map>;
+
+	std::string image;
+	size cell;
+	properties_map properties;
+	tile_properties_map tile_properties;
+};
+
+} // !mlk
+
+#endif // !MALIKANIA_COMMON_TILESET_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/unicode.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,4838 @@
+/*
+ * unicode.cpp -- UTF-8 to UTF-32 conversions and various operations
+ *
+ * Copyright (c) 2013-2018 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 "unicode.hpp"
+
+/*
+ * The following code has been generated from Go mkrunetype adapted to our
+ * needs.
+ */
+
+namespace mlk::unicode {
+
+#define nelem(x) (sizeof (x) / sizeof ((x)[0]))
+
+namespace {
+
+auto search(char32_t c, const char32_t* t, int n, int ne) noexcept -> const char32_t*
+{
+	const char32_t* p;
+	int m;
+
+	while (n > 1) {
+		m = n >> 1;
+		p = t + m * ne;
+
+		if (c >= p[0]) {
+			t = p;
+			n = n - m;
+		} else
+			n = m;
+	}
+
+	if (n && c >= t[0])
+		return t;
+
+	return nullptr;
+}
+
+} // !namespace
+
+namespace {
+
+const char32_t isspacer[] = {
+	0x0009, 0x000d,
+	0x0020, 0x0020,
+	0x0085, 0x0085,
+	0x00a0, 0x00a0,
+	0x1680, 0x1680,
+	0x2000, 0x200a,
+	0x2028, 0x2029,
+	0x202f, 0x202f,
+	0x205f, 0x205f,
+	0x3000, 0x3000,
+	0xfeff, 0xfeff,
+};
+
+} // !namespace
+
+auto isspace(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, isspacer, nelem (isspacer) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t isdigitr[] = {
+	0x0030, 0x0039,
+	0x0660, 0x0669,
+	0x06f0, 0x06f9,
+	0x07c0, 0x07c9,
+	0x0966, 0x096f,
+	0x09e6, 0x09ef,
+	0x0a66, 0x0a6f,
+	0x0ae6, 0x0aef,
+	0x0b66, 0x0b6f,
+	0x0be6, 0x0bef,
+	0x0c66, 0x0c6f,
+	0x0ce6, 0x0cef,
+	0x0d66, 0x0d6f,
+	0x0de6, 0x0def,
+	0x0e50, 0x0e59,
+	0x0ed0, 0x0ed9,
+	0x0f20, 0x0f29,
+	0x1040, 0x1049,
+	0x1090, 0x1099,
+	0x17e0, 0x17e9,
+	0x1810, 0x1819,
+	0x1946, 0x194f,
+	0x19d0, 0x19d9,
+	0x1a80, 0x1a89,
+	0x1a90, 0x1a99,
+	0x1b50, 0x1b59,
+	0x1bb0, 0x1bb9,
+	0x1c40, 0x1c49,
+	0x1c50, 0x1c59,
+	0xa620, 0xa629,
+	0xa8d0, 0xa8d9,
+	0xa900, 0xa909,
+	0xa9d0, 0xa9d9,
+	0xa9f0, 0xa9f9,
+	0xaa50, 0xaa59,
+	0xabf0, 0xabf9,
+	0xff10, 0xff19,
+	0x104a0, 0x104a9,
+	0x11066, 0x1106f,
+	0x110f0, 0x110f9,
+	0x11136, 0x1113f,
+	0x111d0, 0x111d9,
+	0x112f0, 0x112f9,
+	0x114d0, 0x114d9,
+	0x11650, 0x11659,
+	0x116c0, 0x116c9,
+	0x118e0, 0x118e9,
+	0x16a60, 0x16a69,
+	0x16b50, 0x16b59,
+	0x1d7ce, 0x1d7ff,
+};
+
+} // !namespace
+
+auto isdigit(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, isdigitr, nelem (isdigitr) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t isalphar[] = {
+	0x0041, 0x005a,
+	0x0061, 0x007a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00f6,
+	0x00f8, 0x02c1,
+	0x02c6, 0x02d1,
+	0x02e0, 0x02e4,
+	0x0370, 0x0374,
+	0x0376, 0x0377,
+	0x037a, 0x037d,
+	0x0388, 0x038a,
+	0x038e, 0x03a1,
+	0x03a3, 0x03f5,
+	0x03f7, 0x0481,
+	0x048a, 0x052f,
+	0x0531, 0x0556,
+	0x0561, 0x0587,
+	0x05d0, 0x05ea,
+	0x05f0, 0x05f2,
+	0x0620, 0x064a,
+	0x066e, 0x066f,
+	0x0671, 0x06d3,
+	0x06e5, 0x06e6,
+	0x06ee, 0x06ef,
+	0x06fa, 0x06fc,
+	0x0712, 0x072f,
+	0x074d, 0x07a5,
+	0x07ca, 0x07ea,
+	0x07f4, 0x07f5,
+	0x0800, 0x0815,
+	0x0840, 0x0858,
+	0x08a0, 0x08b2,
+	0x0904, 0x0939,
+	0x0958, 0x0961,
+	0x0971, 0x0980,
+	0x0985, 0x098c,
+	0x098f, 0x0990,
+	0x0993, 0x09a8,
+	0x09aa, 0x09b0,
+	0x09b6, 0x09b9,
+	0x09dc, 0x09dd,
+	0x09df, 0x09e1,
+	0x09f0, 0x09f1,
+	0x0a05, 0x0a0a,
+	0x0a0f, 0x0a10,
+	0x0a13, 0x0a28,
+	0x0a2a, 0x0a30,
+	0x0a32, 0x0a33,
+	0x0a35, 0x0a36,
+	0x0a38, 0x0a39,
+	0x0a59, 0x0a5c,
+	0x0a72, 0x0a74,
+	0x0a85, 0x0a8d,
+	0x0a8f, 0x0a91,
+	0x0a93, 0x0aa8,
+	0x0aaa, 0x0ab0,
+	0x0ab2, 0x0ab3,
+	0x0ab5, 0x0ab9,
+	0x0ae0, 0x0ae1,
+	0x0b05, 0x0b0c,
+	0x0b0f, 0x0b10,
+	0x0b13, 0x0b28,
+	0x0b2a, 0x0b30,
+	0x0b32, 0x0b33,
+	0x0b35, 0x0b39,
+	0x0b5c, 0x0b5d,
+	0x0b5f, 0x0b61,
+	0x0b85, 0x0b8a,
+	0x0b8e, 0x0b90,
+	0x0b92, 0x0b95,
+	0x0b99, 0x0b9a,
+	0x0b9e, 0x0b9f,
+	0x0ba3, 0x0ba4,
+	0x0ba8, 0x0baa,
+	0x0bae, 0x0bb9,
+	0x0c05, 0x0c0c,
+	0x0c0e, 0x0c10,
+	0x0c12, 0x0c28,
+	0x0c2a, 0x0c39,
+	0x0c58, 0x0c59,
+	0x0c60, 0x0c61,
+	0x0c85, 0x0c8c,
+	0x0c8e, 0x0c90,
+	0x0c92, 0x0ca8,
+	0x0caa, 0x0cb3,
+	0x0cb5, 0x0cb9,
+	0x0ce0, 0x0ce1,
+	0x0cf1, 0x0cf2,
+	0x0d05, 0x0d0c,
+	0x0d0e, 0x0d10,
+	0x0d12, 0x0d3a,
+	0x0d60, 0x0d61,
+	0x0d7a, 0x0d7f,
+	0x0d85, 0x0d96,
+	0x0d9a, 0x0db1,
+	0x0db3, 0x0dbb,
+	0x0dc0, 0x0dc6,
+	0x0e01, 0x0e30,
+	0x0e32, 0x0e33,
+	0x0e40, 0x0e46,
+	0x0e81, 0x0e82,
+	0x0e87, 0x0e88,
+	0x0e94, 0x0e97,
+	0x0e99, 0x0e9f,
+	0x0ea1, 0x0ea3,
+	0x0eaa, 0x0eab,
+	0x0ead, 0x0eb0,
+	0x0eb2, 0x0eb3,
+	0x0ec0, 0x0ec4,
+	0x0edc, 0x0edf,
+	0x0f40, 0x0f47,
+	0x0f49, 0x0f6c,
+	0x0f88, 0x0f8c,
+	0x1000, 0x102a,
+	0x1050, 0x1055,
+	0x105a, 0x105d,
+	0x1065, 0x1066,
+	0x106e, 0x1070,
+	0x1075, 0x1081,
+	0x10a0, 0x10c5,
+	0x10d0, 0x10fa,
+	0x10fc, 0x1248,
+	0x124a, 0x124d,
+	0x1250, 0x1256,
+	0x125a, 0x125d,
+	0x1260, 0x1288,
+	0x128a, 0x128d,
+	0x1290, 0x12b0,
+	0x12b2, 0x12b5,
+	0x12b8, 0x12be,
+	0x12c2, 0x12c5,
+	0x12c8, 0x12d6,
+	0x12d8, 0x1310,
+	0x1312, 0x1315,
+	0x1318, 0x135a,
+	0x1380, 0x138f,
+	0x13a0, 0x13f4,
+	0x1401, 0x166c,
+	0x166f, 0x167f,
+	0x1681, 0x169a,
+	0x16a0, 0x16ea,
+	0x16f1, 0x16f8,
+	0x1700, 0x170c,
+	0x170e, 0x1711,
+	0x1720, 0x1731,
+	0x1740, 0x1751,
+	0x1760, 0x176c,
+	0x176e, 0x1770,
+	0x1780, 0x17b3,
+	0x1820, 0x1877,
+	0x1880, 0x18a8,
+	0x18b0, 0x18f5,
+	0x1900, 0x191e,
+	0x1950, 0x196d,
+	0x1970, 0x1974,
+	0x1980, 0x19ab,
+	0x19c1, 0x19c7,
+	0x1a00, 0x1a16,
+	0x1a20, 0x1a54,
+	0x1b05, 0x1b33,
+	0x1b45, 0x1b4b,
+	0x1b83, 0x1ba0,
+	0x1bae, 0x1baf,
+	0x1bba, 0x1be5,
+	0x1c00, 0x1c23,
+	0x1c4d, 0x1c4f,
+	0x1c5a, 0x1c7d,
+	0x1ce9, 0x1cec,
+	0x1cee, 0x1cf1,
+	0x1cf5, 0x1cf6,
+	0x1d00, 0x1dbf,
+	0x1e00, 0x1f15,
+	0x1f18, 0x1f1d,
+	0x1f20, 0x1f45,
+	0x1f48, 0x1f4d,
+	0x1f50, 0x1f57,
+	0x1f5f, 0x1f7d,
+	0x1f80, 0x1fb4,
+	0x1fb6, 0x1fbc,
+	0x1fc2, 0x1fc4,
+	0x1fc6, 0x1fcc,
+	0x1fd0, 0x1fd3,
+	0x1fd6, 0x1fdb,
+	0x1fe0, 0x1fec,
+	0x1ff2, 0x1ff4,
+	0x1ff6, 0x1ffc,
+	0x2090, 0x209c,
+	0x210a, 0x2113,
+	0x2119, 0x211d,
+	0x212a, 0x212d,
+	0x212f, 0x2139,
+	0x213c, 0x213f,
+	0x2145, 0x2149,
+	0x2183, 0x2184,
+	0x2c00, 0x2c2e,
+	0x2c30, 0x2c5e,
+	0x2c60, 0x2ce4,
+	0x2ceb, 0x2cee,
+	0x2cf2, 0x2cf3,
+	0x2d00, 0x2d25,
+	0x2d30, 0x2d67,
+	0x2d80, 0x2d96,
+	0x2da0, 0x2da6,
+	0x2da8, 0x2dae,
+	0x2db0, 0x2db6,
+	0x2db8, 0x2dbe,
+	0x2dc0, 0x2dc6,
+	0x2dc8, 0x2dce,
+	0x2dd0, 0x2dd6,
+	0x2dd8, 0x2dde,
+	0x3005, 0x3006,
+	0x3031, 0x3035,
+	0x303b, 0x303c,
+	0x3041, 0x3096,
+	0x309d, 0x309f,
+	0x30a1, 0x30fa,
+	0x30fc, 0x30ff,
+	0x3105, 0x312d,
+	0x3131, 0x318e,
+	0x31a0, 0x31ba,
+	0x31f0, 0x31ff,
+	0x3400, 0x4db5,
+	0x4e00, 0x9fcc,
+	0xa000, 0xa48c,
+	0xa4d0, 0xa4fd,
+	0xa500, 0xa60c,
+	0xa610, 0xa61f,
+	0xa62a, 0xa62b,
+	0xa640, 0xa66e,
+	0xa67f, 0xa69d,
+	0xa6a0, 0xa6e5,
+	0xa717, 0xa71f,
+	0xa722, 0xa788,
+	0xa78b, 0xa78e,
+	0xa790, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xa7f7, 0xa801,
+	0xa803, 0xa805,
+	0xa807, 0xa80a,
+	0xa80c, 0xa822,
+	0xa840, 0xa873,
+	0xa882, 0xa8b3,
+	0xa8f2, 0xa8f7,
+	0xa90a, 0xa925,
+	0xa930, 0xa946,
+	0xa960, 0xa97c,
+	0xa984, 0xa9b2,
+	0xa9e0, 0xa9e4,
+	0xa9e6, 0xa9ef,
+	0xa9fa, 0xa9fe,
+	0xaa00, 0xaa28,
+	0xaa40, 0xaa42,
+	0xaa44, 0xaa4b,
+	0xaa60, 0xaa76,
+	0xaa7e, 0xaaaf,
+	0xaab5, 0xaab6,
+	0xaab9, 0xaabd,
+	0xaadb, 0xaadd,
+	0xaae0, 0xaaea,
+	0xaaf2, 0xaaf4,
+	0xab01, 0xab06,
+	0xab09, 0xab0e,
+	0xab11, 0xab16,
+	0xab20, 0xab26,
+	0xab28, 0xab2e,
+	0xab30, 0xab5a,
+	0xab5c, 0xab5f,
+	0xab64, 0xab65,
+	0xabc0, 0xabe2,
+	0xac00, 0xd7a3,
+	0xd7b0, 0xd7c6,
+	0xd7cb, 0xd7fb,
+	0xf900, 0xfa6d,
+	0xfa70, 0xfad9,
+	0xfb00, 0xfb06,
+	0xfb13, 0xfb17,
+	0xfb1f, 0xfb28,
+	0xfb2a, 0xfb36,
+	0xfb38, 0xfb3c,
+	0xfb40, 0xfb41,
+	0xfb43, 0xfb44,
+	0xfb46, 0xfbb1,
+	0xfbd3, 0xfd3d,
+	0xfd50, 0xfd8f,
+	0xfd92, 0xfdc7,
+	0xfdf0, 0xfdfb,
+	0xfe70, 0xfe74,
+	0xfe76, 0xfefc,
+	0xff21, 0xff3a,
+	0xff41, 0xff5a,
+	0xff66, 0xffbe,
+	0xffc2, 0xffc7,
+	0xffca, 0xffcf,
+	0xffd2, 0xffd7,
+	0xffda, 0xffdc,
+	0x10000, 0x1000b,
+	0x1000d, 0x10026,
+	0x10028, 0x1003a,
+	0x1003c, 0x1003d,
+	0x1003f, 0x1004d,
+	0x10050, 0x1005d,
+	0x10080, 0x100fa,
+	0x10280, 0x1029c,
+	0x102a0, 0x102d0,
+	0x10300, 0x1031f,
+	0x10330, 0x10340,
+	0x10342, 0x10349,
+	0x10350, 0x10375,
+	0x10380, 0x1039d,
+	0x103a0, 0x103c3,
+	0x103c8, 0x103cf,
+	0x10400, 0x1049d,
+	0x10500, 0x10527,
+	0x10530, 0x10563,
+	0x10600, 0x10736,
+	0x10740, 0x10755,
+	0x10760, 0x10767,
+	0x10800, 0x10805,
+	0x1080a, 0x10835,
+	0x10837, 0x10838,
+	0x1083f, 0x10855,
+	0x10860, 0x10876,
+	0x10880, 0x1089e,
+	0x10900, 0x10915,
+	0x10920, 0x10939,
+	0x10980, 0x109b7,
+	0x109be, 0x109bf,
+	0x10a10, 0x10a13,
+	0x10a15, 0x10a17,
+	0x10a19, 0x10a33,
+	0x10a60, 0x10a7c,
+	0x10a80, 0x10a9c,
+	0x10ac0, 0x10ac7,
+	0x10ac9, 0x10ae4,
+	0x10b00, 0x10b35,
+	0x10b40, 0x10b55,
+	0x10b60, 0x10b72,
+	0x10b80, 0x10b91,
+	0x10c00, 0x10c48,
+	0x11003, 0x11037,
+	0x11083, 0x110af,
+	0x110d0, 0x110e8,
+	0x11103, 0x11126,
+	0x11150, 0x11172,
+	0x11183, 0x111b2,
+	0x111c1, 0x111c4,
+	0x11200, 0x11211,
+	0x11213, 0x1122b,
+	0x112b0, 0x112de,
+	0x11305, 0x1130c,
+	0x1130f, 0x11310,
+	0x11313, 0x11328,
+	0x1132a, 0x11330,
+	0x11332, 0x11333,
+	0x11335, 0x11339,
+	0x1135d, 0x11361,
+	0x11480, 0x114af,
+	0x114c4, 0x114c5,
+	0x11580, 0x115ae,
+	0x11600, 0x1162f,
+	0x11680, 0x116aa,
+	0x118a0, 0x118df,
+	0x11ac0, 0x11af8,
+	0x12000, 0x12398,
+	0x13000, 0x1342e,
+	0x16800, 0x16a38,
+	0x16a40, 0x16a5e,
+	0x16ad0, 0x16aed,
+	0x16b00, 0x16b2f,
+	0x16b40, 0x16b43,
+	0x16b63, 0x16b77,
+	0x16b7d, 0x16b8f,
+	0x16f00, 0x16f44,
+	0x16f93, 0x16f9f,
+	0x1b000, 0x1b001,
+	0x1bc00, 0x1bc6a,
+	0x1bc70, 0x1bc7c,
+	0x1bc80, 0x1bc88,
+	0x1bc90, 0x1bc99,
+	0x1d400, 0x1d454,
+	0x1d456, 0x1d49c,
+	0x1d49e, 0x1d49f,
+	0x1d4a5, 0x1d4a6,
+	0x1d4a9, 0x1d4ac,
+	0x1d4ae, 0x1d4b9,
+	0x1d4bd, 0x1d4c3,
+	0x1d4c5, 0x1d505,
+	0x1d507, 0x1d50a,
+	0x1d50d, 0x1d514,
+	0x1d516, 0x1d51c,
+	0x1d51e, 0x1d539,
+	0x1d53b, 0x1d53e,
+	0x1d540, 0x1d544,
+	0x1d54a, 0x1d550,
+	0x1d552, 0x1d6a5,
+	0x1d6a8, 0x1d6c0,
+	0x1d6c2, 0x1d6da,
+	0x1d6dc, 0x1d6fa,
+	0x1d6fc, 0x1d714,
+	0x1d716, 0x1d734,
+	0x1d736, 0x1d74e,
+	0x1d750, 0x1d76e,
+	0x1d770, 0x1d788,
+	0x1d78a, 0x1d7a8,
+	0x1d7aa, 0x1d7c2,
+	0x1d7c4, 0x1d7cb,
+	0x1e800, 0x1e8c4,
+	0x1ee00, 0x1ee03,
+	0x1ee05, 0x1ee1f,
+	0x1ee21, 0x1ee22,
+	0x1ee29, 0x1ee32,
+	0x1ee34, 0x1ee37,
+	0x1ee4d, 0x1ee4f,
+	0x1ee51, 0x1ee52,
+	0x1ee61, 0x1ee62,
+	0x1ee67, 0x1ee6a,
+	0x1ee6c, 0x1ee72,
+	0x1ee74, 0x1ee77,
+	0x1ee79, 0x1ee7c,
+	0x1ee80, 0x1ee89,
+	0x1ee8b, 0x1ee9b,
+	0x1eea1, 0x1eea3,
+	0x1eea5, 0x1eea9,
+	0x1eeab, 0x1eebb,
+	0x20000, 0x2a6d6,
+	0x2a700, 0x2b734,
+	0x2b740, 0x2b81d,
+	0x2f800, 0x2fa1d,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t isalphas[] = {
+	0x00aa,
+	0x00b5,
+	0x00ba,
+	0x02ec,
+	0x02ee,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x0559,
+	0x06d5,
+	0x06ff,
+	0x0710,
+	0x07b1,
+	0x07fa,
+	0x081a,
+	0x0824,
+	0x0828,
+	0x093d,
+	0x0950,
+	0x09b2,
+	0x09bd,
+	0x09ce,
+	0x0a5e,
+	0x0abd,
+	0x0ad0,
+	0x0b3d,
+	0x0b71,
+	0x0b83,
+	0x0b9c,
+	0x0bd0,
+	0x0c3d,
+	0x0cbd,
+	0x0cde,
+	0x0d3d,
+	0x0d4e,
+	0x0dbd,
+	0x0e84,
+	0x0e8a,
+	0x0e8d,
+	0x0ea5,
+	0x0ea7,
+	0x0ebd,
+	0x0ec6,
+	0x0f00,
+	0x103f,
+	0x1061,
+	0x108e,
+	0x10c7,
+	0x10cd,
+	0x1258,
+	0x12c0,
+	0x17d7,
+	0x17dc,
+	0x18aa,
+	0x1aa7,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1fbe,
+	0x2071,
+	0x207f,
+	0x2102,
+	0x2107,
+	0x2115,
+	0x2124,
+	0x2126,
+	0x2128,
+	0x214e,
+	0x2d27,
+	0x2d2d,
+	0x2d6f,
+	0x2e2f,
+	0xa8fb,
+	0xa9cf,
+	0xaa7a,
+	0xaab1,
+	0xaac0,
+	0xaac2,
+	0xfb1d,
+	0xfb3e,
+	0x10808,
+	0x1083c,
+	0x10a00,
+	0x11176,
+	0x111da,
+	0x1133d,
+	0x114c7,
+	0x11644,
+	0x118ff,
+	0x16f50,
+	0x1d4a2,
+	0x1d4bb,
+	0x1d546,
+	0x1ee24,
+	0x1ee27,
+	0x1ee39,
+	0x1ee3b,
+	0x1ee42,
+	0x1ee47,
+	0x1ee49,
+	0x1ee4b,
+	0x1ee54,
+	0x1ee57,
+	0x1ee59,
+	0x1ee5b,
+	0x1ee5d,
+	0x1ee5f,
+	0x1ee64,
+	0x1ee7e,
+};
+
+} // !namespace
+
+auto isalpha(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, isalphar, nelem (isalphar) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = search(c, isalphas, nelem (isalphas), 1);
+
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t isupperr[] = {
+	0x0041, 0x005a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00de,
+	0x0178, 0x0179,
+	0x0181, 0x0182,
+	0x0186, 0x0187,
+	0x0189, 0x018b,
+	0x018e, 0x0191,
+	0x0193, 0x0194,
+	0x0196, 0x0198,
+	0x019c, 0x019d,
+	0x019f, 0x01a0,
+	0x01a6, 0x01a7,
+	0x01ae, 0x01af,
+	0x01b1, 0x01b3,
+	0x01b7, 0x01b8,
+	0x01f6, 0x01f8,
+	0x023a, 0x023b,
+	0x023d, 0x023e,
+	0x0243, 0x0246,
+	0x0388, 0x038a,
+	0x038e, 0x038f,
+	0x0391, 0x03a1,
+	0x03a3, 0x03ab,
+	0x03d2, 0x03d4,
+	0x03f9, 0x03fa,
+	0x03fd, 0x042f,
+	0x04c0, 0x04c1,
+	0x0531, 0x0556,
+	0x10a0, 0x10c5,
+	0x1f08, 0x1f0f,
+	0x1f18, 0x1f1d,
+	0x1f28, 0x1f2f,
+	0x1f38, 0x1f3f,
+	0x1f48, 0x1f4d,
+	0x1f68, 0x1f6f,
+	0x1f88, 0x1f8f,
+	0x1f98, 0x1f9f,
+	0x1fa8, 0x1faf,
+	0x1fb8, 0x1fbc,
+	0x1fc8, 0x1fcc,
+	0x1fd8, 0x1fdb,
+	0x1fe8, 0x1fec,
+	0x1ff8, 0x1ffc,
+	0x210b, 0x210d,
+	0x2110, 0x2112,
+	0x2119, 0x211d,
+	0x212a, 0x212d,
+	0x2130, 0x2133,
+	0x213e, 0x213f,
+	0x2160, 0x216f,
+	0x24b6, 0x24cf,
+	0x2c00, 0x2c2e,
+	0x2c62, 0x2c64,
+	0x2c6d, 0x2c70,
+	0x2c7e, 0x2c80,
+	0xa77d, 0xa77e,
+	0xa7aa, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xff21, 0xff3a,
+	0x10400, 0x10427,
+	0x118a0, 0x118bf,
+	0x1d400, 0x1d419,
+	0x1d434, 0x1d44d,
+	0x1d468, 0x1d481,
+	0x1d49e, 0x1d49f,
+	0x1d4a5, 0x1d4a6,
+	0x1d4a9, 0x1d4ac,
+	0x1d4ae, 0x1d4b5,
+	0x1d4d0, 0x1d4e9,
+	0x1d504, 0x1d505,
+	0x1d507, 0x1d50a,
+	0x1d50d, 0x1d514,
+	0x1d516, 0x1d51c,
+	0x1d538, 0x1d539,
+	0x1d53b, 0x1d53e,
+	0x1d540, 0x1d544,
+	0x1d54a, 0x1d550,
+	0x1d56c, 0x1d585,
+	0x1d5a0, 0x1d5b9,
+	0x1d5d4, 0x1d5ed,
+	0x1d608, 0x1d621,
+	0x1d63c, 0x1d655,
+	0x1d670, 0x1d689,
+	0x1d6a8, 0x1d6c0,
+	0x1d6e2, 0x1d6fa,
+	0x1d71c, 0x1d734,
+	0x1d756, 0x1d76e,
+	0x1d790, 0x1d7a8,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t isuppers[] = {
+	0x0100,
+	0x0102,
+	0x0104,
+	0x0106,
+	0x0108,
+	0x010a,
+	0x010c,
+	0x010e,
+	0x0110,
+	0x0112,
+	0x0114,
+	0x0116,
+	0x0118,
+	0x011a,
+	0x011c,
+	0x011e,
+	0x0120,
+	0x0122,
+	0x0124,
+	0x0126,
+	0x0128,
+	0x012a,
+	0x012c,
+	0x012e,
+	0x0130,
+	0x0132,
+	0x0134,
+	0x0136,
+	0x0139,
+	0x013b,
+	0x013d,
+	0x013f,
+	0x0141,
+	0x0143,
+	0x0145,
+	0x0147,
+	0x014a,
+	0x014c,
+	0x014e,
+	0x0150,
+	0x0152,
+	0x0154,
+	0x0156,
+	0x0158,
+	0x015a,
+	0x015c,
+	0x015e,
+	0x0160,
+	0x0162,
+	0x0164,
+	0x0166,
+	0x0168,
+	0x016a,
+	0x016c,
+	0x016e,
+	0x0170,
+	0x0172,
+	0x0174,
+	0x0176,
+	0x017b,
+	0x017d,
+	0x0184,
+	0x01a2,
+	0x01a4,
+	0x01a9,
+	0x01ac,
+	0x01b5,
+	0x01bc,
+	0x01c4,
+	0x01c7,
+	0x01ca,
+	0x01cd,
+	0x01cf,
+	0x01d1,
+	0x01d3,
+	0x01d5,
+	0x01d7,
+	0x01d9,
+	0x01db,
+	0x01de,
+	0x01e0,
+	0x01e2,
+	0x01e4,
+	0x01e6,
+	0x01e8,
+	0x01ea,
+	0x01ec,
+	0x01ee,
+	0x01f1,
+	0x01f4,
+	0x01fa,
+	0x01fc,
+	0x01fe,
+	0x0200,
+	0x0202,
+	0x0204,
+	0x0206,
+	0x0208,
+	0x020a,
+	0x020c,
+	0x020e,
+	0x0210,
+	0x0212,
+	0x0214,
+	0x0216,
+	0x0218,
+	0x021a,
+	0x021c,
+	0x021e,
+	0x0220,
+	0x0222,
+	0x0224,
+	0x0226,
+	0x0228,
+	0x022a,
+	0x022c,
+	0x022e,
+	0x0230,
+	0x0232,
+	0x0241,
+	0x0248,
+	0x024a,
+	0x024c,
+	0x024e,
+	0x0370,
+	0x0372,
+	0x0376,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x03cf,
+	0x03d8,
+	0x03da,
+	0x03dc,
+	0x03de,
+	0x03e0,
+	0x03e2,
+	0x03e4,
+	0x03e6,
+	0x03e8,
+	0x03ea,
+	0x03ec,
+	0x03ee,
+	0x03f4,
+	0x03f7,
+	0x0460,
+	0x0462,
+	0x0464,
+	0x0466,
+	0x0468,
+	0x046a,
+	0x046c,
+	0x046e,
+	0x0470,
+	0x0472,
+	0x0474,
+	0x0476,
+	0x0478,
+	0x047a,
+	0x047c,
+	0x047e,
+	0x0480,
+	0x048a,
+	0x048c,
+	0x048e,
+	0x0490,
+	0x0492,
+	0x0494,
+	0x0496,
+	0x0498,
+	0x049a,
+	0x049c,
+	0x049e,
+	0x04a0,
+	0x04a2,
+	0x04a4,
+	0x04a6,
+	0x04a8,
+	0x04aa,
+	0x04ac,
+	0x04ae,
+	0x04b0,
+	0x04b2,
+	0x04b4,
+	0x04b6,
+	0x04b8,
+	0x04ba,
+	0x04bc,
+	0x04be,
+	0x04c3,
+	0x04c5,
+	0x04c7,
+	0x04c9,
+	0x04cb,
+	0x04cd,
+	0x04d0,
+	0x04d2,
+	0x04d4,
+	0x04d6,
+	0x04d8,
+	0x04da,
+	0x04dc,
+	0x04de,
+	0x04e0,
+	0x04e2,
+	0x04e4,
+	0x04e6,
+	0x04e8,
+	0x04ea,
+	0x04ec,
+	0x04ee,
+	0x04f0,
+	0x04f2,
+	0x04f4,
+	0x04f6,
+	0x04f8,
+	0x04fa,
+	0x04fc,
+	0x04fe,
+	0x0500,
+	0x0502,
+	0x0504,
+	0x0506,
+	0x0508,
+	0x050a,
+	0x050c,
+	0x050e,
+	0x0510,
+	0x0512,
+	0x0514,
+	0x0516,
+	0x0518,
+	0x051a,
+	0x051c,
+	0x051e,
+	0x0520,
+	0x0522,
+	0x0524,
+	0x0526,
+	0x0528,
+	0x052a,
+	0x052c,
+	0x052e,
+	0x10c7,
+	0x10cd,
+	0x1e00,
+	0x1e02,
+	0x1e04,
+	0x1e06,
+	0x1e08,
+	0x1e0a,
+	0x1e0c,
+	0x1e0e,
+	0x1e10,
+	0x1e12,
+	0x1e14,
+	0x1e16,
+	0x1e18,
+	0x1e1a,
+	0x1e1c,
+	0x1e1e,
+	0x1e20,
+	0x1e22,
+	0x1e24,
+	0x1e26,
+	0x1e28,
+	0x1e2a,
+	0x1e2c,
+	0x1e2e,
+	0x1e30,
+	0x1e32,
+	0x1e34,
+	0x1e36,
+	0x1e38,
+	0x1e3a,
+	0x1e3c,
+	0x1e3e,
+	0x1e40,
+	0x1e42,
+	0x1e44,
+	0x1e46,
+	0x1e48,
+	0x1e4a,
+	0x1e4c,
+	0x1e4e,
+	0x1e50,
+	0x1e52,
+	0x1e54,
+	0x1e56,
+	0x1e58,
+	0x1e5a,
+	0x1e5c,
+	0x1e5e,
+	0x1e60,
+	0x1e62,
+	0x1e64,
+	0x1e66,
+	0x1e68,
+	0x1e6a,
+	0x1e6c,
+	0x1e6e,
+	0x1e70,
+	0x1e72,
+	0x1e74,
+	0x1e76,
+	0x1e78,
+	0x1e7a,
+	0x1e7c,
+	0x1e7e,
+	0x1e80,
+	0x1e82,
+	0x1e84,
+	0x1e86,
+	0x1e88,
+	0x1e8a,
+	0x1e8c,
+	0x1e8e,
+	0x1e90,
+	0x1e92,
+	0x1e94,
+	0x1e9e,
+	0x1ea0,
+	0x1ea2,
+	0x1ea4,
+	0x1ea6,
+	0x1ea8,
+	0x1eaa,
+	0x1eac,
+	0x1eae,
+	0x1eb0,
+	0x1eb2,
+	0x1eb4,
+	0x1eb6,
+	0x1eb8,
+	0x1eba,
+	0x1ebc,
+	0x1ebe,
+	0x1ec0,
+	0x1ec2,
+	0x1ec4,
+	0x1ec6,
+	0x1ec8,
+	0x1eca,
+	0x1ecc,
+	0x1ece,
+	0x1ed0,
+	0x1ed2,
+	0x1ed4,
+	0x1ed6,
+	0x1ed8,
+	0x1eda,
+	0x1edc,
+	0x1ede,
+	0x1ee0,
+	0x1ee2,
+	0x1ee4,
+	0x1ee6,
+	0x1ee8,
+	0x1eea,
+	0x1eec,
+	0x1eee,
+	0x1ef0,
+	0x1ef2,
+	0x1ef4,
+	0x1ef6,
+	0x1ef8,
+	0x1efa,
+	0x1efc,
+	0x1efe,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1f5f,
+	0x2102,
+	0x2107,
+	0x2115,
+	0x2124,
+	0x2126,
+	0x2128,
+	0x2145,
+	0x2183,
+	0x2c60,
+	0x2c67,
+	0x2c69,
+	0x2c6b,
+	0x2c72,
+	0x2c75,
+	0x2c82,
+	0x2c84,
+	0x2c86,
+	0x2c88,
+	0x2c8a,
+	0x2c8c,
+	0x2c8e,
+	0x2c90,
+	0x2c92,
+	0x2c94,
+	0x2c96,
+	0x2c98,
+	0x2c9a,
+	0x2c9c,
+	0x2c9e,
+	0x2ca0,
+	0x2ca2,
+	0x2ca4,
+	0x2ca6,
+	0x2ca8,
+	0x2caa,
+	0x2cac,
+	0x2cae,
+	0x2cb0,
+	0x2cb2,
+	0x2cb4,
+	0x2cb6,
+	0x2cb8,
+	0x2cba,
+	0x2cbc,
+	0x2cbe,
+	0x2cc0,
+	0x2cc2,
+	0x2cc4,
+	0x2cc6,
+	0x2cc8,
+	0x2cca,
+	0x2ccc,
+	0x2cce,
+	0x2cd0,
+	0x2cd2,
+	0x2cd4,
+	0x2cd6,
+	0x2cd8,
+	0x2cda,
+	0x2cdc,
+	0x2cde,
+	0x2ce0,
+	0x2ce2,
+	0x2ceb,
+	0x2ced,
+	0x2cf2,
+	0xa640,
+	0xa642,
+	0xa644,
+	0xa646,
+	0xa648,
+	0xa64a,
+	0xa64c,
+	0xa64e,
+	0xa650,
+	0xa652,
+	0xa654,
+	0xa656,
+	0xa658,
+	0xa65a,
+	0xa65c,
+	0xa65e,
+	0xa660,
+	0xa662,
+	0xa664,
+	0xa666,
+	0xa668,
+	0xa66a,
+	0xa66c,
+	0xa680,
+	0xa682,
+	0xa684,
+	0xa686,
+	0xa688,
+	0xa68a,
+	0xa68c,
+	0xa68e,
+	0xa690,
+	0xa692,
+	0xa694,
+	0xa696,
+	0xa698,
+	0xa69a,
+	0xa722,
+	0xa724,
+	0xa726,
+	0xa728,
+	0xa72a,
+	0xa72c,
+	0xa72e,
+	0xa732,
+	0xa734,
+	0xa736,
+	0xa738,
+	0xa73a,
+	0xa73c,
+	0xa73e,
+	0xa740,
+	0xa742,
+	0xa744,
+	0xa746,
+	0xa748,
+	0xa74a,
+	0xa74c,
+	0xa74e,
+	0xa750,
+	0xa752,
+	0xa754,
+	0xa756,
+	0xa758,
+	0xa75a,
+	0xa75c,
+	0xa75e,
+	0xa760,
+	0xa762,
+	0xa764,
+	0xa766,
+	0xa768,
+	0xa76a,
+	0xa76c,
+	0xa76e,
+	0xa779,
+	0xa77b,
+	0xa780,
+	0xa782,
+	0xa784,
+	0xa786,
+	0xa78b,
+	0xa78d,
+	0xa790,
+	0xa792,
+	0xa796,
+	0xa798,
+	0xa79a,
+	0xa79c,
+	0xa79e,
+	0xa7a0,
+	0xa7a2,
+	0xa7a4,
+	0xa7a6,
+	0xa7a8,
+	0x1d49c,
+	0x1d4a2,
+	0x1d546,
+	0x1d7ca,
+};
+
+} // !namespace
+
+auto isupper(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, isupperr, nelem (isupperr) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = search(c, isuppers, nelem (isuppers), 1);
+
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t islowerr[] = {
+	0x0061, 0x007a,
+	0x00df, 0x00f6,
+	0x00f8, 0x00ff,
+	0x0137, 0x0138,
+	0x0148, 0x0149,
+	0x017e, 0x0180,
+	0x018c, 0x018d,
+	0x0199, 0x019b,
+	0x01aa, 0x01ab,
+	0x01b9, 0x01ba,
+	0x01bd, 0x01bf,
+	0x01dc, 0x01dd,
+	0x01ef, 0x01f0,
+	0x0233, 0x0239,
+	0x023f, 0x0240,
+	0x024f, 0x0293,
+	0x0295, 0x02af,
+	0x037b, 0x037d,
+	0x03ac, 0x03ce,
+	0x03d0, 0x03d1,
+	0x03d5, 0x03d7,
+	0x03ef, 0x03f3,
+	0x03fb, 0x03fc,
+	0x0430, 0x045f,
+	0x04ce, 0x04cf,
+	0x0561, 0x0587,
+	0x1d00, 0x1d2b,
+	0x1d6b, 0x1d77,
+	0x1d79, 0x1d9a,
+	0x1e95, 0x1e9d,
+	0x1eff, 0x1f07,
+	0x1f10, 0x1f15,
+	0x1f20, 0x1f27,
+	0x1f30, 0x1f37,
+	0x1f40, 0x1f45,
+	0x1f50, 0x1f57,
+	0x1f60, 0x1f67,
+	0x1f70, 0x1f7d,
+	0x1f80, 0x1f87,
+	0x1f90, 0x1f97,
+	0x1fa0, 0x1fa7,
+	0x1fb0, 0x1fb4,
+	0x1fb6, 0x1fb7,
+	0x1fc2, 0x1fc4,
+	0x1fc6, 0x1fc7,
+	0x1fd0, 0x1fd3,
+	0x1fd6, 0x1fd7,
+	0x1fe0, 0x1fe7,
+	0x1ff2, 0x1ff4,
+	0x1ff6, 0x1ff7,
+	0x210e, 0x210f,
+	0x213c, 0x213d,
+	0x2146, 0x2149,
+	0x2170, 0x217f,
+	0x24d0, 0x24e9,
+	0x2c30, 0x2c5e,
+	0x2c65, 0x2c66,
+	0x2c73, 0x2c74,
+	0x2c76, 0x2c7b,
+	0x2ce3, 0x2ce4,
+	0x2d00, 0x2d25,
+	0xa72f, 0xa731,
+	0xa771, 0xa778,
+	0xa793, 0xa795,
+	0xab30, 0xab5a,
+	0xab64, 0xab65,
+	0xfb00, 0xfb06,
+	0xfb13, 0xfb17,
+	0xff41, 0xff5a,
+	0x10428, 0x1044f,
+	0x118c0, 0x118df,
+	0x1d41a, 0x1d433,
+	0x1d44e, 0x1d454,
+	0x1d456, 0x1d467,
+	0x1d482, 0x1d49b,
+	0x1d4b6, 0x1d4b9,
+	0x1d4bd, 0x1d4c3,
+	0x1d4c5, 0x1d4cf,
+	0x1d4ea, 0x1d503,
+	0x1d51e, 0x1d537,
+	0x1d552, 0x1d56b,
+	0x1d586, 0x1d59f,
+	0x1d5ba, 0x1d5d3,
+	0x1d5ee, 0x1d607,
+	0x1d622, 0x1d63b,
+	0x1d656, 0x1d66f,
+	0x1d68a, 0x1d6a5,
+	0x1d6c2, 0x1d6da,
+	0x1d6dc, 0x1d6e1,
+	0x1d6fc, 0x1d714,
+	0x1d716, 0x1d71b,
+	0x1d736, 0x1d74e,
+	0x1d750, 0x1d755,
+	0x1d770, 0x1d788,
+	0x1d78a, 0x1d78f,
+	0x1d7aa, 0x1d7c2,
+	0x1d7c4, 0x1d7c9,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t islowers[] = {
+	0x00b5,
+	0x0101,
+	0x0103,
+	0x0105,
+	0x0107,
+	0x0109,
+	0x010b,
+	0x010d,
+	0x010f,
+	0x0111,
+	0x0113,
+	0x0115,
+	0x0117,
+	0x0119,
+	0x011b,
+	0x011d,
+	0x011f,
+	0x0121,
+	0x0123,
+	0x0125,
+	0x0127,
+	0x0129,
+	0x012b,
+	0x012d,
+	0x012f,
+	0x0131,
+	0x0133,
+	0x0135,
+	0x013a,
+	0x013c,
+	0x013e,
+	0x0140,
+	0x0142,
+	0x0144,
+	0x0146,
+	0x014b,
+	0x014d,
+	0x014f,
+	0x0151,
+	0x0153,
+	0x0155,
+	0x0157,
+	0x0159,
+	0x015b,
+	0x015d,
+	0x015f,
+	0x0161,
+	0x0163,
+	0x0165,
+	0x0167,
+	0x0169,
+	0x016b,
+	0x016d,
+	0x016f,
+	0x0171,
+	0x0173,
+	0x0175,
+	0x0177,
+	0x017a,
+	0x017c,
+	0x0183,
+	0x0185,
+	0x0188,
+	0x0192,
+	0x0195,
+	0x019e,
+	0x01a1,
+	0x01a3,
+	0x01a5,
+	0x01a8,
+	0x01ad,
+	0x01b0,
+	0x01b4,
+	0x01b6,
+	0x01c6,
+	0x01c9,
+	0x01cc,
+	0x01ce,
+	0x01d0,
+	0x01d2,
+	0x01d4,
+	0x01d6,
+	0x01d8,
+	0x01da,
+	0x01df,
+	0x01e1,
+	0x01e3,
+	0x01e5,
+	0x01e7,
+	0x01e9,
+	0x01eb,
+	0x01ed,
+	0x01f3,
+	0x01f5,
+	0x01f9,
+	0x01fb,
+	0x01fd,
+	0x01ff,
+	0x0201,
+	0x0203,
+	0x0205,
+	0x0207,
+	0x0209,
+	0x020b,
+	0x020d,
+	0x020f,
+	0x0211,
+	0x0213,
+	0x0215,
+	0x0217,
+	0x0219,
+	0x021b,
+	0x021d,
+	0x021f,
+	0x0221,
+	0x0223,
+	0x0225,
+	0x0227,
+	0x0229,
+	0x022b,
+	0x022d,
+	0x022f,
+	0x0231,
+	0x023c,
+	0x0242,
+	0x0247,
+	0x0249,
+	0x024b,
+	0x024d,
+	0x0371,
+	0x0373,
+	0x0377,
+	0x0390,
+	0x03d9,
+	0x03db,
+	0x03dd,
+	0x03df,
+	0x03e1,
+	0x03e3,
+	0x03e5,
+	0x03e7,
+	0x03e9,
+	0x03eb,
+	0x03ed,
+	0x03f5,
+	0x03f8,
+	0x0461,
+	0x0463,
+	0x0465,
+	0x0467,
+	0x0469,
+	0x046b,
+	0x046d,
+	0x046f,
+	0x0471,
+	0x0473,
+	0x0475,
+	0x0477,
+	0x0479,
+	0x047b,
+	0x047d,
+	0x047f,
+	0x0481,
+	0x048b,
+	0x048d,
+	0x048f,
+	0x0491,
+	0x0493,
+	0x0495,
+	0x0497,
+	0x0499,
+	0x049b,
+	0x049d,
+	0x049f,
+	0x04a1,
+	0x04a3,
+	0x04a5,
+	0x04a7,
+	0x04a9,
+	0x04ab,
+	0x04ad,
+	0x04af,
+	0x04b1,
+	0x04b3,
+	0x04b5,
+	0x04b7,
+	0x04b9,
+	0x04bb,
+	0x04bd,
+	0x04bf,
+	0x04c2,
+	0x04c4,
+	0x04c6,
+	0x04c8,
+	0x04ca,
+	0x04cc,
+	0x04d1,
+	0x04d3,
+	0x04d5,
+	0x04d7,
+	0x04d9,
+	0x04db,
+	0x04dd,
+	0x04df,
+	0x04e1,
+	0x04e3,
+	0x04e5,
+	0x04e7,
+	0x04e9,
+	0x04eb,
+	0x04ed,
+	0x04ef,
+	0x04f1,
+	0x04f3,
+	0x04f5,
+	0x04f7,
+	0x04f9,
+	0x04fb,
+	0x04fd,
+	0x04ff,
+	0x0501,
+	0x0503,
+	0x0505,
+	0x0507,
+	0x0509,
+	0x050b,
+	0x050d,
+	0x050f,
+	0x0511,
+	0x0513,
+	0x0515,
+	0x0517,
+	0x0519,
+	0x051b,
+	0x051d,
+	0x051f,
+	0x0521,
+	0x0523,
+	0x0525,
+	0x0527,
+	0x0529,
+	0x052b,
+	0x052d,
+	0x052f,
+	0x1e01,
+	0x1e03,
+	0x1e05,
+	0x1e07,
+	0x1e09,
+	0x1e0b,
+	0x1e0d,
+	0x1e0f,
+	0x1e11,
+	0x1e13,
+	0x1e15,
+	0x1e17,
+	0x1e19,
+	0x1e1b,
+	0x1e1d,
+	0x1e1f,
+	0x1e21,
+	0x1e23,
+	0x1e25,
+	0x1e27,
+	0x1e29,
+	0x1e2b,
+	0x1e2d,
+	0x1e2f,
+	0x1e31,
+	0x1e33,
+	0x1e35,
+	0x1e37,
+	0x1e39,
+	0x1e3b,
+	0x1e3d,
+	0x1e3f,
+	0x1e41,
+	0x1e43,
+	0x1e45,
+	0x1e47,
+	0x1e49,
+	0x1e4b,
+	0x1e4d,
+	0x1e4f,
+	0x1e51,
+	0x1e53,
+	0x1e55,
+	0x1e57,
+	0x1e59,
+	0x1e5b,
+	0x1e5d,
+	0x1e5f,
+	0x1e61,
+	0x1e63,
+	0x1e65,
+	0x1e67,
+	0x1e69,
+	0x1e6b,
+	0x1e6d,
+	0x1e6f,
+	0x1e71,
+	0x1e73,
+	0x1e75,
+	0x1e77,
+	0x1e79,
+	0x1e7b,
+	0x1e7d,
+	0x1e7f,
+	0x1e81,
+	0x1e83,
+	0x1e85,
+	0x1e87,
+	0x1e89,
+	0x1e8b,
+	0x1e8d,
+	0x1e8f,
+	0x1e91,
+	0x1e93,
+	0x1e9f,
+	0x1ea1,
+	0x1ea3,
+	0x1ea5,
+	0x1ea7,
+	0x1ea9,
+	0x1eab,
+	0x1ead,
+	0x1eaf,
+	0x1eb1,
+	0x1eb3,
+	0x1eb5,
+	0x1eb7,
+	0x1eb9,
+	0x1ebb,
+	0x1ebd,
+	0x1ebf,
+	0x1ec1,
+	0x1ec3,
+	0x1ec5,
+	0x1ec7,
+	0x1ec9,
+	0x1ecb,
+	0x1ecd,
+	0x1ecf,
+	0x1ed1,
+	0x1ed3,
+	0x1ed5,
+	0x1ed7,
+	0x1ed9,
+	0x1edb,
+	0x1edd,
+	0x1edf,
+	0x1ee1,
+	0x1ee3,
+	0x1ee5,
+	0x1ee7,
+	0x1ee9,
+	0x1eeb,
+	0x1eed,
+	0x1eef,
+	0x1ef1,
+	0x1ef3,
+	0x1ef5,
+	0x1ef7,
+	0x1ef9,
+	0x1efb,
+	0x1efd,
+	0x1fbe,
+	0x210a,
+	0x2113,
+	0x212f,
+	0x2134,
+	0x2139,
+	0x214e,
+	0x2184,
+	0x2c61,
+	0x2c68,
+	0x2c6a,
+	0x2c6c,
+	0x2c71,
+	0x2c81,
+	0x2c83,
+	0x2c85,
+	0x2c87,
+	0x2c89,
+	0x2c8b,
+	0x2c8d,
+	0x2c8f,
+	0x2c91,
+	0x2c93,
+	0x2c95,
+	0x2c97,
+	0x2c99,
+	0x2c9b,
+	0x2c9d,
+	0x2c9f,
+	0x2ca1,
+	0x2ca3,
+	0x2ca5,
+	0x2ca7,
+	0x2ca9,
+	0x2cab,
+	0x2cad,
+	0x2caf,
+	0x2cb1,
+	0x2cb3,
+	0x2cb5,
+	0x2cb7,
+	0x2cb9,
+	0x2cbb,
+	0x2cbd,
+	0x2cbf,
+	0x2cc1,
+	0x2cc3,
+	0x2cc5,
+	0x2cc7,
+	0x2cc9,
+	0x2ccb,
+	0x2ccd,
+	0x2ccf,
+	0x2cd1,
+	0x2cd3,
+	0x2cd5,
+	0x2cd7,
+	0x2cd9,
+	0x2cdb,
+	0x2cdd,
+	0x2cdf,
+	0x2ce1,
+	0x2cec,
+	0x2cee,
+	0x2cf3,
+	0x2d27,
+	0x2d2d,
+	0xa641,
+	0xa643,
+	0xa645,
+	0xa647,
+	0xa649,
+	0xa64b,
+	0xa64d,
+	0xa64f,
+	0xa651,
+	0xa653,
+	0xa655,
+	0xa657,
+	0xa659,
+	0xa65b,
+	0xa65d,
+	0xa65f,
+	0xa661,
+	0xa663,
+	0xa665,
+	0xa667,
+	0xa669,
+	0xa66b,
+	0xa66d,
+	0xa681,
+	0xa683,
+	0xa685,
+	0xa687,
+	0xa689,
+	0xa68b,
+	0xa68d,
+	0xa68f,
+	0xa691,
+	0xa693,
+	0xa695,
+	0xa697,
+	0xa699,
+	0xa69b,
+	0xa723,
+	0xa725,
+	0xa727,
+	0xa729,
+	0xa72b,
+	0xa72d,
+	0xa733,
+	0xa735,
+	0xa737,
+	0xa739,
+	0xa73b,
+	0xa73d,
+	0xa73f,
+	0xa741,
+	0xa743,
+	0xa745,
+	0xa747,
+	0xa749,
+	0xa74b,
+	0xa74d,
+	0xa74f,
+	0xa751,
+	0xa753,
+	0xa755,
+	0xa757,
+	0xa759,
+	0xa75b,
+	0xa75d,
+	0xa75f,
+	0xa761,
+	0xa763,
+	0xa765,
+	0xa767,
+	0xa769,
+	0xa76b,
+	0xa76d,
+	0xa76f,
+	0xa77a,
+	0xa77c,
+	0xa77f,
+	0xa781,
+	0xa783,
+	0xa785,
+	0xa787,
+	0xa78c,
+	0xa78e,
+	0xa791,
+	0xa797,
+	0xa799,
+	0xa79b,
+	0xa79d,
+	0xa79f,
+	0xa7a1,
+	0xa7a3,
+	0xa7a5,
+	0xa7a7,
+	0xa7a9,
+	0xa7fa,
+	0x1d4bb,
+	0x1d7cb,
+};
+
+} // !namespace
+
+auto islower(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, islowerr, nelem (islowerr) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = search(c, islowers, nelem (islowers), 1);
+
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t istitler[] = {
+	0x0041, 0x005a,
+	0x00c0, 0x00d6,
+	0x00d8, 0x00de,
+	0x0178, 0x0179,
+	0x0181, 0x0182,
+	0x0186, 0x0187,
+	0x0189, 0x018b,
+	0x018e, 0x0191,
+	0x0193, 0x0194,
+	0x0196, 0x0198,
+	0x019c, 0x019d,
+	0x019f, 0x01a0,
+	0x01a6, 0x01a7,
+	0x01ae, 0x01af,
+	0x01b1, 0x01b3,
+	0x01b7, 0x01b8,
+	0x01f6, 0x01f8,
+	0x023a, 0x023b,
+	0x023d, 0x023e,
+	0x0243, 0x0246,
+	0x0388, 0x038a,
+	0x038e, 0x038f,
+	0x0391, 0x03a1,
+	0x03a3, 0x03ab,
+	0x03f9, 0x03fa,
+	0x03fd, 0x042f,
+	0x04c0, 0x04c1,
+	0x0531, 0x0556,
+	0x10a0, 0x10c5,
+	0x1f08, 0x1f0f,
+	0x1f18, 0x1f1d,
+	0x1f28, 0x1f2f,
+	0x1f38, 0x1f3f,
+	0x1f48, 0x1f4d,
+	0x1f68, 0x1f6f,
+	0x1f88, 0x1f8f,
+	0x1f98, 0x1f9f,
+	0x1fa8, 0x1faf,
+	0x1fb8, 0x1fbc,
+	0x1fc8, 0x1fcc,
+	0x1fd8, 0x1fdb,
+	0x1fe8, 0x1fec,
+	0x1ff8, 0x1ffc,
+	0x2160, 0x216f,
+	0x24b6, 0x24cf,
+	0x2c00, 0x2c2e,
+	0x2c62, 0x2c64,
+	0x2c6d, 0x2c70,
+	0x2c7e, 0x2c80,
+	0xa77d, 0xa77e,
+	0xa7aa, 0xa7ad,
+	0xa7b0, 0xa7b1,
+	0xff21, 0xff3a,
+	0x10400, 0x10427,
+	0x118a0, 0x118bf,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t istitles[] = {
+	0x0100,
+	0x0102,
+	0x0104,
+	0x0106,
+	0x0108,
+	0x010a,
+	0x010c,
+	0x010e,
+	0x0110,
+	0x0112,
+	0x0114,
+	0x0116,
+	0x0118,
+	0x011a,
+	0x011c,
+	0x011e,
+	0x0120,
+	0x0122,
+	0x0124,
+	0x0126,
+	0x0128,
+	0x012a,
+	0x012c,
+	0x012e,
+	0x0132,
+	0x0134,
+	0x0136,
+	0x0139,
+	0x013b,
+	0x013d,
+	0x013f,
+	0x0141,
+	0x0143,
+	0x0145,
+	0x0147,
+	0x014a,
+	0x014c,
+	0x014e,
+	0x0150,
+	0x0152,
+	0x0154,
+	0x0156,
+	0x0158,
+	0x015a,
+	0x015c,
+	0x015e,
+	0x0160,
+	0x0162,
+	0x0164,
+	0x0166,
+	0x0168,
+	0x016a,
+	0x016c,
+	0x016e,
+	0x0170,
+	0x0172,
+	0x0174,
+	0x0176,
+	0x017b,
+	0x017d,
+	0x0184,
+	0x01a2,
+	0x01a4,
+	0x01a9,
+	0x01ac,
+	0x01b5,
+	0x01bc,
+	0x01c5,
+	0x01c8,
+	0x01cb,
+	0x01cd,
+	0x01cf,
+	0x01d1,
+	0x01d3,
+	0x01d5,
+	0x01d7,
+	0x01d9,
+	0x01db,
+	0x01de,
+	0x01e0,
+	0x01e2,
+	0x01e4,
+	0x01e6,
+	0x01e8,
+	0x01ea,
+	0x01ec,
+	0x01ee,
+	0x01f2,
+	0x01f4,
+	0x01fa,
+	0x01fc,
+	0x01fe,
+	0x0200,
+	0x0202,
+	0x0204,
+	0x0206,
+	0x0208,
+	0x020a,
+	0x020c,
+	0x020e,
+	0x0210,
+	0x0212,
+	0x0214,
+	0x0216,
+	0x0218,
+	0x021a,
+	0x021c,
+	0x021e,
+	0x0220,
+	0x0222,
+	0x0224,
+	0x0226,
+	0x0228,
+	0x022a,
+	0x022c,
+	0x022e,
+	0x0230,
+	0x0232,
+	0x0241,
+	0x0248,
+	0x024a,
+	0x024c,
+	0x024e,
+	0x0370,
+	0x0372,
+	0x0376,
+	0x037f,
+	0x0386,
+	0x038c,
+	0x03cf,
+	0x03d8,
+	0x03da,
+	0x03dc,
+	0x03de,
+	0x03e0,
+	0x03e2,
+	0x03e4,
+	0x03e6,
+	0x03e8,
+	0x03ea,
+	0x03ec,
+	0x03ee,
+	0x03f7,
+	0x0460,
+	0x0462,
+	0x0464,
+	0x0466,
+	0x0468,
+	0x046a,
+	0x046c,
+	0x046e,
+	0x0470,
+	0x0472,
+	0x0474,
+	0x0476,
+	0x0478,
+	0x047a,
+	0x047c,
+	0x047e,
+	0x0480,
+	0x048a,
+	0x048c,
+	0x048e,
+	0x0490,
+	0x0492,
+	0x0494,
+	0x0496,
+	0x0498,
+	0x049a,
+	0x049c,
+	0x049e,
+	0x04a0,
+	0x04a2,
+	0x04a4,
+	0x04a6,
+	0x04a8,
+	0x04aa,
+	0x04ac,
+	0x04ae,
+	0x04b0,
+	0x04b2,
+	0x04b4,
+	0x04b6,
+	0x04b8,
+	0x04ba,
+	0x04bc,
+	0x04be,
+	0x04c3,
+	0x04c5,
+	0x04c7,
+	0x04c9,
+	0x04cb,
+	0x04cd,
+	0x04d0,
+	0x04d2,
+	0x04d4,
+	0x04d6,
+	0x04d8,
+	0x04da,
+	0x04dc,
+	0x04de,
+	0x04e0,
+	0x04e2,
+	0x04e4,
+	0x04e6,
+	0x04e8,
+	0x04ea,
+	0x04ec,
+	0x04ee,
+	0x04f0,
+	0x04f2,
+	0x04f4,
+	0x04f6,
+	0x04f8,
+	0x04fa,
+	0x04fc,
+	0x04fe,
+	0x0500,
+	0x0502,
+	0x0504,
+	0x0506,
+	0x0508,
+	0x050a,
+	0x050c,
+	0x050e,
+	0x0510,
+	0x0512,
+	0x0514,
+	0x0516,
+	0x0518,
+	0x051a,
+	0x051c,
+	0x051e,
+	0x0520,
+	0x0522,
+	0x0524,
+	0x0526,
+	0x0528,
+	0x052a,
+	0x052c,
+	0x052e,
+	0x10c7,
+	0x10cd,
+	0x1e00,
+	0x1e02,
+	0x1e04,
+	0x1e06,
+	0x1e08,
+	0x1e0a,
+	0x1e0c,
+	0x1e0e,
+	0x1e10,
+	0x1e12,
+	0x1e14,
+	0x1e16,
+	0x1e18,
+	0x1e1a,
+	0x1e1c,
+	0x1e1e,
+	0x1e20,
+	0x1e22,
+	0x1e24,
+	0x1e26,
+	0x1e28,
+	0x1e2a,
+	0x1e2c,
+	0x1e2e,
+	0x1e30,
+	0x1e32,
+	0x1e34,
+	0x1e36,
+	0x1e38,
+	0x1e3a,
+	0x1e3c,
+	0x1e3e,
+	0x1e40,
+	0x1e42,
+	0x1e44,
+	0x1e46,
+	0x1e48,
+	0x1e4a,
+	0x1e4c,
+	0x1e4e,
+	0x1e50,
+	0x1e52,
+	0x1e54,
+	0x1e56,
+	0x1e58,
+	0x1e5a,
+	0x1e5c,
+	0x1e5e,
+	0x1e60,
+	0x1e62,
+	0x1e64,
+	0x1e66,
+	0x1e68,
+	0x1e6a,
+	0x1e6c,
+	0x1e6e,
+	0x1e70,
+	0x1e72,
+	0x1e74,
+	0x1e76,
+	0x1e78,
+	0x1e7a,
+	0x1e7c,
+	0x1e7e,
+	0x1e80,
+	0x1e82,
+	0x1e84,
+	0x1e86,
+	0x1e88,
+	0x1e8a,
+	0x1e8c,
+	0x1e8e,
+	0x1e90,
+	0x1e92,
+	0x1e94,
+	0x1ea0,
+	0x1ea2,
+	0x1ea4,
+	0x1ea6,
+	0x1ea8,
+	0x1eaa,
+	0x1eac,
+	0x1eae,
+	0x1eb0,
+	0x1eb2,
+	0x1eb4,
+	0x1eb6,
+	0x1eb8,
+	0x1eba,
+	0x1ebc,
+	0x1ebe,
+	0x1ec0,
+	0x1ec2,
+	0x1ec4,
+	0x1ec6,
+	0x1ec8,
+	0x1eca,
+	0x1ecc,
+	0x1ece,
+	0x1ed0,
+	0x1ed2,
+	0x1ed4,
+	0x1ed6,
+	0x1ed8,
+	0x1eda,
+	0x1edc,
+	0x1ede,
+	0x1ee0,
+	0x1ee2,
+	0x1ee4,
+	0x1ee6,
+	0x1ee8,
+	0x1eea,
+	0x1eec,
+	0x1eee,
+	0x1ef0,
+	0x1ef2,
+	0x1ef4,
+	0x1ef6,
+	0x1ef8,
+	0x1efa,
+	0x1efc,
+	0x1efe,
+	0x1f59,
+	0x1f5b,
+	0x1f5d,
+	0x1f5f,
+	0x2132,
+	0x2183,
+	0x2c60,
+	0x2c67,
+	0x2c69,
+	0x2c6b,
+	0x2c72,
+	0x2c75,
+	0x2c82,
+	0x2c84,
+	0x2c86,
+	0x2c88,
+	0x2c8a,
+	0x2c8c,
+	0x2c8e,
+	0x2c90,
+	0x2c92,
+	0x2c94,
+	0x2c96,
+	0x2c98,
+	0x2c9a,
+	0x2c9c,
+	0x2c9e,
+	0x2ca0,
+	0x2ca2,
+	0x2ca4,
+	0x2ca6,
+	0x2ca8,
+	0x2caa,
+	0x2cac,
+	0x2cae,
+	0x2cb0,
+	0x2cb2,
+	0x2cb4,
+	0x2cb6,
+	0x2cb8,
+	0x2cba,
+	0x2cbc,
+	0x2cbe,
+	0x2cc0,
+	0x2cc2,
+	0x2cc4,
+	0x2cc6,
+	0x2cc8,
+	0x2cca,
+	0x2ccc,
+	0x2cce,
+	0x2cd0,
+	0x2cd2,
+	0x2cd4,
+	0x2cd6,
+	0x2cd8,
+	0x2cda,
+	0x2cdc,
+	0x2cde,
+	0x2ce0,
+	0x2ce2,
+	0x2ceb,
+	0x2ced,
+	0x2cf2,
+	0xa640,
+	0xa642,
+	0xa644,
+	0xa646,
+	0xa648,
+	0xa64a,
+	0xa64c,
+	0xa64e,
+	0xa650,
+	0xa652,
+	0xa654,
+	0xa656,
+	0xa658,
+	0xa65a,
+	0xa65c,
+	0xa65e,
+	0xa660,
+	0xa662,
+	0xa664,
+	0xa666,
+	0xa668,
+	0xa66a,
+	0xa66c,
+	0xa680,
+	0xa682,
+	0xa684,
+	0xa686,
+	0xa688,
+	0xa68a,
+	0xa68c,
+	0xa68e,
+	0xa690,
+	0xa692,
+	0xa694,
+	0xa696,
+	0xa698,
+	0xa69a,
+	0xa722,
+	0xa724,
+	0xa726,
+	0xa728,
+	0xa72a,
+	0xa72c,
+	0xa72e,
+	0xa732,
+	0xa734,
+	0xa736,
+	0xa738,
+	0xa73a,
+	0xa73c,
+	0xa73e,
+	0xa740,
+	0xa742,
+	0xa744,
+	0xa746,
+	0xa748,
+	0xa74a,
+	0xa74c,
+	0xa74e,
+	0xa750,
+	0xa752,
+	0xa754,
+	0xa756,
+	0xa758,
+	0xa75a,
+	0xa75c,
+	0xa75e,
+	0xa760,
+	0xa762,
+	0xa764,
+	0xa766,
+	0xa768,
+	0xa76a,
+	0xa76c,
+	0xa76e,
+	0xa779,
+	0xa77b,
+	0xa780,
+	0xa782,
+	0xa784,
+	0xa786,
+	0xa78b,
+	0xa78d,
+	0xa790,
+	0xa792,
+	0xa796,
+	0xa798,
+	0xa79a,
+	0xa79c,
+	0xa79e,
+	0xa7a0,
+	0xa7a2,
+	0xa7a4,
+	0xa7a6,
+	0xa7a8,
+};
+
+} // !namespace
+
+auto istitle(char32_t c) noexcept -> bool
+{
+	const char32_t* p;
+
+	p = search(c, istitler, nelem (istitler) / 2, 2);
+
+	if (p && c >= p[0] && c <= p[1])
+		return true;
+
+	p = search(c, istitles, nelem (istitles), 1);
+
+	if (p && c == p[0])
+		return true;
+
+	return false;
+}
+
+namespace {
+
+const char32_t toupperr[] = {
+	0x0061, 0x007a, 1048544,
+	0x00e0, 0x00f6, 1048544,
+	0x00f8, 0x00fe, 1048544,
+	0x023f, 0x0240, 1059391,
+	0x0256, 0x0257, 1048371,
+	0x028a, 0x028b, 1048359,
+	0x037b, 0x037d, 1048706,
+	0x03ad, 0x03af, 1048539,
+	0x03b1, 0x03c1, 1048544,
+	0x03c3, 0x03cb, 1048544,
+	0x03cd, 0x03ce, 1048513,
+	0x0430, 0x044f, 1048544,
+	0x0450, 0x045f, 1048496,
+	0x0561, 0x0586, 1048528,
+	0x1f00, 0x1f07, 1048584,
+	0x1f10, 0x1f15, 1048584,
+	0x1f20, 0x1f27, 1048584,
+	0x1f30, 0x1f37, 1048584,
+	0x1f40, 0x1f45, 1048584,
+	0x1f60, 0x1f67, 1048584,
+	0x1f70, 0x1f71, 1048650,
+	0x1f72, 0x1f75, 1048662,
+	0x1f76, 0x1f77, 1048676,
+	0x1f78, 0x1f79, 1048704,
+	0x1f7a, 0x1f7b, 1048688,
+	0x1f7c, 0x1f7d, 1048702,
+	0x1f80, 0x1f87, 1048584,
+	0x1f90, 0x1f97, 1048584,
+	0x1fa0, 0x1fa7, 1048584,
+	0x1fb0, 0x1fb1, 1048584,
+	0x1fd0, 0x1fd1, 1048584,
+	0x1fe0, 0x1fe1, 1048584,
+	0x2170, 0x217f, 1048560,
+	0x24d0, 0x24e9, 1048550,
+	0x2c30, 0x2c5e, 1048528,
+	0x2d00, 0x2d25, 1041312,
+	0xff41, 0xff5a, 1048544,
+	0x10428, 0x1044f, 1048536,
+	0x118c0, 0x118df, 1048544,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t touppers[] = {
+	0x00b5, 1049319,
+	0x00ff, 1048697,
+	0x0101, 1048575,
+	0x0103, 1048575,
+	0x0105, 1048575,
+	0x0107, 1048575,
+	0x0109, 1048575,
+	0x010b, 1048575,
+	0x010d, 1048575,
+	0x010f, 1048575,
+	0x0111, 1048575,
+	0x0113, 1048575,
+	0x0115, 1048575,
+	0x0117, 1048575,
+	0x0119, 1048575,
+	0x011b, 1048575,
+	0x011d, 1048575,
+	0x011f, 1048575,
+	0x0121, 1048575,
+	0x0123, 1048575,
+	0x0125, 1048575,
+	0x0127, 1048575,
+	0x0129, 1048575,
+	0x012b, 1048575,
+	0x012d, 1048575,
+	0x012f, 1048575,
+	0x0131, 1048344,
+	0x0133, 1048575,
+	0x0135, 1048575,
+	0x0137, 1048575,
+	0x013a, 1048575,
+	0x013c, 1048575,
+	0x013e, 1048575,
+	0x0140, 1048575,
+	0x0142, 1048575,
+	0x0144, 1048575,
+	0x0146, 1048575,
+	0x0148, 1048575,
+	0x014b, 1048575,
+	0x014d, 1048575,
+	0x014f, 1048575,
+	0x0151, 1048575,
+	0x0153, 1048575,
+	0x0155, 1048575,
+	0x0157, 1048575,
+	0x0159, 1048575,
+	0x015b, 1048575,
+	0x015d, 1048575,
+	0x015f, 1048575,
+	0x0161, 1048575,
+	0x0163, 1048575,
+	0x0165, 1048575,
+	0x0167, 1048575,
+	0x0169, 1048575,
+	0x016b, 1048575,
+	0x016d, 1048575,
+	0x016f, 1048575,
+	0x0171, 1048575,
+	0x0173, 1048575,
+	0x0175, 1048575,
+	0x0177, 1048575,
+	0x017a, 1048575,
+	0x017c, 1048575,
+	0x017e, 1048575,
+	0x017f, 1048276,
+	0x0180, 1048771,
+	0x0183, 1048575,
+	0x0185, 1048575,
+	0x0188, 1048575,
+	0x018c, 1048575,
+	0x0192, 1048575,
+	0x0195, 1048673,
+	0x0199, 1048575,
+	0x019a, 1048739,
+	0x019e, 1048706,
+	0x01a1, 1048575,
+	0x01a3, 1048575,
+	0x01a5, 1048575,
+	0x01a8, 1048575,
+	0x01ad, 1048575,
+	0x01b0, 1048575,
+	0x01b4, 1048575,
+	0x01b6, 1048575,
+	0x01b9, 1048575,
+	0x01bd, 1048575,
+	0x01bf, 1048632,
+	0x01c5, 1048575,
+	0x01c6, 1048574,
+	0x01c8, 1048575,
+	0x01c9, 1048574,
+	0x01cb, 1048575,
+	0x01cc, 1048574,
+	0x01ce, 1048575,
+	0x01d0, 1048575,
+	0x01d2, 1048575,
+	0x01d4, 1048575,
+	0x01d6, 1048575,
+	0x01d8, 1048575,
+	0x01da, 1048575,
+	0x01dc, 1048575,
+	0x01dd, 1048497,
+	0x01df, 1048575,
+	0x01e1, 1048575,
+	0x01e3, 1048575,
+	0x01e5, 1048575,
+	0x01e7, 1048575,
+	0x01e9, 1048575,
+	0x01eb, 1048575,
+	0x01ed, 1048575,
+	0x01ef, 1048575,
+	0x01f2, 1048575,
+	0x01f3, 1048574,
+	0x01f5, 1048575,
+	0x01f9, 1048575,
+	0x01fb, 1048575,
+	0x01fd, 1048575,
+	0x01ff, 1048575,
+	0x0201, 1048575,
+	0x0203, 1048575,
+	0x0205, 1048575,
+	0x0207, 1048575,
+	0x0209, 1048575,
+	0x020b, 1048575,
+	0x020d, 1048575,
+	0x020f, 1048575,
+	0x0211, 1048575,
+	0x0213, 1048575,
+	0x0215, 1048575,
+	0x0217, 1048575,
+	0x0219, 1048575,
+	0x021b, 1048575,
+	0x021d, 1048575,
+	0x021f, 1048575,
+	0x0223, 1048575,
+	0x0225, 1048575,
+	0x0227, 1048575,
+	0x0229, 1048575,
+	0x022b, 1048575,
+	0x022d, 1048575,
+	0x022f, 1048575,
+	0x0231, 1048575,
+	0x0233, 1048575,
+	0x023c, 1048575,
+	0x0242, 1048575,
+	0x0247, 1048575,
+	0x0249, 1048575,
+	0x024b, 1048575,
+	0x024d, 1048575,
+	0x024f, 1048575,
+	0x0250, 1059359,
+	0x0251, 1059356,
+	0x0252, 1059358,
+	0x0253, 1048366,
+	0x0254, 1048370,
+	0x0259, 1048374,
+	0x025b, 1048373,
+	0x025c, 1090895,
+	0x0260, 1048371,
+	0x0261, 1090891,
+	0x0263, 1048369,
+	0x0265, 1090856,
+	0x0266, 1090884,
+	0x0268, 1048367,
+	0x0269, 1048365,
+	0x026b, 1059319,
+	0x026c, 1090881,
+	0x026f, 1048365,
+	0x0271, 1059325,
+	0x0272, 1048363,
+	0x0275, 1048362,
+	0x027d, 1059303,
+	0x0280, 1048358,
+	0x0283, 1048358,
+	0x0287, 1090858,
+	0x0288, 1048358,
+	0x0289, 1048507,
+	0x028c, 1048505,
+	0x0292, 1048357,
+	0x029e, 1090834,
+	0x0345, 1048660,
+	0x0371, 1048575,
+	0x0373, 1048575,
+	0x0377, 1048575,
+	0x03ac, 1048538,
+	0x03c2, 1048545,
+	0x03cc, 1048512,
+	0x03d0, 1048514,
+	0x03d1, 1048519,
+	0x03d5, 1048529,
+	0x03d6, 1048522,
+	0x03d7, 1048568,
+	0x03d9, 1048575,
+	0x03db, 1048575,
+	0x03dd, 1048575,
+	0x03df, 1048575,
+	0x03e1, 1048575,
+	0x03e3, 1048575,
+	0x03e5, 1048575,
+	0x03e7, 1048575,
+	0x03e9, 1048575,
+	0x03eb, 1048575,
+	0x03ed, 1048575,
+	0x03ef, 1048575,
+	0x03f0, 1048490,
+	0x03f1, 1048496,
+	0x03f2, 1048583,
+	0x03f3, 1048460,
+	0x03f5, 1048480,
+	0x03f8, 1048575,
+	0x03fb, 1048575,
+	0x0461, 1048575,
+	0x0463, 1048575,
+	0x0465, 1048575,
+	0x0467, 1048575,
+	0x0469, 1048575,
+	0x046b, 1048575,
+	0x046d, 1048575,
+	0x046f, 1048575,
+	0x0471, 1048575,
+	0x0473, 1048575,
+	0x0475, 1048575,
+	0x0477, 1048575,
+	0x0479, 1048575,
+	0x047b, 1048575,
+	0x047d, 1048575,
+	0x047f, 1048575,
+	0x0481, 1048575,
+	0x048b, 1048575,
+	0x048d, 1048575,
+	0x048f, 1048575,
+	0x0491, 1048575,
+	0x0493, 1048575,
+	0x0495, 1048575,
+	0x0497, 1048575,
+	0x0499, 1048575,
+	0x049b, 1048575,
+	0x049d, 1048575,
+	0x049f, 1048575,
+	0x04a1, 1048575,
+	0x04a3, 1048575,
+	0x04a5, 1048575,
+	0x04a7, 1048575,
+	0x04a9, 1048575,
+	0x04ab, 1048575,
+	0x04ad, 1048575,
+	0x04af, 1048575,
+	0x04b1, 1048575,
+	0x04b3, 1048575,
+	0x04b5, 1048575,
+	0x04b7, 1048575,
+	0x04b9, 1048575,
+	0x04bb, 1048575,
+	0x04bd, 1048575,
+	0x04bf, 1048575,
+	0x04c2, 1048575,
+	0x04c4, 1048575,
+	0x04c6, 1048575,
+	0x04c8, 1048575,
+	0x04ca, 1048575,
+	0x04cc, 1048575,
+	0x04ce, 1048575,
+	0x04cf, 1048561,
+	0x04d1, 1048575,
+	0x04d3, 1048575,
+	0x04d5, 1048575,
+	0x04d7, 1048575,
+	0x04d9, 1048575,
+	0x04db, 1048575,
+	0x04dd, 1048575,
+	0x04df, 1048575,
+	0x04e1, 1048575,
+	0x04e3, 1048575,
+	0x04e5, 1048575,
+	0x04e7, 1048575,
+	0x04e9, 1048575,
+	0x04eb, 1048575,
+	0x04ed, 1048575,
+	0x04ef, 1048575,
+	0x04f1, 1048575,
+	0x04f3, 1048575,
+	0x04f5, 1048575,
+	0x04f7, 1048575,
+	0x04f9, 1048575,
+	0x04fb, 1048575,
+	0x04fd, 1048575,
+	0x04ff, 1048575,
+	0x0501, 1048575,
+	0x0503, 1048575,
+	0x0505, 1048575,
+	0x0507, 1048575,
+	0x0509, 1048575,
+	0x050b, 1048575,
+	0x050d, 1048575,
+	0x050f, 1048575,
+	0x0511, 1048575,
+	0x0513, 1048575,
+	0x0515, 1048575,
+	0x0517, 1048575,
+	0x0519, 1048575,
+	0x051b, 1048575,
+	0x051d, 1048575,
+	0x051f, 1048575,
+	0x0521, 1048575,
+	0x0523, 1048575,
+	0x0525, 1048575,
+	0x0527, 1048575,
+	0x0529, 1048575,
+	0x052b, 1048575,
+	0x052d, 1048575,
+	0x052f, 1048575,
+	0x1d79, 1083908,
+	0x1d7d, 1052390,
+	0x1e01, 1048575,
+	0x1e03, 1048575,
+	0x1e05, 1048575,
+	0x1e07, 1048575,
+	0x1e09, 1048575,
+	0x1e0b, 1048575,
+	0x1e0d, 1048575,
+	0x1e0f, 1048575,
+	0x1e11, 1048575,
+	0x1e13, 1048575,
+	0x1e15, 1048575,
+	0x1e17, 1048575,
+	0x1e19, 1048575,
+	0x1e1b, 1048575,
+	0x1e1d, 1048575,
+	0x1e1f, 1048575,
+	0x1e21, 1048575,
+	0x1e23, 1048575,
+	0x1e25, 1048575,
+	0x1e27, 1048575,
+	0x1e29, 1048575,
+	0x1e2b, 1048575,
+	0x1e2d, 1048575,
+	0x1e2f, 1048575,
+	0x1e31, 1048575,
+	0x1e33, 1048575,
+	0x1e35, 1048575,
+	0x1e37, 1048575,
+	0x1e39, 1048575,
+	0x1e3b, 1048575,
+	0x1e3d, 1048575,
+	0x1e3f, 1048575,
+	0x1e41, 1048575,
+	0x1e43, 1048575,
+	0x1e45, 1048575,
+	0x1e47, 1048575,
+	0x1e49, 1048575,
+	0x1e4b, 1048575,
+	0x1e4d, 1048575,
+	0x1e4f, 1048575,
+	0x1e51, 1048575,
+	0x1e53, 1048575,
+	0x1e55, 1048575,
+	0x1e57, 1048575,
+	0x1e59, 1048575,
+	0x1e5b, 1048575,
+	0x1e5d, 1048575,
+	0x1e5f, 1048575,
+	0x1e61, 1048575,
+	0x1e63, 1048575,
+	0x1e65, 1048575,
+	0x1e67, 1048575,
+	0x1e69, 1048575,
+	0x1e6b, 1048575,
+	0x1e6d, 1048575,
+	0x1e6f, 1048575,
+	0x1e71, 1048575,
+	0x1e73, 1048575,
+	0x1e75, 1048575,
+	0x1e77, 1048575,
+	0x1e79, 1048575,
+	0x1e7b, 1048575,
+	0x1e7d, 1048575,
+	0x1e7f, 1048575,
+	0x1e81, 1048575,
+	0x1e83, 1048575,
+	0x1e85, 1048575,
+	0x1e87, 1048575,
+	0x1e89, 1048575,
+	0x1e8b, 1048575,
+	0x1e8d, 1048575,
+	0x1e8f, 1048575,
+	0x1e91, 1048575,
+	0x1e93, 1048575,
+	0x1e95, 1048575,
+	0x1e9b, 1048517,
+	0x1ea1, 1048575,
+	0x1ea3, 1048575,
+	0x1ea5, 1048575,
+	0x1ea7, 1048575,
+	0x1ea9, 1048575,
+	0x1eab, 1048575,
+	0x1ead, 1048575,
+	0x1eaf, 1048575,
+	0x1eb1, 1048575,
+	0x1eb3, 1048575,
+	0x1eb5, 1048575,
+	0x1eb7, 1048575,
+	0x1eb9, 1048575,
+	0x1ebb, 1048575,
+	0x1ebd, 1048575,
+	0x1ebf, 1048575,
+	0x1ec1, 1048575,
+	0x1ec3, 1048575,
+	0x1ec5, 1048575,
+	0x1ec7, 1048575,
+	0x1ec9, 1048575,
+	0x1ecb, 1048575,
+	0x1ecd, 1048575,
+	0x1ecf, 1048575,
+	0x1ed1, 1048575,
+	0x1ed3, 1048575,
+	0x1ed5, 1048575,
+	0x1ed7, 1048575,
+	0x1ed9, 1048575,
+	0x1edb, 1048575,
+	0x1edd, 1048575,
+	0x1edf, 1048575,
+	0x1ee1, 1048575,
+	0x1ee3, 1048575,
+	0x1ee5, 1048575,
+	0x1ee7, 1048575,
+	0x1ee9, 1048575,
+	0x1eeb, 1048575,
+	0x1eed, 1048575,
+	0x1eef, 1048575,
+	0x1ef1, 1048575,
+	0x1ef3, 1048575,
+	0x1ef5, 1048575,
+	0x1ef7, 1048575,
+	0x1ef9, 1048575,
+	0x1efb, 1048575,
+	0x1efd, 1048575,
+	0x1eff, 1048575,
+	0x1f51, 1048584,
+	0x1f53, 1048584,
+	0x1f55, 1048584,
+	0x1f57, 1048584,
+	0x1fb3, 1048585,
+	0x1fbe, 1041371,
+	0x1fc3, 1048585,
+	0x1fe5, 1048583,
+	0x1ff3, 1048585,
+	0x214e, 1048548,
+	0x2184, 1048575,
+	0x2c61, 1048575,
+	0x2c65, 1037781,
+	0x2c66, 1037784,
+	0x2c68, 1048575,
+	0x2c6a, 1048575,
+	0x2c6c, 1048575,
+	0x2c73, 1048575,
+	0x2c76, 1048575,
+	0x2c81, 1048575,
+	0x2c83, 1048575,
+	0x2c85, 1048575,
+	0x2c87, 1048575,
+	0x2c89, 1048575,
+	0x2c8b, 1048575,
+	0x2c8d, 1048575,
+	0x2c8f, 1048575,
+	0x2c91, 1048575,
+	0x2c93, 1048575,
+	0x2c95, 1048575,
+	0x2c97, 1048575,
+	0x2c99, 1048575,
+	0x2c9b, 1048575,
+	0x2c9d, 1048575,
+	0x2c9f, 1048575,
+	0x2ca1, 1048575,
+	0x2ca3, 1048575,
+	0x2ca5, 1048575,
+	0x2ca7, 1048575,
+	0x2ca9, 1048575,
+	0x2cab, 1048575,
+	0x2cad, 1048575,
+	0x2caf, 1048575,
+	0x2cb1, 1048575,
+	0x2cb3, 1048575,
+	0x2cb5, 1048575,
+	0x2cb7, 1048575,
+	0x2cb9, 1048575,
+	0x2cbb, 1048575,
+	0x2cbd, 1048575,
+	0x2cbf, 1048575,
+	0x2cc1, 1048575,
+	0x2cc3, 1048575,
+	0x2cc5, 1048575,
+	0x2cc7, 1048575,
+	0x2cc9, 1048575,
+	0x2ccb, 1048575,
+	0x2ccd, 1048575,
+	0x2ccf, 1048575,
+	0x2cd1, 1048575,
+	0x2cd3, 1048575,
+	0x2cd5, 1048575,
+	0x2cd7, 1048575,
+	0x2cd9, 1048575,
+	0x2cdb, 1048575,
+	0x2cdd, 1048575,
+	0x2cdf, 1048575,
+	0x2ce1, 1048575,
+	0x2ce3, 1048575,
+	0x2cec, 1048575,
+	0x2cee, 1048575,
+	0x2cf3, 1048575,
+	0x2d27, 1041312,
+	0x2d2d, 1041312,
+	0xa641, 1048575,
+	0xa643, 1048575,
+	0xa645, 1048575,
+	0xa647, 1048575,
+	0xa649, 1048575,
+	0xa64b, 1048575,
+	0xa64d, 1048575,
+	0xa64f, 1048575,
+	0xa651, 1048575,
+	0xa653, 1048575,
+	0xa655, 1048575,
+	0xa657, 1048575,
+	0xa659, 1048575,
+	0xa65b, 1048575,
+	0xa65d, 1048575,
+	0xa65f, 1048575,
+	0xa661, 1048575,
+	0xa663, 1048575,
+	0xa665, 1048575,
+	0xa667, 1048575,
+	0xa669, 1048575,
+	0xa66b, 1048575,
+	0xa66d, 1048575,
+	0xa681, 1048575,
+	0xa683, 1048575,
+	0xa685, 1048575,
+	0xa687, 1048575,
+	0xa689, 1048575,
+	0xa68b, 1048575,
+	0xa68d, 1048575,
+	0xa68f, 1048575,
+	0xa691, 1048575,
+	0xa693, 1048575,
+	0xa695, 1048575,
+	0xa697, 1048575,
+	0xa699, 1048575,
+	0xa69b, 1048575,
+	0xa723, 1048575,
+	0xa725, 1048575,
+	0xa727, 1048575,
+	0xa729, 1048575,
+	0xa72b, 1048575,
+	0xa72d, 1048575,
+	0xa72f, 1048575,
+	0xa733, 1048575,
+	0xa735, 1048575,
+	0xa737, 1048575,
+	0xa739, 1048575,
+	0xa73b, 1048575,
+	0xa73d, 1048575,
+	0xa73f, 1048575,
+	0xa741, 1048575,
+	0xa743, 1048575,
+	0xa745, 1048575,
+	0xa747, 1048575,
+	0xa749, 1048575,
+	0xa74b, 1048575,
+	0xa74d, 1048575,
+	0xa74f, 1048575,
+	0xa751, 1048575,
+	0xa753, 1048575,
+	0xa755, 1048575,
+	0xa757, 1048575,
+	0xa759, 1048575,
+	0xa75b, 1048575,
+	0xa75d, 1048575,
+	0xa75f, 1048575,
+	0xa761, 1048575,
+	0xa763, 1048575,
+	0xa765, 1048575,
+	0xa767, 1048575,
+	0xa769, 1048575,
+	0xa76b, 1048575,
+	0xa76d, 1048575,
+	0xa76f, 1048575,
+	0xa77a, 1048575,
+	0xa77c, 1048575,
+	0xa77f, 1048575,
+	0xa781, 1048575,
+	0xa783, 1048575,
+	0xa785, 1048575,
+	0xa787, 1048575,
+	0xa78c, 1048575,
+	0xa791, 1048575,
+	0xa793, 1048575,
+	0xa797, 1048575,
+	0xa799, 1048575,
+	0xa79b, 1048575,
+	0xa79d, 1048575,
+	0xa79f, 1048575,
+	0xa7a1, 1048575,
+	0xa7a3, 1048575,
+	0xa7a5, 1048575,
+	0xa7a7, 1048575,
+	0xa7a9, 1048575,
+};
+
+} // !namespace
+
+auto toupper(char32_t c) noexcept -> char32_t
+{
+	const char32_t* p;
+
+	p = search(c, toupperr, nelem (toupperr) / 3, 3);
+
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = search(c, touppers, nelem (touppers) / 2, 2);
+
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+namespace {
+
+const char32_t tolowerr[] = {
+	0x0041, 0x005a, 1048608,
+	0x00c0, 0x00d6, 1048608,
+	0x00d8, 0x00de, 1048608,
+	0x0189, 0x018a, 1048781,
+	0x01b1, 0x01b2, 1048793,
+	0x0388, 0x038a, 1048613,
+	0x038e, 0x038f, 1048639,
+	0x0391, 0x03a1, 1048608,
+	0x03a3, 0x03ab, 1048608,
+	0x03fd, 0x03ff, 1048446,
+	0x0400, 0x040f, 1048656,
+	0x0410, 0x042f, 1048608,
+	0x0531, 0x0556, 1048624,
+	0x10a0, 0x10c5, 1055840,
+	0x1f08, 0x1f0f, 1048568,
+	0x1f18, 0x1f1d, 1048568,
+	0x1f28, 0x1f2f, 1048568,
+	0x1f38, 0x1f3f, 1048568,
+	0x1f48, 0x1f4d, 1048568,
+	0x1f68, 0x1f6f, 1048568,
+	0x1f88, 0x1f8f, 1048568,
+	0x1f98, 0x1f9f, 1048568,
+	0x1fa8, 0x1faf, 1048568,
+	0x1fb8, 0x1fb9, 1048568,
+	0x1fba, 0x1fbb, 1048502,
+	0x1fc8, 0x1fcb, 1048490,
+	0x1fd8, 0x1fd9, 1048568,
+	0x1fda, 0x1fdb, 1048476,
+	0x1fe8, 0x1fe9, 1048568,
+	0x1fea, 0x1feb, 1048464,
+	0x1ff8, 0x1ff9, 1048448,
+	0x1ffa, 0x1ffb, 1048450,
+	0x2160, 0x216f, 1048592,
+	0x24b6, 0x24cf, 1048602,
+	0x2c00, 0x2c2e, 1048624,
+	0x2c7e, 0x2c7f, 1037761,
+	0xff21, 0xff3a, 1048608,
+	0x10400, 0x10427, 1048616,
+	0x118a0, 0x118bf, 1048608,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t tolowers[] = {
+	0x0100, 1048577,
+	0x0102, 1048577,
+	0x0104, 1048577,
+	0x0106, 1048577,
+	0x0108, 1048577,
+	0x010a, 1048577,
+	0x010c, 1048577,
+	0x010e, 1048577,
+	0x0110, 1048577,
+	0x0112, 1048577,
+	0x0114, 1048577,
+	0x0116, 1048577,
+	0x0118, 1048577,
+	0x011a, 1048577,
+	0x011c, 1048577,
+	0x011e, 1048577,
+	0x0120, 1048577,
+	0x0122, 1048577,
+	0x0124, 1048577,
+	0x0126, 1048577,
+	0x0128, 1048577,
+	0x012a, 1048577,
+	0x012c, 1048577,
+	0x012e, 1048577,
+	0x0130, 1048377,
+	0x0132, 1048577,
+	0x0134, 1048577,
+	0x0136, 1048577,
+	0x0139, 1048577,
+	0x013b, 1048577,
+	0x013d, 1048577,
+	0x013f, 1048577,
+	0x0141, 1048577,
+	0x0143, 1048577,
+	0x0145, 1048577,
+	0x0147, 1048577,
+	0x014a, 1048577,
+	0x014c, 1048577,
+	0x014e, 1048577,
+	0x0150, 1048577,
+	0x0152, 1048577,
+	0x0154, 1048577,
+	0x0156, 1048577,
+	0x0158, 1048577,
+	0x015a, 1048577,
+	0x015c, 1048577,
+	0x015e, 1048577,
+	0x0160, 1048577,
+	0x0162, 1048577,
+	0x0164, 1048577,
+	0x0166, 1048577,
+	0x0168, 1048577,
+	0x016a, 1048577,
+	0x016c, 1048577,
+	0x016e, 1048577,
+	0x0170, 1048577,
+	0x0172, 1048577,
+	0x0174, 1048577,
+	0x0176, 1048577,
+	0x0178, 1048455,
+	0x0179, 1048577,
+	0x017b, 1048577,
+	0x017d, 1048577,
+	0x0181, 1048786,
+	0x0182, 1048577,
+	0x0184, 1048577,
+	0x0186, 1048782,
+	0x0187, 1048577,
+	0x018b, 1048577,
+	0x018e, 1048655,
+	0x018f, 1048778,
+	0x0190, 1048779,
+	0x0191, 1048577,
+	0x0193, 1048781,
+	0x0194, 1048783,
+	0x0196, 1048787,
+	0x0197, 1048785,
+	0x0198, 1048577,
+	0x019c, 1048787,
+	0x019d, 1048789,
+	0x019f, 1048790,
+	0x01a0, 1048577,
+	0x01a2, 1048577,
+	0x01a4, 1048577,
+	0x01a6, 1048794,
+	0x01a7, 1048577,
+	0x01a9, 1048794,
+	0x01ac, 1048577,
+	0x01ae, 1048794,
+	0x01af, 1048577,
+	0x01b3, 1048577,
+	0x01b5, 1048577,
+	0x01b7, 1048795,
+	0x01b8, 1048577,
+	0x01bc, 1048577,
+	0x01c4, 1048578,
+	0x01c5, 1048577,
+	0x01c7, 1048578,
+	0x01c8, 1048577,
+	0x01ca, 1048578,
+	0x01cb, 1048577,
+	0x01cd, 1048577,
+	0x01cf, 1048577,
+	0x01d1, 1048577,
+	0x01d3, 1048577,
+	0x01d5, 1048577,
+	0x01d7, 1048577,
+	0x01d9, 1048577,
+	0x01db, 1048577,
+	0x01de, 1048577,
+	0x01e0, 1048577,
+	0x01e2, 1048577,
+	0x01e4, 1048577,
+	0x01e6, 1048577,
+	0x01e8, 1048577,
+	0x01ea, 1048577,
+	0x01ec, 1048577,
+	0x01ee, 1048577,
+	0x01f1, 1048578,
+	0x01f2, 1048577,
+	0x01f4, 1048577,
+	0x01f6, 1048479,
+	0x01f7, 1048520,
+	0x01f8, 1048577,
+	0x01fa, 1048577,
+	0x01fc, 1048577,
+	0x01fe, 1048577,
+	0x0200, 1048577,
+	0x0202, 1048577,
+	0x0204, 1048577,
+	0x0206, 1048577,
+	0x0208, 1048577,
+	0x020a, 1048577,
+	0x020c, 1048577,
+	0x020e, 1048577,
+	0x0210, 1048577,
+	0x0212, 1048577,
+	0x0214, 1048577,
+	0x0216, 1048577,
+	0x0218, 1048577,
+	0x021a, 1048577,
+	0x021c, 1048577,
+	0x021e, 1048577,
+	0x0220, 1048446,
+	0x0222, 1048577,
+	0x0224, 1048577,
+	0x0226, 1048577,
+	0x0228, 1048577,
+	0x022a, 1048577,
+	0x022c, 1048577,
+	0x022e, 1048577,
+	0x0230, 1048577,
+	0x0232, 1048577,
+	0x023a, 1059371,
+	0x023b, 1048577,
+	0x023d, 1048413,
+	0x023e, 1059368,
+	0x0241, 1048577,
+	0x0243, 1048381,
+	0x0244, 1048645,
+	0x0245, 1048647,
+	0x0246, 1048577,
+	0x0248, 1048577,
+	0x024a, 1048577,
+	0x024c, 1048577,
+	0x024e, 1048577,
+	0x0370, 1048577,
+	0x0372, 1048577,
+	0x0376, 1048577,
+	0x037f, 1048692,
+	0x0386, 1048614,
+	0x038c, 1048640,
+	0x03cf, 1048584,
+	0x03d8, 1048577,
+	0x03da, 1048577,
+	0x03dc, 1048577,
+	0x03de, 1048577,
+	0x03e0, 1048577,
+	0x03e2, 1048577,
+	0x03e4, 1048577,
+	0x03e6, 1048577,
+	0x03e8, 1048577,
+	0x03ea, 1048577,
+	0x03ec, 1048577,
+	0x03ee, 1048577,
+	0x03f4, 1048516,
+	0x03f7, 1048577,
+	0x03f9, 1048569,
+	0x03fa, 1048577,
+	0x0460, 1048577,
+	0x0462, 1048577,
+	0x0464, 1048577,
+	0x0466, 1048577,
+	0x0468, 1048577,
+	0x046a, 1048577,
+	0x046c, 1048577,
+	0x046e, 1048577,
+	0x0470, 1048577,
+	0x0472, 1048577,
+	0x0474, 1048577,
+	0x0476, 1048577,
+	0x0478, 1048577,
+	0x047a, 1048577,
+	0x047c, 1048577,
+	0x047e, 1048577,
+	0x0480, 1048577,
+	0x048a, 1048577,
+	0x048c, 1048577,
+	0x048e, 1048577,
+	0x0490, 1048577,
+	0x0492, 1048577,
+	0x0494, 1048577,
+	0x0496, 1048577,
+	0x0498, 1048577,
+	0x049a, 1048577,
+	0x049c, 1048577,
+	0x049e, 1048577,
+	0x04a0, 1048577,
+	0x04a2, 1048577,
+	0x04a4, 1048577,
+	0x04a6, 1048577,
+	0x04a8, 1048577,
+	0x04aa, 1048577,
+	0x04ac, 1048577,
+	0x04ae, 1048577,
+	0x04b0, 1048577,
+	0x04b2, 1048577,
+	0x04b4, 1048577,
+	0x04b6, 1048577,
+	0x04b8, 1048577,
+	0x04ba, 1048577,
+	0x04bc, 1048577,
+	0x04be, 1048577,
+	0x04c0, 1048591,
+	0x04c1, 1048577,
+	0x04c3, 1048577,
+	0x04c5, 1048577,
+	0x04c7, 1048577,
+	0x04c9, 1048577,
+	0x04cb, 1048577,
+	0x04cd, 1048577,
+	0x04d0, 1048577,
+	0x04d2, 1048577,
+	0x04d4, 1048577,
+	0x04d6, 1048577,
+	0x04d8, 1048577,
+	0x04da, 1048577,
+	0x04dc, 1048577,
+	0x04de, 1048577,
+	0x04e0, 1048577,
+	0x04e2, 1048577,
+	0x04e4, 1048577,
+	0x04e6, 1048577,
+	0x04e8, 1048577,
+	0x04ea, 1048577,
+	0x04ec, 1048577,
+	0x04ee, 1048577,
+	0x04f0, 1048577,
+	0x04f2, 1048577,
+	0x04f4, 1048577,
+	0x04f6, 1048577,
+	0x04f8, 1048577,
+	0x04fa, 1048577,
+	0x04fc, 1048577,
+	0x04fe, 1048577,
+	0x0500, 1048577,
+	0x0502, 1048577,
+	0x0504, 1048577,
+	0x0506, 1048577,
+	0x0508, 1048577,
+	0x050a, 1048577,
+	0x050c, 1048577,
+	0x050e, 1048577,
+	0x0510, 1048577,
+	0x0512, 1048577,
+	0x0514, 1048577,
+	0x0516, 1048577,
+	0x0518, 1048577,
+	0x051a, 1048577,
+	0x051c, 1048577,
+	0x051e, 1048577,
+	0x0520, 1048577,
+	0x0522, 1048577,
+	0x0524, 1048577,
+	0x0526, 1048577,
+	0x0528, 1048577,
+	0x052a, 1048577,
+	0x052c, 1048577,
+	0x052e, 1048577,
+	0x10c7, 1055840,
+	0x10cd, 1055840,
+	0x1e00, 1048577,
+	0x1e02, 1048577,
+	0x1e04, 1048577,
+	0x1e06, 1048577,
+	0x1e08, 1048577,
+	0x1e0a, 1048577,
+	0x1e0c, 1048577,
+	0x1e0e, 1048577,
+	0x1e10, 1048577,
+	0x1e12, 1048577,
+	0x1e14, 1048577,
+	0x1e16, 1048577,
+	0x1e18, 1048577,
+	0x1e1a, 1048577,
+	0x1e1c, 1048577,
+	0x1e1e, 1048577,
+	0x1e20, 1048577,
+	0x1e22, 1048577,
+	0x1e24, 1048577,
+	0x1e26, 1048577,
+	0x1e28, 1048577,
+	0x1e2a, 1048577,
+	0x1e2c, 1048577,
+	0x1e2e, 1048577,
+	0x1e30, 1048577,
+	0x1e32, 1048577,
+	0x1e34, 1048577,
+	0x1e36, 1048577,
+	0x1e38, 1048577,
+	0x1e3a, 1048577,
+	0x1e3c, 1048577,
+	0x1e3e, 1048577,
+	0x1e40, 1048577,
+	0x1e42, 1048577,
+	0x1e44, 1048577,
+	0x1e46, 1048577,
+	0x1e48, 1048577,
+	0x1e4a, 1048577,
+	0x1e4c, 1048577,
+	0x1e4e, 1048577,
+	0x1e50, 1048577,
+	0x1e52, 1048577,
+	0x1e54, 1048577,
+	0x1e56, 1048577,
+	0x1e58, 1048577,
+	0x1e5a, 1048577,
+	0x1e5c, 1048577,
+	0x1e5e, 1048577,
+	0x1e60, 1048577,
+	0x1e62, 1048577,
+	0x1e64, 1048577,
+	0x1e66, 1048577,
+	0x1e68, 1048577,
+	0x1e6a, 1048577,
+	0x1e6c, 1048577,
+	0x1e6e, 1048577,
+	0x1e70, 1048577,
+	0x1e72, 1048577,
+	0x1e74, 1048577,
+	0x1e76, 1048577,
+	0x1e78, 1048577,
+	0x1e7a, 1048577,
+	0x1e7c, 1048577,
+	0x1e7e, 1048577,
+	0x1e80, 1048577,
+	0x1e82, 1048577,
+	0x1e84, 1048577,
+	0x1e86, 1048577,
+	0x1e88, 1048577,
+	0x1e8a, 1048577,
+	0x1e8c, 1048577,
+	0x1e8e, 1048577,
+	0x1e90, 1048577,
+	0x1e92, 1048577,
+	0x1e94, 1048577,
+	0x1e9e, 1040961,
+	0x1ea0, 1048577,
+	0x1ea2, 1048577,
+	0x1ea4, 1048577,
+	0x1ea6, 1048577,
+	0x1ea8, 1048577,
+	0x1eaa, 1048577,
+	0x1eac, 1048577,
+	0x1eae, 1048577,
+	0x1eb0, 1048577,
+	0x1eb2, 1048577,
+	0x1eb4, 1048577,
+	0x1eb6, 1048577,
+	0x1eb8, 1048577,
+	0x1eba, 1048577,
+	0x1ebc, 1048577,
+	0x1ebe, 1048577,
+	0x1ec0, 1048577,
+	0x1ec2, 1048577,
+	0x1ec4, 1048577,
+	0x1ec6, 1048577,
+	0x1ec8, 1048577,
+	0x1eca, 1048577,
+	0x1ecc, 1048577,
+	0x1ece, 1048577,
+	0x1ed0, 1048577,
+	0x1ed2, 1048577,
+	0x1ed4, 1048577,
+	0x1ed6, 1048577,
+	0x1ed8, 1048577,
+	0x1eda, 1048577,
+	0x1edc, 1048577,
+	0x1ede, 1048577,
+	0x1ee0, 1048577,
+	0x1ee2, 1048577,
+	0x1ee4, 1048577,
+	0x1ee6, 1048577,
+	0x1ee8, 1048577,
+	0x1eea, 1048577,
+	0x1eec, 1048577,
+	0x1eee, 1048577,
+	0x1ef0, 1048577,
+	0x1ef2, 1048577,
+	0x1ef4, 1048577,
+	0x1ef6, 1048577,
+	0x1ef8, 1048577,
+	0x1efa, 1048577,
+	0x1efc, 1048577,
+	0x1efe, 1048577,
+	0x1f59, 1048568,
+	0x1f5b, 1048568,
+	0x1f5d, 1048568,
+	0x1f5f, 1048568,
+	0x1fbc, 1048567,
+	0x1fcc, 1048567,
+	0x1fec, 1048569,
+	0x1ffc, 1048567,
+	0x2126, 1041059,
+	0x212a, 1040193,
+	0x212b, 1040314,
+	0x2132, 1048604,
+	0x2183, 1048577,
+	0x2c60, 1048577,
+	0x2c62, 1037833,
+	0x2c63, 1044762,
+	0x2c64, 1037849,
+	0x2c67, 1048577,
+	0x2c69, 1048577,
+	0x2c6b, 1048577,
+	0x2c6d, 1037796,
+	0x2c6e, 1037827,
+	0x2c6f, 1037793,
+	0x2c70, 1037794,
+	0x2c72, 1048577,
+	0x2c75, 1048577,
+	0x2c80, 1048577,
+	0x2c82, 1048577,
+	0x2c84, 1048577,
+	0x2c86, 1048577,
+	0x2c88, 1048577,
+	0x2c8a, 1048577,
+	0x2c8c, 1048577,
+	0x2c8e, 1048577,
+	0x2c90, 1048577,
+	0x2c92, 1048577,
+	0x2c94, 1048577,
+	0x2c96, 1048577,
+	0x2c98, 1048577,
+	0x2c9a, 1048577,
+	0x2c9c, 1048577,
+	0x2c9e, 1048577,
+	0x2ca0, 1048577,
+	0x2ca2, 1048577,
+	0x2ca4, 1048577,
+	0x2ca6, 1048577,
+	0x2ca8, 1048577,
+	0x2caa, 1048577,
+	0x2cac, 1048577,
+	0x2cae, 1048577,
+	0x2cb0, 1048577,
+	0x2cb2, 1048577,
+	0x2cb4, 1048577,
+	0x2cb6, 1048577,
+	0x2cb8, 1048577,
+	0x2cba, 1048577,
+	0x2cbc, 1048577,
+	0x2cbe, 1048577,
+	0x2cc0, 1048577,
+	0x2cc2, 1048577,
+	0x2cc4, 1048577,
+	0x2cc6, 1048577,
+	0x2cc8, 1048577,
+	0x2cca, 1048577,
+	0x2ccc, 1048577,
+	0x2cce, 1048577,
+	0x2cd0, 1048577,
+	0x2cd2, 1048577,
+	0x2cd4, 1048577,
+	0x2cd6, 1048577,
+	0x2cd8, 1048577,
+	0x2cda, 1048577,
+	0x2cdc, 1048577,
+	0x2cde, 1048577,
+	0x2ce0, 1048577,
+	0x2ce2, 1048577,
+	0x2ceb, 1048577,
+	0x2ced, 1048577,
+	0x2cf2, 1048577,
+	0xa640, 1048577,
+	0xa642, 1048577,
+	0xa644, 1048577,
+	0xa646, 1048577,
+	0xa648, 1048577,
+	0xa64a, 1048577,
+	0xa64c, 1048577,
+	0xa64e, 1048577,
+	0xa650, 1048577,
+	0xa652, 1048577,
+	0xa654, 1048577,
+	0xa656, 1048577,
+	0xa658, 1048577,
+	0xa65a, 1048577,
+	0xa65c, 1048577,
+	0xa65e, 1048577,
+	0xa660, 1048577,
+	0xa662, 1048577,
+	0xa664, 1048577,
+	0xa666, 1048577,
+	0xa668, 1048577,
+	0xa66a, 1048577,
+	0xa66c, 1048577,
+	0xa680, 1048577,
+	0xa682, 1048577,
+	0xa684, 1048577,
+	0xa686, 1048577,
+	0xa688, 1048577,
+	0xa68a, 1048577,
+	0xa68c, 1048577,
+	0xa68e, 1048577,
+	0xa690, 1048577,
+	0xa692, 1048577,
+	0xa694, 1048577,
+	0xa696, 1048577,
+	0xa698, 1048577,
+	0xa69a, 1048577,
+	0xa722, 1048577,
+	0xa724, 1048577,
+	0xa726, 1048577,
+	0xa728, 1048577,
+	0xa72a, 1048577,
+	0xa72c, 1048577,
+	0xa72e, 1048577,
+	0xa732, 1048577,
+	0xa734, 1048577,
+	0xa736, 1048577,
+	0xa738, 1048577,
+	0xa73a, 1048577,
+	0xa73c, 1048577,
+	0xa73e, 1048577,
+	0xa740, 1048577,
+	0xa742, 1048577,
+	0xa744, 1048577,
+	0xa746, 1048577,
+	0xa748, 1048577,
+	0xa74a, 1048577,
+	0xa74c, 1048577,
+	0xa74e, 1048577,
+	0xa750, 1048577,
+	0xa752, 1048577,
+	0xa754, 1048577,
+	0xa756, 1048577,
+	0xa758, 1048577,
+	0xa75a, 1048577,
+	0xa75c, 1048577,
+	0xa75e, 1048577,
+	0xa760, 1048577,
+	0xa762, 1048577,
+	0xa764, 1048577,
+	0xa766, 1048577,
+	0xa768, 1048577,
+	0xa76a, 1048577,
+	0xa76c, 1048577,
+	0xa76e, 1048577,
+	0xa779, 1048577,
+	0xa77b, 1048577,
+	0xa77d, 1013244,
+	0xa77e, 1048577,
+	0xa780, 1048577,
+	0xa782, 1048577,
+	0xa784, 1048577,
+	0xa786, 1048577,
+	0xa78b, 1048577,
+	0xa78d, 1006296,
+	0xa790, 1048577,
+	0xa792, 1048577,
+	0xa796, 1048577,
+	0xa798, 1048577,
+	0xa79a, 1048577,
+	0xa79c, 1048577,
+	0xa79e, 1048577,
+	0xa7a0, 1048577,
+	0xa7a2, 1048577,
+	0xa7a4, 1048577,
+	0xa7a6, 1048577,
+	0xa7a8, 1048577,
+	0xa7aa, 1006268,
+	0xa7ab, 1006257,
+	0xa7ac, 1006261,
+	0xa7ad, 1006271,
+	0xa7b0, 1006318,
+	0xa7b1, 1006294,
+};
+
+} // !namespace
+
+auto tolower(char32_t c) noexcept -> char32_t
+{
+	const char32_t* p;
+
+	p = search(c, tolowerr, nelem (tolowerr) / 3, 3);
+
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = search(c, tolowers, nelem (tolowers) / 2, 2);
+
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+namespace {
+
+const char32_t totitler[] = {
+	0x0061, 0x007a, 1048544,
+	0x00e0, 0x00f6, 1048544,
+	0x00f8, 0x00fe, 1048544,
+	0x023f, 0x0240, 1059391,
+	0x0256, 0x0257, 1048371,
+	0x028a, 0x028b, 1048359,
+	0x037b, 0x037d, 1048706,
+	0x03ad, 0x03af, 1048539,
+	0x03b1, 0x03c1, 1048544,
+	0x03c3, 0x03cb, 1048544,
+	0x03cd, 0x03ce, 1048513,
+	0x0430, 0x044f, 1048544,
+	0x0450, 0x045f, 1048496,
+	0x0561, 0x0586, 1048528,
+	0x1f00, 0x1f07, 1048584,
+	0x1f10, 0x1f15, 1048584,
+	0x1f20, 0x1f27, 1048584,
+	0x1f30, 0x1f37, 1048584,
+	0x1f40, 0x1f45, 1048584,
+	0x1f60, 0x1f67, 1048584,
+	0x1f70, 0x1f71, 1048650,
+	0x1f72, 0x1f75, 1048662,
+	0x1f76, 0x1f77, 1048676,
+	0x1f78, 0x1f79, 1048704,
+	0x1f7a, 0x1f7b, 1048688,
+	0x1f7c, 0x1f7d, 1048702,
+	0x1f80, 0x1f87, 1048584,
+	0x1f90, 0x1f97, 1048584,
+	0x1fa0, 0x1fa7, 1048584,
+	0x1fb0, 0x1fb1, 1048584,
+	0x1fd0, 0x1fd1, 1048584,
+	0x1fe0, 0x1fe1, 1048584,
+	0x2170, 0x217f, 1048560,
+	0x24d0, 0x24e9, 1048550,
+	0x2c30, 0x2c5e, 1048528,
+	0x2d00, 0x2d25, 1041312,
+	0xff41, 0xff5a, 1048544,
+	0x10428, 0x1044f, 1048536,
+	0x118c0, 0x118df, 1048544,
+};
+
+} // !namespace
+
+namespace {
+
+const char32_t totitles[] = {
+	0x00b5, 1049319,
+	0x00ff, 1048697,
+	0x0101, 1048575,
+	0x0103, 1048575,
+	0x0105, 1048575,
+	0x0107, 1048575,
+	0x0109, 1048575,
+	0x010b, 1048575,
+	0x010d, 1048575,
+	0x010f, 1048575,
+	0x0111, 1048575,
+	0x0113, 1048575,
+	0x0115, 1048575,
+	0x0117, 1048575,
+	0x0119, 1048575,
+	0x011b, 1048575,
+	0x011d, 1048575,
+	0x011f, 1048575,
+	0x0121, 1048575,
+	0x0123, 1048575,
+	0x0125, 1048575,
+	0x0127, 1048575,
+	0x0129, 1048575,
+	0x012b, 1048575,
+	0x012d, 1048575,
+	0x012f, 1048575,
+	0x0131, 1048344,
+	0x0133, 1048575,
+	0x0135, 1048575,
+	0x0137, 1048575,
+	0x013a, 1048575,
+	0x013c, 1048575,
+	0x013e, 1048575,
+	0x0140, 1048575,
+	0x0142, 1048575,
+	0x0144, 1048575,
+	0x0146, 1048575,
+	0x0148, 1048575,
+	0x014b, 1048575,
+	0x014d, 1048575,
+	0x014f, 1048575,
+	0x0151, 1048575,
+	0x0153, 1048575,
+	0x0155, 1048575,
+	0x0157, 1048575,
+	0x0159, 1048575,
+	0x015b, 1048575,
+	0x015d, 1048575,
+	0x015f, 1048575,
+	0x0161, 1048575,
+	0x0163, 1048575,
+	0x0165, 1048575,
+	0x0167, 1048575,
+	0x0169, 1048575,
+	0x016b, 1048575,
+	0x016d, 1048575,
+	0x016f, 1048575,
+	0x0171, 1048575,
+	0x0173, 1048575,
+	0x0175, 1048575,
+	0x0177, 1048575,
+	0x017a, 1048575,
+	0x017c, 1048575,
+	0x017e, 1048575,
+	0x017f, 1048276,
+	0x0180, 1048771,
+	0x0183, 1048575,
+	0x0185, 1048575,
+	0x0188, 1048575,
+	0x018c, 1048575,
+	0x0192, 1048575,
+	0x0195, 1048673,
+	0x0199, 1048575,
+	0x019a, 1048739,
+	0x019e, 1048706,
+	0x01a1, 1048575,
+	0x01a3, 1048575,
+	0x01a5, 1048575,
+	0x01a8, 1048575,
+	0x01ad, 1048575,
+	0x01b0, 1048575,
+	0x01b4, 1048575,
+	0x01b6, 1048575,
+	0x01b9, 1048575,
+	0x01bd, 1048575,
+	0x01bf, 1048632,
+	0x01c4, 1048577,
+	0x01c6, 1048575,
+	0x01c7, 1048577,
+	0x01c9, 1048575,
+	0x01ca, 1048577,
+	0x01cc, 1048575,
+	0x01ce, 1048575,
+	0x01d0, 1048575,
+	0x01d2, 1048575,
+	0x01d4, 1048575,
+	0x01d6, 1048575,
+	0x01d8, 1048575,
+	0x01da, 1048575,
+	0x01dc, 1048575,
+	0x01dd, 1048497,
+	0x01df, 1048575,
+	0x01e1, 1048575,
+	0x01e3, 1048575,
+	0x01e5, 1048575,
+	0x01e7, 1048575,
+	0x01e9, 1048575,
+	0x01eb, 1048575,
+	0x01ed, 1048575,
+	0x01ef, 1048575,
+	0x01f1, 1048577,
+	0x01f3, 1048575,
+	0x01f5, 1048575,
+	0x01f9, 1048575,
+	0x01fb, 1048575,
+	0x01fd, 1048575,
+	0x01ff, 1048575,
+	0x0201, 1048575,
+	0x0203, 1048575,
+	0x0205, 1048575,
+	0x0207, 1048575,
+	0x0209, 1048575,
+	0x020b, 1048575,
+	0x020d, 1048575,
+	0x020f, 1048575,
+	0x0211, 1048575,
+	0x0213, 1048575,
+	0x0215, 1048575,
+	0x0217, 1048575,
+	0x0219, 1048575,
+	0x021b, 1048575,
+	0x021d, 1048575,
+	0x021f, 1048575,
+	0x0223, 1048575,
+	0x0225, 1048575,
+	0x0227, 1048575,
+	0x0229, 1048575,
+	0x022b, 1048575,
+	0x022d, 1048575,
+	0x022f, 1048575,
+	0x0231, 1048575,
+	0x0233, 1048575,
+	0x023c, 1048575,
+	0x0242, 1048575,
+	0x0247, 1048575,
+	0x0249, 1048575,
+	0x024b, 1048575,
+	0x024d, 1048575,
+	0x024f, 1048575,
+	0x0250, 1059359,
+	0x0251, 1059356,
+	0x0252, 1059358,
+	0x0253, 1048366,
+	0x0254, 1048370,
+	0x0259, 1048374,
+	0x025b, 1048373,
+	0x025c, 1090895,
+	0x0260, 1048371,
+	0x0261, 1090891,
+	0x0263, 1048369,
+	0x0265, 1090856,
+	0x0266, 1090884,
+	0x0268, 1048367,
+	0x0269, 1048365,
+	0x026b, 1059319,
+	0x026c, 1090881,
+	0x026f, 1048365,
+	0x0271, 1059325,
+	0x0272, 1048363,
+	0x0275, 1048362,
+	0x027d, 1059303,
+	0x0280, 1048358,
+	0x0283, 1048358,
+	0x0287, 1090858,
+	0x0288, 1048358,
+	0x0289, 1048507,
+	0x028c, 1048505,
+	0x0292, 1048357,
+	0x029e, 1090834,
+	0x0345, 1048660,
+	0x0371, 1048575,
+	0x0373, 1048575,
+	0x0377, 1048575,
+	0x03ac, 1048538,
+	0x03c2, 1048545,
+	0x03cc, 1048512,
+	0x03d0, 1048514,
+	0x03d1, 1048519,
+	0x03d5, 1048529,
+	0x03d6, 1048522,
+	0x03d7, 1048568,
+	0x03d9, 1048575,
+	0x03db, 1048575,
+	0x03dd, 1048575,
+	0x03df, 1048575,
+	0x03e1, 1048575,
+	0x03e3, 1048575,
+	0x03e5, 1048575,
+	0x03e7, 1048575,
+	0x03e9, 1048575,
+	0x03eb, 1048575,
+	0x03ed, 1048575,
+	0x03ef, 1048575,
+	0x03f0, 1048490,
+	0x03f1, 1048496,
+	0x03f2, 1048583,
+	0x03f3, 1048460,
+	0x03f5, 1048480,
+	0x03f8, 1048575,
+	0x03fb, 1048575,
+	0x0461, 1048575,
+	0x0463, 1048575,
+	0x0465, 1048575,
+	0x0467, 1048575,
+	0x0469, 1048575,
+	0x046b, 1048575,
+	0x046d, 1048575,
+	0x046f, 1048575,
+	0x0471, 1048575,
+	0x0473, 1048575,
+	0x0475, 1048575,
+	0x0477, 1048575,
+	0x0479, 1048575,
+	0x047b, 1048575,
+	0x047d, 1048575,
+	0x047f, 1048575,
+	0x0481, 1048575,
+	0x048b, 1048575,
+	0x048d, 1048575,
+	0x048f, 1048575,
+	0x0491, 1048575,
+	0x0493, 1048575,
+	0x0495, 1048575,
+	0x0497, 1048575,
+	0x0499, 1048575,
+	0x049b, 1048575,
+	0x049d, 1048575,
+	0x049f, 1048575,
+	0x04a1, 1048575,
+	0x04a3, 1048575,
+	0x04a5, 1048575,
+	0x04a7, 1048575,
+	0x04a9, 1048575,
+	0x04ab, 1048575,
+	0x04ad, 1048575,
+	0x04af, 1048575,
+	0x04b1, 1048575,
+	0x04b3, 1048575,
+	0x04b5, 1048575,
+	0x04b7, 1048575,
+	0x04b9, 1048575,
+	0x04bb, 1048575,
+	0x04bd, 1048575,
+	0x04bf, 1048575,
+	0x04c2, 1048575,
+	0x04c4, 1048575,
+	0x04c6, 1048575,
+	0x04c8, 1048575,
+	0x04ca, 1048575,
+	0x04cc, 1048575,
+	0x04ce, 1048575,
+	0x04cf, 1048561,
+	0x04d1, 1048575,
+	0x04d3, 1048575,
+	0x04d5, 1048575,
+	0x04d7, 1048575,
+	0x04d9, 1048575,
+	0x04db, 1048575,
+	0x04dd, 1048575,
+	0x04df, 1048575,
+	0x04e1, 1048575,
+	0x04e3, 1048575,
+	0x04e5, 1048575,
+	0x04e7, 1048575,
+	0x04e9, 1048575,
+	0x04eb, 1048575,
+	0x04ed, 1048575,
+	0x04ef, 1048575,
+	0x04f1, 1048575,
+	0x04f3, 1048575,
+	0x04f5, 1048575,
+	0x04f7, 1048575,
+	0x04f9, 1048575,
+	0x04fb, 1048575,
+	0x04fd, 1048575,
+	0x04ff, 1048575,
+	0x0501, 1048575,
+	0x0503, 1048575,
+	0x0505, 1048575,
+	0x0507, 1048575,
+	0x0509, 1048575,
+	0x050b, 1048575,
+	0x050d, 1048575,
+	0x050f, 1048575,
+	0x0511, 1048575,
+	0x0513, 1048575,
+	0x0515, 1048575,
+	0x0517, 1048575,
+	0x0519, 1048575,
+	0x051b, 1048575,
+	0x051d, 1048575,
+	0x051f, 1048575,
+	0x0521, 1048575,
+	0x0523, 1048575,
+	0x0525, 1048575,
+	0x0527, 1048575,
+	0x0529, 1048575,
+	0x052b, 1048575,
+	0x052d, 1048575,
+	0x052f, 1048575,
+	0x1d79, 1083908,
+	0x1d7d, 1052390,
+	0x1e01, 1048575,
+	0x1e03, 1048575,
+	0x1e05, 1048575,
+	0x1e07, 1048575,
+	0x1e09, 1048575,
+	0x1e0b, 1048575,
+	0x1e0d, 1048575,
+	0x1e0f, 1048575,
+	0x1e11, 1048575,
+	0x1e13, 1048575,
+	0x1e15, 1048575,
+	0x1e17, 1048575,
+	0x1e19, 1048575,
+	0x1e1b, 1048575,
+	0x1e1d, 1048575,
+	0x1e1f, 1048575,
+	0x1e21, 1048575,
+	0x1e23, 1048575,
+	0x1e25, 1048575,
+	0x1e27, 1048575,
+	0x1e29, 1048575,
+	0x1e2b, 1048575,
+	0x1e2d, 1048575,
+	0x1e2f, 1048575,
+	0x1e31, 1048575,
+	0x1e33, 1048575,
+	0x1e35, 1048575,
+	0x1e37, 1048575,
+	0x1e39, 1048575,
+	0x1e3b, 1048575,
+	0x1e3d, 1048575,
+	0x1e3f, 1048575,
+	0x1e41, 1048575,
+	0x1e43, 1048575,
+	0x1e45, 1048575,
+	0x1e47, 1048575,
+	0x1e49, 1048575,
+	0x1e4b, 1048575,
+	0x1e4d, 1048575,
+	0x1e4f, 1048575,
+	0x1e51, 1048575,
+	0x1e53, 1048575,
+	0x1e55, 1048575,
+	0x1e57, 1048575,
+	0x1e59, 1048575,
+	0x1e5b, 1048575,
+	0x1e5d, 1048575,
+	0x1e5f, 1048575,
+	0x1e61, 1048575,
+	0x1e63, 1048575,
+	0x1e65, 1048575,
+	0x1e67, 1048575,
+	0x1e69, 1048575,
+	0x1e6b, 1048575,
+	0x1e6d, 1048575,
+	0x1e6f, 1048575,
+	0x1e71, 1048575,
+	0x1e73, 1048575,
+	0x1e75, 1048575,
+	0x1e77, 1048575,
+	0x1e79, 1048575,
+	0x1e7b, 1048575,
+	0x1e7d, 1048575,
+	0x1e7f, 1048575,
+	0x1e81, 1048575,
+	0x1e83, 1048575,
+	0x1e85, 1048575,
+	0x1e87, 1048575,
+	0x1e89, 1048575,
+	0x1e8b, 1048575,
+	0x1e8d, 1048575,
+	0x1e8f, 1048575,
+	0x1e91, 1048575,
+	0x1e93, 1048575,
+	0x1e95, 1048575,
+	0x1e9b, 1048517,
+	0x1ea1, 1048575,
+	0x1ea3, 1048575,
+	0x1ea5, 1048575,
+	0x1ea7, 1048575,
+	0x1ea9, 1048575,
+	0x1eab, 1048575,
+	0x1ead, 1048575,
+	0x1eaf, 1048575,
+	0x1eb1, 1048575,
+	0x1eb3, 1048575,
+	0x1eb5, 1048575,
+	0x1eb7, 1048575,
+	0x1eb9, 1048575,
+	0x1ebb, 1048575,
+	0x1ebd, 1048575,
+	0x1ebf, 1048575,
+	0x1ec1, 1048575,
+	0x1ec3, 1048575,
+	0x1ec5, 1048575,
+	0x1ec7, 1048575,
+	0x1ec9, 1048575,
+	0x1ecb, 1048575,
+	0x1ecd, 1048575,
+	0x1ecf, 1048575,
+	0x1ed1, 1048575,
+	0x1ed3, 1048575,
+	0x1ed5, 1048575,
+	0x1ed7, 1048575,
+	0x1ed9, 1048575,
+	0x1edb, 1048575,
+	0x1edd, 1048575,
+	0x1edf, 1048575,
+	0x1ee1, 1048575,
+	0x1ee3, 1048575,
+	0x1ee5, 1048575,
+	0x1ee7, 1048575,
+	0x1ee9, 1048575,
+	0x1eeb, 1048575,
+	0x1eed, 1048575,
+	0x1eef, 1048575,
+	0x1ef1, 1048575,
+	0x1ef3, 1048575,
+	0x1ef5, 1048575,
+	0x1ef7, 1048575,
+	0x1ef9, 1048575,
+	0x1efb, 1048575,
+	0x1efd, 1048575,
+	0x1eff, 1048575,
+	0x1f51, 1048584,
+	0x1f53, 1048584,
+	0x1f55, 1048584,
+	0x1f57, 1048584,
+	0x1fb3, 1048585,
+	0x1fbe, 1041371,
+	0x1fc3, 1048585,
+	0x1fe5, 1048583,
+	0x1ff3, 1048585,
+	0x214e, 1048548,
+	0x2184, 1048575,
+	0x2c61, 1048575,
+	0x2c65, 1037781,
+	0x2c66, 1037784,
+	0x2c68, 1048575,
+	0x2c6a, 1048575,
+	0x2c6c, 1048575,
+	0x2c73, 1048575,
+	0x2c76, 1048575,
+	0x2c81, 1048575,
+	0x2c83, 1048575,
+	0x2c85, 1048575,
+	0x2c87, 1048575,
+	0x2c89, 1048575,
+	0x2c8b, 1048575,
+	0x2c8d, 1048575,
+	0x2c8f, 1048575,
+	0x2c91, 1048575,
+	0x2c93, 1048575,
+	0x2c95, 1048575,
+	0x2c97, 1048575,
+	0x2c99, 1048575,
+	0x2c9b, 1048575,
+	0x2c9d, 1048575,
+	0x2c9f, 1048575,
+	0x2ca1, 1048575,
+	0x2ca3, 1048575,
+	0x2ca5, 1048575,
+	0x2ca7, 1048575,
+	0x2ca9, 1048575,
+	0x2cab, 1048575,
+	0x2cad, 1048575,
+	0x2caf, 1048575,
+	0x2cb1, 1048575,
+	0x2cb3, 1048575,
+	0x2cb5, 1048575,
+	0x2cb7, 1048575,
+	0x2cb9, 1048575,
+	0x2cbb, 1048575,
+	0x2cbd, 1048575,
+	0x2cbf, 1048575,
+	0x2cc1, 1048575,
+	0x2cc3, 1048575,
+	0x2cc5, 1048575,
+	0x2cc7, 1048575,
+	0x2cc9, 1048575,
+	0x2ccb, 1048575,
+	0x2ccd, 1048575,
+	0x2ccf, 1048575,
+	0x2cd1, 1048575,
+	0x2cd3, 1048575,
+	0x2cd5, 1048575,
+	0x2cd7, 1048575,
+	0x2cd9, 1048575,
+	0x2cdb, 1048575,
+	0x2cdd, 1048575,
+	0x2cdf, 1048575,
+	0x2ce1, 1048575,
+	0x2ce3, 1048575,
+	0x2cec, 1048575,
+	0x2cee, 1048575,
+	0x2cf3, 1048575,
+	0x2d27, 1041312,
+	0x2d2d, 1041312,
+	0xa641, 1048575,
+	0xa643, 1048575,
+	0xa645, 1048575,
+	0xa647, 1048575,
+	0xa649, 1048575,
+	0xa64b, 1048575,
+	0xa64d, 1048575,
+	0xa64f, 1048575,
+	0xa651, 1048575,
+	0xa653, 1048575,
+	0xa655, 1048575,
+	0xa657, 1048575,
+	0xa659, 1048575,
+	0xa65b, 1048575,
+	0xa65d, 1048575,
+	0xa65f, 1048575,
+	0xa661, 1048575,
+	0xa663, 1048575,
+	0xa665, 1048575,
+	0xa667, 1048575,
+	0xa669, 1048575,
+	0xa66b, 1048575,
+	0xa66d, 1048575,
+	0xa681, 1048575,
+	0xa683, 1048575,
+	0xa685, 1048575,
+	0xa687, 1048575,
+	0xa689, 1048575,
+	0xa68b, 1048575,
+	0xa68d, 1048575,
+	0xa68f, 1048575,
+	0xa691, 1048575,
+	0xa693, 1048575,
+	0xa695, 1048575,
+	0xa697, 1048575,
+	0xa699, 1048575,
+	0xa69b, 1048575,
+	0xa723, 1048575,
+	0xa725, 1048575,
+	0xa727, 1048575,
+	0xa729, 1048575,
+	0xa72b, 1048575,
+	0xa72d, 1048575,
+	0xa72f, 1048575,
+	0xa733, 1048575,
+	0xa735, 1048575,
+	0xa737, 1048575,
+	0xa739, 1048575,
+	0xa73b, 1048575,
+	0xa73d, 1048575,
+	0xa73f, 1048575,
+	0xa741, 1048575,
+	0xa743, 1048575,
+	0xa745, 1048575,
+	0xa747, 1048575,
+	0xa749, 1048575,
+	0xa74b, 1048575,
+	0xa74d, 1048575,
+	0xa74f, 1048575,
+	0xa751, 1048575,
+	0xa753, 1048575,
+	0xa755, 1048575,
+	0xa757, 1048575,
+	0xa759, 1048575,
+	0xa75b, 1048575,
+	0xa75d, 1048575,
+	0xa75f, 1048575,
+	0xa761, 1048575,
+	0xa763, 1048575,
+	0xa765, 1048575,
+	0xa767, 1048575,
+	0xa769, 1048575,
+	0xa76b, 1048575,
+	0xa76d, 1048575,
+	0xa76f, 1048575,
+	0xa77a, 1048575,
+	0xa77c, 1048575,
+	0xa77f, 1048575,
+	0xa781, 1048575,
+	0xa783, 1048575,
+	0xa785, 1048575,
+	0xa787, 1048575,
+	0xa78c, 1048575,
+	0xa791, 1048575,
+	0xa793, 1048575,
+	0xa797, 1048575,
+	0xa799, 1048575,
+	0xa79b, 1048575,
+	0xa79d, 1048575,
+	0xa79f, 1048575,
+	0xa7a1, 1048575,
+	0xa7a3, 1048575,
+	0xa7a5, 1048575,
+	0xa7a7, 1048575,
+	0xa7a9, 1048575,
+};
+
+} // !namespace
+
+auto totitle(char32_t c) noexcept -> char32_t
+{
+	const char32_t* p;
+
+	p = search(c, totitler, nelem (totitler) / 3, 3);
+
+	if (p && c >= p[0] && c <= p[1])
+		return c + p[2] - 1048576;
+
+	p = search(c, totitles, nelem (totitles) / 2, 2);
+
+	if (p && c == p[0])
+		return c + p[1] - 1048576;
+
+	return c;
+}
+
+void encode(char32_t c, char res[5]) noexcept
+{
+	switch (nbytes_point(c)) {
+	case 1:
+		res[0] = static_cast<char>(c);
+		res[1] = '\0';
+		break;
+	case 2:
+		res[0] = 0xC0 | ((c >> 6)  & 0x1F);
+		res[1] = 0x80 | (c & 0x3F);
+		res[2] = '\0';
+		break;
+	case 3:
+		res[0] = 0xE0 | ((c >> 12) & 0xF );
+		res[1] = 0x80 | ((c >> 6)  & 0x3F);
+		res[2] = 0x80 | (c & 0x3F);
+		res[3] = '\0';
+		break;
+	case 4:
+		res[0] = 0xF0 | ((c >> 18) & 0x7 );
+		res[1] = 0x80 | ((c >> 12) & 0x3F);
+		res[2] = 0x80 | ((c >> 6)  & 0x3F);
+		res[3] = 0x80 | (c & 0x3F);
+		res[4] = '\0';
+		break;
+	default:
+		break;
+	}
+}
+
+void decode(char32_t& c, const char* res) noexcept
+{
+	c = 0;
+
+	switch (nbytes_utf8(res[0])) {
+	case 1:
+		c = res[0];
+		break;
+	case 2:
+		c =  (res[0] & 0x1f) << 6;
+		c |= (res[1] & 0x3f);
+		break;
+	case 3:
+		c =  (res[0] & 0x0f) << 12;
+		c |= (res[1] & 0x3f) << 6;
+		c |= (res[2] & 0x3f);
+		break;
+	case 4:
+		c =  (res[0] & 0x07) << 16;
+		c |= (res[1] & 0x3f) << 12;
+		c |= (res[2] & 0x3f) << 6;
+		c |= (res[3] & 0x3f);
+	default:
+		break;
+	}
+}
+
+auto nbytes_utf8(char c) noexcept -> int
+{
+	if (static_cast<unsigned char>(c) <= 127)
+		return 1;
+	if ((c & 0xE0) == 0xC0)
+		return 2;
+	if ((c & 0xF0) == 0xE0)
+		return 3;
+	if ((c & 0xF8) == 0xF0)
+		return 4;
+
+	return -1;
+}
+
+auto nbytes_point(char32_t c) noexcept -> int
+{
+	if (c <= 0x7F)
+		return 1;
+	if (c <= 0x7FF)
+		return 2;
+	if (c <= 0xFFFF)
+		return 3;
+	if (c <= 0x1FFFFF)
+		return 4;
+
+	return -1;
+}
+
+auto length(std::string_view str) -> unsigned
+{
+	unsigned total = 0;
+
+	for_each(str, [&] (auto) {
+		++ total;
+	});
+
+	return total;
+}
+
+auto to_utf8(std::u32string_view array) -> std::string
+{
+	std::string res;
+
+	for (size_t i = 0; i < array.size(); ++i) {
+		char tmp[5];
+		int size = nbytes_point(array[i]);
+
+		if (size < 0)
+			throw std::invalid_argument("invalid sequence");
+
+		encode(array[i], tmp);
+		res.insert(res.length(), tmp);
+	}
+
+	return res;
+}
+
+auto to_utf32(std::string_view str) -> std::u32string
+{
+	std::u32string res;
+
+	for_each(str, [&] (char32_t code) {
+		res.push_back(code);
+	});
+
+	return res;
+}
+
+auto toupper(std::u32string_view str) -> std::u32string
+{
+	std::u32string res(str);
+
+	for (size_t i = 0; i < str.size(); ++i)
+		res[i] = toupper(str[i]);
+
+	return res;
+}
+
+auto toupper(std::string_view str) -> std::string
+{
+	std::string res;
+	char buffer[5];
+
+	for_each(str, [&] (auto code) {
+		encode(toupper(code), buffer);
+		res += buffer;
+	});
+
+	return res;
+}
+
+auto tolower(std::u32string_view str) -> std::u32string
+{
+	std::u32string ret(str);
+
+	for (size_t i = 0; i < str.size(); ++i)
+		ret[i] = tolower(str[i]);
+
+	return ret;
+}
+
+auto tolower(std::string_view str) -> std::string
+{
+	std::string res;
+	char buffer[5];
+
+	for_each(str, [&] (auto code) {
+		encode(tolower(code), buffer);
+		res += buffer;
+	});
+
+	return res;
+}
+
+} // !mlk::unicode
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/unicode.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,236 @@
+/*
+ * unicode.hpp -- UTF-8 to UTF-32 conversions and various operations
+ *
+ * Copyright (c) 2013-2018 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_UNICODE_HPP
+#define MLK_CORE_UNICODE_HPP
+
+/**
+ * \file unicode.hpp
+ * \brief UTF-8 to UTF-32 conversions
+ * \author David Demelier <markand@malikania.fr>
+ * \warning These files are auto-generated!
+ */
+
+#include <stdexcept>
+#include <string>
+#include <string_view>
+
+/**
+ * \brief Unicode namespace.
+ */
+namespace mlk::unicode {
+
+/**
+ * Encode the unicode code point into multibyte string.
+ *
+ * \param point the unicode code point
+ * \param res the output buffer
+ */
+void encode(char32_t point, char res[5]) noexcept;
+
+/**
+ * Decode the multibyte buffer into an unicode code point.
+ *
+ * \param c the code point destination
+ * \param res the multibyte string.
+ */
+void decode(char32_t& c, const char* res) noexcept;
+
+/**
+ * Get the number of bytes for the first multi byte character from a
+ * utf-8 string.
+ *
+ * This can be used to iterate a valid UTF-8 string to jump to the next
+ * real character.
+ *
+ * \param c the first multi byte character
+ * \return the number of bytes [1-4] or -1 if invalid
+ */
+auto nbytes_utf8(char c) noexcept -> int;
+
+/**
+ * Get the number of bytes for the unicode point.
+ *
+ * \param point the unicode point
+ * \return the number of bytes [1-4] or -1 if invalid
+ */
+auto nbytes_point(char32_t point) noexcept -> int;
+
+/**
+ * Get real number of character in a string.
+ *
+ * \param str the string
+ * \return the length
+ * \throw std::invalid_argument on invalid sequence
+ */
+auto length(std::string_view str) -> unsigned;
+
+/**
+ * Iterate over all real characters in the UTF-8 string.
+ *
+ * The function must have the following signature:
+ *  void f(char ch)
+ *
+ * \param str the UTF-8 string
+ * \param function the function callback
+ * \throw std::invalid_argument on invalid sequence
+ */
+template <typename Func>
+void for_each(std::string_view str, Func function)
+{
+	for (size_t i = 0; i < str.size(); ) {
+		char32_t point = 0;
+		int size = nbytes_utf8(str[i]);
+
+		if (size < 0)
+			throw std::invalid_argument("invalid sequence");
+
+		decode(point, str.data() + i);
+		function(point);
+
+		i += size;
+	}
+}
+
+/**
+ * Convert a UTF-32 string to UTF-8 string.
+ *
+ * \param array the UTF-32 string
+ * \return the UTF-8 string
+ * \throw std::invalid_argument on invalid sequence
+ */
+auto to_utf8(std::u32string_view array) -> std::string;
+
+/**
+ * Convert a UTF-8 string to UTF-32 string.
+ *
+ * \param str the UTF-8 string
+ * \return the UTF-32 string
+ * \throw std::invalid_argument on invalid sequence
+ */
+auto to_utf32(std::string_view str) -> std::u32string;
+
+/**
+ * Check if the unicode character is space.
+ *
+ * \param c the character
+ * \return true if space
+ */
+auto isspace(char32_t c) noexcept -> bool;
+
+/**
+ * Check if the unicode character is digit.
+ *
+ * \param c the character
+ * \return true if digit
+ */
+auto isdigit(char32_t c) noexcept -> bool;
+
+/**
+ * Check if the unicode character is alpha category.
+ *
+ * \param c the character
+ * \return true if alpha
+ */
+auto isalpha(char32_t c) noexcept -> bool;
+
+/**
+ * Check if the unicode character is upper case.
+ *
+ * \param c the character
+ * \return true if upper case
+ */
+auto isupper(char32_t c) noexcept -> bool;
+
+/**
+ * Check if the unicode character is lower case.
+ *
+ * \param c the character
+ * \return true if lower case
+ */
+auto islower(char32_t c) noexcept -> bool;
+
+/**
+ * Check if the unicode character is title case.
+ *
+ * \param c the character
+ * \return true if title case
+ */
+auto istitle(char32_t c) noexcept -> bool;
+
+/**
+ * Convert to upper case.
+ *
+ * \param c the character
+ * \return the upper case character
+ */
+auto toupper(char32_t c) noexcept -> char32_t;
+
+/**
+ * Convert to lower case.
+ *
+ * \param c the character
+ * \return the lower case character
+ */
+auto tolower(char32_t c) noexcept -> char32_t;
+
+/**
+ * Convert to title case.
+ *
+ * \param c the character
+ * \return the title case character
+ */
+auto totitle(char32_t c) noexcept -> char32_t;
+
+/**
+ * Convert the UTF-32 string to upper case.
+ *
+ * \param str the string
+ * \return the upper case string
+ */
+auto toupper(std::u32string_view str) -> std::u32string;
+
+/**
+ * Convert the UTF-8 string to upper case.
+ *
+ * \param str the string
+ * \return the upper case string
+ * \warning very slow at the moment
+ */
+auto toupper(std::string_view str) -> std::string;
+
+/**
+ * Convert the UTF-32 string to lower case.
+ *
+ * \param str the string
+ * \return the lower case string
+ */
+auto tolower(std::u32string_view str) -> std::u32string;
+
+/**
+ * Convert the UTF-8 string to lower case.
+ *
+ * \param str the string
+ * \return the lower case string
+ * \warning very slow at the moment
+ */
+auto tolower(std::string_view str) -> std::string;
+
+} // !mlk::unicode
+
+#endif // !MLK_CORE_UNICODE_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/util.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,443 @@
+/*
+ * util.cpp -- malikania utilities
+ *
+ * Copyright (c) 2013-2018 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 <cassert>
+#include <climits>
+#include <string>
+#include <stdexcept>
+
+#if defined(__linux__)
+#  include <unistd.h>
+
+#  include <cerrno>
+#  include <cstring>
+#  include <stdexcept>
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__) || defined(__OpenBSD__)
+#  if defined(__NetBSD__)
+#    include <sys/param.h>
+#  else
+#    include <sys/types.h>
+#  endif
+
+#  if defined(__OpenBSD__)
+#    include <unistd.h>
+#  endif
+
+#  include <sys/sysctl.h>
+
+#  include <array>
+#  include <cerrno>
+#  include <climits>
+#  include <cstddef>
+#  include <cstdlib>
+#  include <cstring>
+#  include <stdexcept>
+#elif defined(__APPLE__)
+#  include <cerrno>
+#  include <cstring>
+#  include <libproc.h>
+#  include <unistd.h>
+#elif defined(_WIN32)
+#  include <Windows.h>
+#endif
+
+#include <boost/filesystem.hpp>
+
+#include "util.hpp"
+#include "size.hpp"
+
+namespace {
+
+#if defined(__linux__)
+
+std::string executable_path()
+{
+    std::string result;
+
+    result.resize(2048, '\0');
+
+    auto size = readlink("/proc/self/exe", &result[0], 2048);
+
+    if (size < 0) {
+        throw std::runtime_error(std::strerror(errno));
+    }
+
+    result.resize(size, '\0');
+
+    return result;
+}
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+
+std::string executable_path()
+{
+    std::array<int, 4> mib{ { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 } };
+    std::string result;
+    std::size_t size = PATH_MAX + 1;
+
+    result.resize(size, '\0');
+
+    if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) {
+        throw std::runtime_error(std::strerror(errno));
+    }
+
+    result.resize(size, '\0');
+
+    return result;
+}
+
+#elif defined(__APPLE__)
+
+std::string executable_path()
+{
+    std::string result;
+    std::size_t size = PROC_PIDPATHINFO_MAXSIZE;
+
+    result.resize(size, '\0');
+
+    if ((size = proc_pidpath(getpid(), &result[0], size)) == 0) {
+        throw std::runtime_error(std::strerror(errno));
+    }
+
+    result.resize(size, '\0');
+
+    return result;
+}
+
+#elif defined(_WIN32)
+
+std::string executable_path()
+{
+    std::string result;
+    std::size_t size = PATH_MAX;
+
+    result.resize(size, '\0');
+
+    if (!(size = GetModuleFileNameA(nullptr, &result[0], size))) {
+        throw std::runtime_error("GetModuleFileName error");
+    }
+
+    result.resize(size, '\0');
+
+    return result;
+}
+
+#elif defined(__NetBSD__)
+
+std::string executable_path()
+{
+        std::string result;
+
+#if defined(KERN_PROC_PATHNAME)
+        std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
+        std::size_t size = MAXPATHLEN;
+
+        result.resize(size, '\0');
+
+        if (sysctl(mib.data(), 4, &result[0], &size, nullptr, 0) < 0) {
+                throw std::runtime_error(std::strerror(errno));
+        }
+
+        result.resize(size, '\0');
+#else
+        result.resize(2048, '\0');
+
+        auto size = readlink("/proc/curproc/exe", &result[0], 2048);
+
+        if (size < 0) {
+                throw std::runtime_error(std::strerror(errno));
+        }
+
+        result.resize(size, '\0');
+#endif
+
+        return result;
+}
+
+#elif defined(__OpenBSD__)
+
+std::string executable_path()
+{
+    char **paths, *path;
+    std::string result;
+    std::size_t len;
+    std::array<int, 4> mib{ CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV };
+
+    if (sysctl(mib.data(), 4, nullptr, &len, nullptr, 0) == -1) {
+        throw std::runtime_error(std::strerror(errno));
+    }
+    if ((paths = static_cast<char **>(malloc(len))) == nullptr) {
+        throw std::runtime_error(std::strerror(errno));
+    }
+
+    sysctl(mib.data(), 4, paths, &len, nullptr, 0);
+
+    if ((path = static_cast<char *>(std::malloc(PATH_MAX + 1))) == nullptr) {
+        std::free(paths);
+        throw std::runtime_error(std::strerror(errno));
+    }
+
+    realpath(paths[0], path);
+    result = path;
+
+    std::free(paths);
+    std::free(path);
+
+    return result;
+}
+
+#else
+
+std::string executable_path()
+{
+    // Not supported.
+    return "";
+}
+
+#endif
+
+} // !namespace
+
+namespace mlk {
+
+namespace util {
+
+std::vector<std::string> split(const std::string &list, const std::string &delimiters, int max)
+{
+    std::vector<std::string> result;
+    std::size_t next = -1, current;
+    int count = 1;
+    bool finished = false;
+
+    if (list.empty()) {
+        return result;
+    }
+
+    do {
+        std::string val;
+
+        current = next + 1;
+        next = list.find_first_of(delimiters, current);
+
+        // split max, get until the end
+        if (max >= 0 && count++ >= max) {
+            val = list.substr(current, std::string::npos);
+            finished = true;
+        } else {
+            val = list.substr(current, next - current);
+            finished = next == std::string::npos;
+        }
+
+        result.push_back(val);
+    } while (!finished);
+
+    return result;
+}
+
+std::string basedir() noexcept
+{
+    std::string result = "./";
+
+    try {
+        boost::filesystem::path tmp = executable_path();
+
+        tmp = tmp.parent_path();    // remove executable name
+        tmp = tmp.parent_path();    // remove "bin"
+        result = tmp.string();
+    } catch (...) {
+        // TODO: add log.
+    }
+
+    return result;
+}
+
+/*
+ * json utilities
+ * ------------------------------------------------------------------
+ */
+
+namespace json {
+
+/*
+ * XXX: until json get a non-throwing function to check if a pointer exists,
+ * use this to check for a value at the given pointer or throw a
+ * property_error.
+ */
+nlohmann::json require(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& pointer,
+                       const nlohmann::json::value_t expected)
+{
+    assert(object.is_object());
+
+    nlohmann::json value;
+
+    try {
+        value = object.at(pointer);
+    } catch (...) {
+        throw property_error(object, pointer, nlohmann::json::value_t::null, expected);
+    }
+
+    if (value.type() != expected) {
+        throw property_error(object, pointer, value.type(), expected);
+    }
+
+    return value;
+}
+
+nlohmann::json get(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const nlohmann::json::value_t expected,
+                   const nlohmann::json def)
+{
+    assert(object.is_object());
+
+    nlohmann::json value;
+
+    try {
+        value = object.at(pointer);
+    } catch (...) {
+    }
+
+    if (value.type() != expected)
+        value = def;
+
+    return value;
+}
+
+nlohmann::json require_array(const nlohmann::json& object,
+                             const nlohmann::json::json_pointer& pointer)
+{
+    return require(object, pointer, nlohmann::json::value_t::array);
+}
+
+bool require_bool(const nlohmann::json& object,
+                  const nlohmann::json::json_pointer& pointer)
+{
+    return require(object, pointer, nlohmann::json::value_t::boolean);
+}
+
+int require_int(const nlohmann::json& object,
+                const nlohmann::json::json_pointer& pointer)
+{
+    nlohmann::json v;
+
+    try {
+        v = object.at(pointer);
+
+        if (v.is_number_integer())
+            return v.get<int>();
+        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
+            return static_cast<int>(v.get<unsigned>());
+    } catch (...) {
+    }
+
+    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_integer);
+}
+
+nlohmann::json require_object(const nlohmann::json& object,
+                              const nlohmann::json::json_pointer& pointer)
+{
+    return require(object, pointer, nlohmann::json::value_t::object);
+}
+
+std::string require_string(const nlohmann::json& object,
+                           const nlohmann::json::json_pointer& pointer)
+{
+    return require(object, pointer, nlohmann::json::value_t::string);
+}
+
+unsigned require_uint(const nlohmann::json& object,
+                      const nlohmann::json::json_pointer& pointer)
+{
+    nlohmann::json v;
+
+    try {
+        v = object.at(pointer);
+
+        if (v.is_number_unsigned())
+            return v.get<unsigned>();
+        else if (v.is_number_integer() && v.get<int>() >= 0)
+            return static_cast<unsigned>(v.get<int>());
+    } catch (...) {
+    }
+
+    throw property_error(object, pointer, v.type(), nlohmann::json::value_t::number_unsigned);
+}
+
+mlk::size require_size(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& index)
+{
+    using pointer = nlohmann::json::json_pointer;
+
+    return {
+        require_uint(object, pointer(index.to_string() + "/width")),
+        require_uint(object, pointer(index.to_string() + "/height"))
+    };
+}
+
+int get_int(const nlohmann::json &object,
+            const nlohmann::json::json_pointer &pointer,
+            int value) noexcept
+{
+    try {
+        auto v = object.at(pointer);
+
+        if (v.is_number_integer())
+            value = v.get<int>();
+        else if (v.is_number_unsigned() && v.get<unsigned>() <= static_cast<unsigned>(INT_MAX))
+            value = static_cast<int>(v.get<unsigned>());
+    } catch (...) {
+    }
+
+    return value;
+}
+
+unsigned get_uint(const nlohmann::json &object,
+                  const nlohmann::json::json_pointer& pointer,
+                  unsigned value) noexcept
+{
+    try {
+        auto v = object.at(pointer);
+
+        if (v.is_number_unsigned())
+            value = v.get<unsigned>();
+        else if (v.is_number_integer() && v.get<int>() >= 0)
+            value = static_cast<unsigned>(v.get<int>());
+    } catch (...) {
+    }
+
+    return value;
+}
+
+mlk::size get_size(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const mlk::size& def) noexcept
+{
+    using json_pointer = nlohmann::json::json_pointer;
+
+    return {
+        get_uint(object, json_pointer(pointer.to_string() + "/width"), def.width),
+        get_uint(object, json_pointer(pointer.to_string() + "/height"), def.height)
+    };
+}
+
+} // !json
+
+} // !util
+
+} // !mlk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libmlk/malikania/util.hpp	Thu Oct 25 21:36:14 2018 +0200
@@ -0,0 +1,331 @@
+/*
+ * util.hpp -- malikania utilities
+ *
+ * Copyright (c) 2013-2018 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 MALIKANIA_UTIL_HPP
+#define MALIKANIA_UTIL_HPP
+
+/**
+ * \file util.hpp
+ * \brief Some utilities
+ */
+
+#include <algorithm>
+#include <exception>
+#include <string>
+#include <vector>
+
+#include "json.hpp"
+#include "size.hpp"
+
+namespace mlk {
+
+/**
+ * \brief Utilities.
+ */
+namespace util {
+
+/**
+ * Split a line into a list of tokens.
+ *
+ * If max is less than 0, split as much as possible, otherwise split at most
+ * the number of specified tokens.
+ *
+ * \param line the line to split
+ * \param delim the list of delimiters allowed
+ * \param max the maximum number of tokens
+ * \return the list
+ */
+std::vector<std::string> split(const std::string& line, const std::string& delim, int max = -1);
+
+/**
+ * Get the base directory of the current executable.
+ *
+ * \return the base directory or empty if impossible to get
+ */
+std::string basedir() noexcept;
+
+/**
+ * Clamp the value between low and high.
+ *
+ * \param value the value
+ * \param low the minimum value
+ * \param high the maximum value
+ * \return the value between minimum and maximum
+ */
+template <typename T>
+constexpr T clamp(T value, T low, T high) noexcept
+{
+    return (value < high) ? std::max(value, low) : std::min(value, high);
+}
+
+/**
+ * \brief Utilities for networking.
+ */
+namespace net {
+
+/**
+ * Shortcut for util::split(line, " ", max);
+ *
+ * \param line the line
+ * \param max the maximum of tokens to split
+ * \return the list
+ */
+inline std::vector<std::string> split(const std::string& line, int max = -1)
+{
+    return util::split(line, " ", max);
+}
+
+} // !net
+
+namespace json {
+
+/**
+ * \brief Indicates a property error in an object.
+ */
+class property_error : public std::exception {
+private:
+    nlohmann::json m_object;
+    nlohmann::json::json_pointer m_pointer;
+    nlohmann::json::value_t m_type;
+    nlohmann::json::value_t m_expected;
+    std::string m_message;
+
+public:
+    /**
+     * Construct the property error.
+     *
+     * \param object the faulty object
+     * \param pointer the property path
+     * \param type the found type
+     * \param expected the expected type
+     */
+    inline property_error(nlohmann::json object,
+                          nlohmann::json::json_pointer pointer,
+                          nlohmann::json::value_t type,
+                          nlohmann::json::value_t expected) noexcept
+        : m_object(std::move(object))
+        , m_pointer(std::move(pointer))
+        , m_type(type)
+        , m_expected(expected)
+    {
+        m_message += "invalid '" + m_pointer.to_string() + "' property ";
+        m_message += "(" + nlohmann::json(expected).type_name();
+        m_message += " expected, got ";
+        m_message += nlohmann::json(type).type_name() + ")";
+    }
+
+    /**
+     * Get the faulty object.
+     *
+     * return the object
+     */
+    inline const nlohmann::json& object() const noexcept
+    {
+        return m_object;
+    }
+
+    /**
+     * Get the pointer to the property.
+     *
+     * \return the pointer
+     */
+    inline const nlohmann::json::json_pointer& pointer() const noexcept
+    {
+        return m_pointer;
+    }
+
+    /**
+     * Get the actual type.
+     *
+     * \return the type
+     */
+    inline nlohmann::json::value_t type() const noexcept
+    {
+        return m_type;
+    }
+
+    /**
+     * Get the expected type.
+     *
+     * \return the type
+     */
+    inline nlohmann::json::value_t expected() const noexcept
+    {
+        return m_expected;
+    }
+
+    /**
+     * Get a human formatted message.
+     *
+     * \return the message
+     */
+    const char* what() const noexcept
+    {
+        return m_message.c_str();
+    }
+};
+
+/**
+ * Require a value at the given pointer, if the value does not match the
+ * expected type, an exception is thrown.
+ *
+ * If the property does not exists, a property_error with a null found type is
+ * thrown.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \param expected the expected type
+ * \throw property_error on errors
+ */
+nlohmann::json require(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& pointer,
+                       const nlohmann::json::value_t expected);
+
+/**
+ * Require an array at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+nlohmann::json require_array(const nlohmann::json& object,
+                             const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require a boolean at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+bool require_bool(const nlohmann::json& object,
+                  const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require an integer at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+int require_int(const nlohmann::json& object,
+                const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require an object at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+nlohmann::json require_object(const nlohmann::json& object,
+                              const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require a string at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+std::string require_string(const nlohmann::json& object,
+                           const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require an unsigned integer at the given pointer.
+ *
+ * \pre object.is_object()
+ * \param object the object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+unsigned require_uint(const nlohmann::json& object,
+                      const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Require a size object at the given pointer.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \return the value
+ * \throw property_error on errors
+ */
+mlk::size require_size(const nlohmann::json& object,
+                       const nlohmann::json::json_pointer& pointer);
+
+/**
+ * Get an integer at the given pointer or default value if not present or
+ * invalid.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value
+ * \return an integer
+ */
+int get_int(const nlohmann::json& object,
+            const nlohmann::json::json_pointer& pointer,
+            int def = 0) noexcept;
+
+/**
+ * Get an unsigned integer at the given pointer or default value if not present
+ * or invalid.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value
+ * \return an integer
+ */
+unsigned get_uint(const nlohmann::json& object,
+                  const nlohmann::json::json_pointer& pointer,
+                  unsigned def = 0) noexcept;
+
+/**
+ * Get a size object at the given pointer or a default value.
+ *
+ * \pre object.is_object
+ * \param object the json object
+ * \param pointer the pointer to the property
+ * \param def the default value to return in case of error
+ * \return a size or a default value
+ */
+mlk::size get_size(const nlohmann::json& object,
+                   const nlohmann::json::json_pointer& pointer,
+                   const mlk::size& def = {}) noexcept;
+
+} // !json
+
+} // !util
+
+} // !mlk
+
+#endif // !MALIKANIA_UTIL_HPP
--- a/libserver/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#
-# CMakeLists.txt -- CMake build system for malikania
-#
-# Copyright (c) 2013-2018 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.
-#
-
-project(libmlk-server)
-
-find_package(Threads REQUIRED)
-
-set(
-	HEADERS
-	${libmlk-server_SOURCE_DIR}/malikania/server/client.hpp
-	${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.hpp
-	${libmlk-server_SOURCE_DIR}/malikania/server/server.hpp
-)
-
-set(
-	SOURCES
-	${libmlk-server_SOURCE_DIR}/malikania/server/client.cpp
-	${libmlk-server_SOURCE_DIR}/malikania/server/net/auth_handler.cpp
-	${libmlk-server_SOURCE_DIR}/malikania/server/server.cpp
-)
-
-set(
-	LIBRARIES
-	OpenSSL::Crypto
-	OpenSSL::SSL
-	Threads::Threads
-	libmlk-common
-	libmlk-db
-)
-
-if (CMAKE_SYSTEM_NAME MATCHES Windows)
-	list(APPEND LIBRARIES mswsock)
-endif ()
-
-malikania_define_library(
-	TARGET libmlk-server
-	SOURCES ${HEADERS} ${SOURCES}
-	LIBRARIES ${LIBRARIES}
-	PUBLIC_INCLUDES
-		${libmlk-server_SOURCE_DIR}
-	PRIVATE_INCLUDES
-		${libmlk-server_SOURCE_DIR}/malikania/server
-)
--- a/libserver/malikania/server/client.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-/*
- * client.cpp -- client connected to the server
- *
- * Copyright (c) 2013-2018 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 <cassert>
-#include <iostream>
-
-#include "client.hpp"
-#include "server.hpp"
-
-namespace mlk::server {
-
-void client::handle_read(std::error_code code, std::size_t xfer)
-{
-    // TODO: use fixed size stream + verify exceed.
-    if (code)
-        server_.handle_disconnect(shared_from_this());
-    else {
-        std::string message(
-            boost::asio::buffers_begin(input_.data()),
-            boost::asio::buffers_begin(input_.data()) + xfer - 4
-        );
-
-        // Remove early in case of errors.
-        input_.consume(xfer);
-
-        try {
-            server_.handle_read(shared_from_this(), nlohmann::json::parse(message));
-        } catch (const std::exception& ex) {
-            std::cerr << ex.what() << std::endl;
-        }
-
-        do_read();
-    }
-}
-
-void client::handle_write(std::error_code code, std::size_t)
-{
-    if (code) {
-        server_.handle_disconnect(shared_from_this());
-    } else {
-        output_.pop_front();
-
-        if (!output_.empty()) {
-            do_write();
-        } else if (state_ == state::closing) {
-            server_.handle_disconnect(shared_from_this());
-        }
-    }
-}
-
-void client::do_read()
-{
-    const auto self = shared_from_this();
-
-    boost::asio::async_read_until(socket_, input_, "\r\n\r\n", [self] (auto code, auto xfer) {
-        self->handle_read(code, xfer);
-    });
-}
-
-void client::do_write()
-{
-    assert(!output_.empty());
-
-    const auto self = shared_from_this();
-
-    boost::asio::async_write(socket_, boost::asio::buffer(output_[0]), [self] (auto code, auto xfer) {
-        self->handle_write(code, xfer);
-    });
-}
-
-void client::handshake()
-{
-    const auto self = shared_from_this();
-
-    socket_.async_handshake(boost::asio::ssl::stream_base::server, [self] (auto code) {
-        if (code) {
-            std::cerr << "handshake failure: " << code << std::endl;
-        } else {
-            self->read();
-        }
-    });
-}
-
-void client::read()
-{
-    do_read();
-}
-
-client::client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context)
-    : server_(server)
-    , socket_(service, context)
-{
-}
-
-auto client::get_state() const noexcept -> state
-{
-    return state_;
-}
-
-void client::set_state(state state) noexcept
-{
-    state_ = state;
-}
-
-auto client::get_account() const noexcept -> const db::account&
-{
-    assert(state_ == state::ready);
-
-    return *account_;
-}
-
-auto client::get_account() noexcept -> db::account&
-{
-    assert(state_ == state::ready);
-
-    return *account_;
-}
-
-void client::set_account(db::account account) noexcept
-{
-    account_ = std::move(account);
-}
-
-void client::send(nlohmann::json message)
-{
-    assert(message.is_object());
-
-    if (state_ == state::closing) {
-        return;
-    }
-
-    const auto in_progress = !output_.empty();
-
-    output_.push_back(message.dump() + "\r\n\r\n");
-
-    if (!in_progress) {
-        do_write();
-    }
-}
-
-void client::ok(std::string cmd, nlohmann::json message)
-{
-    assert(message.is_null() || message.is_object());
-
-    if (!message.is_object()) {
-        message = nlohmann::json::object();
-    }
-
-    message["command"] = std::move(cmd);
-    message["status"] = true;
-
-    send(std::move(message));
-}
-
-void client::error(std::string cmd, std::error_code code)
-{
-    send({
-        { "command",    std::move(cmd)  },
-        { "status",     code.value()    },
-        { "error",      code.message()  }
-    });
-}
-
-void client::fatal(std::string cmd, std::error_code code)
-{
-    error(std::move(cmd), std::move(code));
-    state_ = state::closing;
-}
-
-} // !mlk::server
--- a/libserver/malikania/server/client.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,184 +0,0 @@
-/*
- * client.hpp -- client connected to the server
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_CLIENT_HPP
-#define MALIKANIA_SERVER_CLIENT_HPP
-
-/**
- * \file client.hpp
- * \brief Client connected to the server.
- */
-
-#include <cstdint>
-#include <deque>
-#include <memory>
-#include <optional>
-#include <sstream>
-#include <string>
-#include <system_error>
-
-#include <boost/asio.hpp>
-#include <boost/asio/ssl.hpp>
-
-#include <json.hpp>
-
-#include "db/account.hpp"
-
-namespace mlk::server {
-
-class server;
-
-/**
- * \brief Client connected to the server.
- */
-class client : public std::enable_shared_from_this<client> {
-public:
-    friend class server;
-
-    /**
-     * \brief Describe client current state
-     */
-    enum class state : std::uint8_t {
-        /**
-         * The client is authenticating.
-         */
-        authenticating,
-
-        /**
-         * The client is ready for I/O
-         */
-        ready,
-
-        /**
-         * The client is closing.
-         *
-         * No more messages are accepted for output, the pending queue is sent
-         * to the client and then disconnected.
-         */
-        closing
-    };
-
-private:
-    server& server_;
-    state state_{state::authenticating};
-
-    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
-    boost::asio::streambuf input_;
-    std::deque<std::string> output_;
-    std::optional<db::account> account_;
-
-    void handle_read(std::error_code, std::size_t);
-    void handle_write(std::error_code, std::size_t);
-
-    void do_read();
-    void do_write();
-
-    void handshake();
-    void read();
-
-public:
-    /**
-     * Create the client in the associated server.
-     *
-     * \param server the server object
-     * \param service the main loop service
-     * \param context the ssl context
-     */
-    client(server& server, boost::asio::io_service& service, boost::asio::ssl::context& context);
-
-    /**
-     * Get the client state.
-     *
-     * \return the state
-     */
-    auto get_state() const noexcept -> state;
-
-    /**
-     * Set the client state.
-     *
-     * \param new_state the state
-     */
-    void set_state(state state) noexcept;
-
-    /**
-     * Get the underlying account database object.
-     *
-     * \pre get_state() == ready
-     * \return the account
-     */
-    auto get_account() const noexcept -> const db::account&;
-
-    /**
-     * Overloaded function.
-     *
-     * \pre get_state() == ready
-     * \return the account
-     */
-    auto get_account() noexcept -> db::account&;
-
-    /**
-     * Set the client account.
-     *
-     * \param account the account
-     */
-    void set_account(db::account account) noexcept;
-
-    /**
-     * Send a command and its arguments.
-     *
-     * \pre !cmd.empty()
-     * \param message the message to send
-     */
-    void send(nlohmann::json message);
-
-    /**
-     * Send successful command result.
-     *
-     * \pre !cmd.empty()
-     * \pre args.is_object() || args.is_object()
-     * \param cmd the command name
-     * \param args the extra data
-     */
-    void ok(std::string cmd, nlohmann::json args = nullptr);
-
-    /**
-     * Send a error command result.
-     *
-     * \pre !cmd.empty()
-     * \pre code
-     * \param cmd the command name
-     * \param code the error code
-     */
-    void error(std::string cmd, std::error_code code);
-
-    /**
-     * Send a fatal error result.
-     *
-     * The client state is changed to closing.
-     *
-     * \pre !cmd.empty()
-     * \pre code
-     * \param cmd the command name
-     * \param code the error code
-     */
-    void fatal(std::string cmd, std::error_code code);
-};
-
-} // !mlk::server
-
-#endif // !MALIKANIA_SERVER_CLIENT_HPP
--- a/libserver/malikania/server/hash.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,262 +0,0 @@
-/*
- * hash.hpp -- hash functions
- *
- * Copyright (c) 2013-2016 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 MALIKANIA_SERVER_HASH_HPP
-#define MALIKANIA_SERVER_HASH_HPP
-
-/**
- * \file hash.hpp
- * \brief Hash functions.
- * \author David Demelier <markand@malikania.fr>
- */
-
-/**
- * \brief Define buffer size.
- */
-#if !defined(HASH_BUFFER_SIZE)
-#   define HASH_BUFFER_SIZE 2048
-#endif
-
-#include <cassert>
-#include <istream>
-#include <iterator>
-#include <sstream>
-#include <string>
-
-#include <openssl/sha.h>
-#include <openssl/md5.h>
-
-/**
- * \brief Hash namespace.
- */
-namespace hash {
-
-/**
- * \cond HASH_HIDDEN_SYMBOLS
- */
-
-namespace detail {
-
-template <typename Context>
-using init_func = int (*)(Context *);
-
-template <typename Context>
-using update_func = int (*)(Context *, const void *, size_t);
-
-template <typename Context>
-using final_func = int (*)(unsigned char *, Context *);
-
-template <typename Context, size_t Length, typename InputIt>
-inline std::string convert(InputIt it,
-                           InputIt end,
-                           init_func<Context> init,
-                           update_func<Context> update,
-                           final_func<Context> finalize)
-{
-    unsigned char digest[Length] = { 0 };
-    char hash[Length * 2 + 1];
-    char buf[HASH_BUFFER_SIZE];
-
-    Context ctx;
-    init(&ctx);
-
-    while (it != end) {
-        unsigned i;
-
-        for (i = 0; it != end && i < HASH_BUFFER_SIZE; ++i) {
-            buf[i] = *it++;
-        }
-
-        update(&ctx, buf, i);
-    }
-
-    finalize(digest, &ctx);
-
-    for (unsigned long i = 0; i < Length; i++) {
-        std::snprintf(&hash[i * 2], 2 + 1, "%02x", static_cast<unsigned>(digest[i]));
-    }
-
-    return std::string(hash);
-}
-
-} // !namespace
-
-/**
- * \endcond
- */
-
-/**
- * \brief The scheme to use.
- */
-enum class scheme {
-    md5,        //!< MD5
-    sha1,       //!< SHA-1
-    sha256,     //!< SHA-256
-    sha512      //!< SHA-512
-};
-
-/**
- * Generic function.
- *
- * \param scheme the scheme to use
- * \param it the first character
- * \param end the last character
- * \return the string
- */
-template <typename InputIt>
-inline std::string to_string(scheme scheme, InputIt it, InputIt end)
-{
-    assert(scheme >= scheme::md5 && scheme <= scheme::sha512);
-
-    std::string result;
-
-    switch (scheme) {
-    case scheme::md5:
-        result = detail::convert<MD5_CTX, MD5_DIGEST_LENGTH>(it, end, MD5_Init, MD5_Update, MD5_Final);
-        break;
-    case scheme::sha1:
-        result = detail::convert<SHA_CTX, SHA_DIGEST_LENGTH>(it, end, SHA1_Init, SHA1_Update, SHA1_Final);;
-        break;
-    case scheme::sha256:
-        result = detail::convert<SHA256_CTX, SHA256_DIGEST_LENGTH>(it, end, SHA256_Init, SHA256_Update, SHA256_Final);
-        break;
-    case scheme::sha512:
-        result = detail::convert<SHA512_CTX, SHA512_DIGEST_LENGTH>(it, end, SHA512_Init, SHA512_Update, SHA512_Final);
-        break;
-    default:
-        break;
-    }
-
-    return result;
-}
-
-/**
- * Overload for std::istream.
- *
- * \param scheme the scheme to use
- * \param input the input stream
- * \return the string
- */
-inline std::string to_string(scheme scheme, std::istream &input)
-{
-    return to_string(scheme, std::istreambuf_iterator<char>(input), std::istreambuf_iterator<char>());
-}
-
-/**
- * Overload for std::string.
- *
- * \param scheme the scheme to use
- * \param input the input stream
- * \return the string
- */
-inline std::string to_string(scheme scheme, const std::string &input)
-{
-    return to_string(scheme, input.begin(), input.end());
-}
-
-/**
- * Hash using MD5.
- *
- * \param input the input string
- * \return the hashed string
- */
-inline std::string md5(const std::string &input)
-{
-    return to_string(scheme::md5, input);
-}
-
-/**
- * Hash using MD5.
- *
- * \param input the input stream
- * \return the hashed string
- */
-inline std::string md5(std::istream &input)
-{
-    return to_string(scheme::md5, input);
-}
-
-/**
- * Hash using SHA1.
- *
- * \param input the input string
- * \return the hashed string
- */
-inline std::string sha1(const std::string &input)
-{
-    return to_string(scheme::sha1, input);
-}
-
-/**
- * Hash using SHA1.
- *
- * \param input the input stream
- * \return the hashed string
- */
-inline std::string sha1(std::istream &input)
-{
-    return to_string(scheme::sha1, input);
-}
-
-/**
- * Hash using SHA256.
- *
- * \param input the input string
- * \return the hashed string
- */
-inline std::string sha256(const std::string &input)
-{
-    return to_string(scheme::sha256, input);
-}
-
-/**
- * Hash using SHA256.
- *
- * \param input the input stream
- * \return the hashed string
- */
-inline std::string sha256(std::istream &input)
-{
-    return to_string(scheme::sha256, input);
-}
-
-/**
- * Hash using SHA512.
- *
- * \param input the input string
- * \return the hashed string
- */
-inline std::string sha512(const std::string &input)
-{
-    return to_string(scheme::sha512, input);
-}
-
-/**
- * Hash using SHA512.
- *
- * \param input the input stream
- * \return the hashed string
- */
-inline std::string sha512(std::istream &input)
-{
-    return to_string(scheme::sha512, input);
-}
-
-} // !hash
-
-#endif // !MALIKANIA_SERVER_HASH_HPP
--- a/libserver/malikania/server/net/auth_handler.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * auth_handler.cpp -- handle "auth" network command
- *
- * Copyright (c) 2013-2018 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 <util.hpp>
-
-#include <malikania/error/auth_error.hpp>
-
-#include "db/account.hpp"
-
-#include "auth_handler.hpp"
-#include "client.hpp"
-#include "server.hpp"
-
-namespace mlk::server {
-
-namespace {
-
-auto is_connected(const server& server, const db::account& account) noexcept -> bool
-{
-    const auto& clients = server.get_clients();
-    const auto it = std::find_if(clients.begin(), clients.end(), [&] (const auto& c) {
-        if (c->get_state() != client::state::ready)
-            return false;
-
-        return c->get_account().get_login() == account.get_login();
-    });
-
-    return it != clients.end();
-}
-
-} // !namespace
-
-void auth_handler::exec(server& server, client& clt, nlohmann::json object) noexcept
-{
-    auto ac = db::account::authenticate(
-        util::json::require_string(object, "/login"_json_pointer),
-        util::json::require_string(object, "/password"_json_pointer)
-    );
-
-    if (!ac)
-        clt.fatal("auth", auth_error::invalid_credentials);
-    else if (is_connected(server, *ac))
-        clt.fatal("auth", auth_error::already_connected);
-    else {
-        clt.set_account(std::move(*ac));
-        clt.set_state(client::state::ready);
-        clt.ok("auth");
-    }
-}
-
-} // !mlk::server
--- a/libserver/malikania/server/net/auth_handler.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,44 +0,0 @@
-/*
- * auth_handler.hpp -- handle "auth" network command
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_AUTH_HANDLER_HPP
-#define MALIKANIA_SERVER_AUTH_HANDLER_HPP
-
-/**
- * \file auth_handler.hpp
- * \brief Handle "auth" network command.
- */
-
-#include "handler.hpp"
-
-namespace mlk::server {
-
-/**
- * \brief Handle "auth" network command.
- */
-class auth_handler : public handler {
-public:
-    /**
-     * \copydoc handler::exec
-     */
-    void exec(server& server, client& clt, nlohmann::json object) noexcept override;
-};
-
-} // !mlk::server
-
-#endif // !MALIKANIA_SERVER_AUTH_HANDLER_HPP
--- a/libserver/malikania/server/net/handler.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-/*
- * handler.hpp -- server side remote command
- *
- * Copyright (c) 2013-2018 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 MALIKANIA_SERVER_HANDLER_HPP
-#define MALIKANIA_SERVER_HANDLER_HPP
-
-/**
- * \file handler.hpp
- * \brief Server side remote command.
- */
-
-#include <memory>
-
-#include <json.hpp>
-
-namespace mlk::server {
-
-class server;
-class client;
-
-/**
- * \brief Server side remote command.
- */
-class handler {
-public:
-    /**
-     * Default constructor.
-     */
-    handler() noexcept = default;
-
-    /**
-     * Virtual destructor defaulted.
-     */
-    virtual ~handler() noexcept = default;
-
-    /**
-     * Execute the function from the given client.
-     *
-     * \param server the global object server
-     * \param client the client
-     * \param message the network message
-     */
-    virtual void exec(server& server, client& client, nlohmann::json message) noexcept = 0;
-};
-
-} // !mlk::server
-
-#endif // !MALIKANIA_SERVER_HANDLER_HPP
--- a/libserver/malikania/server/server.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * server.cpp -- malikania basic server
- *
- * Copyright (c) 2016-2018 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 "client.hpp"
-#include "server.hpp"
-#include "util.hpp"
-
-#include "net/auth_handler.hpp"
-
-namespace mlk::server {
-
-namespace {
-
-auto endpoint(const settings& params) -> boost::asio::ip::tcp::endpoint
-{
-    // TODO: add more settings there.
-    return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), params.port);
-}
-
-} // !namespace
-
-void server::load()
-{
-    handle("auth", std::make_unique<auth_handler>());
-}
-
-void server::start()
-{
-    auto clt = std::make_shared<client>(*this, service_, context_);
-
-    acceptor_.async_accept(clt->socket_.lowest_layer(), [this, clt] (auto code) {
-        handle_accept(std::move(clt), code);
-    });
-}
-
-server::server(boost::asio::io_service& service, const settings& settings)
-    : service_(service)
-    , acceptor_(service, endpoint(settings))
-    , context_(boost::asio::ssl::context::sslv23)
-{
-    context_.use_certificate_chain_file(settings.certificate);
-    context_.use_private_key_file(settings.key, boost::asio::ssl::context::pem);
-
-    load();
-    start();
-}
-
-auto server::get_clients() const noexcept -> const clients&
-{
-    return clients_;
-}
-
-auto server::get_clients() noexcept -> clients&
-{
-    return clients_;
-}
-
-void server::handle(std::string name, std::unique_ptr<handler> handler)
-{
-    assert(!name.empty());
-    assert(handler);
-
-    commands_.emplace(std::move(name), std::move(handler));
-}
-
-void server::handle_disconnect(std::shared_ptr<client> clt)
-{
-    clients_.erase(std::move(clt));
-}
-
-void server::handle_read(std::shared_ptr<client> clt, nlohmann::json message)
-{
-    assert(message.is_object());
-
-    const auto cmd = message.find("command");
-
-    if (cmd == message.end() || !cmd->is_string()) {
-        // TODO: log error.
-        return;
-    }
-
-    const auto it = commands_.find(*cmd);
-
-    if (it == commands_.end()) {
-        // TODO: log error.
-        return;
-    }
-
-    try {
-        it->second->exec(*this, *clt, std::move(message));
-    } catch (const std::exception&) {
-        // TODO: log error.
-    }
-}
-
-void server::handle_accept(std::shared_ptr<client> clt, std::error_code code)
-{
-    if (code) {
-        // TODO: log error.
-    } else {
-        clt->handshake();
-        clients_.insert(std::move(clt));
-    }
-
-    start();
-}
-
-} // !mlk::server
--- a/libserver/malikania/server/server.hpp	Wed Oct 24 21:13:12 2018 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-/*
- * server.hpp -- malikania basic server
- *
- * Copyright (c) 2016-2018 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 MALIKANIA_SERVER_SERVER_HPP
-#define MALIKANIA_SERVER_SERVER_HPP
-
-/**
- * \file server.hpp
- * \brief Malikania basic server.
- */
-
-#include <cstdint>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <system_error>
-
-#include <boost/asio.hpp>
-#include <boost/asio/ssl.hpp>
-
-#include <json.hpp>
-
-#include "net/handler.hpp"
-
-namespace mlk::server {
-
-class client;
-class database;
-
-/**
- * \brief Server parameters
- */
-struct settings {
-    std::uint16_t port{3320};
-    std::string certificate;
-    std::string key;
-};
-
-/**
- * \file server.hpp
- * \brief Malikania basic server.
- */
-class server {
-public:
-    using clients = std::unordered_set<std::shared_ptr<client>>;
-    using commands = std::unordered_map<std::string, std::unique_ptr<handler>>;
-
-private:
-    boost::asio::io_service& service_;
-    boost::asio::ip::tcp::acceptor acceptor_;
-    boost::asio::ssl::context context_;
-
-    clients clients_;
-    commands commands_;
-
-    void load();
-    void start();
-
-public:
-    /**
-     * Construct a server object.
-     *
-     * \param ctx the IO context
-     * \param settings the settings
-     */
-    server(boost::asio::io_service& service, const settings& settings);
-
-    /**
-     * Get the set of connected clients.
-     *
-     * \return the clients
-     */
-    auto get_clients() const noexcept -> const clients&;
-
-    /**
-     * Overloaded function.
-     *
-     * \return the clients
-     */
-    auto get_clients() noexcept -> clients&;
-
-    /**
-     * Add network handler.
-     *
-     * If a handler is already present, it will be replaced with the new one.
-     *
-     * \pre handler != nullptr
-     * \pre !name.empty()
-     * \param handler the handler
-     */
-    void handle(std::string name, std::unique_ptr<handler> handler);
-
-    /**
-     * Handle client disconnection.
-     *
-     * \param client the client
-     */
-    virtual void handle_disconnect(std::shared_ptr<client> client);
-
-    /**
-     * Asynchronous function called once a message has been completely and
-     * successfully received from the client.
-     *
-     * The default implementation searches for a handler defined for this
-     * message.
-     *
-     * \param client the client
-     * \param message the message received
-     */
-    virtual void handle_read(std::shared_ptr<client>, nlohmann::json message);
-
-    /**
-     * Handle accept of a new client.
-     *
-     * \param client the newly received client
-     * \param code the error code if any
-     */
-    virtual void handle_accept(std::shared_ptr<client> client, std::error_code code);
-};
-
-} // !mlk
-
-#endif // !MALIKANIA_SERVER_SERVER_HPP
--- a/tests/libclient/js-color/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libclient/js-color/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -54,7 +54,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 0);
 	BOOST_TEST(component(ctx_, "g") == 0);
@@ -73,7 +73,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 255);
 	BOOST_TEST(component(ctx_, "g") == 255);
@@ -92,7 +92,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 0);
 	BOOST_TEST(component(ctx_, "g") == 0);
@@ -111,7 +111,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 10);
 	BOOST_TEST(component(ctx_, "g") == 20);
@@ -130,7 +130,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 10);
 	BOOST_TEST(component(ctx_, "g") == 20);
@@ -149,7 +149,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 10);
 	BOOST_TEST(component(ctx_, "g") == 20);
@@ -168,7 +168,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 10);
 	BOOST_TEST(component(ctx_, "g") == 20);
@@ -191,7 +191,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	BOOST_TEST(component(ctx_, "r") == 10);
 	BOOST_TEST(component(ctx_, "g") == 20);
@@ -220,7 +220,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -242,7 +242,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -268,7 +268,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -290,7 +290,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -312,7 +312,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -334,7 +334,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -356,7 +356,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -380,7 +380,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "Error");
@@ -402,7 +402,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "Error");
@@ -428,7 +428,7 @@
 BOOST_AUTO_TEST_CASE(success)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto color = duk::require<mlk::client::color>(ctx, 0);
+		const auto color = mlk::js::duk::require<mlk::client::color>(ctx, 0);
 
 		duk_push_uint(ctx, color.red);
 		duk_put_global_string(ctx, "r");
@@ -446,7 +446,7 @@
 	const auto ret = duk_peval_string(ctx_, "draw('#ff0000');");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "r");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 255U);
@@ -465,7 +465,7 @@
 BOOST_AUTO_TEST_CASE(fail)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::client::color>(ctx, 0);
+		mlk::js::duk::require<mlk::client::color>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -481,7 +481,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "Error");
@@ -498,7 +498,7 @@
 BOOST_AUTO_TEST_CASE(fail_alpha)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::client::color>(ctx, 0);
+		mlk::js::duk::require<mlk::client::color>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -514,7 +514,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -540,7 +540,7 @@
 BOOST_AUTO_TEST_CASE(normal)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto color = duk::get<mlk::client::color>(ctx, 0);
+		const auto color = mlk::js::duk::get<mlk::client::color>(ctx, 0);
 
 		duk_push_uint(ctx, color.red);
 		duk_put_global_string(ctx, "r");
@@ -558,7 +558,7 @@
 	const auto ret = duk_peval_string(ctx_, "draw('#ff0000');");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "r");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 255U);
@@ -577,7 +577,7 @@
 BOOST_AUTO_TEST_CASE(adjust_rgb)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto color = duk::get<mlk::client::color>(ctx, 0);
+		const auto color = mlk::js::duk::get<mlk::client::color>(ctx, 0);
 
 		duk_push_uint(ctx, color.red);
 		duk_put_global_string(ctx, "r");
@@ -595,7 +595,7 @@
 	const auto ret = duk_peval_string(ctx_, "draw('#ghijkl');");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "r");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 0U);
@@ -616,7 +616,7 @@
 BOOST_AUTO_TEST_CASE(adjust_all)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto color = duk::get<mlk::client::color>(ctx, 0);
+		const auto color = mlk::js::duk::get<mlk::client::color>(ctx, 0);
 
 		duk_push_uint(ctx, color.red);
 		duk_put_global_string(ctx, "r");
@@ -634,7 +634,7 @@
 	const auto ret = duk_peval_string(ctx_, "draw({ red: -1, green: 256, blue: 100, alpha: 800 });");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "r");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 0U);
@@ -655,7 +655,7 @@
 BOOST_AUTO_TEST_CASE(something_else)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto color = duk::get<mlk::client::color>(ctx, 0);
+		const auto color = mlk::js::duk::get<mlk::client::color>(ctx, 0);
 
 		duk_push_uint(ctx, color.red);
 		duk_put_global_string(ctx, "r");
@@ -673,7 +673,7 @@
 	const auto ret = duk_peval_string(ctx_, "draw(null);");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "r");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 0U);
--- a/tests/libcommon/js-elapsed-timer/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/js-elapsed-timer/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -50,12 +50,12 @@
 BOOST_AUTO_TEST_CASE(standard)
 {
 	if (duk_peval_string(ctx_, "timer = new Malikania.ElapsedTimer();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	std::this_thread::sleep_for(300ms);
 
 	if (duk_peval_string(ctx_, "result = timer.elapsed();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "result");
 	assert_range(duk_to_int(ctx_, -1), 300);
@@ -65,7 +65,7 @@
 BOOST_AUTO_TEST_CASE(pause)
 {
 	if (duk_peval_string(ctx_, "timer = new Malikania.ElapsedTimer();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	/*
 	 * Simulate a pause in the game like this:
@@ -78,17 +78,17 @@
 	std::this_thread::sleep_for(10ms);
 
 	if (duk_peval_string(ctx_, "timer.pause();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	std::this_thread::sleep_for(5ms);
 
 	if (duk_peval_string(ctx_, "timer.restart();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	std::this_thread::sleep_for(6ms);
 
 	if (duk_peval_string(ctx_, "result = timer.elapsed()") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "result");
 	assert_range(duk_to_int(ctx_, -1), 16);
@@ -98,17 +98,17 @@
 BOOST_AUTO_TEST_CASE(doublecheck)
 {
 	if (duk_peval_string(ctx_, "timer = new Malikania.ElapsedTimer();") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	std::this_thread::sleep_for(50ms);
 
 	if (duk_peval_string(ctx_, "result = timer.elapsed()") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	std::this_thread::sleep_for(50ms);
 
 	if (duk_peval_string(ctx_, "result = timer.elapsed()") != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "result");
 	assert_range(duk_to_int(ctx_, -1), 100);
--- a/tests/libcommon/js-line/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/js-line/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -21,8 +21,6 @@
 
 #include <malikania/line.hpp>
 
-#include <malikania/js_line.hpp>
-
 #include <malikania/js/test/js_api_fixture.hpp>
 
 namespace mlk {
@@ -49,7 +47,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
@@ -76,7 +74,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -103,7 +101,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -130,7 +128,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -167,7 +165,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -189,7 +187,7 @@
 BOOST_AUTO_TEST_CASE(success)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto line = duk::require<mlk::line>(ctx, 0);
+		const auto line = mlk::js::duk::require<mlk::line>(ctx, 0);
 
 		duk_push_int(ctx, line.x1);
 		duk_put_global_string(ctx, "x1");
@@ -207,7 +205,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({ x1: 50, y1: 80, x2: 100, y2: 200 });");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 50);
@@ -226,7 +224,7 @@
 BOOST_AUTO_TEST_CASE(fail)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::line>(ctx, 0);
+		mlk::js::duk::require<mlk::line>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -242,7 +240,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -264,7 +262,7 @@
 BOOST_AUTO_TEST_CASE(adjust_all)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto line = duk::get<mlk::line>(ctx, 0);
+		const auto line = mlk::js::duk::get<mlk::line>(ctx, 0);
 
 		duk_push_int(ctx, line.x1);
 		duk_put_global_string(ctx, "x1");
@@ -282,7 +280,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({});");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x1");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
--- a/tests/libcommon/js-point/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/js-point/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -21,8 +21,6 @@
 
 #include <malikania/point.hpp>
 
-#include <malikania/js_point.hpp>
-
 #include <malikania/js/test/js_api_fixture.hpp>
 
 namespace mlk {
@@ -47,7 +45,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
@@ -66,7 +64,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == -10);
@@ -85,7 +83,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 100);
@@ -104,7 +102,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 100);
@@ -135,7 +133,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -157,7 +155,7 @@
 BOOST_AUTO_TEST_CASE(success)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto point = duk::require<mlk::point>(ctx, 0);
+		const auto point = mlk::js::duk::require<mlk::point>(ctx, 0);
 
 		duk_push_int(ctx, point.x);
 		duk_put_global_string(ctx, "x");
@@ -171,7 +169,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({ x: 100, y: 200 });");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 100);
@@ -184,7 +182,7 @@
 BOOST_AUTO_TEST_CASE(fail)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::point>(ctx, 0);
+		mlk::js::duk::require<mlk::point>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -200,7 +198,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -222,7 +220,7 @@
 BOOST_AUTO_TEST_CASE(adjust_all)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto point = duk::get<mlk::point>(ctx, 0);
+		const auto point = mlk::js::duk::get<mlk::point>(ctx, 0);
 
 		duk_push_int(ctx, point.x);
 		duk_put_global_string(ctx, "x");
@@ -236,7 +234,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({});");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
--- a/tests/libcommon/js-rectangle/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/js-rectangle/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -47,7 +47,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
@@ -74,7 +74,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -101,7 +101,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -128,7 +128,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 10);
@@ -165,7 +165,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -191,7 +191,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -215,7 +215,7 @@
 BOOST_AUTO_TEST_CASE(success)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto rect = duk::require<mlk::rectangle>(ctx, 0);
+		const auto rect = mlk::js::duk::require<mlk::rectangle>(ctx, 0);
 
 		duk_push_int(ctx, rect.x);
 		duk_put_global_string(ctx, "x");
@@ -233,7 +233,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({ x: 50, y: 80, width: 100, height: 200 });");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 50);
@@ -252,7 +252,7 @@
 BOOST_AUTO_TEST_CASE(fail)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::rectangle>(ctx, 0);
+		mlk::js::duk::require<mlk::rectangle>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -268,7 +268,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -290,7 +290,7 @@
 BOOST_AUTO_TEST_CASE(adjust_all)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto rect = duk::get<mlk::rectangle>(ctx, 0);
+		const auto rect = mlk::js::duk::get<mlk::rectangle>(ctx, 0);
 
 		duk_push_int(ctx, rect.x);
 		duk_put_global_string(ctx, "x");
@@ -307,7 +307,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({});");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "x");
 	BOOST_TEST(duk_to_int(ctx_, -1) == 0);
--- a/tests/libcommon/js-size/main.cpp	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/js-size/main.cpp	Thu Oct 25 21:36:14 2018 +0200
@@ -43,7 +43,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 0U);
@@ -62,7 +62,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 100U);
@@ -81,7 +81,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 100U);
@@ -100,7 +100,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 100U);
@@ -131,7 +131,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -157,7 +157,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -179,7 +179,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -201,7 +201,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -223,7 +223,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "RangeError");
@@ -247,7 +247,7 @@
 BOOST_AUTO_TEST_CASE(success)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto size = duk::require<mlk::size>(ctx, 0);
+		const auto size = mlk::js::duk::require<mlk::size>(ctx, 0);
 
 		duk_push_uint(ctx, size.width);
 		duk_put_global_string(ctx, "w");
@@ -261,7 +261,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({ width: 100, height: 200 });");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 100U);
@@ -274,7 +274,7 @@
 BOOST_AUTO_TEST_CASE(fail)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		duk::require<mlk::size>(ctx, 0);
+		mlk::js::duk::require<mlk::size>(ctx, 0);
 
 		return 0;
 	}, 1);
@@ -290,7 +290,7 @@
 	);
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "name");
 	BOOST_TEST(duk_to_string(ctx_, -1) == "TypeError");
@@ -312,7 +312,7 @@
 BOOST_AUTO_TEST_CASE(adjust_all)
 {
 	duk_push_c_function(ctx_, [] (auto ctx) {
-		const auto size = duk::get<mlk::size>(ctx, 0);
+		const auto size = mlk::js::duk::get<mlk::size>(ctx, 0);
 
 		duk_push_uint(ctx, size.width);
 		duk_put_global_string(ctx, "w");
@@ -326,7 +326,7 @@
 	const auto ret = duk_peval_string(ctx_, "build({});");
 
 	if (ret != 0)
-		throw duk::get_stack(ctx_, -1);
+		throw mlk::js::duk::get_stack(ctx_, -1);
 
 	duk_get_global_string(ctx_, "w");
 	BOOST_TEST(duk_to_uint(ctx_, -1) == 0U);
--- a/tests/libcommon/size/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/size/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -18,6 +18,6 @@
 
 malikania_create_test(
 	NAME size
-	LIBRARIES libmlk-common
+	LIBRARIES libmlk
 	SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
 )
--- a/tests/libcommon/util/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/libcommon/util/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -18,6 +18,6 @@
 
 malikania_create_test(
 	NAME util
-	LIBRARIES libmlk-common
+	LIBRARIES libmlk
 	SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
 )
--- a/tests/tools/tileset/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ b/tests/tools/tileset/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -51,7 +51,7 @@
 
 malikania_create_test(
 	NAME mlk-tileset
-	LIBRARIES libmlk-common
+	LIBRARIES libmlk
 	SOURCES
 		${TILESET_SOURCES}
 		${TILESET_OUTPUTS}
--- a/tools/tileset/CMakeLists.txt	Wed Oct 24 21:13:12 2018 +0200
+++ b/tools/tileset/CMakeLists.txt	Thu Oct 25 21:36:14 2018 +0200
@@ -23,6 +23,6 @@
 malikania_define_executable(
 	TARGET mlk-tileset
 	INCLUDES ${Boost_INCLUDE_DIRS}
-	LIBRARIES ${Boost_LIBRARIES} json libmlk-common
+	LIBRARIES ${Boost_LIBRARIES} json libmlk
 	SOURCES ${mlk-tileset_SOURCE_DIR}/main.cpp
 )