Give every exported function a declaration, and check that it stays that way

Closes internal-consistency items 7 through 15. Nineteen non-static functions
were in the ABI with no declaration anywhere, so no consumer could call them
and any consumer could collide with them.

The four gamepad_handle_* functions are the ones that mattered: controller.h
declared akgl_controller_handle_button_down and three siblings that did not
exist, so anything compiled against the header alone failed to link. The
definitions carry the declared names now, which also closes Defects -> Known
and still open item 10, and their documentation moved to the header.

The rest are either declared under a "part of the internal API" block --
akgl_game_save_actors and akgl_game_load_versioncmp, which tests/game.c had to
declare for itself, plus six tilemap loader helpers the untested-loader work
wants to reach -- or static, which is what the four save iterators and
load_objectnamemap should always have been. akgl_path_relative_from is deleted:
declared nowhere, called from nowhere, never wrote its output, and leaked a
pooled string on every call, so it closes Known and still open item 4 and item
40 by ceasing to exist.

scripts/check_api_surface.sh keeps it closed. It reads the built library's
dynamic symbol table and every public header with comments stripped, and fails
on an exported akgl_* symbol that is declared nowhere. Stripping comments is
the whole point -- four of these were mentioned in controller.h prose, which is
how they went unnoticed.

The pool-size ceilings are defined once, in heap.h, so the #ifndef override
hook fires for the first time; actor.h, sprite.h and character.h were defining
the same four unconditionally from headers heap.h includes above its own guard.
tests/header_pool_override.c fails the compile if that regresses.

Also here: (void) rather than () on the twelve no-argument entry points,
AKERR_NOIGNORE only on declarations, static helpers with the akgl_ prefix
dropped, and the six parameter-name mismatches. akgl_get_json_with_default had
its two contexts swapped rather than merely misspelled -- the incoming one was
`err` and its own was `e`, which is the name reserved for an incoming one.

24/24 pass, reindent --check clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 23:44:38 -04:00
parent 9924d74dcc
commit 3a262bee54
29 changed files with 676 additions and 620 deletions

View File

