R3 batch 2: owner rulings 1, 4, 7 - five objects complete

Owner rulings 2026-09-24:
- Q1 YES (04, not 04b): _actor_aim_grenade in its natural /Od body (no goto),
  keeping the BUG comment on January's vehicle-path read of the unassigned
  aim_vector. Header-inline helpers replace the TU redirects and the NODUP
  cross_product2d hand copy; the new COMDATs are identical to January's.
  Unlocks collision_features, real_math and path_smoothing (config 03A) and
  retires the stale collision_features rejection (04). The decals rejection,
  path_structure_bsp and path_obstacle_avoidance are untouched.
- Q4 YES (A2): _first_person_weapon_update without the two NULL guards,
  VALID_INDEX at all five /Od-attested sites, and the three magnitude
  redirects removed. The weapon-switch invariant is documented in source.
- Q7 YES: _uncompress_int32_to_real_vector3d stages the conversion through a
  real temp with a declaration-time initialiser (debug exact; verbatim in
  Oct-2001 2276P and the Sept-2001 cache.exe).

Full gate: 383 -> 388 / 468 Halo objects, +3 functions (+2,211 meaningful
code), 0 regressions, parks 82 -> 80, admission 0 contradicted / 0 revoked,
fake-match leads 26, pytest 1,161 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jonas VolmanandClaude Opus 5.5 committed 2026-09-25 00:04:10 -07:00
1 parent 6c44a15135
commit 3ee32a5cb5
6 files changed
+61 -94

No files matched your search

