Mercurial > molko
changeset 593:f9e85d0aca74
rpg: introduce private loader_file to help allocating map/tileset
The structure is just an allocator and owner for various data shared among the
map and the tileset such as sprites, textures, animations...
While here, simplify use of mlk_(map|tileset)_loader_file so they don't have a
public structure anymore but insert themselves into the loader->data.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 21 Mar 2023 14:08:15 +0100 |
parents | 1560ef13146c |
children | e070fdcc592b |
files | CMakeLists.txt examples/example-map/example-map.c examples/example-tileset/example-tileset.c extern/LICENSE.libutlist.txt extern/VERSION.libutlist.txt extern/libutlist/CMakeLists.txt extern/libutlist/utlist.h libmlk-rpg/CMakeLists.txt libmlk-rpg/mlk/rpg/loader-file_p.c libmlk-rpg/mlk/rpg/loader-file_p.h libmlk-rpg/mlk/rpg/map-loader-file.c libmlk-rpg/mlk/rpg/map-loader-file.h libmlk-rpg/mlk/rpg/map-loader.c libmlk-rpg/mlk/rpg/map-loader.h libmlk-rpg/mlk/rpg/tileset-loader-file.c libmlk-rpg/mlk/rpg/tileset-loader-file.h libmlk-rpg/mlk/rpg/tileset-loader.c libmlk-rpg/mlk/rpg/tileset-loader.h tests/test-tileset.c |
diffstat | 19 files changed, 1657 insertions(+), 358 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Mar 20 21:59:02 2023 +0100 +++ b/CMakeLists.txt Tue Mar 21 14:08:15 2023 +0100 @@ -87,6 +87,7 @@ add_subdirectory(extern/libsqlite) add_subdirectory(extern/libdt) +add_subdirectory(extern/libutlist) if (MLK_WITH_DOXYGEN) add_subdirectory(doc)
--- a/examples/example-map/example-map.c Mon Mar 20 21:59:02 2023 +0100 +++ b/examples/example-map/example-map.c Tue Mar 21 14:08:15 2023 +0100 @@ -20,6 +20,8 @@ #include <stdio.h> #include <string.h> +#include <mlk/util/util.h> + #include <mlk/core/animation.h> #include <mlk/core/core.h> #include <mlk/core/err.h> @@ -54,11 +56,9 @@ #include <assets/maps/world.h> #include <assets/tilesets/world.h> -static struct mlk_tileset_loader_file tileset_loader_file; static struct mlk_tileset_loader tileset_loader; static struct mlk_tileset tileset; -static struct mlk_map_loader_file map_loader_file; static struct mlk_map_loader map_loader; static struct mlk_map map; @@ -89,14 +89,13 @@ } table_textures[] = { { "world.png", &mlk_registry_textures[MLK_REGISTRY_TEXTURE_WORLD] }, { "animation-water.png", &mlk_registry_textures[MLK_REGISTRY_TEXTURE_WATER] }, + { "john.png", &mlk_registry_textures[MLK_REGISTRY_TEXTURE_JOHN] }, { NULL, NULL } }; static struct mlk_texture * -init_texture(struct mlk_tileset_loader *loader, const char *ident) +find_texture(const char *ident) { - (void)loader; - char filepath[MLK_PATH_MAX], filename[FILENAME_MAX + 1]; mlk_util_strlcpy(filepath, ident, sizeof (filepath)); @@ -110,8 +109,26 @@ return NULL; } +static struct mlk_texture * +tileset_new_texture(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset, const char *ident) +{ + (void)loader; + (void)tileset; + + return find_texture(ident); +} + +static struct mlk_texture * +map_new_texture(struct mlk_map_loader *loader, struct mlk_map *map, const char *ident) +{ + (void)loader; + (void)map; + + return find_texture(ident); +} + struct mlk_tileset * -init_tileset(struct mlk_map_loader *loader, struct mlk_map *map, const char *ident) +map_new_tileset(struct mlk_map_loader *loader, struct mlk_map *map, const char *ident) { (void)loader; (void)map; @@ -140,22 +157,21 @@ * registry which is not supported by itself in * mlk_tileset_loader_file. */ - mlk_tileset_loader_file_init(&tileset_loader_file, &tileset_loader, ""); - tileset_loader.init_texture = init_texture; + mlk_tileset_loader_file_init(&tileset_loader, ""); + tileset_loader.new_texture = tileset_new_texture; /* * Create our map loader. It will also search for a tileset to be found * on disk by default which we would like to avoid. We override the * init_tileset function. */ - mlk_map_loader_file_init(&map_loader_file, &map_loader, ""); - map_loader.init_tileset = init_tileset; + mlk_map_loader_file_init(&map_loader, ""); + map_loader.new_tileset = map_new_tileset; + map_loader.new_texture = map_new_texture; if (mlk_map_loader_openmem(&map_loader, &map, assets_maps_world, sizeof (assets_maps_world)) < 0) mlk_panic(); - // TODO: this not handled by the map yet. - map.player_sprite = &mlk_registry_sprites[MLK_REGISTRY_TEXTURE_JOHN]; mlk_map_init(&map); } @@ -233,8 +249,9 @@ static void quit(void) { - mlk_map_loader_file_finish(&map_loader_file); - mlk_tileset_loader_file_finish(&tileset_loader_file); + mlk_map_finish(&map); + mlk_map_loader_file_finish(&map_loader); + mlk_tileset_loader_file_finish(&tileset_loader); mlk_example_finish(); }
--- a/examples/example-tileset/example-tileset.c Mon Mar 20 21:59:02 2023 +0100 +++ b/examples/example-tileset/example-tileset.c Tue Mar 21 14:08:15 2023 +0100 @@ -20,6 +20,8 @@ #include <stdio.h> #include <string.h> +#include <mlk/util/util.h> + #include <mlk/core/animation.h> #include <mlk/core/core.h> #include <mlk/core/err.h> @@ -47,7 +49,6 @@ #include <assets/tilesets/world.h> -static struct mlk_tileset_loader_file loader_file; static struct mlk_tileset_loader loader; static struct mlk_tileset tileset; @@ -70,9 +71,10 @@ }; static struct mlk_texture * -init_texture(struct mlk_tileset_loader *loader, const char *ident) +new_texture(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset, const char *ident) { (void)loader; + (void)tileset; char filepath[MLK_PATH_MAX], filename[FILENAME_MAX + 1]; @@ -88,9 +90,10 @@ } static struct mlk_sprite * -init_sprite(struct mlk_tileset_loader *loader) +new_sprite(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset) { (void)loader; + (void)tileset; /* Just ensure we haven't reach the limit. */ assert(spritesz < MLK_UTIL_SIZE(sprites)); @@ -99,9 +102,10 @@ } static struct mlk_animation * -init_animation(struct mlk_tileset_loader *loader) +new_animation(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset) { (void)loader; + (void)tileset; /* Just ensure we haven't reach the limit. */ assert(animationsz < MLK_UTIL_SIZE(animations)); @@ -122,10 +126,10 @@ * Images are loaded from the libmlk-example registry from RAM and * sprites animations are statically allocated. */ - mlk_tileset_loader_file_init(&loader_file, &loader, ""); - loader.init_texture = init_texture; - loader.init_sprite = init_sprite; - loader.init_animation = init_animation; + mlk_tileset_loader_file_init(&loader, ""); + loader.new_texture = new_texture; + loader.new_sprite = new_sprite; + loader.new_animation = new_animation; if (mlk_tileset_loader_openmem(&loader, &tileset, assets_tilesets_world, sizeof (assets_tilesets_world)) < 0) mlk_panic(); @@ -197,7 +201,7 @@ static void quit(void) { - mlk_tileset_loader_file_finish(&loader_file); + mlk_tileset_loader_file_finish(&loader); mlk_example_finish(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extern/LICENSE.libutlist.txt Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,21 @@ +Copyright (c) 2005-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extern/VERSION.libutlist.txt Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,1 @@ +2.3.0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extern/libutlist/CMakeLists.txt Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,27 @@ +# +# CMakeLists.txt -- CMake build system for utlist +# +# Copyright (c) 2016-2022 David Demelier <markand@malikania.fr> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# + +cmake_minimum_required(VERSION 3.0) +project(libmlk-utlist) +add_library(libmlk-utlist INTERFACE) +target_include_directories( + libmlk-utlist + INTERFACE + $<BUILD_INTERFACE:${libmlk-utlist_SOURCE_DIR}> +) +target_sources(libmlk-utlist INTERFACE ${libmlk-utlist_SOURCE_DIR}/utlist.h)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extern/libutlist/utlist.h Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2007-2021, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.3.0 + +#include <assert.h> + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */
--- a/libmlk-rpg/CMakeLists.txt Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/CMakeLists.txt Tue Mar 21 14:08:15 2023 +0100 @@ -20,6 +20,8 @@ set( SOURCES + ${libmlk-rpg_SOURCE_DIR}/mlk/rpg/loader-file_p.c + ${libmlk-rpg_SOURCE_DIR}/mlk/rpg/loader-file_p.h ${libmlk-rpg_SOURCE_DIR}/mlk/rpg/map-loader-file.c ${libmlk-rpg_SOURCE_DIR}/mlk/rpg/map-loader.c ${libmlk-rpg_SOURCE_DIR}/mlk/rpg/map.c @@ -80,6 +82,7 @@ LIBRARIES libmlk-ui libmlk-sqlite + libmlk-utlist INCLUDES PRIVATE $<BUILD_INTERFACE:${libmlk-rpg_BINARY_DIR}> PUBLIC $<BUILD_INTERFACE:${libmlk-rpg_SOURCE_DIR}>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/mlk/rpg/loader-file_p.c Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,173 @@ +/* + * loader-file_p.c -- resource loader for map/tileset + * + * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <assert.h> +#include <stdio.h> + +#include <utlist.h> + +#include <mlk/util/util.h> + +#include <mlk/core/alloc.h> +#include <mlk/core/animation.h> +#include <mlk/core/image.h> +#include <mlk/core/sprite.h> +#include <mlk/core/texture.h> + +#include "loader-file_p.h" + +struct texture_node { + struct mlk_texture texture; + struct texture_node *next; +}; + +struct sprite_node { + struct mlk_sprite sprite; + struct sprite_node *next; +}; + +struct animation_node { + struct mlk_animation animation; + struct animation_node *next; +}; + +struct mlk__loader_file { + char directory[MLK_PATH_MAX]; + struct texture_node *textures; + struct sprite_node *sprites; + struct animation_node *animations; +}; + +static inline void +free_textures(struct mlk__loader_file *loader) +{ + struct texture_node *node, *tmp; + + LL_FOREACH_SAFE(loader->textures, node, tmp) { + mlk_texture_finish(&node->texture); + mlk_alloc_free(node); + } + + loader->textures = NULL; +} + +static inline void +free_sprites(struct mlk__loader_file *loader) +{ + struct sprite_node *node, *tmp; + + LL_FOREACH_SAFE(loader->sprites, node, tmp) + mlk_alloc_free(node); + + loader->sprites = NULL; +} + +static inline void +free_animations(struct mlk__loader_file *loader) +{ + struct animation_node *node, *tmp; + + LL_FOREACH_SAFE(loader->animations, node, tmp) + mlk_alloc_free(node); + + loader->animations = NULL; +} + +struct mlk__loader_file * +mlk__loader_file_new(const char *file) +{ + assert(file); + + struct mlk__loader_file *loader; + char filepath[MLK_PATH_MAX]; + + if (!(loader = mlk_alloc_new0(1, sizeof (*loader)))) + return NULL; + + mlk_util_strlcpy(filepath, file, sizeof (filepath)); + mlk_util_strlcpy(loader->directory, mlk_util_dirname(filepath), sizeof (loader->directory)); + + return loader; +} + +const char * +mlk__loader_file_directory(const struct mlk__loader_file *loader) +{ + assert(loader); + + return loader->directory; +} + +struct mlk_texture * +mlk__loader_file_texture_open(struct mlk__loader_file *loader, const char *ident) +{ + struct texture_node *node; + char path[MLK_PATH_MAX]; + + if (!(node = mlk_alloc_new0(1, sizeof (*node)))) + return NULL; + + snprintf(path, sizeof (path), "%s/%s", loader->directory, ident); + + if (mlk_image_open(&node->texture, path) < 0) { + mlk_alloc_free(node); + return NULL; + } + + LL_APPEND(loader->textures, node); + + return &node->texture; +} + +struct mlk_sprite * +mlk__loader_file_sprite_new(struct mlk__loader_file *loader) +{ + struct sprite_node *node; + + if (!(node = mlk_alloc_new0(1, sizeof (*node)))) + return NULL; + + LL_APPEND(loader->sprites, node); + + return &node->sprite; +} + +struct mlk_animation * +mlk__loader_file_animation_new(struct mlk__loader_file *loader) +{ + struct animation_node *node; + + if (!(node = mlk_alloc_new0(1, sizeof (*node)))) + return NULL; + + LL_APPEND(loader->animations, node); + + return &node->animation; +} + +void +mlk__loader_file_free(struct mlk__loader_file *loader) +{ + assert(loader); + + free_textures(loader); + free_sprites(loader); + free_animations(loader); + + mlk_alloc_free(loader); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libmlk-rpg/mlk/rpg/loader-file_p.h Tue Mar 21 14:08:15 2023 +0100 @@ -0,0 +1,46 @@ +/* + * loader-file_p.h -- resource loader for map/tileset + * + * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef MLK_RPG_LOADER_FILE_P_H +#define MLK_RPG_LOADER_FILE_P_H + +struct mlk_animation; +struct mlk_sprite; +struct mlk_texture; + +struct mlk__loader_file; + +struct mlk__loader_file * +mlk__loader_file_new(const char *path); + +const char * +mlk__loader_file_directory(const struct mlk__loader_file *loader); + +struct mlk_texture * +mlk__loader_file_texture_open(struct mlk__loader_file *loader, const char *ident); + +struct mlk_sprite * +mlk__loader_file_sprite_new(struct mlk__loader_file *loader); + +struct mlk_animation * +mlk__loader_file_animation_new(struct mlk__loader_file *loader); + +void +mlk__loader_file_free(struct mlk__loader_file *loader); + +#endif /* !MLK_RPG_LOADER_FILE_P_H */
--- a/libmlk-rpg/mlk/rpg/map-loader-file.c Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/map-loader-file.c Tue Mar 21 14:08:15 2023 +0100 @@ -17,203 +17,183 @@ */ #include <assert.h> +#include <stdio.h> +#include <string.h> + +#include <mlk/util/util.h> #include <mlk/core/alloc.h> #include <mlk/core/image.h> #include <mlk/core/sprite.h> #include <mlk/core/texture.h> +#include "loader-file_p.h" #include "map-loader-file.h" #include "map-loader.h" #include "map.h" +#include "tileset-loader-file.h" #include "tileset-loader.h" +#include "tileset.h" + +struct self { + /* Resources allocator. */ + struct mlk__loader_file *loader; + + /* Own allocated tiles. */ + unsigned int *tiles[MLK_MAP_LAYER_TYPE_LAST]; -static inline void * -allocate(void ***array, size_t width) -{ - void **ptr, *elem; + /* + * We use a tileset file loader if new_tileset function isn't present in + * this map loader. + */ + struct mlk_tileset_loader tileset_loader; + struct mlk_tileset tileset; - /* Not yet allocated? Allocate a new pointer element. */ - if (!*array) - ptr = mlk_alloc_new0(1, sizeof (void *)); - else - ptr = mlk_alloc_expand(*array, 1); + /* Own allocated blocks. */ + struct mlk_map_block *blocks; +}; - if (!ptr) - return NULL; +static struct self * +self_new(const char *path) +{ + struct self *self; - /* Now allocate the element itself because. */ - if (!(elem = mlk_alloc_new0(1, width))) + if (!(self = mlk_alloc_new0(1, sizeof (*self)))) + return NULL; + if (!(self->loader = mlk__loader_file_new(path))) { + mlk_alloc_free(self); return NULL; + } - /* Store it into the array of elements. */ - ptr[mlk_alloc_getn(ptr) - 1] = elem; - *array = ptr; + return self; +} - return elem; +static void +self_free(struct self *self) +{ + mlk__loader_file_free(self->loader); + + for (int i = 0; i < MLK_MAP_LAYER_TYPE_LAST; ++i) + mlk_alloc_free(self->tiles[i]); + + mlk_tileset_loader_file_finish(&self->tileset_loader); + mlk_alloc_free(self->blocks); } static struct mlk_texture * -init_texture(struct mlk_map_loader *self, - struct mlk_map *map, - const char *ident) +new_texture(struct mlk_map_loader *loader, + struct mlk_map *map, + const char *ident) { (void)map; - struct mlk_map_loader_file *file = self->data; - struct mlk_texture *texture; - char path[MLK_PATH_MAX]; - - snprintf(path, sizeof (path), "%s/%s", file->directory, ident); + struct self *self = loader->data; - /* No need to deallocate, already done in finish anyway. */ - if (!(texture = allocate((void ***)&file->textures, sizeof (struct mlk_texture)))) - return NULL; - if (mlk_image_open(texture, path) < 0) - return NULL; - - return texture; + return mlk__loader_file_texture_open(self->loader, ident); } static struct mlk_sprite * -init_sprite(struct mlk_map_loader *self, struct mlk_map *map) +new_sprite(struct mlk_map_loader *loader, struct mlk_map *map) { (void)map; - struct mlk_map_loader_file *file = self->data; + struct self *self = loader->data; - return allocate((void ***)&file->sprites, sizeof (struct mlk_sprite)); + return mlk__loader_file_sprite_new(self->loader); } static struct mlk_tileset * -init_tileset(struct mlk_map_loader *self, - struct mlk_map *map, - const char *ident) +new_tileset(struct mlk_map_loader *loader, + struct mlk_map *map, + const char *ident) { (void)map; - struct mlk_map_loader_file *file = self->data; - struct mlk_tileset_loader tileset_loader; + struct self *self = loader->data; char path[MLK_PATH_MAX]; - snprintf(path, sizeof (path), "%s/%s", file->directory, ident); + snprintf(path, sizeof (path), "%s/%s", mlk__loader_file_directory(self->loader), ident); /* * Just make sure that we don't leak in case tileset directory is listed * more than once. */ - mlk_tileset_loader_file_finish(&file->tileset_loader_file); - mlk_tileset_loader_file_init(&file->tileset_loader_file, &tileset_loader, path); + mlk_tileset_loader_file_finish(&self->tileset_loader); + mlk_tileset_loader_file_init(&self->tileset_loader, path); - if (mlk_tileset_loader_open(&tileset_loader, &file->tileset, path) < 0) + if (mlk_tileset_loader_open(&self->tileset_loader, &self->tileset, path) < 0) return NULL; - return &file->tileset; + return &self->tileset; } static unsigned int * -alloc_tiles(struct mlk_map_loader *self, - struct mlk_map *map, - enum mlk_map_layer_type type, - size_t n) +new_tiles(struct mlk_map_loader *loader, + struct mlk_map *map, + enum mlk_map_layer_type type, + size_t n) { (void)map; - struct mlk_map_loader_file *file = self->data; + struct self *self = loader->data; - return file->tiles[type] = mlk_alloc_new0(n, sizeof (unsigned int)); + return self->tiles[type] = mlk_alloc_new0(n, sizeof (unsigned int)); } static struct mlk_map_block * -expand_blocks(struct mlk_map_loader *self, +expand_blocks(struct mlk_map_loader *loader, struct mlk_map *map, struct mlk_map_block *blocks, size_t blocksz) { (void)map; - struct mlk_map_loader_file *file = self->data; + struct self *self = loader->data; struct mlk_map_block *ptr; - if (!file->blocks) + if (!self->blocks) ptr = mlk_alloc_new0(1, sizeof (*ptr)); else - ptr = mlk_alloc_expand(file->blocks, blocksz); + ptr = mlk_alloc_expand(self->blocks, blocksz); if (ptr) - file->blocks = blocks; + self->blocks = blocks; return ptr; } -static void -finish(void ***ptr, void (*finish)(void *)) +int +mlk_map_loader_file_init(struct mlk_map_loader *loader, const char *filename) { - size_t len; + assert(loader); + assert(filename); - /* Already cleared. */ - if (!*ptr) - return; + struct self *self; - len = mlk_alloc_getn(*ptr); + memset(loader, 0, sizeof (*loader)); - for (size_t i = 0; i < len; ++i) - finish((*ptr)[i]); + if (!(self = self_new(filename))) + return -1; - mlk_alloc_free(*ptr); - *ptr = NULL; -} + loader->data = self; + loader->new_tileset = new_tileset; + loader->new_texture = new_texture; + loader->new_sprite = new_sprite; + loader->new_tiles = new_tiles; + loader->expand_blocks = expand_blocks; -static void -finish_texture(void *element) -{ - mlk_texture_finish(element); - mlk_alloc_free(element); + return 0; } void -mlk_map_loader_file_init(struct mlk_map_loader_file *file, - struct mlk_map_loader *loader, - const char *filename) +mlk_map_loader_file_finish(struct mlk_map_loader *loader) { - assert(file); assert(loader); - char filepath[MLK_PATH_MAX]; + struct self *self = loader->data; - if (!file->directory[0]) { - /* Determine base filename base directory. */ - mlk_util_strlcpy(filepath, filename, sizeof (filepath)); - mlk_util_strlcpy(file->directory, mlk_util_dirname(filepath), sizeof (file->directory)); - } - if (!loader->data) - loader->data = file; - if (!loader->init_tileset) - loader->init_tileset = init_tileset; - if (!loader->init_texture) - loader->init_texture = init_texture; - if (!loader->init_sprite) - loader->init_sprite = init_sprite; - if (!loader->alloc_tiles) - loader->alloc_tiles = alloc_tiles; - if (!loader->expand_blocks) - loader->expand_blocks = expand_blocks; + if (self) + self_free(self); + + memset(loader, 0, sizeof (*loader)); } - -void -mlk_map_loader_file_finish(struct mlk_map_loader_file *file) -{ - assert(file); - - for (int i = 0; i < MLK_MAP_LAYER_TYPE_LAST; ++i) { - mlk_alloc_free(file->tiles[i]); - file->tiles[i] = NULL; - } - - mlk_tileset_loader_file_finish(&file->tileset_loader_file); - mlk_alloc_free(file->blocks); - file->blocks = NULL; - - finish((void ***)&file->sprites, mlk_alloc_free); - finish((void ***)&file->textures, finish_texture); -}
--- a/libmlk-rpg/mlk/rpg/map-loader-file.h Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/map-loader-file.h Tue Mar 21 14:08:15 2023 +0100 @@ -22,64 +22,59 @@ /** * \file map-loader-file.h * \brief Map file loader implementation + * + * This loader implements the map loader interface from a file into a directory + * (usually ending with `.map` extension). It will find and allocate resources + * on the go depending on the file map content. + * + * For convenience, this loader will also use ::mlk_tileset_loader for loading + * and associating the tileset. If this behavior is not desired the function + * ::mlk_map_loader::new_tileset can be overriden after calling + * ::mlk_map_loader_file_init. + * + * Example of use: + * + * ```c + * struct mlk_map_loader loader; + * struct mlk_map map; + * + * // The loader needs to know the map location to retrieve relative files. + * const char *map_path = "/path/to/world.map"; + * + * // Initialize the loader, it will be filled with custom internal functions. + * if (mlk_map_loader_file_init(&loader, map_path) < 0) + * mlk_panic(); + * + * // Load the map from the file on disk. + * if (mlk_map_loader_open(&loader, &map, map_path) < 0) + * mlk_panic(); + * + * // Don't forget to initialize the map before use. + * mlk_map_init(&map); + * + * // Destroy the resources. + * mlk_map_finish(&map); + * mlk_map_loader_file_finish(&loader); + * ``` */ -#include <mlk/util/util.h> - -#include "map.h" -#include "tileset-loader-file.h" -#include "tileset.h" - struct mlk_map_loader; -struct mlk_sprite; -struct mlk_texture; /** - * \struct mlk_map_loader_file - * \brief Map loader file structure - */ -struct mlk_map_loader_file { - /** - * (read-only) - * - * Computed map file directory. - */ - char directory[MLK_PATH_MAX]; - - /** \cond MLK_PRIVATE_DECLS */ - unsigned int *tiles[MLK_MAP_LAYER_TYPE_LAST]; - - struct mlk_texture **textures; - struct mlk_sprite **sprites; - - /* - * We use a tileset file loader if init_tileset function isn't present - * in this map loader. - */ - struct mlk_tileset_loader_file tileset_loader_file; - struct mlk_tileset tileset; - - struct mlk_map_block *blocks; - /** \endcond MLK_PRIVATE_DECLS */ -}; - -/** - * Initialize the map loader. + * Initialize the loader with internal functions and internal data to allocate + * and find appropriate resources relative to the map filename. * - * All loader member functions will be set and ::mlk_map_loader::data will be - * set to file loader. + * After loading the map with this underlying loader, it should be kept until + * the map is no longer used. * - * \pre file != NULL * \pre loader != NULL * \pre filename != NULL - * \param file the file loader * \param loader the abstract loader interface * \param filename path to the map file + * \return 0 on success or -1 on error */ -void -mlk_map_loader_file_init(struct mlk_map_loader_file *file, - struct mlk_map_loader *loader, - const char *filename); +int +mlk_map_loader_file_init(struct mlk_map_loader *loader, const char *filename); /** * Cleanup allocated resources by this file loader. @@ -89,6 +84,6 @@ * \warning the map loaded with this loader must not be used */ void -mlk_map_loader_file_finish(struct mlk_map_loader_file *file); +mlk_map_loader_file_finish(struct mlk_map_loader *file); #endif /* !MLK_RPG_MAP_LOADER_FILE_H */
--- a/libmlk-rpg/mlk/rpg/map-loader.c Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/map-loader.c Tue Mar 21 14:08:15 2023 +0100 @@ -54,7 +54,7 @@ * The next line after a layer declaration is a list of plain integer * that fill the layer tiles. */ - if (!(tiles = loader->alloc_tiles(loader, map, layer_type, amount))) + if (!(tiles = loader->new_tiles(loader, map, layer_type, amount))) return -1; for (unsigned int tile; fscanf(fp, "%u\n", &tile) && current < amount; ++current) @@ -82,12 +82,12 @@ snprintf(fmt, sizeof (fmt), "%%d|%%d|%%u|%%u|%%d|%%%zu[^\n]\n", sizeof (exec) - 1); while (fscanf(fp, fmt, &x, &y, &w, &h, &isblock, exec) >= 5) { - if (!loader->load_object) { + if (!loader->new_object) { mlk_tracef("ignoring object %d,%d,%u,%u,%d,%s", x, y, w, h, isblock, exec); continue; } - loader->load_object(loader, map, x, y, w, h, exec); + loader->new_object(loader, map, x, y, w, h, exec); /* * Actions do not have concept of collisions because they are @@ -151,7 +151,7 @@ if (!(p = strchr(line, '|'))) return mlk_errf("could not parse tileset"); - if (!(map->tileset = loader->init_tileset(loader, map, p + 1))) + if (!(map->tileset = loader->new_tileset(loader, map, p + 1))) return -1; return 0; @@ -219,9 +219,9 @@ if (sscanf(line, format, &w, &h, ident) != 3) return mlk_errf("invalid player sprite"); - if (!(texture = loader->init_texture(loader, map, ident))) + if (!(texture = loader->new_texture(loader, map, ident))) return -1; - if (!(sprite = loader->init_sprite(loader, map))) + if (!(sprite = loader->new_sprite(loader, map))) return -1; sprite->cellw = w;
--- a/libmlk-rpg/mlk/rpg/map-loader.h Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/map-loader.h Tue Mar 21 14:08:15 2023 +0100 @@ -36,6 +36,11 @@ * This module provides a generic way to open maps. It uses a callback similar * to the ::mlk_tileset_loader. */ + +/** + * \struct mlk_map_loader + * \brief Abstract map loader structure + */ struct mlk_map_loader { /** * (read-write, borrowed, optional) @@ -54,9 +59,9 @@ * \param ident the texture name (or path) * \return the tileset to use or NULL on failure */ - struct mlk_tileset * (*init_tileset)(struct mlk_map_loader *self, - struct mlk_map *map, - const char *ident); + struct mlk_tileset * (*new_tileset)(struct mlk_map_loader *self, + struct mlk_map *map, + const char *ident); /** * (read-write) @@ -67,9 +72,9 @@ * \param ident the texture name (or path) * \return a borrowed texture or NULL on failure */ - struct mlk_texture * (*init_texture)(struct mlk_map_loader *self, - struct mlk_map *map, - const char *ident); + struct mlk_texture * (*new_texture)(struct mlk_map_loader *self, + struct mlk_map *map, + const char *ident); /** * (read-write) @@ -80,8 +85,8 @@ * \return a unused sprite * \return a borrowed sprite or NULL on failure */ - struct mlk_sprite * (*init_sprite)(struct mlk_map_loader *self, - struct mlk_map *map); + struct mlk_sprite * (*new_sprite)(struct mlk_map_loader *self, + struct mlk_map *map); /** * (read-write) @@ -94,10 +99,10 @@ * \param n the number of tile items (rows * columns) * \return a pointer to a usable area or NULL on failure */ - unsigned int * (*alloc_tiles)(struct mlk_map_loader *self, - struct mlk_map *map, - enum mlk_map_layer_type type, - size_t n); + unsigned int * (*new_tiles)(struct mlk_map_loader *self, + struct mlk_map *map, + enum mlk_map_layer_type type, + size_t n); /** * (read-write, optional) @@ -112,13 +117,13 @@ * \param h the object height * \param argument optional data to pass to the object */ - void (*load_object)(struct mlk_map_loader *self, - struct mlk_map *map, - int x, - int y, - unsigned int w, - unsigned int h, - const char *argument); + void (*new_object)(struct mlk_map_loader *self, + struct mlk_map *map, + int x, + int y, + unsigned int w, + unsigned int h, + const char *argument); /** * (read-write)
--- a/libmlk-rpg/mlk/rpg/tileset-loader-file.c Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/tileset-loader-file.c Tue Mar 21 14:08:15 2023 +0100 @@ -25,35 +25,46 @@ #include <mlk/core/sprite.h> #include <mlk/core/texture.h> +#include "loader-file_p.h" #include "tileset-loader-file.h" #include "tileset-loader.h" #include "tileset.h" -static inline void * -allocate(void ***array, size_t width) -{ - void **ptr, *elem; +struct self { + /* Resources allocator. */ + struct mlk__loader_file *loader; - /* Not yet allocated? Allocate a new pointer element. */ - if (!*array) - ptr = mlk_alloc_new0(1, sizeof (void *)); - else - ptr = mlk_alloc_expand(*array, 1); + /* Arrays reallocated on purpose. */ + struct mlk_tileset_collision *tilecollisions; + struct mlk_tileset_animation *tileanimations; +}; + +static struct self * +self_new(const char *path) +{ + struct self *self; - if (!ptr) + if (!(self = mlk_alloc_new0(1, sizeof (*self)))) return NULL; + if (!(self->loader = mlk__loader_file_new(path))) { + mlk_alloc_free(self); + return NULL; + } - /* Now allocate the element itself because. */ - if (!(elem = mlk_alloc_new0(1, width))) - return NULL; + return self; +} - /* Store it into the array of elements. */ - ptr[mlk_alloc_getn(ptr) - 1] = elem; - *array = ptr; +static void +self_free(struct self *self) +{ + mlk__loader_file_free(self->loader); - return elem; + /* Clear array of collisions/animations .*/ + mlk_alloc_free(self->tilecollisions); + mlk_alloc_free(self->tileanimations); } + static void * expand(void **array, size_t n, size_t w) { @@ -70,128 +81,96 @@ return ptr; } -static void -finish(void ***ptr, void (*finish)(void *)) -{ - size_t len; - - /* Already cleared. */ - if (!*ptr) - return; - - len = mlk_alloc_getn(*ptr); - - for (size_t i = 0; i < len; ++i) - finish((*ptr)[i]); - - mlk_alloc_free(*ptr); - *ptr = NULL; -} - -static void -finish_texture(void *element) +static struct mlk_texture * +new_texture(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset, const char *ident) { - mlk_texture_finish(element); - mlk_alloc_free(element); -} + (void)tileset; -static struct mlk_texture * -init_texture(struct mlk_tileset_loader *self, const char *ident) -{ - struct mlk_tileset_loader_file *file = self->data; - struct mlk_texture *texture; - char path[MLK_PATH_MAX]; + struct self *self = loader->data; - snprintf(path, sizeof (path), "%s/%s", file->directory, ident); - - /* No need to deallocate, already done in finish anyway. */ - if (!(texture = allocate((void ***)&file->textures, sizeof (struct mlk_texture)))) - return NULL; - if (mlk_image_open(texture, path) < 0) - return NULL; - - return texture; + return mlk__loader_file_texture_open(self->loader, ident); } static struct mlk_sprite * -init_sprite(struct mlk_tileset_loader *self) +new_sprite(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset) { - struct mlk_tileset_loader_file *file = self->data; + (void)tileset; - return allocate((void ***)&file->sprites, sizeof (struct mlk_sprite)); + struct self *self = loader->data; + + return mlk__loader_file_sprite_new(self->loader); } static struct mlk_animation * -init_animation(struct mlk_tileset_loader *self) +new_animation(struct mlk_tileset_loader *loader, struct mlk_tileset *tileset) { - struct mlk_tileset_loader_file *file = self->data; + (void)tileset; - return allocate((void ***)&file->animations, sizeof (struct mlk_animation)); + struct self *self = loader->data; + + return mlk__loader_file_animation_new(self->loader); } struct mlk_tileset_collision * -expand_collisions(struct mlk_tileset_loader *self, +expand_collisions(struct mlk_tileset_loader *loader, + struct mlk_tileset *tileset, struct mlk_tileset_collision *array, size_t arraysz) { + (void)tileset; (void)array; - struct mlk_tileset_loader_file *file = self->data; + struct self *self = loader->data; - return expand((void **)&file->tilecollisions, arraysz, sizeof (struct mlk_tileset_collision)); + return expand((void **)&self->tilecollisions, arraysz, sizeof (struct mlk_tileset_collision)); } struct mlk_tileset_animation * -expand_animations(struct mlk_tileset_loader *self, +expand_animations(struct mlk_tileset_loader *loader, + struct mlk_tileset *tileset, struct mlk_tileset_animation *array, size_t arraysz) { + (void)tileset; (void)array; - struct mlk_tileset_loader_file *file = self->data; + struct self *self = loader->data; - return expand((void **)&file->tileanimations, arraysz, sizeof (struct mlk_tileset_animation)); + return expand((void **)&self->tileanimations, arraysz, sizeof (struct mlk_tileset_animation)); } -void -mlk_tileset_loader_file_init(struct mlk_tileset_loader_file *file, - struct mlk_tileset_loader *loader, - const char *filename) +int +mlk_tileset_loader_file_init(struct mlk_tileset_loader *loader, const char *filename) { - assert(file); assert(loader); assert(filename); - char filepath[MLK_PATH_MAX]; + struct self *self; - memset(file, 0, sizeof (*file)); memset(loader, 0, sizeof (*loader)); - /* Determine base filename base directory. */ - mlk_util_strlcpy(filepath, filename, sizeof (filepath)); - mlk_util_strlcpy(file->directory, mlk_util_dirname(filepath), sizeof (file->directory)); + if (!(self = self_new(filename))) + return -1; - loader->data = file; - loader->init_texture = init_texture; - loader->init_sprite = init_sprite; - loader->init_animation = init_animation; + loader->data = self; + loader->new_texture = new_texture; + loader->new_sprite = new_sprite; + loader->new_animation = new_animation; loader->expand_collisions = expand_collisions; loader->expand_animations = expand_animations; + + return 0; } void -mlk_tileset_loader_file_finish(struct mlk_tileset_loader_file *file) +mlk_tileset_loader_file_finish(struct mlk_tileset_loader *loader) { - assert(file); + assert(loader); + + struct self *self = loader->data; - /* Finalize individual elements. */ - finish((void ***)&file->textures, finish_texture); - finish((void ***)&file->sprites, mlk_alloc_free); - finish((void ***)&file->animations, mlk_alloc_free); + if (self) + self_free(self); - /* Clear array of collisions/animations .*/ - mlk_alloc_free(file->tilecollisions); - mlk_alloc_free(file->tileanimations); - - memset(file, 0, sizeof (*file)); + memset(loader, 0, sizeof (*loader)); }
--- a/libmlk-rpg/mlk/rpg/tileset-loader-file.h Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/tileset-loader-file.h Tue Mar 21 14:08:15 2023 +0100 @@ -31,59 +31,27 @@ * the allocator functions can be changed. */ -#include <mlk/util/util.h> - -struct mlk_animation; -struct mlk_sprite; -struct mlk_texture; -struct mlk_tileset_animation; -struct mlk_tileset_collision; struct mlk_tileset_loader; -/** - * \struct mlk_tileset_loader_file - * \brief Tileset file loader structure - */ -struct mlk_tileset_loader_file { - /** - * (read-only) - * - * Computed tileset file directory. - */ - char directory[MLK_PATH_MAX]; - - /** \cond MLK_PRIVATE_DECLS */ - struct mlk_texture **textures; - struct mlk_sprite **sprites; - struct mlk_animation **animations; - struct mlk_tileset_collision *tilecollisions; - struct mlk_tileset_animation *tileanimations; - /** \endcond MLK_PRIVATE_DECLS */ -}; - #if defined(__cplusplus) extern "C" { #endif /** - * Fill the abstract loader with appropriate implementation. + * Initialize the loader with internal functions and internal data to allocate + * and find appropriate resources relative to the tileset filename. * - * All loader member functions will be set and ::mlk_tileset_loader::data will - * be set to file loader. + * After loading the tileset with this underlying loader, it should be kept + * until the tileset is no longer used. * - * The file and loader structure are zero'ed before being initialized. - * - * \pre file != NULL * \pre loader != NULL * \pre filename != NULL - * \param file the file loader * \param loader the abstract loader interface * \param filename path to the tileset file + * \return 0 on success or -1 on error */ -void -mlk_tileset_loader_file_init(struct mlk_tileset_loader_file *file, - struct mlk_tileset_loader *loader, - const char *filename); +int +mlk_tileset_loader_file_init(struct mlk_tileset_loader *loader, const char *filename); /** * Cleanup allocated resources by this file loader. @@ -92,7 +60,7 @@ * \param file the file loader */ void -mlk_tileset_loader_file_finish(struct mlk_tileset_loader_file *file); +mlk_tileset_loader_file_finish(struct mlk_tileset_loader *file); #if defined(__cplusplus) }
--- a/libmlk-rpg/mlk/rpg/tileset-loader.c Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/tileset-loader.c Tue Mar 21 14:08:15 2023 +0100 @@ -106,7 +106,7 @@ size_t collisionsz = 0; while (fscanf(fp, "%u|%d|%d|%u|%u\n", &id, &x, &y, &w, &h) == 5) { - if (!(array = loader->expand_collisions(loader, collisions, collisionsz + 1))) + if (!(array = loader->expand_collisions(loader, tileset, collisions, collisionsz + 1))) return -1; collisions = array; @@ -158,13 +158,13 @@ * 4. Link the animation to the tileset animation. */ while (fscanf(fp, fmt, &id, filename, &delay) == 3) { - if (!(texture = loader->init_texture(loader, filename))) + if (!(texture = loader->new_texture(loader, tileset, filename))) return -1; - if (!(sprite = loader->init_sprite(loader))) + if (!(sprite = loader->new_sprite(loader, tileset))) return -1; - if (!(animation = loader->init_animation(loader))) + if (!(animation = loader->new_animation(loader, tileset))) return -1; - if (!(array = loader->expand_animations(loader, tileanimations, tileanimationsz + 1))) + if (!(array = loader->expand_animations(loader, tileset, tileanimations, tileanimationsz + 1))) return -1; /* Bind the texture to the new sprite. */ @@ -211,9 +211,9 @@ return mlk_errf("missing tile dimensions before image"); if (!(p = strchr(line, '|'))) return mlk_errf("could not parse image"); - if (!(texture = loader->init_texture(loader, p + 1))) + if (!(texture = loader->new_texture(loader, tileset, p + 1))) return -1; - if (!(sprite = loader->init_sprite(loader))) + if (!(sprite = loader->new_sprite(loader, tileset))) return -1; /* Initialize the sprite with the texture. */
--- a/libmlk-rpg/mlk/rpg/tileset-loader.h Mon Mar 20 21:59:02 2023 +0100 +++ b/libmlk-rpg/mlk/rpg/tileset-loader.h Tue Mar 21 14:08:15 2023 +0100 @@ -45,7 +45,7 @@ /** * \struct mlk_tileset_loader - * \brief Abstract loader structure + * \brief Abstract tileset loader structure * * All function pointers must be set. */ @@ -63,11 +63,13 @@ * Open a texture from the given ident name. * * \param self this loader + * \param tileset the underlying tileset being loaded * \param ident the texture name (or path) * \return a borrowed texture or NULL on failure */ - struct mlk_texture * (*init_texture)(struct mlk_tileset_loader *self, - const char *ident); + struct mlk_texture * (*new_texture)(struct mlk_tileset_loader *self, + struct mlk_tileset *tileset, + const char *ident); /** * (read-write) @@ -75,10 +77,11 @@ * Return a sprite that the loader needs. * * \param self this loader - * \return a unused sprite + * \param tileset the underlying tileset being loaded * \return a borrowed sprite or NULL on failure */ - struct mlk_sprite * (*init_sprite)(struct mlk_tileset_loader *self); + struct mlk_sprite * (*new_sprite)(struct mlk_tileset_loader *self, + struct mlk_tileset *tileset); /** * (read-write) @@ -86,10 +89,11 @@ * Return an animation that the loader needs. * * \param self this loader - * \return a unused animation + * \param tileset the underlying tileset being loaded * \return a borrowed animation or NULL on failure */ - struct mlk_animation * (*init_animation)(struct mlk_tileset_loader *self); + struct mlk_animation * (*new_animation)(struct mlk_tileset_loader *self, + struct mlk_tileset *tileset); /** * (read-write) @@ -97,18 +101,20 @@ * Expand the collision array by one element. * * \param self this loader + * \param tileset the underlying tileset being loaded * \param array the old array (can be NULL) to reallocate * \param arraysz the new array size (usually +1 than before) * \return a unused animation */ struct mlk_tileset_collision * (*expand_collisions)(struct mlk_tileset_loader *self, + struct mlk_tileset *tileset, struct mlk_tileset_collision *array, size_t arraysz); /** * (read-write) * - * Expand the animation array by one element. + * Expand the tileset animation array by one element. * * \param self this loader * \param array the old array (can be NULL) to reallocate @@ -116,6 +122,7 @@ * \return a unused animation */ struct mlk_tileset_animation * (*expand_animations)(struct mlk_tileset_loader *self, + struct mlk_tileset *tileset, struct mlk_tileset_animation *array, size_t arraysz);
--- a/tests/test-tileset.c Mon Mar 20 21:59:02 2023 +0100 +++ b/tests/test-tileset.c Tue Mar 21 14:08:15 2023 +0100 @@ -31,7 +31,6 @@ * Convenient struct that pack all the required data. */ struct tileset { - struct mlk_tileset_loader_file file; struct mlk_tileset_loader loader; struct mlk_tileset tileset; }; @@ -39,7 +38,7 @@ static inline int tileset_open(struct tileset *ts, const char *path) { - mlk_tileset_loader_file_init(&ts->file, &ts->loader, path); + mlk_tileset_loader_file_init(&ts->loader, path); return mlk_tileset_loader_open(&ts->loader, &ts->tileset, path); } @@ -47,7 +46,7 @@ static inline void tileset_finish(struct tileset *ts) { - mlk_tileset_loader_file_finish(&ts->file); + mlk_tileset_loader_file_finish(&ts->loader); } static void