Document what the functions actually do instead of that they can fail

The Doxygen comments were generated from the declarations, so 217 @throws
lines across 21 headers read "When the corresponding validation or operation
fails" and told a caller nothing beyond the status name. The @param lines were
the same shape: every output was "Output destination populated by the
function", every instance "Object to initialize, inspect, or modify".

Rewritten against the implementations, following the pattern libakstdlib
already uses:

- @throws names the condition. akgl_sprite_load_json separated AKERR_KEY
  (absent) from AKERR_TYPE (present, wrong type) from AKERR_OUTOFBOUNDS
  (filename too long, or array indexed past its end), and gained AKGL_ERR_SDL
  and AKGL_ERR_HEAP, which it raises and never declared.
- Parameters say whether they are required, what a NULL means, and what is
  written on a failure path. Where an argument is not checked, the doc says so:
  akgl_heap_next_actor's dest is a crash on NULL, not an error, and
  akgl_render_2d_frame_start dereferences self before testing it.
- The conventions move up to the file blocks so the per-function docs stay
  short. json_helpers.h states once that absence is an error here and that
  json_t * results are borrowed; heap.h explains the pool model and the
  acquire asymmetry; physics.h carries the thrust/environmental/velocity table.
- Struct fields, enum values, macros and exported globals are documented,
  including the dead ones - sprite_w/sprite_h, movetimer, p_scale and
  timer_gravity are read by nothing, and say so.

Also fixes ten comments in error.h and audio.h that opened with /** rather
than /**<, so Doxygen attached them to the following entity and rendered the
text as part of the macro's value. Verified against the generated HTML.

Comments only - no declaration changed. Doxygen builds clean under
WARN_AS_ERROR, scripts/reindent.sh --check passes, 19/19 suites pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-31 11:02:20 -04:00
parent 582008a411
commit f0858b0d38
28 changed files with 3090 additions and 1012 deletions

View File

@@ -88,12 +88,21 @@ akerr_ErrorContext *akgl_get_json_properties_integer(json_t *obj, char *key, int
}
/**
* @brief Get json properties number.
* @param obj Object to initialize, inspect, or modify.
* @param key JSON object key or property name to locate.
* @param dest Output destination populated by the function.
* @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_* Propagates an error reported by a delegated operation.
* @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)
{
@@ -106,12 +115,20 @@ akerr_ErrorContext *akgl_get_json_properties_number(json_t *obj, char *key, floa
}
/**
* @brief Get json properties float.
* @param obj Object to initialize, inspect, or modify.
* @param key JSON object key or property name to locate.
* @param dest Output destination populated by the function.
* @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_* Propagates an error reported by a delegated operation.
* @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)
{
@@ -124,12 +141,22 @@ akerr_ErrorContext *akgl_get_json_properties_float(json_t *obj, char *key, float
}
/**
* @brief Get json properties double.
* @param obj Object to initialize, inspect, or modify.
* @param key JSON object key or property name to locate.
* @param dest Output destination populated by the function.
* @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_* Propagates an error reported by a delegated operation.
* @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)
{
@@ -262,13 +289,39 @@ akerr_ErrorContext *akgl_tilemap_load_tilesets(akgl_Tilemap *dest, json_t *root,
}
/**
* @brief Tilemap load layer object actor.
* @param curobj Tilemap object being populated.
* @param layerdatavalue JSON object describing the layer entry.
* @param layerid Zero-based tilemap layer index.
* @param dirname Directory containing paths referenced by the document.
* @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 When the corresponding validation or operation fails.
* @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)
{
@@ -392,14 +445,32 @@ akerr_ErrorContext *akgl_tilemap_load_layer_tile(akgl_Tilemap *dest, json_t *roo
}
/**
* @brief Tilemap load layer image.
* @param dest Output destination populated by the function.
* @param root Base directory used to resolve the path.
* @param layerid Zero-based tilemap layer index.
* @param dirname Directory containing paths referenced by the document.
* @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 When the corresponding validation or operation fails.
* @throws AKGL_ERR_SDL When the corresponding validation or operation fails.
* @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)
{
@@ -488,11 +559,31 @@ akerr_ErrorContext *akgl_tilemap_load_layers(akgl_Tilemap *dest, json_t *root, a
}
/**
* @brief Tilemap load physics.
* @param dest Output destination populated by the function.
* @param root Base directory used to resolve the path.
* @return `NULL` on success, otherwise an error context owned by the caller.
* @throws AKERR_KEY When the corresponding validation or operation fails.
* @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)
{