Stop returning past CLEANUP, and validate the arguments every sibling validates
Closes internal-consistency items 16 and 17. Ten *_RETURN macros sat inside ATTEMPT blocks, which return past CLEANUP and skip every release in it. The one that mattered was the success path of akgl_get_json_tilemap_property: it leaked two of the string pool's 256 entries on every lookup that *found* what it was asked for, and a map load does that several times per layer. tests/tilemap.c now runs each of its three paths -- found, absent, wrong type -- twice the pool size and asserts the pool is where it started. Against the old code that test does not merely fail, it segfaults, which is Defects item 30 seen from the outside: pool exhaustion arriving as a NULL strncpy rather than as AKGL_ERR_HEAP. Two of the conversions needed more than swapping the macro. In akgl_get_json_tilemap_property a plain break would have fallen through to the "property not found" FAIL_RETURN after FINISH, reporting a miss for something found, so the success path sets a flag. In akgl_collide_rectangles the eight early exits are followed by `*collide = false;`, which would have overwritten the hit that broke out; each corner test writes the flag itself, so that line is gone rather than moved. The same function also released its scratch string once per loop iteration while continuing to use it, so the slot was free while still live -- one claim now covers the whole scan. akgl_controller_default is the other behavioural one: its SUCCEED_RETURN was the last statement in the ATTEMPT block, so the path that falls out of FINISH reached the closing brace of a non-void function. scripts/check_error_protocol.py keeps both rules enforced -- a *_RETURN inside ATTEMPT, and a return out of a HANDLE block -- as the error_protocol test. Neither produces a compiler diagnostic and neither fails a test run until the pool it drains is empty, which is why both have already shipped once. For item 17: the eight typed JSON accessors that validated their container and then wrote through dest unconditionally now check key and dest as the two string accessors always did; the null physics backend checks its actors like the arcade one; and akgl_render_2d_frame_start, _frame_end and _shutdown check self, which the first two read straight through. tests/renderer.c calls all three with NULL, which segfaulted before. 25/25 pass, memcheck clean, reindent --check clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -446,10 +446,10 @@ akerr_ErrorContext *akgl_controller_pushmap(int controlmapid, akgl_Control *cont
|
||||
int newmapid = 0;
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
FAIL_ZERO_RETURN(errctx, control, AKERR_NULLPOINTER, "NULL Control");
|
||||
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
|
||||
FAIL_ZERO_BREAK(errctx, control, AKERR_NULLPOINTER, "NULL Control");
|
||||
FAIL_NONZERO_BREAK(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
|
||||
newmapid = akgl_controlmaps[controlmapid].nextMap;
|
||||
FAIL_ZERO_RETURN(errctx, (AKGL_MAX_CONTROLS - newmapid), AKERR_OUTOFBOUNDS, "Control map ID %d is full", controlmapid);
|
||||
FAIL_ZERO_BREAK(errctx, (AKGL_MAX_CONTROLS - newmapid), AKERR_OUTOFBOUNDS, "Control map ID %d is full", controlmapid);
|
||||
memcpy((void *)&akgl_controlmaps[controlmapid].controls[newmapid], control, sizeof(akgl_Control));
|
||||
akgl_controlmaps[controlmapid].nextMap = newmapid + 1;
|
||||
} CLEANUP {
|
||||
@@ -466,7 +466,7 @@ akerr_ErrorContext *akgl_controller_default(int controlmapid, char *actorname, i
|
||||
PREPARE_ERROR(errctx);
|
||||
ATTEMPT {
|
||||
// set up the control map
|
||||
FAIL_NONZERO_RETURN(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
|
||||
FAIL_NONZERO_BREAK(errctx, (controlmapid >= AKGL_MAX_CONTROL_MAPS), AKERR_OUTOFBOUNDS, "ID %d exceeds maximum %d", controlmapid, AKGL_MAX_CONTROL_MAPS);
|
||||
memset((void *)&control, 0x00, sizeof(akgl_Control));
|
||||
controlmap = &akgl_controlmaps[controlmapid];
|
||||
controlmap->kbid = kbid;
|
||||
@@ -542,12 +542,13 @@ akerr_ErrorContext *akgl_controller_default(int controlmapid, char *actorname, i
|
||||
control.handler_on = &akgl_actor_cmhf_right_on;
|
||||
control.handler_off = &akgl_actor_cmhf_right_off;
|
||||
CATCH(errctx, akgl_controller_pushmap(controlmapid, &control));
|
||||
|
||||
SUCCEED_RETURN(errctx);
|
||||
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
// The SUCCEED_RETURN used to sit at the end of the ATTEMPT block, which
|
||||
// returned past CLEANUP and left this function with no return statement at
|
||||
// all on the path that falls out of FINISH.
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akgl_controller_poll_key(int *keycode, bool *available)
|
||||
|
||||
@@ -600,12 +600,12 @@ akerr_ErrorContext *akgl_game_load(char *fpath)
|
||||
CATCH(e, read_exact((void *)&savegame, 1, sizeof(akgl_Game), fp));
|
||||
CATCH(e, akgl_game_load_versioncmp("library", (char *)&savegame.libversion, (char *)AKGL_VERSION));
|
||||
CATCH(e, akgl_game_load_versioncmp("game", (char *)&savegame.version, (char *)&akgl_game.version));
|
||||
FAIL_NONZERO_RETURN(
|
||||
FAIL_NONZERO_BREAK(
|
||||
e,
|
||||
strncmp((char *)&savegame.name, (char *)&akgl_game.name, 256),
|
||||
AKERR_API,
|
||||
"Savegame is not compatible with this game");
|
||||
FAIL_NONZERO_RETURN(
|
||||
FAIL_NONZERO_BREAK(
|
||||
e,
|
||||
strncmp((char *)&savegame.uri, (char *)&akgl_game.uri, 256),
|
||||
AKERR_API,
|
||||
|
||||
@@ -17,6 +17,8 @@ akerr_ErrorContext *akgl_get_json_object_value(json_t *obj, char *key, json_t **
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -28,6 +30,8 @@ akerr_ErrorContext *akgl_get_json_boolean_value(json_t *obj, char *key, bool *de
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_boolean(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -39,6 +43,8 @@ akerr_ErrorContext *akgl_get_json_integer_value(json_t *obj, char *key, int *des
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL object pointer");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -50,6 +56,8 @@ akerr_ErrorContext *akgl_get_json_number_value(json_t *obj, char *key, float *de
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_number(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -61,6 +69,8 @@ akerr_ErrorContext *akgl_get_json_double_value(json_t *obj, char *key, double *d
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_number(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -73,8 +83,8 @@ akerr_ErrorContext *akgl_get_json_string_value(json_t *obj, char *key, akgl_Stri
|
||||
json_t *value = NULL;
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
|
||||
value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
@@ -96,6 +106,8 @@ akerr_ErrorContext *akgl_get_json_array_value(json_t *obj, char *key, json_t **d
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, obj, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, key, AKERR_NULLPOINTER, "NULL key string");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_object_get(obj, key);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_KEY, "Key %s not found in object", key);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_array(value)), AKERR_TYPE, "Key %s in object has incorrect type", key);
|
||||
@@ -107,6 +119,7 @@ akerr_ErrorContext *akgl_get_json_array_index_object(json_t *array, int index, j
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_array_get(array, index);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_object(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
|
||||
@@ -118,6 +131,7 @@ akerr_ErrorContext *akgl_get_json_array_index_integer(json_t *array, int index,
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_array_get(array, index);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_integer(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
|
||||
@@ -129,7 +143,7 @@ akerr_ErrorContext *akgl_get_json_array_index_string(json_t *array, int index, a
|
||||
{
|
||||
PREPARE_ERROR(errctx);
|
||||
FAIL_ZERO_RETURN(errctx, array, AKERR_NULLPOINTER, "NULL pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer reference");
|
||||
FAIL_ZERO_RETURN(errctx, dest, AKERR_NULLPOINTER, "NULL destination pointer");
|
||||
json_t *value = json_array_get(array, index);
|
||||
FAIL_ZERO_RETURN(errctx, value, AKERR_OUTOFBOUNDS, "Index %d out of bounds for array", index);
|
||||
FAIL_ZERO_RETURN(errctx, (json_is_string(value)), AKERR_TYPE, "Index %d in object has incorrect type", index);
|
||||
|
||||
@@ -16,6 +16,7 @@ akerr_ErrorContext *akgl_physics_null_gravity(akgl_PhysicsBackend *self, akgl_Ac
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
@@ -23,13 +24,16 @@ akerr_ErrorContext *akgl_physics_null_collide(akgl_PhysicsBackend *self, akgl_Ac
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
FAIL_ZERO_RETURN(e, a1, AKERR_NULLPOINTER, "a1");
|
||||
FAIL_ZERO_RETURN(e, a2, AKERR_NULLPOINTER, "a2");
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akgl_physics_null_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
||||
akerr_ErrorContext *akgl_physics_null_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor");
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
@@ -77,7 +81,7 @@ akerr_ErrorContext *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akgl_physics_arcade_move(struct akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
||||
akerr_ErrorContext *akgl_physics_arcade_move(akgl_PhysicsBackend *self, akgl_Actor *actor, float32_t dt)
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
|
||||
@@ -68,12 +68,14 @@ akerr_ErrorContext *akgl_render_2d_bind(akgl_RenderBackend *self)
|
||||
akerr_ErrorContext *akgl_render_2d_shutdown(akgl_RenderBackend *self)
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
SUCCEED_RETURN(e);
|
||||
}
|
||||
|
||||
akerr_ErrorContext *akgl_render_2d_frame_start(akgl_RenderBackend *self)
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
|
||||
SDL_SetRenderDrawColor(self->sdl_renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(self->sdl_renderer);
|
||||
@@ -83,6 +85,7 @@ akerr_ErrorContext *akgl_render_2d_frame_start(akgl_RenderBackend *self)
|
||||
akerr_ErrorContext *akgl_render_2d_frame_end(akgl_RenderBackend *self)
|
||||
{
|
||||
PREPARE_ERROR(e);
|
||||
FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self");
|
||||
FAIL_ZERO_RETURN(e, self->sdl_renderer, AKERR_NULLPOINTER, "No valid SDL rendering backend");
|
||||
SDL_RenderPresent(self->sdl_renderer);
|
||||
SUCCEED_RETURN(e);
|
||||
|
||||
@@ -30,6 +30,7 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
|
||||
json_t *property = NULL;
|
||||
akgl_String *tmpstr = NULL;
|
||||
akgl_String *typestr = NULL;
|
||||
bool found = false;
|
||||
int i = 0;
|
||||
// This is not a generic JSON helper. It assumes we are receiving an object with a 'properties' key
|
||||
// inside of it. That key is an array of objects, and each object has a name, type, and value.
|
||||
@@ -39,9 +40,13 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
|
||||
CATCH(errctx, akgl_get_json_array_value(obj, "properties", &properties));
|
||||
for (i = 0; i < json_array_size(properties); i++) {
|
||||
CATCH(errctx, akgl_get_json_array_index_object(properties, i, &property));
|
||||
// One scratch string for the whole scan. akgl_get_json_string_value
|
||||
// reuses a non-NULL *dest without taking another reference, so
|
||||
// releasing it per iteration -- which this used to do -- dropped the
|
||||
// refcount to zero while the slot was still in use, and the next
|
||||
// claim anywhere could have been handed the same one.
|
||||
CATCH(errctx, akgl_get_json_string_value(property, "name", &tmpstr));
|
||||
if ( strcmp(tmpstr->data, key) != 0 ) {
|
||||
CATCH(errctx, akgl_heap_release_string(tmpstr));
|
||||
continue;
|
||||
}
|
||||
CATCH(errctx, akgl_get_json_string_value(property, "type", &typestr));
|
||||
@@ -49,7 +54,8 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
|
||||
FAIL_BREAK(errctx, AKERR_TYPE, "Property %s is present but is incorrect type(expected %s got %s)", key, type, (char *)typestr->data);
|
||||
}
|
||||
*dest = property;
|
||||
SUCCEED_RETURN(errctx);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
} CLEANUP {
|
||||
if ( tmpstr != NULL ) {
|
||||
@@ -61,6 +67,13 @@ akerr_ErrorContext *akgl_get_json_tilemap_property(json_t *obj, char *key, char
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
|
||||
// The success path used to be a SUCCEED_RETURN from inside the ATTEMPT
|
||||
// block, which returned past CLEANUP and leaked both scratch strings on
|
||||
// every successful lookup. The pool is 256 entries and a map load does this
|
||||
// many times per layer.
|
||||
if ( found == true ) {
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
FAIL_RETURN(errctx, AKERR_KEY, "Property not found in properties map");
|
||||
}
|
||||
|
||||
@@ -567,7 +580,7 @@ akerr_ErrorContext *akgl_tilemap_load(char *fname, akgl_Tilemap *dest)
|
||||
|
||||
dest->orientation = 0;
|
||||
if ( (dest->width * dest->height) >= (AKGL_TILEMAP_MAX_WIDTH * AKGL_TILEMAP_MAX_HEIGHT) ) {
|
||||
FAIL_RETURN(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size");
|
||||
FAIL_BREAK(errctx, AKERR_OUTOFBOUNDS, "Map exceeds the maximum size");
|
||||
}
|
||||
|
||||
CATCH(errctx, akgl_tilemap_load_layers((akgl_Tilemap *)dest, (json_t *)json, dirnamestr));
|
||||
|
||||
22
src/util.c
22
src/util.c
@@ -73,7 +73,7 @@ static akerr_ErrorContext *path_relative_root(char *root, char *path, akgl_Strin
|
||||
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);
|
||||
FAIL_BREAK(e, AKERR_OUTOFBOUNDS, "Total path length (%d) is greater than maximum akgl_String length (%d)", (rootlen + pathlen), AKGL_MAX_STRING_LENGTH);
|
||||
}
|
||||
DISABLE_GCC_WARNING_FORMAT_TRUNCATION
|
||||
CATCH(e, aksl_snprintf(
|
||||
@@ -180,41 +180,43 @@ akerr_ErrorContext *akgl_collide_rectangles(SDL_FRect *r1, SDL_FRect *r2, bool *
|
||||
|
||||
// is the upper left corner of r1 contacting r2?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r1p.topleft, &r2p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the upper left corner of r2 contacting r1?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r2p.topleft, &r1p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the top right corner of r1 contacting r2?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r1p.topright, &r2p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the top right corner of r2 contacting r1?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r2p.topright, &r1p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the bottom left corner of r1 contacting r2?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r1p.bottomleft, &r2p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the bottom left corner of r2 contacting r1?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r2p.bottomleft, &r1p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the bottom right corner of r1 contacting r2?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r1p.bottomright, &r2p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
// is the bottom right corner of r2 contacting r1?
|
||||
CATCH(errctx, akgl_collide_point_rectangle(&r2p.bottomright, &r1p, collide));
|
||||
if ( *collide == true ) { SUCCEED_RETURN(errctx); }
|
||||
if ( *collide == true ) { break; }
|
||||
|
||||
} CLEANUP {
|
||||
} PROCESS(errctx) {
|
||||
} FINISH(errctx, true);
|
||||
|
||||
*collide = false;
|
||||
// No trailing `*collide = false;` here. Each corner test writes the flag
|
||||
// itself, so the eighth one leaves it false when nothing hit; assigning
|
||||
// after FINISH would overwrite the hit that broke out of the block early.
|
||||
SUCCEED_RETURN(errctx);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user