From e03cec1c388da349e31af8a4f323c0715f54379c Mon Sep 17 00:00:00 2001 From: Andrew Kesterson Date: Sun, 24 May 2026 09:51:58 -0400 Subject: [PATCH] Fixed loading of tilesets and images from tilemaps to correctly use relative paths. FPS has dropped dramatically on tilemap rendering - impact as much as 75% - and I think it has something to do with the string limits that had to be increased to make this work. Probably has to do with SDL string property hashing in the registry methods related to tilesets. --- include/akgl/SDL_GameControllerDB.h | 2 +- include/akgl/error.h | 11 +++++ include/akgl/staticstring.h | 5 ++- include/akgl/tilemap.h | 13 +++--- include/akgl/util.h | 3 ++ src/staticstring.c | 15 +++++++ src/tilemap.c | 56 +++++++++++++++--------- src/util.c | 67 +++++++++++++++++++++++++++++ 8 files changed, 143 insertions(+), 29 deletions(-) diff --git a/include/akgl/SDL_GameControllerDB.h b/include/akgl/SDL_GameControllerDB.h index d0de86c..fbfd383 100644 --- a/include/akgl/SDL_GameControllerDB.h +++ b/include/akgl/SDL_GameControllerDB.h @@ -1,7 +1,7 @@ #ifndef _SDL_GAMECONTROLLERDB_H_ #define _SDL_GAMECONTROLLERDB_H_ -// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Thu May 21 09:41:48 PM EDT 2026 +// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Sun May 24 09:44:15 AM EDT 2026 #define AKGL_SDL_GAMECONTROLLER_DB_LEN 2228 diff --git a/include/akgl/error.h b/include/akgl/error.h index 96e5c41..a1f0098 100644 --- a/include/akgl/error.h +++ b/include/akgl/error.h @@ -1,6 +1,17 @@ #ifndef _ERROR_H_ #define _ERROR_H_ +// This macro is used to silence warnings on string concatenation operations that may fail. +// e.g., combining two element of PATH_MAX into a string buffer of AKGL_STRING_MAX_LENGTH. +// We have to draw a line in the sand somewhere or we will just let our buffers grow forever +// to keep the compiler happy. +#define DISABLE_GCC_WARNING_FORMAT_TRUNCATION \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wformat-truncation\"") + +#define RESTORE_GCC_WARNINGS \ + _Pragma("GCC diagnostic pop") + #define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1 #endif // _ERROR_H_ diff --git a/include/akgl/staticstring.h b/include/akgl/staticstring.h index 6cb1d03..704a70c 100644 --- a/include/akgl/staticstring.h +++ b/include/akgl/staticstring.h @@ -3,8 +3,9 @@ #include "string.h" #include +#include -#define AKGL_MAX_STRING_LENGTH 256 +#define AKGL_MAX_STRING_LENGTH PATH_MAX typedef struct { @@ -13,5 +14,5 @@ typedef struct } akgl_String; akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init); - +akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dst, int count); #endif //_STRING_H_ diff --git a/include/akgl/tilemap.h b/include/akgl/tilemap.h index bbfd930..6f8a917 100644 --- a/include/akgl/tilemap.h +++ b/include/akgl/tilemap.h @@ -1,6 +1,7 @@ #ifndef _TILEMAP_H_ #define _TILEMAP_H_ +#include #include "actor.h" #include "staticstring.h" #include @@ -11,7 +12,7 @@ #define AKGL_TILEMAP_MAX_TILESETS 16 #define AKGL_TILEMAP_MAX_TILES_PER_IMAGE 65536 #define AKGL_TILEMAP_MAX_TILESET_NAME_SIZE 512 -#define AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE 512 +#define AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE PATH_MAX #define AKGL_TILEMAP_MAX_OBJECT_NAME_SIZE 512 #define AKGL_TILEMAP_MAX_OBJECTS_PER_LAYER 128 @@ -112,11 +113,11 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_tilemap_property(json_t *obj, c akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_properties_string(json_t *obj, char *key, akgl_String **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_properties_integer(json_t *obj, char *key, int *dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int tilesetidx); -akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid); -akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid); -akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root); -akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx); -akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root); +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname); +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname); +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, akgl_String *dirname); +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname); +akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname); akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_release(akgl_Tilemap *dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor); diff --git a/include/akgl/util.h b/include/akgl/util.h index f59f0b0..ed28f49 100644 --- a/include/akgl/util.h +++ b/include/akgl/util.h @@ -2,6 +2,7 @@ #define _UTIL_H_ #include +#include typedef struct point { int x; @@ -22,6 +23,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_rectangle_points(RectanglePoints *dest, akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_point_rectangle(point *p, RectanglePoints *r, bool *collide); akerr_ErrorContext AKERR_NOIGNORE *akgl_collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *collide); +akerr_ErrorContext AKERR_NOIGNORE *akgl_path_relative(char *root, char *path, akgl_String *dst); + // These are REALLY slow routines that are only useful in testing harnesses akerr_ErrorContext AKERR_NOIGNORE *akgl_compare_sdl_surfaces(SDL_Surface *s1, SDL_Surface *s2); akerr_ErrorContext AKERR_NOIGNORE *akgl_render_and_compare(SDL_Texture *t1, SDL_Texture *t2, int x, int y, int w, int h, char *writeout); diff --git a/src/staticstring.c b/src/staticstring.c index 0d3afc0..5c85fe6 100644 --- a/src/staticstring.c +++ b/src/staticstring.c @@ -1,5 +1,6 @@ #include #include +#include akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init) { @@ -13,3 +14,17 @@ akerr_ErrorContext *akgl_string_initialize(akgl_String *obj, char *init) obj->refcount = 1; SUCCEED_RETURN(errctx); } + +akerr_ErrorContext *akgl_string_copy(akgl_String *src, akgl_String *dst, int count) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "NULL argument"); + FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument"); + if ( count == 0 ) { + count = AKGL_MAX_STRING_LENGTH; + } + if ( (char *)dst->data != strncpy((char *)&src->data, (char *)&dst->data, count) ) { + FAIL_RETURN(e, errno, "strncpy"); + } + SUCCEED_RETURN(e); +} diff --git a/src/tilemap.c b/src/tilemap.c index 395be42..59a30d8 100644 --- a/src/tilemap.c +++ b/src/tilemap.c @@ -1,9 +1,13 @@ +#include +#include + #include #include #include -#include #include + #include +#include #include #include @@ -13,6 +17,7 @@ #include #include + akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char *type, json_t **dest) { PREPARE_ERROR(errctx); @@ -96,7 +101,7 @@ akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, floa SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx) +akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilemap *dest, int tsidx, akgl_String *dirname) { PREPARE_ERROR(errctx); akgl_String *tmpstr = NULL; @@ -118,13 +123,15 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets_each(json_t *tileset, akgl_Tilema ); CATCH(errctx, akgl_get_json_string_value((json_t *)tileset, "image", &tmpstr)); - + + DISABLE_GCC_WARNING_FORMAT_TRUNCATION snprintf((char *)&dest->tilesets[tsidx].imagefilename, AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE, - "%s%s", - SDL_GetBasePath(), + "%s/%s", + dirname->data, tmpstr->data ); + RESTORE_GCC_WARNINGS dest->tilesets[tsidx].texture = IMG_LoadTexture(renderer, (char *)&dest->tilesets[tsidx].imagefilename); FAIL_ZERO_BREAK(errctx, dest->tilesets[tsidx].texture, AKERR_NULLPOINTER, "Failed loading tileset image : %s", SDL_GetError()); @@ -190,7 +197,7 @@ akerr_ErrorContext *akgl_tilemap_compute_tileset_offsets(akgl_Tilemap *dest, int SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root) +akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root, akgl_String *dirname) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "Received NULL tilemap pointer"); @@ -205,7 +212,7 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root) CATCH(errctx, akgl_get_json_array_value(root, "tilesets", &tilesets)) for (i = 0; i < json_array_size((json_t *)tilesets); i++) { CATCH(errctx, akgl_get_json_array_index_object((json_t *)tilesets, i, &jstileset)); - CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i)); + CATCH(errctx, akgl_tilemap_load_tilesets_each(jstileset, dest, i, dirname)); CATCH(errctx, akgl_tilemap_compute_tileset_offsets(dest, i)); dest->numtilesets += 1; } @@ -216,7 +223,7 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root) SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid) +akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid, akgl_String *dirname) { PREPARE_ERROR(errctx); akgl_String *tmpstr = NULL; @@ -260,7 +267,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *cur SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid) +akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname) { PREPARE_ERROR(errctx); json_t *layerdata = NULL; @@ -290,7 +297,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t * CATCH(errctx, akgl_get_json_string_value((json_t *)layerdatavalue, "type", &tmpstr)); if ( strcmp(tmpstr->data, "actor") == 0 ) { - CATCH(errctx, akgl_tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid)); + CATCH(errctx, akgl_tilemap_load_layer_object_actor(curobj, layerdatavalue, layerid, dirname)); } else if ( strcmp(tmpstr->data, "perspective") == 0 ) { curobj->visible = false; if ( strcmp((char *)curobj->name, "p_foreground") == 0 ) { @@ -313,7 +320,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_objects(akgl_Tilemap *dest, json_t * SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid) +akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname) { PREPARE_ERROR(errctx); json_t *layerdata = NULL; @@ -322,6 +329,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference"); + FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname"); ATTEMPT { CATCH(errctx, akgl_get_json_integer_value(root, "height", &dest->layers[layerid].height)); @@ -341,7 +349,7 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid) +akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname) { PREPARE_ERROR(errctx); akgl_String *tmpstr; @@ -349,18 +357,21 @@ akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *ro FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination tilemap reference"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "NULL tilemap root reference"); + FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname"); ATTEMPT { CATCH(errctx, akgl_heap_next_string(&tmpstr)); CATCH(errctx, akgl_heap_next_string(&fpath)); CATCH(errctx, akgl_get_json_string_value(root, "image", &tmpstr)); + DISABLE_GCC_WARNING_FORMAT_TRUNCATION snprintf((char *)&fpath->data, AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE, - "%s%s", - SDL_GetBasePath(), + "%s/%s", + dirname->data, tmpstr->data ); + RESTORE_GCC_WARNINGS dest->layers[layerid].texture = IMG_LoadTexture(renderer, (char *)fpath->data); FAIL_ZERO_BREAK(errctx, dest->layers[layerid].texture, AKGL_ERR_SDL, "%s", SDL_GetError()); @@ -375,11 +386,12 @@ akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *ro SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root) +akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, akgl_String *dirname) { PREPARE_ERROR(errctx); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL tilemap pointer"); FAIL_ZERO_RETURN(errctx, root, AKERR_NULLPOINTER, "akgl_tilemap_load_layers received NULL json object pointer"); + FAIL_ZERO_RETURN(errctx, dirname, AKERR_NULLPOINTER, "dirname"); json_t *layers = NULL; json_t *layer = NULL; akgl_String *tmpstr = NULL; @@ -406,13 +418,13 @@ akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root) SDL_Log("Layer %d has type %s", layerid, tmpstr->data); if ( strncmp((char *)tmpstr->data, "objectgroup", strlen((char *)tmpstr->data)) == 0 ) { dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_OBJECTS; - CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid)); + CATCH(errctx, akgl_tilemap_load_layer_objects((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname)); } else if ( strncmp((char *)tmpstr->data, "tilelayer", strlen((char *)tmpstr->data)) == 0 ) { dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_TILES; - CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid)); + CATCH(errctx, akgl_tilemap_load_layer_tile((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname)); } else if ( strncmp((char *)tmpstr->data, "imagelayer", strlen((char *)tmpstr->data)) == 0 ) { dest->layers[layerid].type = AKGL_TILEMAP_LAYER_TYPE_IMAGE; - CATCH(errctx, akgl_tilemap_load_layer_image((akgl_Tilemap *)dest, (json_t *)layer, layerid)); + CATCH(errctx, akgl_tilemap_load_layer_image((akgl_Tilemap *)dest, (json_t *)layer, layerid, dirname)); } layer = NULL; layerid += 1; @@ -429,6 +441,7 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest) json_t *json = NULL; //akgl_String *tmpstr = NULL; json_error_t error; + akgl_String *dirnamestr = NULL; FAIL_ZERO_RETURN(errctx, fname, AKERR_NULLPOINTER, "load_tilemap received null filename"); FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "load_tilemap received null tilemap"); @@ -437,10 +450,13 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest) dest->p_foreground_scale = 1.0; dest->p_vanishing_scale = 1.0; + PASS(errctx, akgl_heap_next_string(&dirnamestr)); ATTEMPT { //CATCH(errctx, akgl_heap_next_string(&tmpstr)); //CATCH(errctx, akgl_string_initialize(tmpstr, NULL)); //SDL_snprintf(tmpstr->data, AKGL_MAX_STRING_LENGTH, "%s%s", SDL_GetBasePath(), fname); + CATCH(errctx, aksl_realpath(fname, (char *)&dirnamestr->data)); + dirname((char *)&dirnamestr->data); json = json_load_file(fname, 0, &error); FAIL_ZERO_BREAK( errctx, @@ -461,8 +477,8 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest) FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size"); } - CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json)); - CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json)); + CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json, dirnamestr)); + CATCH(errctx, akgl_tilemap_load_tilesets((akgl_Tilemap *)dest, (json_t *)json, dirnamestr)); if ( dest->p_foreground_y && dest->p_vanishing_y ) { // How much bigger is the foreground vs the vanishing point? diff --git a/src/util.c b/src/util.c index 1c75a25..93763a6 100644 --- a/src/util.c +++ b/src/util.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -9,6 +10,72 @@ #include #include #include +#include + +#include + +akerr_ErrorContext *akgl_path_relative_root(char *root, char *path, akgl_String *dst) +{ + PREPARE_ERROR(e); + akgl_String *pathbuf; + akgl_String *strbuf; + char *result; + int rootlen; + int pathlen; + int count; + + FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument"); + FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument"); + FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument"); + + PASS(e, akgl_heap_next_string(&strbuf)); + PASS(e, akgl_heap_next_string(&pathbuf)); + + ATTEMPT { + // Is it relative to the root? + rootlen = strlen(root); + pathlen = strlen(path); + if ( (rootlen + pathlen) >= AKGL_MAX_STRING_LENGTH ) { + FAIL_RETURN(e, AKERR_OUTOFBOUNDS, "Total path length (%d) is greater than maximum akgl_String length (%d)", (rootlen + pathlen), AKGL_MAX_STRING_LENGTH); + } + CATCH(e, aksl_sprintf(&count, (char *)&pathbuf->data, "%s/%s", root, path)); + CATCH(e, aksl_realpath((char *)&pathbuf->data, (char *)&strbuf->data)); + CATCH(e, akgl_string_copy(strbuf, dst, 0)); + } CLEANUP { + IGNORE(akgl_heap_release_string(strbuf)); + IGNORE(akgl_heap_release_string(pathbuf)); + } PROCESS(e) { + } FINISH(e, true); + SUCCEED_RETURN(e); +} + +akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst) +{ + PREPARE_ERROR(e); + akgl_String *strbuf; + char *result; + + FAIL_ZERO_RETURN(e, root, AKERR_NULLPOINTER, "NULL argument"); + FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "NULL argument"); + FAIL_ZERO_RETURN(e, dst, AKERR_NULLPOINTER, "NULL argument"); + + PASS(e, akgl_heap_next_string(&strbuf)); + + ATTEMPT { + // Is path relative to our current working directory? + CATCH(e, aksl_realpath(path, (char *)&strbuf->data)); + // Yes it is. strbuf->data contains the absolute path. + CATCH(e, akgl_string_copy(strbuf, dst, 0)); + } CLEANUP { + IGNORE(akgl_heap_release_string(strbuf)); + } PROCESS(e) { + } HANDLE(e, ENOENT) { + // Path is not relative to our current working directory + // Noop - execution proceeds after the break + return akgl_path_relative_root(root, path, dst); + } FINISH(e, true); + SUCCEED_RETURN(e); +} akerr_ErrorContext *akgl_rectangle_points(RectanglePoints *dest, SDL_FRect *rect) {