@@ -328,7 +328,7 @@ void akgl_registry_iterate_actor(void *userdata, SDL_PropertiesID registry, cons
} FINISH_NORETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_left_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "actor");
@@ -342,7 +342,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_on(akgl_Actor *obj, SDL_
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_left_off(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -357,7 +357,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_left_off(akgl_Actor *obj, SDL
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_right_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -371,7 +371,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_on(akgl_Actor *obj, SDL
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_right_off(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -386,7 +386,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_right_off(akgl_Actor *obj, SD
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_up_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -400,7 +400,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_on(akgl_Actor *obj, SDL_Ev
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_up_off(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -415,7 +415,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_up_off(akgl_Actor *obj, SDL_E
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_down_on(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");
@@ -429,7 +429,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_down_on(akgl_Actor *obj, SDL_
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event)
akerr_ErrorContext *akgl_actor_cmhf_down_off(akgl_Actor *obj, SDL_Event *event)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL actor");

View File

@@ -283,7 +283,7 @@ static void SDLCALL audio_stream_callback(void *userdata, SDL_AudioStream *strea
}
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_init(void)
akerr_ErrorContext *akgl_audio_init(void)
{
SDL_AudioSpec spec;
@@ -322,7 +322,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_init(void)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_shutdown(void)
akerr_ErrorContext *akgl_audio_shutdown(void)
{
SDL_AudioStream *closing = audiostream;
@@ -337,7 +337,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_shutdown(void)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_tone(int voice, float32_t hz, uint32_t ms)
akerr_ErrorContext *akgl_audio_tone(int voice, float32_t hz, uint32_t ms)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -350,7 +350,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_tone(int voice, float32_t hz, uint
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_sweep(int voice, float32_t from_hz, float32_t to_hz, float32_t step_hz, uint32_t ms)
akerr_ErrorContext *akgl_audio_sweep(int voice, float32_t from_hz, float32_t to_hz, float32_t step_hz, uint32_t ms)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -368,7 +368,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_sweep(int voice, float32_t from_hz
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_stop(int voice)
akerr_ErrorContext *akgl_audio_stop(int voice)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -382,7 +382,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_stop(int voice)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_waveform(int voice, akgl_AudioWaveform waveform)
akerr_ErrorContext *akgl_audio_waveform(int voice, akgl_AudioWaveform waveform)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -401,7 +401,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_waveform(int voice, akgl_AudioWave
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_envelope(int voice, uint32_t attack, uint32_t decay, float32_t sustain, uint32_t release)
akerr_ErrorContext *akgl_audio_envelope(int voice, uint32_t attack, uint32_t decay, float32_t sustain, uint32_t release)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -422,7 +422,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_envelope(int voice, uint32_t attac
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_volume(float32_t level)
akerr_ErrorContext *akgl_audio_volume(float32_t level)
{
PREPARE_ERROR(errctx);
FAIL_NONZERO_RETURN(
@@ -439,7 +439,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_volume(float32_t level)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_voice_active(int voice, bool *active)
akerr_ErrorContext *akgl_audio_voice_active(int voice, bool *active)
{
PREPARE_ERROR(errctx);
PASS(errctx, check_voice(voice));
@@ -452,7 +452,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_voice_active(int voice, bool *acti
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_audio_mix(float32_t *dest, int frames)
akerr_ErrorContext *akgl_audio_mix(float32_t *dest, int frames)
{
akgl_AudioVoice *voice = NULL;
float32_t sum = 0.0f;

View File

@@ -72,7 +72,7 @@ akerr_ErrorContext *akgl_character_sprite_get(akgl_Character *basechar, int stat
// SDL iterator so we can't return error information here, void only
// this means we don't have anywhere to send exceptions up to, so if we hit an error, we log and exit(1) here
void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID registry, const char *name)
void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID props, const char *name)
{
PREPARE_ERROR(errctx);
akgl_Sprite *spriteptr;
@@ -80,7 +80,7 @@ void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID regis
ATTEMPT {
FAIL_ZERO_BREAK(errctx, opflags, AKERR_NULLPOINTER, "Character state sprite iterator received null iterator op pointer");
FAIL_ZERO_BREAK(errctx, name, AKERR_NULLPOINTER, "Character state sprite iterator received null sprite name");
spriteptr = (akgl_Sprite *)SDL_GetPointerProperty(registry, name, NULL);
spriteptr = (akgl_Sprite *)SDL_GetPointerProperty(props, name, NULL);
FAIL_ZERO_BREAK(errctx, spriteptr, AKERR_NULLPOINTER, "Character state sprite for %s not found", name);
if ( AKGL_BITMASK_HAS(opflags->flags, AKGL_ITERATOR_OP_RELEASE) ) {
CATCH(errctx, akgl_heap_release_sprite(spriteptr));
@@ -114,7 +114,7 @@ void akgl_character_state_sprites_iterate(void *userdata, SDL_PropertiesID regis
* @throws AKERR_OUTOFBOUNDS If the array is indexed past its end.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
static akerr_ErrorContext *akgl_character_load_json_state_int_from_strings(json_t *states, int *dest)
static akerr_ErrorContext *character_load_json_state_int_from_strings(json_t *states, int *dest)
{
int i = 0;
long newstate = 0;
@@ -161,7 +161,7 @@ static akerr_ErrorContext *akgl_character_load_json_state_int_from_strings(json_
* @throws AKERR_OUTOFBOUNDS If an array is indexed past its end.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
static akerr_ErrorContext *akgl_character_load_json_inner(json_t *json, akgl_Character *obj)
static akerr_ErrorContext *character_load_json_inner(json_t *json, akgl_Character *obj)
{
PREPARE_ERROR(errctx);
json_t *mappings = NULL;
@@ -189,7 +189,7 @@ static akerr_ErrorContext *akgl_character_load_json_inner(json_t *json, akgl_Cha
CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &tmpstr2));
CATCH(errctx, akgl_get_json_array_value((json_t *)curmapping, "state", &statearray));
CATCH(errctx, akgl_character_load_json_state_int_from_strings(statearray, &stateval));
CATCH(errctx, character_load_json_state_int_from_strings(statearray, &stateval));
CATCH(errctx, akgl_get_json_string_value((json_t *)curmapping, "sprite", &tmpstr));
FAIL_ZERO_BREAK(
@@ -236,7 +236,7 @@ akerr_ErrorContext *akgl_character_load_json(char *filename)
AKERR_NULLPOINTER,
"Error while loading character from %s on line %d: %s", filename, error.line, error.text
);
CATCH(errctx, akgl_character_load_json_inner(json, obj));
CATCH(errctx, character_load_json_inner(json, obj));
CATCH(errctx, akgl_get_json_integer_value(json, "speedtime", (int *)&obj->speedtime));
obj->speedtime = obj->speedtime * AKGL_TIME_ONEMS_NS;
CATCH(errctx, akgl_get_json_number_value(json, "speed_x", &obj->sx));

View File

@@ -182,7 +182,7 @@ static bool keybuffer_take(akgl_Keystroke *dest)
return true;
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
akerr_ErrorContext *akgl_controller_list_keyboards(void)
{
int count;
SDL_KeyboardID *keyboards = SDL_GetKeyboards(&count);
@@ -206,7 +206,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_list_keyboards(void)
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_open_gamepads(void)
akerr_ErrorContext *akgl_controller_open_gamepads(void)
{
int count = 0;
int i = 0;
@@ -302,32 +302,7 @@ akerr_ErrorContext *akgl_controller_handle_event(void *appstate, SDL_Event *even
SUCCEED_RETURN(errctx);
}
/**
* @brief Set the movement and facing bits on the "player" actor for a D-pad or arrow press.
*
* A whole-application shortcut rather than a control-map binding: it looks the
* actor up by the literal name `"player"` in #AKGL_REGISTRY_ACTOR instead of
* being told which actor to act on, so it only ever drives one. The
* akgl_actor_cmhf_* handlers are the general form.
*
* Facing follows movement unless the actor sets `movement_controls_face`, which
* is how an actor that aims independently of the direction it walks opts out.
*
* Declared nowhere -- `controller.h` advertises this as
* `akgl_controller_handle_button_down`, which does not exist. TODO.md, "Known
* and still open" item 10.
*
* @param appstate Passed through from SDL. Required as a non-`NULL` token only;
* never read.
* @param event The button or key event. Required. Both the gamepad and
* keyboard unions are read on every call, so the arm that does
* not correspond to the event type is read as garbage -- which
* works only because no real button and keycode pair collides.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p appstate or @p event is `NULL`, or if there is
* no actor registered under the name "player".
*/
akerr_ErrorContext *gamepad_handle_button_down(void *appstate, SDL_Event *event)
akerr_ErrorContext *akgl_controller_handle_button_down(void *appstate, SDL_Event *event)
{
akgl_Actor *player = NULL;
@@ -378,23 +353,7 @@ akerr_ErrorContext *gamepad_handle_button_down(void *appstate, SDL_Event *event)
SUCCEED_RETURN(errctx);
}
/**
* @brief Clear the movement bit on the "player" actor for a D-pad or arrow release, and reset its animation.
*
* The counterpart to gamepad_handle_button_down. It clears only the movement
* bit, leaving the facing bits alone so the actor keeps looking the way it was
* walking, and rewinds `curSpriteFrameId` to 0 so the next step starts from the
* first frame of the walk cycle rather than wherever it stopped.
*
* Declared nowhere; see gamepad_handle_button_down.
*
* @param appstate Passed through from SDL. Required as a non-`NULL` token only.
* @param event The button or key release event. Required.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p appstate or @p event is `NULL`, or if there is
* no actor registered under the name "player".
*/
akerr_ErrorContext *gamepad_handle_button_up(void *appstate, SDL_Event *event)
akerr_ErrorContext *akgl_controller_handle_button_up(void *appstate, SDL_Event *event)
{
akgl_Actor *player = NULL;
@@ -433,31 +392,7 @@ akerr_ErrorContext *gamepad_handle_button_up(void *appstate, SDL_Event *event)
SUCCEED_RETURN(errctx);
}
/**
* @brief Open a gamepad that has just been plugged in, and log its mapping.
*
* SDL delivers no button events from an unopened gamepad, so a device that
* appears mid-session has to be opened here the way akgl_controller_open_gamepads
* opens the ones present at startup. A gamepad SDL has already opened is logged
* and left alone.
*
* The mapping is logged because a controller with no entry in the database
* produces no button events at all, and that is otherwise indistinguishable
* from a broken binding.
*
* Declared nowhere; see gamepad_handle_button_down.
*
* @param appstate Passed through from SDL. Required as a non-`NULL` token only.
* @param event The SDL_EVENT_GAMEPAD_ADDED event. Required. The joystick id
* is read out of the `gbutton` arm rather than `gdevice`, which
* works because the two share their `which` field.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p appstate or @p event is `NULL`.
*
* @note A failed `SDL_OpenGamepad` is logged rather than reported: this returns
* success and the device stays silent.
*/
akerr_ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
akerr_ErrorContext *akgl_controller_handle_added(void *appstate, SDL_Event *event)
{
SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL;
@@ -487,22 +422,7 @@ akerr_ErrorContext *gamepad_handle_added(void *appstate, SDL_Event *event)
SUCCEED_RETURN(errctx);
}
/**
* @brief Close a gamepad that has been unplugged.
*
* An unplugged device SDL still has open is a leaked handle, so this closes it.
* A removal for a device that was never opened is logged and otherwise ignored.
* Any control map still holding that `jsid` simply stops matching -- the map is
* not torn down.
*
* Declared nowhere; see gamepad_handle_button_down.
*
* @param appstate Passed through from SDL. Required as a non-`NULL` token only.
* @param event The SDL_EVENT_GAMEPAD_REMOVED event. Required.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p appstate or @p event is `NULL`.
*/
akerr_ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
akerr_ErrorContext *akgl_controller_handle_removed(void *appstate, SDL_Event *event)
{
SDL_JoystickID which;
SDL_Gamepad *gamepad = NULL;
@@ -521,7 +441,7 @@ akerr_ErrorContext *gamepad_handle_removed(void *appstate, SDL_Event *event)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akgl_Control *control)
akerr_ErrorContext *akgl_controller_pushmap(int controlmapid, akgl_Control *control)
{
int newmapid = 0;
PREPARE_ERROR(errctx);
@@ -538,7 +458,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_pushmap(int controlmapid, akg
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid)
akerr_ErrorContext *akgl_controller_default(int controlmapid, char *actorname, int kbid, int jsid)
{
akgl_ControlMap *controlmap;
akgl_Control control;
@@ -630,7 +550,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, cha
} FINISH(errctx, true);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_key(int *keycode, bool *available)
akerr_ErrorContext *akgl_controller_poll_key(int *keycode, bool *available)
{
akgl_Keystroke keystroke;
@@ -655,7 +575,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_key(int *keycode, bool *
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_keystroke(akgl_Keystroke *dest, bool *available)
akerr_ErrorContext *akgl_controller_poll_keystroke(akgl_Keystroke *dest, bool *available)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL keystroke destination");
@@ -666,7 +586,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_poll_keystroke(akgl_Keystroke
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_flush_keys(void)
akerr_ErrorContext *akgl_controller_flush_keys(void)
{
PREPARE_ERROR(errctx);
keybuffer_head = 0;

View File

@@ -236,7 +236,7 @@ static akerr_ErrorContext *flood_region(SDL_Surface *surface, int x, int y, uint
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_point(akgl_RenderBackend *self, float32_t x, float32_t y, SDL_Color color)
akerr_ErrorContext *akgl_draw_point(akgl_RenderBackend *self, float32_t x, float32_t y, SDL_Color color)
{
SDL_Color previous;
@@ -258,7 +258,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_point(akgl_RenderBackend *self, flo
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_line(akgl_RenderBackend *self, float32_t x1, float32_t y1, float32_t x2, float32_t y2, SDL_Color color)
akerr_ErrorContext *akgl_draw_line(akgl_RenderBackend *self, float32_t x1, float32_t y1, float32_t x2, float32_t y2, SDL_Color color)
{
SDL_Color previous;
@@ -280,7 +280,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_line(akgl_RenderBackend *self, floa
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color)
akerr_ErrorContext *akgl_draw_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color)
{
SDL_Color previous;
@@ -303,7 +303,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_rect(akgl_RenderBackend *self, SDL_
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_filled_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color)
akerr_ErrorContext *akgl_draw_filled_rect(akgl_RenderBackend *self, SDL_FRect *rect, SDL_Color color)
{
SDL_Color previous;
@@ -326,7 +326,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_filled_rect(akgl_RenderBackend *sel
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_circle(akgl_RenderBackend *self, float32_t x, float32_t y, float32_t radius, SDL_Color color)
akerr_ErrorContext *akgl_draw_circle(akgl_RenderBackend *self, float32_t x, float32_t y, float32_t radius, SDL_Color color)
{
SDL_Color previous;
SDL_FPoint octants[8];
@@ -393,7 +393,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_circle(akgl_RenderBackend *self, fl
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_flood_fill(akgl_RenderBackend *self, int x, int y, SDL_Color color)
akerr_ErrorContext *akgl_draw_flood_fill(akgl_RenderBackend *self, int x, int y, SDL_Color color)
{
SDL_Surface *target = NULL;
SDL_Surface *rgba = NULL;
@@ -481,7 +481,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_flood_fill(akgl_RenderBackend *self
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_copy_region(akgl_RenderBackend *self, SDL_Rect *src, SDL_Surface **dest)
akerr_ErrorContext *akgl_draw_copy_region(akgl_RenderBackend *self, SDL_Rect *src, SDL_Surface **dest)
{
SDL_Surface *saved = NULL;
int width = 0;
@@ -551,7 +551,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_copy_region(akgl_RenderBackend *sel
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_draw_paste_region(akgl_RenderBackend *self, SDL_Surface *src, float32_t x, float32_t y)
akerr_ErrorContext *akgl_draw_paste_region(akgl_RenderBackend *self, SDL_Surface *src, float32_t x, float32_t y)
{
SDL_Texture *patch = NULL;
SDL_FRect dest;

View File

@@ -129,7 +129,7 @@ void akgl_game_lowfps(void)
return;
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
akerr_ErrorContext *akgl_game_init(void)
{
int screenwidth = 0;
int screenheight = 0;
@@ -213,7 +213,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void)
akerr_ErrorContext *akgl_game_state_lock(void)
{
PREPARE_ERROR(e);
// Milliseconds, which is what SDL_Delay takes. This used to count against
@@ -240,14 +240,14 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_lock(void)
SDL_GetError());
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_state_unlock(void)
akerr_ErrorContext *akgl_game_state_unlock(void)
{
PREPARE_ERROR(e);
SDL_UnlockMutex(akgl_game.statelock);
SUCCEED_RETURN(e);
}
void akgl_game_update_fps()
void akgl_game_update_fps(void)
{
SDL_Time curTime;
curTime = SDL_GetTicksNS();
@@ -287,7 +287,7 @@ void akgl_game_update_fps()
* **exits the process**. A failed write during a save terminates the
* game rather than returning an error.
*/
void akgl_game_save_actorname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
static void save_actorname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_Actor *actor = NULL;
@@ -305,7 +305,7 @@ void akgl_game_save_actorname_iterator(void *userdata, SDL_PropertiesID props, c
/**
* @brief Write one sprite's name and save-time address to the name table.
*
* As akgl_game_save_actorname_iterator, but the name field is
* As save_actorname_iterator, but the name field is
* #AKGL_SPRITE_MAX_NAME_LENGTH wide.
*
* @param userdata The open output stream, as a `FILE *`. Required.
@@ -313,9 +313,9 @@ void akgl_game_save_actorname_iterator(void *userdata, SDL_PropertiesID props, c
* @param name The sprite's registry key, written at fixed width.
*
* @warning Terminates the process on an unhandled error. See
* akgl_game_save_actorname_iterator.
* save_actorname_iterator.
*/
void akgl_game_save_spritename_iterator(void *userdata, SDL_PropertiesID props, const char *name)
static void save_spritename_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_Sprite *sprite = NULL;
@@ -333,7 +333,7 @@ void akgl_game_save_spritename_iterator(void *userdata, SDL_PropertiesID props,
/**
* @brief Write one spritesheet's name and save-time address to the name table.
*
* As akgl_game_save_actorname_iterator, but the name field is
* As save_actorname_iterator, but the name field is
* #AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH wide -- the widest of the four, since
* a spritesheet is keyed by its resolved path.
*
@@ -342,12 +342,12 @@ void akgl_game_save_spritename_iterator(void *userdata, SDL_PropertiesID props,
* @param name The spritesheet's registry key, written at fixed width.
*
* @warning Terminates the process on an unhandled error. See
* akgl_game_save_actorname_iterator.
* save_actorname_iterator.
* @note This is the table the loader disagrees with: it reads every table at
* #AKGL_ACTOR_MAX_NAME_LENGTH, so a save containing any spritesheet cannot
* be read back. TODO.md, "Known and still open" item 7.
*/
void akgl_game_save_spritesheetname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
static void save_spritesheetname_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
akgl_SpriteSheet *spritesheet = NULL;
@@ -365,7 +365,7 @@ void akgl_game_save_spritesheetname_iterator(void *userdata, SDL_PropertiesID pr
/**
* @brief Write one character's name and save-time address to the name table.
*
* As akgl_game_save_actorname_iterator, but the name field is
* As save_actorname_iterator, but the name field is
* #AKGL_CHARACTER_MAX_NAME_LENGTH wide.
*
* @param userdata The open output stream, as a `FILE *`. Required.
@@ -373,9 +373,9 @@ void akgl_game_save_spritesheetname_iterator(void *userdata, SDL_PropertiesID pr
* @param name The character's registry key, written at fixed width.
*
* @warning Terminates the process on an unhandled error. See
* akgl_game_save_actorname_iterator.
* save_actorname_iterator.
*/
void akgl_game_save_charactername_iterator(void *userdata, SDL_PropertiesID props, const char *name)
static void save_charactername_iterator(void *userdata, SDL_PropertiesID props, const char *name)
{
FILE *fp = (FILE *)userdata;
PREPARE_ERROR(e);
@@ -390,30 +390,11 @@ void akgl_game_save_charactername_iterator(void *userdata, SDL_PropertiesID prop
} FINISH_NORETURN(e);
}
/**
* @brief Write all four name-to-address tables, each with its terminator.
*
* Actors, sprites, spritesheets, characters, in that order -- which is the order
* akgl_game_load reads them back in. Each table is the registry enumerated one
* entry at a time, followed by a zeroed name field and a zeroed pointer that the
* reader stops on.
*
* @param fp The open save-game stream. Required.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p fp is `NULL`.
* @throws AKERR_IO On a stream error or a short write while emitting a
* terminator.
*
* @note Only the terminators are written through the error-reporting path. The
* entries themselves go through `SDL_EnumerateProperties`, whose callbacks
* cannot report failure upward and instead terminate the process -- so a
* write error mid-table never reaches this function's return value.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
akerr_ErrorContext *akgl_game_save_actors(FILE *fp)
{
PREPARE_ERROR(e);
// Each name table ends with a zeroed name field and a zeroed pointer, which
// is what akgl_game_load_objectnamemap() looks for to stop reading. The
// is what load_objectnamemap() looks for to stop reading. The
// terminator has to come from a buffer at least as long as the longest name
// field: writing N bytes from the address of a single char would emit N-1
// bytes of whatever happened to follow it on the stack, which both leaks
@@ -428,28 +409,28 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
// write the actor name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_ACTOR,
&akgl_game_save_actorname_iterator,
&save_actorname_iterator,
(void *)fp);
CATCH(e, write_exact((void *)&nullbuf, 1, AKGL_ACTOR_MAX_NAME_LENGTH, fp));
CATCH(e, write_exact((void *)&nullbuf, 1, sizeof(akgl_Actor *), fp));
// write the sprite name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITE,
&akgl_game_save_spritename_iterator,
&save_spritename_iterator,
(void *)fp);
CATCH(e, write_exact((void *)&nullbuf, 1, AKGL_SPRITE_MAX_NAME_LENGTH, fp));
CATCH(e, write_exact((void *)&nullbuf, 1, sizeof(akgl_Sprite *), fp));
// write the spritesheet name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_SPRITESHEET,
&akgl_game_save_spritesheetname_iterator,
&save_spritesheetname_iterator,
(void *)fp);
CATCH(e, write_exact((void *)&nullbuf, 1, AKGL_SPRITE_SHEET_MAX_FILENAME_LENGTH, fp));
CATCH(e, write_exact((void *)&nullbuf, 1, sizeof(akgl_SpriteSheet *), fp));
// write the character name pointer table
SDL_EnumerateProperties(
AKGL_REGISTRY_CHARACTER,
&akgl_game_save_charactername_iterator,
&save_charactername_iterator,
(void *)fp);
CATCH(e, write_exact((void *)&nullbuf, 1, AKGL_CHARACTER_MAX_NAME_LENGTH, fp));
CATCH(e, write_exact((void *)&nullbuf, 1, sizeof(akgl_Character *), fp));
@@ -459,7 +440,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save_actors(FILE *fp)
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
akerr_ErrorContext *akgl_game_save(char *fpath)
{
FILE *fp = NULL;
PREPARE_ERROR(e);
@@ -517,7 +498,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
* rather than being reported: the entry is written with whatever
* `SDL_GetPointerProperty` returned.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_PropertiesID map, int namelength, int ptrlength, SDL_PropertiesID registry)
static akerr_ErrorContext *load_objectnamemap(FILE *fp, SDL_PropertiesID map, int namelength, int ptrlength, SDL_PropertiesID registry)
{
void *ptr = NULL;
char ptrstring[32];
@@ -561,27 +542,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_objectnamemap(FILE *fp, SDL_Pr
SUCCEED_RETURN(e);
}
/**
* @brief Refuse a save file unless its version matches the running one exactly.
*
* Both strings are parsed as semver and compared with `"="` -- exact equality,
* not compatibility. That is deliberate and strict: a save file is a raw memory
* image of `akgl_Game` and the object tables, so any change to a struct layout
* invalidates it, and semver has no way to say "the layout did not move".
*
* @param versiontype What is being compared -- `"library"` or `"game"`. Used
* only to build the message, but required, since a bare
* "incompatible version" error would not say which.
* @param newversion The version read out of the save file. Required.
* @param curversion The version of the running program or library. Required.
* @return `NULL` when the two match, otherwise an error context owned by the
* caller.
* @throws AKERR_NULLPOINTER If any of the three arguments is `NULL`.
* @throws AKERR_VALUE If either string is not valid semver. The message says
* which side it came from.
* @throws AKERR_API If both parse but are not equal. The message quotes both.
*/
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(char *versiontype, char *newversion, char *curversion)
akerr_ErrorContext *akgl_game_load_versioncmp(char *versiontype, char *newversion, char *curversion)
{
semver_t current_version = {};
semver_t compare_version = {};
@@ -622,7 +583,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load_versioncmp(char *versiontype,
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
akerr_ErrorContext *akgl_game_load(char *fpath)
{
akgl_Game savegame;
SDL_PropertiesID actormap;
@@ -653,16 +614,16 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
memcpy((void *)&akgl_game, (void *)&savegame, sizeof(akgl_Game));
// Load actor name map
actormap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, actormap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Actor *), AKGL_REGISTRY_ACTOR));
CATCH(e, load_objectnamemap(fp, actormap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Actor *), AKGL_REGISTRY_ACTOR));
// Load sprite name map
spritemap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, spritemap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Sprite *), AKGL_REGISTRY_SPRITE));
CATCH(e, load_objectnamemap(fp, spritemap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Sprite *), AKGL_REGISTRY_SPRITE));
// Load spritesheet name map
spritesheetmap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, spritesheetmap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_SpriteSheet *), AKGL_REGISTRY_SPRITESHEET));
CATCH(e, load_objectnamemap(fp, spritesheetmap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_SpriteSheet *), AKGL_REGISTRY_SPRITESHEET));
// Load character name map
charactermap = SDL_CreateProperties();
CATCH(e, akgl_game_load_objectnamemap(fp, charactermap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Character *), AKGL_REGISTRY_CHARACTER));
CATCH(e, load_objectnamemap(fp, charactermap, AKGL_ACTOR_MAX_NAME_LENGTH, sizeof(akgl_Character *), AKGL_REGISTRY_CHARACTER));
// Now that we have all of our pointer maps built, we can load the actual binary objects and reset their pointers
} CLEANUP {
if ( fp != NULL ) {
@@ -674,7 +635,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags)
akerr_ErrorContext *akgl_game_update(akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {

View File

@@ -20,7 +20,7 @@ akgl_SpriteSheet akgl_heap_spritesheets[AKGL_MAX_HEAP_SPRITESHEET];
akgl_Character akgl_heap_characters[AKGL_MAX_HEAP_CHARACTER];
akgl_String akgl_heap_strings[AKGL_MAX_HEAP_STRING];
akerr_ErrorContext *akgl_heap_init()
akerr_ErrorContext *akgl_heap_init(void)
{
PREPARE_ERROR(errctx);
int i = 0;

View File

@@ -57,7 +57,7 @@ akerr_ErrorContext *akgl_get_json_number_value(json_t *obj, char *key, float *de
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_double_value(json_t *obj, char *key, double *dest)
akerr_ErrorContext *akgl_get_json_double_value(json_t *obj, char *key, double *dest)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
@@ -146,25 +146,28 @@ akerr_ErrorContext *akgl_get_json_array_index_string(json_t *array, int index, a
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_json_with_default(akerr_ErrorContext *err, void *defval, void *dest, uint32_t defsize)
akerr_ErrorContext *akgl_get_json_with_default(akerr_ErrorContext *e, void *defval, void *dest, uint32_t defsize)
{
PREPARE_ERROR(e);
if ( err == NULL ) {
SUCCEED_RETURN(e);
// Two contexts, and the names now say which is which: `e` is the incoming
// one being inspected, `errctx` is this function's own. They were `err` and
// `e`, which put the incoming context under the name the convention
// reserves for exactly that and the local one under a name that reads like
// it.
PREPARE_ERROR(errctx);
if ( e == NULL ) {
SUCCEED_RETURN(errctx);
}
int docopy = 0;
FAIL_ZERO_RETURN(e, err, AKERR_NULLPOINTER, "err");
FAIL_ZERO_RETURN(e, defval, AKERR_NULLPOINTER, "defval");
FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dest");
FAIL_ZERO_RETURN(errctx, defval, AKERR_NULLPOINTER, "defval");
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "dest");
ATTEMPT {
} CLEANUP {
} PROCESS(err) {
} HANDLE_GROUP(err, AKERR_KEY) {
} HANDLE_GROUP(err, AKERR_INDEX) {
} PROCESS(e) {
} HANDLE_GROUP(e, AKERR_KEY) {
} HANDLE_GROUP(e, AKERR_INDEX) {
memcpy(dest, defval, defsize);
} FINISH(err, true);
} FINISH(e, true);
SUCCEED_RETURN(e);
SUCCEED_RETURN(errctx);
}

View File

@@ -12,28 +12,28 @@
#include <akgl/heap.h>
#include <akgl/registry.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
akerr_ErrorContext *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
akerr_ErrorContext *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
akerr_ErrorContext *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *self)
akerr_ErrorContext *akgl_physics_init_null(akgl_PhysicsBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -47,7 +47,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_null(akgl_PhysicsBackend *s
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
akerr_ErrorContext *akgl_physics_arcade_gravity(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -69,7 +69,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBacke
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
akerr_ErrorContext *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -77,7 +77,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBacke
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
akerr_ErrorContext *akgl_physics_arcade_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -88,7 +88,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(struct akgl_PhysicsB
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend *self)
akerr_ErrorContext *akgl_physics_init_arcade(akgl_PhysicsBackend *self)
{
akgl_String *tmp;
PREPARE_ERROR(e);
@@ -121,7 +121,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags)
akerr_ErrorContext *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags = {
@@ -227,7 +227,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *se
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_factory(akgl_PhysicsBackend *self, akgl_String *type)
akerr_ErrorContext *akgl_physics_factory(akgl_PhysicsBackend *self, akgl_String *type)
{
uint32_t hashval;
PREPARE_ERROR(e);

View File

@@ -25,7 +25,7 @@ SDL_PropertiesID AKGL_REGISTRY_MUSIC = 0;
SDL_PropertiesID AKGL_REGISTRY_FONT = 0;
SDL_PropertiesID AKGL_REGISTRY_PROPERTIES = 0;
akerr_ErrorContext *akgl_registry_init()
akerr_ErrorContext *akgl_registry_init(void)
{
PREPARE_ERROR(errctx);
ATTEMPT {
@@ -42,7 +42,7 @@ akerr_ErrorContext *akgl_registry_init()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_actor()
akerr_ErrorContext *akgl_registry_init_actor(void)
{
PREPARE_ERROR(errctx);
if ( AKGL_REGISTRY_ACTOR != 0 ) {
@@ -53,7 +53,7 @@ akerr_ErrorContext *akgl_registry_init_actor()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_font()
akerr_ErrorContext *akgl_registry_init_font(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_FONT = SDL_CreateProperties();
@@ -61,7 +61,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_init_font()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_music()
akerr_ErrorContext *akgl_registry_init_music(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_MUSIC = SDL_CreateProperties();
@@ -69,7 +69,7 @@ akerr_ErrorContext *akgl_registry_init_music()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_properties()
akerr_ErrorContext *akgl_registry_init_properties(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_PROPERTIES = SDL_CreateProperties();
@@ -77,7 +77,7 @@ akerr_ErrorContext *akgl_registry_init_properties()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_actor_state_strings()
akerr_ErrorContext *akgl_registry_init_actor_state_strings(void)
{
int i = 0;
int flag = 0;
@@ -94,7 +94,7 @@ akerr_ErrorContext *akgl_registry_init_actor_state_strings()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_sprite()
akerr_ErrorContext *akgl_registry_init_sprite(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_SPRITE = SDL_CreateProperties();
@@ -102,7 +102,7 @@ akerr_ErrorContext *akgl_registry_init_sprite()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_spritesheet()
akerr_ErrorContext *akgl_registry_init_spritesheet(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_SPRITESHEET = SDL_CreateProperties();
@@ -110,7 +110,7 @@ akerr_ErrorContext *akgl_registry_init_spritesheet()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akgl_registry_init_character()
akerr_ErrorContext *akgl_registry_init_character(void)
{
PREPARE_ERROR(errctx);
AKGL_REGISTRY_CHARACTER = SDL_CreateProperties();
@@ -118,7 +118,7 @@ akerr_ErrorContext *akgl_registry_init_character()
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_load_properties(char *fname)
akerr_ErrorContext *akgl_registry_load_properties(char *fname)
{
json_t *json = NULL;
json_t *props = NULL;
@@ -175,16 +175,16 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_registry_load_properties(char *fname)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_set_property(char *name, char *src)
akerr_ErrorContext *akgl_set_property(char *name, char *value)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, name, AKERR_NULLPOINTER, "NULL char *");
FAIL_ZERO_RETURN(e, src, AKERR_NULLPOINTER, "NULL char *");
SDL_SetStringProperty(AKGL_REGISTRY_PROPERTIES, name, src);
FAIL_ZERO_RETURN(e, value, AKERR_NULLPOINTER, "NULL char *");
SDL_SetStringProperty(AKGL_REGISTRY_PROPERTIES, name, value);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_get_property(char *name, akgl_String **dest, char *def)
akerr_ErrorContext *akgl_get_property(char *name, akgl_String **dest, char *def)
{
const char *value = NULL;
size_t valuelen = 0;

View File

@@ -14,7 +14,7 @@
#include <akerror.h>
#include <akstdlib.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_init(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_init(akgl_RenderBackend *self)
{
akgl_String *width = NULL;
akgl_String *height = NULL;
@@ -47,7 +47,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_init(akgl_RenderBackend *self)
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_bind(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_bind(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -65,13 +65,13 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_bind(akgl_RenderBackend *self)
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_shutdown(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_shutdown(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_start(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_frame_start(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
@@ -80,7 +80,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_start(akgl_RenderBackend
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_end(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_frame_end(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
@@ -88,7 +88,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_frame_end(akgl_RenderBackend *
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_texture(akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip)
akerr_ErrorContext *akgl_render_2d_draw_texture(akgl_RenderBackend *self, SDL_Texture *texture, SDL_FRect *src, SDL_FRect *dest, double angle, SDL_FPoint *center, SDL_FlipMode flip)
{
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
@@ -113,13 +113,13 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_texture(akgl_RenderBacken
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_mesh(akgl_RenderBackend *self)
akerr_ErrorContext *akgl_render_2d_draw_mesh(akgl_RenderBackend *self)
{
PREPARE_ERROR(e);
FAIL_RETURN(e, AKERR_API, "Not implemented");
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_render_2d_draw_world(akgl_RenderBackend *self, akgl_Iterator *opflags)
akerr_ErrorContext *akgl_render_2d_draw_world(akgl_RenderBackend *self, akgl_Iterator *opflags)
{
PREPARE_ERROR(e);
akgl_Iterator defflags;

View File

@@ -67,7 +67,7 @@ akerr_ErrorContext *akgl_spritesheet_coords_for_frame(akgl_Sprite *self, SDL_FRe
* @throws AKGL_ERR_SDL If the image cannot be decoded.
* @throws AKGL_ERR_HEAP If the spritesheet or string pool is exhausted.
*/
static akerr_ErrorContext *akgl_sprite_load_json_spritesheet(json_t *json, akgl_SpriteSheet **sheet, char *relative_path)
static akerr_ErrorContext *sprite_load_json_spritesheet(json_t *json, akgl_SpriteSheet **sheet, char *relative_path)
{
PREPARE_ERROR(errctx);
json_t *spritesheet_json = NULL;
@@ -145,7 +145,7 @@ akerr_ErrorContext *akgl_sprite_load_json(char *filename)
"Error while loading sprite from %s on line %d: %s", filename, error.line, error.text
);
CATCH(errctx, akgl_sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename_copy->data)));
CATCH(errctx, sprite_load_json_spritesheet((json_t *)json, &sheet, dirname(filename_copy->data)));
CATCH(errctx, akgl_get_json_string_value((json_t *)json, "name", &spritename));
CATCH(errctx,
akgl_sprite_initialize(

View File

@@ -10,7 +10,7 @@
#include <akgl/registry.h>
#include <akgl/game.h>
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath, int size)
akerr_ErrorContext *akgl_text_loadfont(char *name, char *filepath, int size)
{
TTF_Font *font = NULL;
@@ -37,7 +37,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_loadfont(char *name, char *filepath
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_unloadfont(char *name)
akerr_ErrorContext *akgl_text_unloadfont(char *name)
{
TTF_Font *font = NULL;
@@ -54,7 +54,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_unloadfont(char *name)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y)
akerr_ErrorContext *akgl_text_rendertextat(TTF_Font *font, char *text, SDL_Color color, int wraplength, int x, int y)
{
SDL_Surface *textsurf = NULL;
SDL_Texture *texture = NULL;
@@ -96,7 +96,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_rendertextat(TTF_Font *font, char *
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure(TTF_Font *font, char *text, int *w, int *h)
akerr_ErrorContext *akgl_text_measure(TTF_Font *font, char *text, int *w, int *h)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, font, AKERR_NULLPOINTER, "NULL font");
@@ -114,7 +114,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure(TTF_Font *font, char *text,
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_text_measure_wrapped(TTF_Font *font, char *text, int wraplength, int *w, int *h)
akerr_ErrorContext *akgl_text_measure_wrapped(TTF_Font *font, char *text, int wraplength, int *w, int *h)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, font, AKERR_NULLPOINTER, "NULL font");

View File

@@ -87,23 +87,6 @@ akerr_ErrorContext *akgl_get_json_properties_integer(json_t *obj, char *key, int
SUCCEED_RETURN(errctx);
}
/**
* @brief Read a Tiled custom property declared as `number`.
*
* Tiled writes `float` for a floating-point property, so this matches nothing
* Tiled produces today -- akgl_get_json_properties_float is the one the loader
* uses. Kept because a hand-written or older map may still say `number`.
*
* @param obj The Tiled object whose `properties` array to search. Required.
* @param key The property name. Required.
* @param dest Receives the value as a `float`. Not written on any failure path.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p obj or @p key is `NULL`.
* @throws AKERR_KEY If the property is absent, or @p obj has no properties.
* @throws AKERR_TYPE If the property is not declared `number`, or its `value` is
* not numeric.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, float *dest)
{
PREPARE_ERROR(errctx);
@@ -114,22 +97,6 @@ akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, floa
SUCCEED_RETURN(errctx);
}
/**
* @brief Read a Tiled custom property declared as `float`.
*
* The spelling Tiled actually writes for a floating-point property. This is how
* the `scale` on a perspective marker is read.
*
* @param obj The Tiled object whose `properties` array to search. Required.
* @param key The property name. Required.
* @param dest Receives the value. Not written on any failure path.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p obj or @p key is `NULL`.
* @throws AKERR_KEY If the property is absent, or @p obj has no properties.
* @throws AKERR_TYPE If the property is not declared `float`, or its `value` is
* not numeric.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
akerr_ErrorContext *akgl_get_json_properties_float(json_t *obj, char *key, float *dest)
{
PREPARE_ERROR(errctx);
@@ -140,24 +107,6 @@ akerr_ErrorContext *akgl_get_json_properties_float(json_t *obj, char *key, float
SUCCEED_RETURN(errctx);
}
/**
* @brief Read a Tiled custom property declared as `float`, at full precision.
*
* Same declared type as akgl_get_json_properties_float, but writing a `double`.
* The map's physics constants are `double`, which is the reason both exist.
*
* @param obj The Tiled object whose `properties` array to search. Required.
* @param key The property name. Required.
* @param dest Receives the value. Not written on any failure path.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p obj or @p key is `NULL`.
* @throws AKERR_KEY If the property is absent, or @p obj has no properties.
* akgl_tilemap_load_physics passes this status to
* akgl_get_json_with_default, which turns it into a default of 0.0.
* @throws AKERR_TYPE If the property is not declared `float`, or its `value` is
* not numeric.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
akerr_ErrorContext *akgl_get_json_properties_double(json_t *obj, char *key, double *dest)
{
PREPARE_ERROR(errctx);
@@ -288,41 +237,6 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root,
SUCCEED_RETURN(errctx);
}
/**
* @brief Turn one Tiled object into a live actor, creating it if it is not already registered.
*
* The object's name is the actor's registry key, which is what lets the same
* actor be placed by more than one object: the first placement creates it and
* binds the character named in the object's `character` property, and every
* later one just takes another reference. Either way the object's position,
* visibility, and layer are copied onto the actor and the object keeps a
* pointer to it.
*
* Not declared in tilemap.h -- reachable only through
* akgl_tilemap_load_layer_objects.
*
* @param curobj The tilemap object, with its `name`, `x`, `y`, and
* `visible` already read from JSON. Required, unchecked,
* dereferenced at once.
* @param layerdatavalue The object's JSON, for the custom properties --
* `character` and `state` -- that are not part of Tiled's
* own object fields. Required.
* @param layerid The layer this object sits on, copied onto the actor.
* @param dirname Directory to resolve paths against. Accepted for
* signature consistency; nothing here uses it.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_KEY If the object's name is empty -- an actor has to be
* addressable -- if the `character` or `state` property is absent, or if
* the named character is not in #AKGL_REGISTRY_CHARACTER.
* @throws AKERR_TYPE If `character` is not declared `string` or `state` not
* declared `int`.
* @throws AKERR_NULLPOINTER If akgl_actor_initialize refuses the name.
* @throws AKGL_ERR_HEAP If the actor or string pool is exhausted.
*
* @note An existing actor's `character` and position are overwritten by each
* later placement, so two objects naming one actor leave it wherever the
* last one put it rather than producing two.
*/
akerr_ErrorContext *akgl_tilemap_load_layer_object_actor(akgl_TilemapObject *curobj, json_t *layerdatavalue, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
@@ -444,34 +358,6 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo
SUCCEED_RETURN(errctx);
}
/**
* @brief Load one image layer: upload its image as a single texture.
*
* An image layer is one picture rather than a grid, which is what a parallax
* backdrop wants. The layer's `width` and `height` are taken from the loaded
* texture rather than from the JSON, so they are always the true pixel size.
*
* Not declared in tilemap.h -- reachable only through akgl_tilemap_load_layers.
*
* @param dest The map to load into. Required.
* @param root The layer's JSON object. Required.
* @param layerid Which layer slot to fill. Not bounds-checked.
* @param dirname Directory to resolve the layer's `image` path against.
* Required. Joined with a `/` rather than run through
* akgl_path_relative, so unlike a tileset image this path is not
* canonicalized and an absolute one will not work.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p dest, @p root, or @p dirname is `NULL`.
* @throws AKERR_KEY If the layer has no `image`.
* @throws AKERR_TYPE If `image` is not a string.
* @throws AKGL_ERR_SDL If the image cannot be loaded. The message carries
* `SDL_GetError()`.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*
* @note The joined path is truncated at
* #AKGL_TILEMAP_MAX_TILESET_FILENAME_SIZE without complaint, so an
* over-long path fails as a missing file rather than as a length error.
*/
akerr_ErrorContext *akgl_tilemap_load_layer_image(akgl_Tilemap *dest, json_t *root, int layerid, akgl_String *dirname)
{
PREPARE_ERROR(errctx);
@@ -558,33 +444,6 @@ akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, a
SUCCEED_RETURN(errctx);
}
/**
* @brief Give the map its own physics backend, if it asked for one.
*
* A map with a `physics.model` custom property gets its own backend, configured
* from `physics.gravity.x`/`.y`/`.z` and `physics.drag.x`/`.y`/`.z`, and
* `use_own_physics` is set so a caller knows to simulate through it. Each
* constant defaults to 0.0 when absent, so a map can name a model and override
* only what it cares about.
*
* Absence is the normal case at every level: a map with no properties at all, or
* with properties but no `physics.model`, uses the game's global backend and
* this returns success. That is why the AKERR_KEY handlers here are not error
* paths -- they are the "map did not ask" path.
*
* Not declared in tilemap.h -- reachable only through akgl_tilemap_load.
*
* @param dest The map to configure. Required in practice; not checked here,
* since akgl_tilemap_load has already done so.
* @param root The map's root JSON object. Required, likewise.
* @return `NULL` on success -- including when the map declared no physics at
* all -- otherwise an error context owned by the caller.
* @throws AKERR_KEY If `physics.model` names a backend that does not exist.
* @throws AKERR_TYPE If `physics.model` is not declared `string`, or one of the
* six constants is not declared `float`.
* @throws AKERR_NULLPOINTER If akgl_physics_factory refuses its arguments.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*/
akerr_ErrorContext *akgl_tilemap_load_physics(akgl_Tilemap *dest, json_t *root)
{
PREPARE_ERROR(e);
@@ -913,7 +772,7 @@ akerr_ErrorContext *akgl_tilemap_draw_tileset(akgl_Tilemap *map, int tilesetidx)
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor)
akerr_ErrorContext *akgl_tilemap_scale_actor(akgl_Tilemap *map, akgl_Actor *actor)
{
PREPARE_ERROR(e);
@@ -930,7 +789,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_scale_actor(akgl_Tilemap *map, a
SUCCEED_RETURN(e);
}
akerr_ErrorContext AKERR_NOIGNORE *akgl_tilemap_release(akgl_Tilemap *dest)
akerr_ErrorContext *akgl_tilemap_release(akgl_Tilemap *dest)
{
// Release all tileset textures
// Release all image layer textures

View File

@@ -27,7 +27,8 @@
* the join, so symlinks and `..` are folded out and the result is absolute. It
* is the fallback, reached only once resolving @p path on its own has failed.
*
* Not declared in util.h -- it is reachable only through akgl_path_relative.
* `static`: it is reachable only through akgl_path_relative, which is the only
* caller and the only sensible one.
*
* @param root Directory to resolve against, normally `dirname` of the file that
* named @p path. Required. A trailing `/` is harmless; the join adds
@@ -50,7 +51,7 @@
* scratch strings are never released. This is exactly the hazard AGENTS.md
* warns about; it wants a `FAIL_BREAK`.
*/
akerr_ErrorContext *akgl_path_relative_root(char *root, char *path, akgl_String *dst)
static akerr_ErrorContext *path_relative_root(char *root, char *path, akgl_String *dst)
{
PREPARE_ERROR(e);
akgl_String *pathbuf;
@@ -128,47 +129,11 @@ akerr_ErrorContext *akgl_path_relative(char *root, char *path, akgl_String *dst)
} FINISH(e, true);
if ( relative_to_root == true ) {
PASS(e, akgl_path_relative_root(root, path, dst));
PASS(e, path_relative_root(root, path, dst));
}
SUCCEED_RETURN(e);
}
/**
* @brief Intended to resolve @p path against the directory holding @p from. Unfinished.
*
* The shape is there -- resolve @p from, take its `dirname` -- but the body
* stops before it does anything with @p path, and `*dst` is never written. Not
* declared in util.h, and nothing in the tree calls it.
*
* @param path The path to resolve. Required, and currently ignored past the
* `NULL` check.
* @param from A file whose directory @p path should be taken as relative to.
* Required. Must exist -- it is resolved with `realpath(3)`.
* @param dst Intended to receive the resolved path. Never written. Note the
* double indirection, which disagrees with the rest of the
* `akgl_path_relative*` family; see TODO.md item 40.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_NULLPOINTER If @p path or @p from is `NULL`.
* @throws ENOENT If @p from does not exist, along with any other `errno`
* `realpath(3)` raises.
* @throws AKGL_ERR_HEAP If the string pool is exhausted.
*
* @warning Known defect: the scratch string it claims is never released, so 256
* calls exhaust the string pool. TODO.md, "Known and still open" item 4.
*/
akerr_ErrorContext *akgl_path_relative_from(char *path, char *from, akgl_String **dst)
{
akgl_String *dirnamestr;
PREPARE_ERROR(e);
FAIL_ZERO_RETURN(e, path, AKERR_NULLPOINTER, "path");
FAIL_ZERO_RETURN(e, from, AKERR_NULLPOINTER, "from");
PASS(e, akgl_heap_next_string(&dirnamestr));
PASS(e, aksl_realpath(from, (char *)&dirnamestr->data, sizeof(dirnamestr->data)));
dirname((char *)&dirnamestr->data);
SUCCEED_RETURN(e);
}
akerr_ErrorContext *akgl_rectangle_points(akgl_RectanglePoints *dest, SDL_FRect *rect)
{
PREPARE_ERROR(errctx);