+5 -5
View File
@@ -468,7 +468,7 @@
{ "name": "source/rasterizer/rasterizer_swizzle.c", "index": 93, "status": "Matching" },
{ "name": "source/rasterizer/rasterizer_memory_pool.c", "index": 94, "status": "Matching" },
{ "name": "source/rasterizer/rasterizer_lights.c", "index": 95, "status": "NonMatching" },
{ "name": "source/rasterizer/rasterizer_geometry.c", "index": 96, "status": "NonMatching" },
{ "name": "source/rasterizer/rasterizer_geometry.c", "index": 96, "status": "Matching" },
{ "name": "source/rasterizer/rasterizer_frame_statistics.c", "index": 97, "status": "Matching" },
{ "name": "source/rasterizer/rasterizer_debug.c", "index": 98, "status": "Matching" },
{ "name": "source/rasterizer/rasterizer_cinematics.c", "index": 99, "status": "Matching" },
@@ -511,7 +511,7 @@
{ "name": "source/physics/collision_usage.c", "index": 136, "status": "Matching" },
{ "name": "source/physics/collision_models.c", "index": 137, "status": "Matching" },
{ "name": "source/physics/collision_model_definitions.c", "index": 138, "status": "Matching" },
{ "name": "source/physics/collision_features.c", "index": 139, "status": "NonMatching" },
{ "name": "source/physics/collision_features.c", "index": 139, "status": "Matching" },
{ "name": "source/physics/collision_debug.c", "index": 140, "status": "Matching" },
{ "name": "source/physics/collision_bsp_definitions.c", "index": 141, "status": "Matching" },
{ "name": "source/physics/collision_bsp.c", "index": 142, "status": "NonMatching" },
@@ -594,7 +594,7 @@
{ "name": "source/memory/zlib/adler32.c", "index": 219, "status": "Matching" },
{ "name": "source/math/vector_tree.c", "index": 220, "status": "Matching" },
{ "name": "source/math/trig_math.c", "index": 221, "status": "Matching" },
{ "name": "source/math/real_math.c", "index": 222, "status": "NonMatching" },
{ "name": "source/math/real_math.c", "index": 222, "status": "Matching" },
{ "name": "source/math/random_math.c", "index": 223, "status": "Matching" },
{ "name": "source/math/probability.c", "index": 224, "status": "Matching" },
{ "name": "source/math/periodic_functions.c", "index": 225, "status": "NonMatching" },
@@ -634,7 +634,7 @@
{ "name": "source/interface/inventory_displays.c", "index": 259, "status": "Matching" },
{ "name": "source/interface/interface_panels.c", "index": 260, "status": "Matching" },
{ "name": "source/interface/interface.c", "index": 261, "status": "NonMatching" },
{ "name": "source/interface/first_person_weapons.c", "index": 262, "status": "NonMatching" },
{ "name": "source/interface/first_person_weapons.c", "index": 262, "status": "Matching" },
{ "name": "source/interface/event_manager.c", "index": 263, "status": "Matching" },
{ "name": "source/interface/attract_mode.c", "index": 264, "status": "Matching" },
{ "name": "source/interface/weapon_hud_interface_definition.c", "index": 265, "status": "Matching" },
@@ -799,7 +799,7 @@
{ "name": "source/bitmaps/libtiff/tif_aux.c", "index": 424, "status": "Matching" },
{ "name": "source/ai/props.c", "index": 425, "status": "Matching" },
{ "name": "source/ai/path_structure_bsp.c", "index": 426, "status": "NonMatching" },
{ "name": "source/ai/path_smoothing.c", "index": 427, "status": "NonMatching" },
{ "name": "source/ai/path_smoothing.c", "index": 427, "status": "Matching" },
{ "name": "source/ai/path_obstacles.c", "index": 428, "status": "NonMatching" },
{ "name": "source/ai/path_obstacle_avoidance.c", "index": 429, "status": "NonMatching" },
{ "name": "source/ai/path.c", "index": 430, "status": "Matching" },
-8
View File
@@ -40,14 +40,6 @@
"reason": "The exact typed decal_projection_create body naturally inlines cross_product2d as January requires, but VC7 also emits an unreferenced select-any helper body absent from the January Decals split. January's selected Actor Combat copy is no-duplicates. The exact target function receives valid credit; the surplus helper receives none and blocks whole-object Matching.",
"evidence": "docs/object_matching_logs/decals_obj_color_projection_packet_20260912.md#inline-helper-ownership-boundary",
"reopen": "Remove only when authentic source topology or an ordinary compatible canonical link preserves decal_projection_create while preventing or safely coalescing the extra helper. Do not force inline, manually expand the helper for code shaping, alter COMDAT metadata, or count the candidate-only bytes."
},
{
"unit": "source/physics/collision_features",
"class": "candidate-only-comdat-owner",
"symbol": "_projection_from_vector3d",
"reason": "All 20 target-owned functions compare strict exact, but the rebuilt translation unit also emits an 80-byte _projection_from_vector3d COMDAT absent from its January split. That surplus body reverses the x/y dominant-axis choice and differs from January's selected decals owner; exact external callers do not establish correct helper code or ownership.",
"evidence": "docs/object_matching_logs/shared_inline_helper_admission_audit_20260904.md#collision-features-projection-owner",
"reopen": "Correct the shared helper from January evidence, preserve inherited target-function matches across all header consumers, and establish authentic compatible ownership through ordinary source topology and link verification. Do not invent a helper owner, force emission, or credit the decals-owned 80 bytes from a candidate-only copy."
}
]
}
-30
View File
@@ -474,25 +474,6 @@
"objdiff_percent": 99.87755
}
},
{
"unit": "source/rasterizer/rasterizer_geometry",
"function": "_uncompress_int32_to_real_vector3d",
"class": "instruction-scheduling",
"evidence": "Equal 144-byte padded size, 9/9 relocations with identical destinations, and 49/49 instructions. The sole divergence is one dependency-free transposition: January emits `fadd __real@3f800000` before `shl eax, 0x16`, the candidate emits them in the opposite order, which also shifts that one relocation's address by three bytes. The bit fields, scales, masks, destructive shift sequence and the local real_vector3d copied out through *result are all confirmed. Measured inert: folding the second shift into the k expression, long-hand assignment, hoisting the shift ahead of j, and a division form of the k scale.",
"measurements": {
"target": {
"size": 144,
"relocation_count": 9,
"normalized_sha256": "aa4a69683e5f1148413f6029acfc993341346b93b86809dcb5450896dcc3944d"
},
"base": {
"size": 144,
"relocation_count": 9,
"normalized_sha256": "8199ac81b23a4607ed8191dbe2f95cb11da85761ecf353514714f8261562b55f"
},
"objdiff_percent": 95.0
}
},
{
"unit": "source/ai/actions",
"function": "_actor_action_handle_vehicle_entry",
@@ -1253,17 +1234,6 @@
"objdiff_percent": 62.452587
}
},
{
"unit": "source/interface/first_person_weapons",
"function": "_first_person_weapon_update",
"class": "unclassified",
"evidence": "Complete HCEA-corroborated first-person weapon state machine with the four-player January loop. Wave 2 of claude/fifty-objects-20260925 applied three /Od-attested corrections. First, a key-frame result no longer advances the state: January @0x13c jumps past next_state on _animation_key_frame, and /Od 0x61fb2e has an empty then-arm. Second, both animation updates call the TU wrapper animation_update_render_only (/Od 0x61fb22/0x61fc59); no other January object references the wrapper, so it was inlined here. Third, turning is a real_vector2d aggregate (/Od RTC descriptor turning, 8 bytes). With the null guards removed and magnitude3d inlined, the function is strictly exact (scratch/w/first_person_weapons/final_exact.c). January omits both null tests after count ? TAG_BLOCK_GET_ELEMENT(...) : NULL, and the later /Od build omits them too. That is an authentic unguarded dereference and is owner-gated. The retained defined-behaviour body keeps both guards and keeps magnitude3d out of line, so no helper COMDAT is emitted while the caller is non-exact. Zero credit. Reopen on an owner ruling admitting the unguarded dereference. See scratch/w/first_person_weapons/LEDGER.md.",
"measurements": {
"target": { "size": 1536, "relocation_count": 59, "normalized_sha256": "912d95683f14ff39699c4a1056af92441a20ceeec0584007512ce26b5d70478d" },
"base": { "size": 1520, "relocation_count": 60, "normalized_sha256": "ba39884c90f2ef0afbac711db26225e2b41d4fc0719a2afbe0a600883e2bf8ff" },
"objdiff_percent": 95.21506
}
},
{
"unit": "source/math/geometry",
"function": "_convex_polygon2d_clip_to_plane",
+27 -34
View File
@@ -126,8 +126,6 @@ symbols in this file:
/* ---------- headers */
#define cross_product2d cross_product2d_inline
#define normalize2d normalize2d_inline
#define normalize3d normalize3d_inline
#define point_from_line3d actor_combat_point_from_line3d_inline
#define vector_from_points3d actor_combat_vector_from_points3d_inline
@@ -166,8 +164,6 @@ symbols in this file:
#undef vector_from_points3d
#undef point_from_line3d
#undef normalize3d
#undef normalize2d
#undef cross_product2d
#include "math/real_math_declarations.h"
@@ -520,13 +516,6 @@ static struct projectile_definition *actor_get_grenade_definition(
return projectile_definition;
}
real cross_product2d(
real_vector2d const *a,
real_vector2d const *b)
{
return a->i*b->j - a->j*b->i;
}
static void actor_combat_find_nearby_target(
real_point3d *target_point,
real miss_distance)
@@ -1556,8 +1545,8 @@ long actor_aim_grenade(
real_vector3d *vector)
{
struct actor_datum *actor = actor_get(actor_index);
real_vector3d aim_vector;
long target_unit_index = NONE;
real_vector3d aim_vector;
if (actor->control.grenade_current_prop_index != NONE)
{
@@ -1572,12 +1561,12 @@ long actor_aim_grenade(
if (prop->state < _prop_state_unacknowledged ||
prop->state > _prop_state_becoming_acknowledged)
{
real_point3d desired_grenade_target = prop->body_position;
real_point3d new_desired_target = prop->body_position;
desired_grenade_target.z += 0.2f;
new_desired_target.z += 0.2f;
actor_combat_retarget_grenade(
actor_index,
&desired_grenade_target);
&new_desired_target);
}
}
@@ -1585,33 +1574,30 @@ long actor_aim_grenade(
if (actor->input.vehicle_index == NONE)
{
real_vector2d aim_vector2d;
real_vector2d horizontal_aim_vector;
aim_vector = actor->control.grenade_current_aim_vector;
aim_vector2d.i = aim_vector.i;
aim_vector2d.j = aim_vector.j;
horizontal_aim_vector = *(real_vector2d const *)&aim_vector;
if (normalize2d(&aim_vector2d) > 0.0f &&
aim_vector2d.i*actor->input.facing_vector.i +
aim_vector2d.j*actor->input.facing_vector.j <
GRENADE_AIMING_ANGLE_COSINE)
if (normalize2d(&horizontal_aim_vector) > 0.0f &&
dot_product2d(
(real_vector2d const *)&actor->input.facing_vector,
&horizontal_aim_vector) < GRENADE_AIMING_ANGLE_COSINE)
{
boolean counterclockwise =
aim_vector2d.j*actor->input.facing_vector.i -
aim_vector2d.i*actor->input.facing_vector.j > 0.0f;
real_vector3d new_aim_vector = actor->input.facing_vector;
real magnitude;
boolean counterclockwise = cross_product2d(
(real_vector2d const *)&actor->input.facing_vector,
&horizontal_aim_vector) > 0.0f;
rotate_vector_about_axis(
&new_aim_vector,
global_up3d,
(counterclockwise ? 1 : -1)*GRENADE_AIMING_ANGLE_SINE,
GRENADE_AIMING_ANGLE_COSINE);
magnitude = square_root(
aim_vector.i*aim_vector.i + aim_vector.j*aim_vector.j);
new_aim_vector.i *= magnitude;
new_aim_vector.j *= magnitude;
scale_vector2d(
(real_vector2d const *)&new_aim_vector,
magnitude2d((real_vector2d const *)&aim_vector),
(real_vector2d *)&new_aim_vector);
new_aim_vector.k = aim_vector.k;
match_assert_valid_real_normal3d(
@@ -1623,9 +1609,16 @@ long actor_aim_grenade(
}
}
vector->i = aim_vector.i*actor->control.grenade_current_aim_speed;
vector->j = aim_vector.j*actor->control.grenade_current_aim_speed;
vector->k = aim_vector.k*actor->control.grenade_current_aim_speed;
/* BUG (preserved for exact matching): aim_vector is assigned only on foot.
* With actor->input.vehicle_index != NONE, January branches from its vehicle
* test straight to this scale and reads the unassigned local; the 2011 HCEX
* and later /Od builds keep the same path. A corrected build should start
* from actor->control.grenade_current_aim_vector.
*/
scale_vector3d(
&aim_vector,
actor->control.grenade_current_aim_speed,
vector);
return target_unit_index;
}
+23 -14
View File
@@ -97,9 +97,6 @@ symbols in this file:
/* ---------- headers */
#define REAL_MATH_EXTERNAL_SIGNED_ANGULAR_DIFFERENCE
#define REAL_MATH_EXTERNAL_SQUARE_ROOT
#define REAL_MATH_EXTERNAL_MAGNITUDE_SQUARED3D
#define REAL_MATH_EXTERNAL_MAGNITUDE3D
#define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM
#define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM_RANGE
#include "interface/first_person_weapons.h"
@@ -1120,8 +1117,9 @@ static void first_person_weapon_build_node_matrices(
model_get_node_orientations(model, first_person_weapon->node_orientations);
}
firing_animation_index= (first_person_weapon_animations->animations.count>
_first_person_weapon_animation_ammunition) ?
firing_animation_index= VALID_INDEX(
_first_person_weapon_animation_ammunition,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_ammunition].animation_index :
@@ -1201,8 +1199,9 @@ static void first_person_weapon_build_node_matrices(
first_person_weapon->node_orientations);
}
overlay_animation_index= (first_person_weapon_animations->animations.count>
_first_person_weapon_animation_overlays) ?
overlay_animation_index= VALID_INDEX(
_first_person_weapon_animation_overlays,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_overlays].animation_index :
@@ -1479,8 +1478,9 @@ static void first_person_weapon_switch_weapons(
struct game_globals_first_person_interface *first_person_interface;
first_person_weapon->overlay_animation_index= NONE;
if (first_person_weapon_animations->animations.count>
_first_person_weapon_animation_overlays)
if (VALID_INDEX(
_first_person_weapon_animation_overlays,
first_person_weapon_animations->animations.count))
{
short overlay_animation_index= animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
@@ -1758,9 +1758,16 @@ static void first_person_weapon_update(
struct animation_graph_first_person_weapon_animations);
first_person_weapon->moving_animation.frame_index= 0;
/* no NULL test (January, and the later first-party build at /Od 0x61fcea): a non-NONE
* weapon_index is only committed by first_person_weapon_switch_weapons inside
* `if (animation_graph->first_person_weapon_animations.count)` for this same graph. The
* new-map reset leaves weapon_index 0 but unit_index NONE, and first_person_weapon_new_unit
* runs switch_weapons (which stores NONE first) as soon as it sets unit_index, so the NULL
* arm above cannot reach this read. */
first_person_weapon->moving_animation.index=
first_person_weapon_animations &&
first_person_weapon_animations->animations.count>_first_person_weapon_animation_moving ?
VALID_INDEX(
_first_person_weapon_animation_moving,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_moving].animation_index :
@@ -1780,10 +1787,12 @@ static void first_person_weapon_update(
struct animation_graph_first_person_weapon_animations);
first_person_weapon->overcharged_jitter_animation.frame_index= 0.0f;
/* no NULL test: the same switch_weapons invariant as the moving animation above
* (January and /Od 0x61fd96). */
first_person_weapon->overcharged_jitter_animation.index=
first_person_weapon_animations &&
first_person_weapon_animations->animations.count>
_first_person_weapon_animation_overcharged_jitter ?
VALID_INDEX(
_first_person_weapon_animation_overcharged_jitter,
first_person_weapon_animations->animations.count) ?
animation_graph_animation_index_get(
&first_person_weapon_animations->animations)
[_first_person_weapon_animation_overcharged_jitter].animation_index :
+6 -3
View File
@@ -538,12 +538,15 @@ real_vector3d uncompress_int32_to_real_vector3d(
unsigned long compressed)
{
real_vector3d v;
real value = (real)(long)(compressed<<21);
v.i = ((real)(long)(compressed<<21) * (1.0f/1048576.0f) + 1.0f) * (1.0f/2047.0f);
v.i = (value * (1.0f/1048576.0f) + 1.0f) * (1.0f/2047.0f);
compressed >>= 11;
v.j = ((real)(long)(compressed<<21) * (1.0f/1048576.0f) + 1.0f) * (1.0f/2047.0f);
value = (real)(long)(compressed<<21);
v.j = (value * (1.0f/1048576.0f) + 1.0f) * (1.0f/2047.0f);
compressed >>= 11;
v.k = ((real)(long)(compressed<<22) * (1.0f/2097152.0f) + 1.0f) * (1.0f/1023.0f);
value = (real)(long)(compressed<<22);
v.k = (value * (1.0f/2097152.0f) + 1.0f) * (1.0f/1023.0f);
return v;
}