Reconcile HUD geometry and sound caller graphs

This commit is contained in:
Jonas Volman committed 2026-09-08 20:28:22 -07:00
1 parent 78cd2f4e67
commit 4ebe0ff6ec
17 files changed
+2506 -45

No files matched your search

+90 -2
View File
@@ -3109,9 +3109,9 @@
"base": {
"size": 672,
"relocation_count": 43,
"normalized_sha256": "885fdb81966284acf5ba7138a4bbbd2221e7c61c89114fdd6f53b96ba89b81a0"
"normalized_sha256": "7db4861a5adf519e0457a0ffe785ff4d47e878fda47cc0a06206a529776ac91c"
},
"objdiff_percent": 97.67839
"objdiff_percent": 97.76382
}
},
{
@@ -6215,6 +6215,94 @@
"base": { "size": 608, "relocation_count": 26, "normalized_sha256": "bcf4cefdb82e1d1eb34388f794ca0cdcc98c1d01b635c44e8796e744e9e3e920" },
"objdiff_percent": 95.505615
}
},
{
"unit": "source/interface/hud_messaging",
"function": "_hud_messaging_update",
"class": "unclassified",
"evidence": "Complete typed HUD renderer reconstructed from January control flow, strings, data and relocations, with the independent HCEA implementation as a semantic map. It covers objective/help/custom state messages, text and bitmap elements, button remapping, split-screen placement, timed item messages, fading and expiration. January is 2,928 padded bytes with 137 relocations; canonical is 2,880 with 136. The remaining delta is a VC7 load/register schedule around split-screen line placement, not known missing behavior. No compiler steering, raw offsets, fake dependency, undefined behavior, representation pun, forced inline or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/hud_messaging_obj_renderer_caller_closure_20260908.md.",
"measurements": {
"target": { "size": 2928, "relocation_count": 137, "normalized_sha256": "26a1376422e3742f9d65a8d83241919e1e370769a936ade9c9b859829d7d1b7b" },
"base": { "size": 2880, "relocation_count": 136, "normalized_sha256": "d5d32a3ce4d44e6e87ed3daa1d1ce28cf1a746bd53ae0b678bf7ec99a955d815" },
"objdiff_percent": 66.71034
}
},
{
"unit": "source/math/geometry",
"function": "_convex_hull3d_begin",
"class": "unclassified",
"evidence": "Complete typed tetrahedron seed reconstructed from January assertions, layout and disassembly with the independent HCEA convex-hull source as a semantic map. January is 1,488 padded bytes and canonical is 1,472, both with 28 relocations and the same ordered destinations. The remaining 16-byte aggregate-home and topology schedule difference has no proved source-level mechanism. No compiler steering, raw layout, fake dependency, extra helper owner, forced inline or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md.",
"measurements": {
"target": { "size": 1488, "relocation_count": 28, "normalized_sha256": "d3e0ba2e2077ce1e167e22398d1ce4b18329c8a3301ef606b46f56456ee282bc" },
"base": { "size": 1472, "relocation_count": 28, "normalized_sha256": "1b5824819ddffa0f161d260eec28283d4b1e3fc5a7422dee6ec8e5f5653f8804" },
"objdiff_percent": 93.49269
}
},
{
"unit": "source/math/geometry",
"function": "_convex_hull3d_expand",
"class": "unclassified",
"evidence": "Complete typed convex-hull expansion reconstructed from January assertions, layout and disassembly with independent HCEA source as a semantic map. Target and canonical are both 1,808 padded bytes with 67 relocations and the same ordered destinations. Final audit moved indexed surface and edge pointer formation behind the corresponding bounds validation, removing abstract-C undefined behavior from the earlier, superficially closer candidate. That valid source perturbs VC7 allocation and relocation addresses across the horizon walk, reducing objdiff to 84.64924 percent; it is retained because semantic validity outranks similarity. No compiler steering, raw layout, fake dependency, extra helper owner, forced inline, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md.",
"measurements": {
"target": { "size": 1808, "relocation_count": 67, "normalized_sha256": "bad17e2158737f40d52656451e9f2ce826a8b27d395f84032536dceab223fbb0" },
"base": { "size": 1808, "relocation_count": 67, "normalized_sha256": "3b42a1bae5ecb4b878280d95cedf580f83abd8c332c193aae825d59a7ae3c5f1" },
"objdiff_percent": 84.64924
}
},
{
"unit": "source/sound/sound_manager",
"function": "_update_channel_for_looping_sound",
"class": "unclassified",
"evidence": "Complete typed looping-channel update reconstructed from January code/relocations and HCEA semantics. It covers properties, cache transitions, linked permutations, definition changes and crossfades. January is 1,360 padded bytes with 61 relocations; canonical is 1,376 with 61. No unique source-level mechanism explains the residual, so it receives zero exact credit. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.",
"measurements": {
"target": { "size": 1360, "relocation_count": 61, "normalized_sha256": "457d122269e7e06c8b4a557f80eef434e3eb925aa8aaaa73d9e69bce635a6d78" },
"base": { "size": 1376, "relocation_count": 61, "normalized_sha256": "5fa29769faf68bcaa178d1a65f979d8641b10b3727a404d00849b345f42a0ff7" },
"objdiff_percent": 96.782814
}
},
{
"unit": "source/sound/sound_manager",
"function": "_update_channel_for_impulse_sound",
"class": "unclassified",
"evidence": "Complete typed impulse-channel property/queue/update body retained under its corrected semantic target name. January and canonical are both 528 padded bytes with all 29 relocations; normalized code still differs through ordinary scheduling. January bodies, callers and HCEA names independently prove the target symbol rotation. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.",
"measurements": {
"target": { "size": 528, "relocation_count": 29, "normalized_sha256": "b700f26596f466e5771d4f875b0d3a9354600ee13b4dd1553cde0c48d45d8202" },
"base": { "size": 528, "relocation_count": 29, "normalized_sha256": "2d81b0f86a90351a964320970505552d0b323a95a871e75b9d8a4e50cf29eeb4" },
"objdiff_percent": 94.03593
}
},
{
"unit": "source/sound/sound_manager",
"function": "_limit_pitch",
"class": "unclassified",
"evidence": "Complete ordinary-C pitch-bend limiter reconstructed from January and HCEA behavior. January is 96 padded bytes and canonical is 112, both with one relocation. The residual is not assigned a speculative compiler mechanism and receives zero exact credit. No compiler steering, fake dependency, undefined behavior or nonsensical branch is retained. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.",
"measurements": {
"target": { "size": 96, "relocation_count": 1, "normalized_sha256": "fa465e8daf708a83bb16686d2c254a68937b1ad2c600be40817358dbdd31c6f3" },
"base": { "size": 112, "relocation_count": 1, "normalized_sha256": "c296946da525c8cb2c0e7c9ddced82e27a98fbfb51f9eb7e032bcecd8c880375" },
"objdiff_percent": 91.57895
}
},
{
"unit": "source/sound/sound_manager",
"function": "_sound_set_definition_end",
"class": "unclassified",
"evidence": "Complete queued-end-definition transition retained under its corrected semantic target name. January and canonical are both 208 padded bytes with all nine relocations; the code hash remains different. It uses the now-exact bounded channel-summary builder and the typed instance-limit flow. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.",
"measurements": {
"target": { "size": 208, "relocation_count": 9, "normalized_sha256": "06f507e27039b2a39192271221e6c7b703aee4d5ae45becdac84f3543095ec13" },
"base": { "size": 208, "relocation_count": 9, "normalized_sha256": "4f5a03ed0532981c792521f1ce7cdcc47eef5a3ee2d038caf65f57b1987675d9" },
"objdiff_percent": 91.884056
}
},
{
"unit": "source/sound/sound_manager",
"function": "_sound_find_like_channel",
"class": "unclassified",
"evidence": "Complete typed best-channel selector for a supplied channel list. Target and canonical are both 272 padded bytes with all 16 relocations and 97.15909 percent similarity; the remaining normalized difference is not assigned a speculative mechanism. No compiler steering, raw offset, fake dependency, undefined behavior or nonsensical branch is retained. Zero exact credit. See docs/object_matching_logs/sound_manager_obj_idle_channel_spine_20260908.md.",
"measurements": {
"target": { "size": 272, "relocation_count": 16, "normalized_sha256": "240a72742e0bdcb19f5bbf684bcffaefcf37a483a9fc59393cf4a6d0c36984b3" },
"base": { "size": 272, "relocation_count": 16, "normalized_sha256": "a695dfa283a53aeabb3923c2186b6690ff2b319205588ba7cebefce9b7c91c56" },
"objdiff_percent": 97.15909
}
}
]
}
+5 -4
View File
@@ -7546,22 +7546,22 @@
{ "file_offset": 1820560, "flags": 32, "name": "_source_distance" },
{ "file_offset": 1820768, "flags": 32, "name": "_sound_find_like_channel" },
{ "file_offset": 1821040, "flags": 32, "name": "_sound_preempts_sound" },
{ "file_offset": 1821296, "flags": 32, "name": "_sound_find_best_channel" },
{ "file_offset": 1821296, "flags": 32, "name": "_update_channel_for_impulse_sound" },
{ "file_offset": 1821824, "flags": 32, "name": "_looping_sound_new" },
{ "file_offset": 1822128, "flags": 32, "name": "_looping_sound_new_sound" },
{ "file_offset": 1822128, "flags": 32, "name": "_sound_set_definition_end" },
{ "file_offset": 1822336, "flags": 32, "name": "_detail_sound_random_offset" },
{ "file_offset": 1822528, "flags": 32, "name": "_sound_stop_impulse" },
{ "file_offset": 1822656, "flags": 32, "name": "_sound_stop_impulse_by_source_and_definition" },
{ "file_offset": 1822768, "flags": 32, "name": "_sound_stop_all" },
{ "file_offset": 1822864, "flags": 32, "name": "_source_audible" },
{ "file_offset": 1823104, "flags": 32, "name": "_refresh_sounds" },
{ "file_offset": 1823648, "flags": 32, "name": "_sound_find_channel" },
{ "file_offset": 1823648, "flags": 32, "name": "_sound_find_best_channel" },
{ "file_offset": 1824064, "flags": 32, "name": "_update_potentially_audible_looping_sound" },
{ "file_offset": 1824544, "flags": 32, "name": "_update_channel_for_looping_sound" },
{ "file_offset": 1825904, "flags": 32, "name": "_sound_new_impulse" },
{ "file_offset": 1826880, "flags": 32, "name": "_sound_refresh_looping" },
{ "file_offset": 1828016, "flags": 32, "name": "_refresh_listener" },
{ "file_offset": 1828512, "flags": 32, "name": "_update_channel_for_impulse_sound" },
{ "file_offset": 1828512, "flags": 32, "name": "_sound_find_channel" },
{ "file_offset": 1829008, "flags": 32, "name": "_update_channels" },
{ "file_offset": 1829872, "flags": 32, "name": "_process_looping_sounds" },
{ "file_offset": 1830368, "flags": 32, "name": "_sound_idle" },
@@ -22064,6 +22064,7 @@
{ "file_offset": 3034188, "flags": 0, "name": "_button_mappings", "static": true },
{ "file_offset": 3034204, "flags": 0, "name": "_time_code_time" },
{ "file_offset": 3034208, "flags": 0, "name": "_time_code_stop_time" },
{ "file_offset": 3034212, "flags": 0, "name": "_split_screen_hud_message_offset", "static": true },
{ "file_offset": 3034216, "flags": 0, "name": "?overcharge_count@?1??hud_render_unit_interface@@9@9", "static": true },
{ "file_offset": 3034220, "flags": 0, "name": "_scale" },
{ "file_offset": 3034244, "flags": 0, "name": "_data_002e4c84" },
@@ -0,0 +1,110 @@
# Geometry: convex-hull seed and expansion reconciliation (2026-09-08)
## Result and scope
This packet restores the two public implementations required by the already
exact `convex_hull3d` caller:
- `convex_hull3d_begin` selects four affinely independent points, initializes
the tetrahedron topology, constructs its four outward-facing planes, and
clears the unused vertex, edge, and surface tails.
- `convex_hull3d_expand` classifies a new point, removes visible faces, finds
and walks the horizon, splices surviving vertex fans, and constructs the new
face fan with bounded caller-provided storage.
Both functions are complete, typed, public C implementations. They are honest
fuzzy residuals and receive zero exact credit. Geometry moves from 18 exact / 6
residual / 6 unwritten to **18 exact / 8 residual / 4 unwritten**. The packet
converts **3,296 target padded bytes** from unwritten to reviewed residual code
without changing any inherited exact function.
`source/math/geometry.h` already contained the release-PDB-authenticated
`vertex3d`, `edge3d`, and `surface3d` layouts and the correct public prototypes,
so it was not edited. The two optional polygon clipping functions remain
outside this packet. No configuration file was edited in this lane.
## Provenance and reconstruction
The January split object is authoritative for code, assertion lines, public
linkage, padded extents, and relocation ownership. Semantic source evidence is
the read-only HCEA material at:
- `scratch/_halocea-reference-20260907/src/blam/structures/convex_hull3d_begin.c`
- `scratch/_halocea-reference-20260907/src/blam/math/convex_hull3d_expand.c`
Fable commit `f9dd42218b3325e1b3a63d7860e5309f69fd3d8e`
authenticates the surrounding public caller, point test, owner types, and
prototypes, but did not contain bodies for this pair. The implementation keeps
project `real` and `boolean` types, one parameter per line, C89 block-scoped
declarations, explicit returns, and the existing public owner names.
The January assertion evidence is retained verbatim: the four pointer checks
at source lines 1710-1713 and 1973-1976, the boundary assertion at 2076, the
vertex-fan assertions at 2098-2099, and the horizon/vertex assertions at
2167-2171 and 2206-2210. The point, vertex, edge, surface, and plane accesses
agree with the owner layouts and the January offsets.
No helper was exported or force-retained. In particular, Geometry emits no
`_point_from_line3d` symbol. January has no Geometry definition or reference
for that helper; its 48-byte `IMAGE_COMDAT_SELECT_NODUPLICATES` owner belongs
elsewhere. The seed's line-distance math therefore remains naturally inlined
with typed vectors instead of creating a conflicting helper owner.
## Measured object evidence
The final isolated candidate is `scratch/geometry-safe-20260908.obj`.
| Function | Target size / relocs | Candidate size / relocs | Target normalized SHA-256 | Candidate normalized SHA-256 |
| --- | ---: | ---: | --- | --- |
| `_convex_hull3d_begin` | 1488 / 28 | 1472 / 28 | `d3e0ba2e2077ce1e167e22398d1ce4b18329c8a3301ef606b46f56456ee282bc` | `1b5824819ddffa0f161d260eec28283d4b1e3fc5a7422dee6ec8e5f5653f8804` |
| `_convex_hull3d_expand` | 1808 / 67 | 1808 / 67 | `bad17e2158737f40d52656451e9f2ce826a8b27d395f84032536dceab223fbb0` | `3b42a1bae5ecb4b878280d95cedf580f83abd8c332c193aae825d59a7ae3c5f1` |
For begin, the ordered relocation-destination sequence is equal, although
later relocation addresses move with the 16-byte extent difference. Target and
candidate contain 482 and 484 decoded instructions. The prefix and the entire
line-search body are instruction-aligned; the dominant unresolved mechanism is
a 12-byte aggregate-home difference (`0x4c` versus `0x40` stack frames), plus
the resulting plane-expression and topology scheduling. A projected-point
aggregate experiment was rejected because VC7 scalarized it more aggressively,
regressing the candidate to 1456 padded bytes and a `0x34` frame.
For expand, padded size, relocation count, and the ordered relocation-
destination sequence agree. Final root audit found that the earlier
98.264755-percent candidate formed indexed pointers before checking their
indices. That abstract-C undefined behavior was rejected. Canonical now
validates each surface or edge index before forming its pointer; the resulting
valid source is **84.64924 percent** similar because VC7 changes allocation and
relocation addresses across the horizon walk. Semantic validity takes priority
over the older superficial similarity, and no allocator-only source steering
was attempted.
## Gates and integration metadata
The required isolated gate passes compilation and the emitted-symbol guard:
```text
python -B tools/campaign/gate.py source/math/geometry --all \
--out scratch/geometry-safe-20260908.obj \
--forbid-emitted-symbol _point_from_line3d
exact 18, residual 8, unwritten 4 (30 listed)
```
The source-policy scan reports zero review leads:
```text
python -B tools/fake_match_scan.py --fail-on-findings source/math/geometry.c
Scanned 1 file; 0 review leads
```
The read-only parked-function audit reports 351 active, zero stale, and zero
invalid entries. No configure or Ninja command was run, as required by the lane
scope.
Root integration should retain both functions as zero-credit fuzzy parks with
class `unclassified`, using the target/candidate measurements above and the
ordinary post-integration objdiff percentages. Do not add or change symbol
ownership: both public target symbols already exist in `config/symbols.json`.
The park evidence should point to this ledger and state that begin is a
16-byte/aggregate-home scheduling residual, while expand has exact size and
relocation layout with only the measured instruction/register schedule left.
@@ -0,0 +1,69 @@
# HUD, Geometry, and Sound caller-packet reconciliation (2026-09-08)
## Canonical result
This packet advances the semantic exact ledger from **1,035,078 meaningful
code bytes / 6,600 accepted exact functions** to **1,037,674 meaningful code
bytes / 6,611 accepted exact functions**. The net admitted result is **+2,596
meaningful / +2,688 padded code bytes across 11 functions**, with zero losses
in the 8,245-function rename-stable census.
| Family | Exact functions | Meaningful bytes | Padded bytes |
| --- | ---: | ---: | ---: |
| HUD messaging | 3 | 562 | 576 |
| Sound manager | 8 | 2,034 | 2,112 |
| Geometry | 0 | 0 | 0 |
| **Total** | **11** | **2,596** | **2,688** |
The exact functions are `_compare_messages`, `_render_state_text`,
`_render_state_bitmap`, `_sound_idle`, `_sound_update_time`,
`_update_channels`, `_channel_set_properties_hardware`,
`_channel_queue_sound`, `_channel_get_state`,
`_sound_manager_master_gain`, and `_sound_channel_summary_build`.
## Honest residual accounting
The packet also replaces unwritten stubs with **8,688 padded bytes** of
complete, typed, reviewed source. These functions remain fuzzy and receive
**zero exact credit**:
| Family | Complete fuzzy target scope |
| --- | ---: |
| HUD messaging renderer | 2,928 padded bytes |
| Geometry convex-hull seed and expansion | 3,296 padded bytes |
| Sound channel update/select graph | 2,464 padded bytes |
| **Total** | **8,688 padded bytes** |
Together, the exact and fuzzy work reconstructs 11,376 padded target bytes.
The individual ledgers retain target/candidate hashes, relocation counts,
provenance, residual classifications, and reopen conditions. No fuzzy body is
counted in the canonical exact-byte total.
The final review keeps valid source ahead of superficial similarity. Bounds
are validated before Geometry forms indexed pointers; HUD's icon helper is
const-correct; and Sound definition flags, instance-limit constants, and
cross-TU prototypes live with their genuine owners. The four corrected
Sound-manager private names are supported by January caller relocations and
the release-PDB/HCEA semantic map. Renaming them earns no byte credit.
## Validation
The canonical integration was configured and rebuilt as a whole. Focused
gates report HUD **33 exact / 3 residual / 0 unwritten**, Geometry **18 / 8 /
4**, and Sound **40 / 13 / 12**. All three pass the forbidden
`_point_from_line3d` emission guard. The stable before/after census reports
**11 gains / 2,688 padded bytes and zero regressions**. The source-policy scan
reports zero fake-match review leads in the changed C files, the object
admission audit adds no unsupported whole-object candidate, and the test suite
reports **1,147 passed, 2 skipped, and 26 subtests passed**.
All retained residuals are registered in `config/parked.json`; the park audit
reports **359 active, zero stale, and zero invalid**. The packet does not cross
the **1,041,486 meaningful-byte** treemap cadence threshold, so the canonical
treemap is intentionally not refreshed by this commit.
## Detailed ledgers
- `hud_messaging_obj_renderer_caller_closure_20260908.md`
- `geometry_obj_convex_hull3d_begin_expand_reconciliation_20260908.md`
- `sound_manager_obj_idle_channel_spine_20260908.md`
@@ -0,0 +1,71 @@
# `hud_messaging.obj` renderer caller closure (2026-09-08)
## Result
This packet reconstructs the complete `hud_messaging_update` graph rather than
tuning another isolated residual. The unit moves from 30 exact / 2 residual /
4 unwritten January owners to **33 exact / 3 residual / 0 unwritten** owners.
Three ordinary C helpers become strict exact:
| function | meaningful bytes | padded bytes |
| --- | ---: | ---: |
| `_compare_messages` | 47 | 48 |
| `_render_state_text` | 128 | 128 |
| `_render_state_bitmap` | 387 | 400 |
| **total** | **562** | **576** |
`_hud_messaging_update` is a complete, typed **fuzzy** reconstruction and
receives zero exact credit. January is 2,928 padded bytes with 137
relocations and normalized SHA-256
`26a1376422e3742f9d65a8d83241919e1e370769a936ade9c9b859829d7d1b7b`;
the retained candidate is 2,880 padded bytes with 136 relocations and
normalized SHA-256
`d5d32a3ce4d44e6e87ed3daa1d1ce28cf1a746bd53ae0b678bf7ec99a955d815`.
The production objdiff percentage is recorded in `config/parked.json` after
the aggregate full build.
## Evidence and reconstruction
January target code, assertion strings, relocations, data placement and
function boundaries are authoritative. The independent HCEA reconstruction
at `scratch/_halocea-reference-20260907/src/blam/interface/` supplies the
semantic control-flow map. Every target dependency was checked against the
January relocation stream; later-build behavior was not assumed when January
disagreed.
The retained renderer covers objective/help/custom state messages, text and
bitmap elements, input-button remapping, split-screen placement, timed item
messages, fading and expiration. It uses named HUD enums, `real` and the
existing typed tag macros. Exact-coordinate assertion wrappers preserve the
January file/line payloads. Target evidence requires two calls to
`global_scenario_get` on the scenario-name path and distinct raw-text and
item-text draw call sites; both are represented directly instead of being
compiler-steered away.
The target data directly identifies `button_mappings`, `time_code_time`, and
`time_code_stop_time`. The adjacent four-byte value 17 is named
`split_screen_hud_message_offset`, consistent with both its two January uses
and the cross-build source. It is target-static in `config/symbols.json`.
The remaining renderer delta is not missing high-level behavior. Size and
relocation comparison leaves a 48-byte / one-relocation difference dominated
by VC7's duplicate `hud_msg_def` load and stack/register schedule around the
split-screen line-top calculation. Further expression perturbation would be
low-leverage compiler steering, so the body is parked for future source/local
evidence.
## House-rule and gate checks
- No private `code + address` or global `bss + address` names were introduced.
- Typed tag access, named enum constants and project scalar/color/geometry
types are used throughout.
- `get_flash_color` is declared in its genuine `hud_draw.h` owner.
- No forced inline, volatile/register steering, raw offset, fake dependency,
undefined behavior, pointer representation pun, inline assembly or
nonsensical branch is retained.
- The isolated full-unit gate reports 33 exact / 3 residual / 0 unwritten.
- The `_point_from_line3d` emitted-symbol guard and changed-source fake-match
scan pass.
Final production build, rename-stable sweep, park audit, semantic totals and
tool-suite results are recorded in the aggregate reconciliation checkpoint.
@@ -0,0 +1,162 @@
# `sound_manager.obj` idle/channel spine reconstruction (2026-09-08)
## Result
This packet advances `source/sound/sound_manager` from **32 strict-exact,
8 residual, and 25 unwritten** January function owners to **40 strict-exact,
13 residual, and 12 unwritten** owners. It adds eight genuine exact functions,
**2,034 meaningful / 2,112 padded exact code bytes**, and retains five newly emitted, complete
source reconstructions as measured fuzzy results with zero exact credit. No
inherited residual was tuned.
The requested caller-connected spine covers 3,792 January padded bytes. With
its two direct private dependencies, this packet reconstructs 4,576 January
padded bytes in total.
| semantic source function | January owner used for the gate | January padded bytes | result |
|---|---|---:|---|
| `sound_idle` | `_sound_idle` | 112 | **exact** |
| `sound_update_time` | `_sound_update_time` | 48 | **exact** |
| `update_channels` | `_update_channels` | 864 | **exact** |
| `update_channel_for_looping_sound` | `_update_channel_for_looping_sound` | 1,360 | fuzzy: candidate 1,376, same relocation count, SHA differs; **zero credit** |
| `update_channel_for_impulse_sound` | `_update_channel_for_impulse_sound` after reviewed rotation | 528 | fuzzy: same size and relocation count, SHA differs; **zero credit** |
| `channel_set_properties_hardware` | `_channel_set_properties_hardware` | 160 | **exact** |
| `channel_queue_sound` | `_channel_queue_sound` | 128 | **exact** |
| `channel_get_state` | `_channel_get_state` | 240 | **exact** |
| `limit_pitch` | `_limit_pitch` | 96 | fuzzy: candidate 112, SHA differs; **zero credit** |
| `sound_manager_master_gain` | `_sound_manager_master_gain` | 48 | **exact** |
| `sound_set_definition_end` | `_sound_set_definition_end` after reviewed rename | 208 | fuzzy: same size and relocation count, SHA differs; **zero credit** |
| `sound_channel_summary_build` | `_sound_channel_summary_build` | 512 | **exact** |
| `sound_find_like_channel` | `_sound_find_like_channel` | 272 | fuzzy: same size and relocation count, SHA differs; **zero credit** |
The exact rows have the following January normalized hashes:
| owner | padded bytes | relocations | normalized SHA-256 prefix |
|---|---:|---:|---|
| `_sound_idle` | 112 | 14 | `085dc0186fa101c9` |
| `_sound_update_time` | 48 | 5 | `33deb9bf16ce2e64` |
| `_update_channels` | 864 | 51 | `a51542452d0006ed` |
| `_channel_set_properties_hardware` | 160 | 12 | `e647272b731fcf12` |
| `_channel_queue_sound` | 128 | 9 | `fa9faabe6c779a25` |
| `_channel_get_state` | 240 | 15 | `e9fb1d4c81e46eae` |
| `_sound_manager_master_gain` | 48 | 2 | `6a2cc8c25eac16c9` |
| `_sound_channel_summary_build` | 512 | 32 | `f4ae27c691076057` |
## Reconstruction evidence
January target disassembly, relocation order, object layout, and assertion
strings are authoritative. The HCEA reconstruction beneath
`scratch/_halocea-reference-20260907/src/blam/{cache,sound,game}` supplies the
semantic control-flow and field-name map, while donor commit
`e4c48546384d1c11c7c1463db0c6676d283aff7e` supplies an independent repository
source checkpoint. Later-build behavior was discarded wherever the January
object disproved it. In particular, this packet uses the January 0x3C sound
platform vtable shape and the older two-argument channel queue / one-argument
channel update calls rather than the later HCEA interfaces.
The exact `_sound_idle` body naturally inlines the exact
`sound_update_time` arithmetic. The source-level cast precedes the subtraction,
which produces January's `fild`/`fisub` sequence both in the standalone helper
and at the expected inline site. `_update_channels` closes without compiler
steering: the January expression order for the relative-position magnitude and
the nested mouth-aperture call reproduce all 864 bytes and all 51 relocation
sites. No `point_from_line3d` helper or COMDAT is emitted.
The newly exact channel helpers use project datum/tag accessors and the
January platform interface. The channel state machine releases finished
cache permutations, promotes queued permutations, and advances the estimated
tick count. The master-gain helper preserves January's dialog-class exception
and global non-dialog multiplier. A root audit restored the four target-proven
capacity assertions missing from the initial donor lift and the two genuine
`sound_class_get` calls. That closes the complete 512-byte channel-summary
builder exactly; it is a semantic bounds repair, not allocator tuning.
## Required symbol corrections
The current symbol labels cannot be used as semantic source names. These are
target-symbol corrections for the owning symbol map, not aliases to publish in
C:
1. January address `0x001BCA70`, previously `_sound_find_best_channel`, is
semantic `update_channel_for_impulse_sound`. Its 528-byte body constructs
platform properties, asserts the permutation is cached, queues it, records
the playing channel, and updates the hardware channel. The exact
`_update_channels` relocation at function offset `0x02E1` targets this owner.
An initial isolated diagnostic used an alias; production now carries the
corrected target label directly.
2. January address `0x001BD3A0`, previously `_sound_find_channel`, is semantic
`sound_find_best_channel`. Its 416-byte body scans compatible hardware
channels, immediately accepts a free one, and otherwise uses
`sound_preempts_sound` and distance to select the best victim.
3. January address `0x001BE6A0`, previously
`_update_channel_for_impulse_sound`, is semantic `sound_find_channel`. Its
496-byte body performs speech/source reuse and instance-limit selection. Its
calls at offsets `0x0167` and `0x01D3` target the 416-byte owner currently
labeled `_sound_find_channel`, confirming the semantic best-channel helper
in item 2. It does not update impulse hardware properties. Rotate all
three labels atomically only after checking every call relocation; no credit
is claimed for the 416- or 496-byte owners in this packet.
4. January address `0x001BCDB0`, previously `_looping_sound_new_sound`, is
semantic `sound_set_definition_end`. It swaps to the queued end definition,
reselects pitch range/permutation, applies instance limits, and stops the
selected sound. `_update_channel_for_looping_sound` relocates to it at
function offset `0x03D7`. Production now carries the corrected target label
directly. This is distinct
from January's exact `_update_potentially_audible_looping_sound`, which is the
allocator called `looping_sound_new_sound` by the later HCEA reconstruction.
These mappings are supported jointly by the HCEA release-PDB semantic names,
the January bodies, and the January caller relocations. The source retains the
semantic names and deliberately does not claim an exact or fuzzy result under
a knowingly wrong semantic label.
## Cross-TU ownership
The three temporary gate declarations were removed from `sound_manager.c`.
Their genuine owners and exact signatures are now published in
`sound_definitions.h` and `game_sound.h`:
```c
/* sound_definitions.h; definition is sound_definitions.c:167 */
real sound_permutation_get_real_mouth_aperture(
struct sound_permutation *permutation,
long tick_index);
/* game_sound.h; definition is game_sound.c:911 */
void game_sound_set_mouth_aperture(
long object_index,
real mouth_aperture);
/* game_sound.h; declaration game_sound.c:224, definition game_sound.c:1009 */
boolean track_object_impulse_sound(
long object_index,
void const *attachment_data,
struct sound_source *source);
```
January `game_sound.obj` defines `_track_object_impulse_sound` with
COFF storage class 2 (external), and January `sound_manager.obj` carries an
undefined class-2 external for it. The production definition is therefore
external and the old duplicate static declaration is gone. The shared
16-entry instance-limit constants likewise moved from `sound_classes.c` to
their genuine `sound_classes.h` owner; the full consumer rebuild verifies the
migration.
## Validation
The final isolated production command was:
```text
python -B tools/campaign/gate.py source/sound/sound_manager --all --forbid-emitted-symbol _point_from_line3d --out %TEMP%\sound_packet_final.obj
```
It reported **40 exact / 13 residual / 12 unwritten of 65**, and the forbidden
emitted-symbol guard passed. `relocdiff` independently reports that exact
`_update_channels` has 864/864 bytes, 51/51 relocations, an equal normalized
hash, and zero differing relocation rows. The summary builder is also exact
at 512/512 bytes and 32/32 relocations. The source fake-match scan reports
zero review leads, and `git diff --check -- source/sound/sound_manager.c`
passes apart from Git's existing LF-to-CRLF advisory.
The aggregate root checkpoint records the shared symbol map, park entries,
full build, rename-stable sweep, tests, commit and publication results.
+16
View File
@@ -385,3 +385,19 @@ and [the Physics research ledger](object_matching_logs/physics_obj_update_new_re
This gain does not yet cross the **1,041,486-byte** treemap cadence threshold,
so the next verified packet should be accumulated before refreshing the map.
## 2026-09-08 HUD, Geometry, and Sound caller packet
The canonical semantic ledger now reports **1,037,674 meaningful exact code
bytes / 6,611 accepted exact functions**. HUD and Sound add **11 exact
functions / 2,596 meaningful / 2,688 padded bytes**, with zero losses in the
8,245-function stable census. Geometry adds no exact credit in this packet.
An additional **8,688 padded bytes** of complete HUD, Geometry, and Sound
source are retained as explicitly documented zero-credit fuzzies. The batch
uses genuine declaration owners and semantic private names, passes the full
build, park, fake-match, admission, and test gates, and does not emit the
forbidden `_point_from_line3d` helper. See
[the aggregate ledger](object_matching_logs/hud_geometry_sound_caller_packet_reconciliation_20260908.md).
The **1,041,486-byte** treemap refresh threshold has not yet been crossed.
+3
View File
@@ -57,6 +57,9 @@ void hud_calculate_point(
point2d *result);
long get_flash_duration(
struct hud_color_definition const *hud_color);
pixel32 get_flash_color(
struct hud_color_definition const *hud_color,
long reference_value);
void hud_draw_meter(
short local_player_index,
struct hud_absolute_placement_definition const *placement,
+724 -4
View File
@@ -117,18 +117,30 @@ symbols in this file:
#include "cseries/cseries.h"
#include "cseries/errors.h"
#include "bitmaps/bitmap_group.h"
#include "bitmaps/bitmaps.h"
#include "cache/texture_cache.h"
#include "cutscene/cinematics.h"
#include "game/game.h"
#include "game/game_engine.h"
#include "game/players.h"
#include "input/input_abstraction.h"
#include "interface/hud_draw.h"
#include "interface/hud.h"
#include "interface/hud_messaging.h"
#include "interface/interface.h"
#include "interface/ui_widget.h"
#include "items/item_definitions.h"
#include "rasterizer/rasterizer.h"
#include "render/render.h"
#include "render/render_debug.h"
#include "saved games/game_state.h"
#include "scenario/scenario.h"
#include "scenario/scenario_definitions.h"
#include "tag_files/tag_groups.h"
#include "text/draw_string.h"
#include "text/font_group.h"
#include "text/text_group.h"
#include "text/unicode.h"
#include <stddef.h>
@@ -162,6 +174,78 @@ enum hud_number_show_flags
NUMBER_OF_HUD_NUMBER_SHOW_FLAGS
};
enum hud_icon_flags
{
_hud_icon_use_text_bit,
_hud_icon_use_color_bit,
_hud_icon_absolute_width_bit,
NUMBER_OF_HUD_ICON_FLAGS
};
enum hud_flash_flags
{
_hud_flash_reverse_colors_bit,
NUMBER_OF_HUD_FLASH_FLAGS
};
enum text_justification
{
_text_justification_left,
_text_justification_right,
_text_justification_center,
NUMBER_OF_TEXT_JUSTIFICATIONS
};
enum hud_icon_type
{
_icon_a_button,
_icon_b_button,
_icon_x_button,
_icon_y_button,
_icon_black_button,
_icon_white_button,
_icon_left_trigger,
_icon_right_trigger,
_icon_dpad_up,
_icon_dpad_down,
_icon_dpad_left,
_icon_dpad_right,
_icon_start_button,
_icon_back_button,
_icon_left_thumb,
_icon_right_thumb,
_icon_left_stick,
_icon_right_stick,
_icon_action,
_icon_throw_grenade,
_icon_primary_trigger,
_icon_integrated_light,
_icon_jump,
_icon_use_equipment,
_icon_rotate_weapons,
_icon_rotate_grenades,
_icon_crouch,
_icon_zoom,
_icon_accept,
_icon_back,
_icon_move,
_icon_look,
_icon_custom_1,
_icon_custom_2,
_icon_custom_3,
_icon_custom_4,
_icon_custom_5,
_icon_custom_6,
_icon_custom_7,
_icon_custom_8,
NUMBER_OF_ICON_TYPES
};
enum
{
NUMBER_OF_HUD_CUSTOM_ICONS = _icon_custom_8 - _icon_custom_1 + 1
};
/* ---------- macros */
#define HUD_MESSAGE_TEXT_DEFINITION_GET(index) \
@@ -206,7 +290,16 @@ struct hud_state_message_element
byte data;
};
struct icon_hud_element_definition;
struct icon_hud_element_definition
{
short sequence_index;
short width_offset;
point2d offset;
pixel32 color;
char frame_rate;
byte flags;
short text_index;
};
struct hud_state_message_text_info_definition
{
@@ -356,7 +449,8 @@ struct hud_timer_definition
struct hud_messaging_parameters_definition
{
byte reserved000[0x48];
struct hud_absolute_placement_definition absolute_placement;
struct hud_placement_definition placement;
struct tag_reference single_player_font;
struct tag_reference multi_player_font;
real up_time;
@@ -451,6 +545,16 @@ static struct hud_message_definition *find_free_hud_message(
struct hud_messaging_datum_definition *datum,
long item_definition_index,
char message_offset);
static void render_state_text(
rectangle2d *bounds,
rectangle2d *cursor_bounds,
wchar_t const *text,
boolean custom);
static void render_state_bitmap(
rectangle2d *bounds,
rectangle2d *cursor_bounds,
pixel32 color,
struct icon_hud_element_definition const *icon);
/* ---------- globals */
@@ -458,11 +562,102 @@ static struct hud_messaging_globals_definition *hud_messaging_globals;
extern struct hud_globals_definition *hud_globals;
extern struct hud_messaging_parameters_definition *hud_msg_def;
extern struct hud_scripted_globals_definition *hud_scripted_globals;
extern long time_code_time;
extern long time_code_stop_time;
static char button_mappings[_icon_custom_1 - _icon_action] =
{
2,
6,
7,
5,
0,
4,
3,
1,
11,
10,
_icon_a_button,
_icon_b_button,
_icon_left_stick,
_icon_right_stick
};
long time_code_time = NONE;
long time_code_stop_time = NONE;
static long split_screen_hud_message_offset = 17;
/* ---------- public code */
static void render_state_text(
rectangle2d *bounds,
rectangle2d *cursor_bounds,
wchar_t const *text,
boolean custom)
{
rectangle2d text_bounds;
draw_string_set_indents(cursor_bounds->x0 - bounds->x0, 0);
draw_unicode_string_compute_bounds(bounds, text, &text_bounds, cursor_bounds);
cursor_bounds->x0 -= 3;
text_bounds.x0 = bounds->x0;
if (custom && game_engine_running())
draw_string_and_hack_in_icons(&text_bounds, NULL, NULL, 0, text, TRUE);
else
rasterizer_draw_unicode_string(&text_bounds, NULL, NULL, 0, text);
bounds->y0 = cursor_bounds->y0;
return;
}
static void render_state_bitmap(
rectangle2d *bounds,
rectangle2d *cursor_bounds,
pixel32 color,
struct icon_hud_element_definition const *icon)
{
long bitmap_group_index = hud_globals->messaging.messaging_icons.index;
long frame_index = 0;
struct bitmap_data *bitmap = NULL;
real_rectangle2d const *clip = NULL;
real scale;
point2d point;
if (icon->frame_rate)
frame_index = game_time_get() / icon->frame_rate;
hud_retrieve_bitmap_and_bounding_rect(
bitmap_group_index,
icon->sequence_index,
frame_index,
&bitmap,
&clip);
if (bitmap && _texture_cache_bitmap_get_hardware_format(bitmap, FALSE, TRUE))
{
scale = local_player_count() > 1 ? 0.75f : 1.0f;
point.x = (short)(icon->offset.x * scale + cursor_bounds->x0);
point.y = (short)(cursor_bounds->y1 - icon->offset.y * scale);
hud_draw_bitmap_direct(
bitmap,
_hud_corner_bottom_left,
&point,
clip,
scale,
0.0f,
TEST_FLAG(icon->flags, _hud_icon_use_color_bit) ? icon->color : color,
FALSE);
if (TEST_FLAG(icon->flags, _hud_icon_absolute_width_bit))
cursor_bounds->x0 = (short)(icon->width_offset * scale + point.x);
else if (clip)
{
cursor_bounds->x0 = (short)(
((clip->x1 - clip->x0) * bitmap->width + icon->width_offset) * scale + point.x);
}
else
{
cursor_bounds->x0 = (short)(
(bitmap->width + icon->width_offset) * scale + point.x);
}
}
return;
}
void hud_messaging_initialize(
void)
{
@@ -1205,3 +1400,528 @@ static long compare_messages(
return difference;
}
void hud_messaging_update(
short local_player_index)
{
if (!cinematic_in_progress() &&
local_player_index != NONE &&
game_engine_hud_draw_messages(local_player_get_player_index(local_player_index)))
{
long font_index = local_player_count() > 1 &&
hud_msg_def->multi_player_font.index != NONE
? hud_msg_def->multi_player_font.index
: hud_msg_def->single_player_font.index;
boolean split_screen = local_player_count() > 1;
point2d screen_point;
struct font_header *font;
short line_top;
short line_height;
short first_line_height;
struct hud_messaging_datum_definition *datum;
short maximum_message_count;
boolean objective_active;
boolean help_active;
boolean state_active;
rectangle2d line_bounds;
rectangle2d line_cursor;
real_argb_color state_color;
real_argb_color text_color;
pixel32 pixel_color = 0;
hud_calculate_point(
local_player_index,
&hud_msg_def->absolute_placement,
&hud_msg_def->placement,
NULL,
local_player_count() > 1,
0.0f,
&screen_point);
font = font_definition_get(font_index);
line_top = screen_point.y;
if (split_screen)
{
line_height = font->leading_height + font->ascending_height;
line_top -= (short)split_screen_hud_message_offset;
}
else
{
line_height = font->leading_height +
font->descending_height +
font->ascending_height;
}
first_line_height = line_height;
datum = &hud_messaging_globals->message_data[render.local_player_index];
maximum_message_count = 4 - (local_player_count() > 1);
objective_active = hud_messaging_globals->objective.message &&
hud_messaging_globals->objective.uptime;
help_active = hud_scripted_globals->show_hud_help_text &&
hud_messaging_globals->help_message;
state_active = datum->state_message.valid &&
(datum->state_message.state_message || datum->state_message.message_buffer[0]);
if (objective_active || help_active || state_active)
{
struct game_input_preferences preferences;
struct hud_message_text_definition *hud_messages;
struct hud_state_message_definition *message;
input_abstraction_get_local_player_preferences(
local_player_index,
&preferences);
if (objective_active)
{
struct hud_color_definition *objective_color =
&hud_globals->messaging.objective_color;
pixel32 flash_color = get_flash_color(
objective_color,
game_time_get() + hud_messaging_globals->objective.uptime -
objective_color->custom.objective.up_ticks -
objective_color->custom.objective.fade_ticks);
pixel32_to_real_argb_color(flash_color, &state_color);
state_color.alpha *= MIN(
(real)hud_messaging_globals->objective.uptime /
objective_color->custom.objective.fade_ticks,
1.0f);
pixel_color = real_argb_color_to_pixel32(&state_color);
}
else if (help_active)
{
if (hud_messaging_globals->use_flash)
{
pixel_color = get_flash_color(
&hud_globals->messaging.color,
hud_messaging_globals->flash_start_time);
}
else if (TEST_FLAG(
hud_globals->messaging.color.flash_flags,
_hud_flash_reverse_colors_bit))
{
pixel_color = hud_globals->messaging.color.flash_color;
}
else
{
pixel_color = hud_globals->messaging.color.color;
}
pixel32_to_real_argb_color(pixel_color, &state_color);
}
else
{
state_color = hud_msg_def->state_color;
pixel_color = real_argb_color_to_pixel32(&state_color);
}
line_bounds.y0 = line_top;
line_bounds.x0 = screen_point.x;
line_bounds.y1 = 5 * line_height + line_top;
line_bounds.x1 = render.camera.window_bounds.x1 -
render.camera.viewport_bounds.x0;
line_cursor = line_bounds;
draw_string_set_draw_mode(
font_index,
NONE,
_text_justification_left,
0,
&state_color);
if (!objective_active && !help_active &&
!datum->state_message.state_message)
{
if (datum->state_message.message_buffer[0])
{
render_state_text(
&line_bounds,
&line_cursor,
datum->state_message.message_buffer,
TRUE);
}
}
else
{
word text_position;
short element_index;
if (objective_active)
{
struct scenario *scenario;
match_vassert(
"c:\\halo\\SOURCE\\interface\\hud_messaging.c",
0x420,
hud_messaging_globals->objective.message &&
hud_messaging_globals->objective.uptime,
"hud_messaging_globals->objective.message && hud_messaging_globals->objective.uptime");
hud_messaging_globals->objective.uptime = MAX(
hud_messaging_globals->objective.uptime - game_time_get_elapsed(),
0);
scenario = global_scenario_get();
hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET(
scenario->hud_messages.index);
message = hud_messaging_globals->objective.message;
}
else if (help_active)
{
struct scenario *scenario = global_scenario_get();
hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET(
scenario->hud_messages.index);
message = hud_messaging_globals->help_message;
}
else
{
match_vassert(
"c:\\halo\\SOURCE\\interface\\hud_messaging.c",
0x42C,
state_active,
"show_state_message");
hud_messages = HUD_MESSAGE_TEXT_DEFINITION_GET(
hud_globals->messaging.hud_messages.index);
message = datum->state_message.state_message;
}
text_position = message->text_start_index;
for (element_index = 0;
element_index < message->element_count;
element_index++)
{
struct hud_state_message_element *element = TAG_BLOCK_GET_ELEMENT(
&hud_messages->elements,
message->element_start_index + element_index,
struct hud_state_message_element);
switch (element->type)
{
case _hud_message_type_text:
{
wchar_t const *text = tag_data_get_pointer(
&hud_messages->text_data,
text_position * sizeof(wchar_t),
element->data * sizeof(wchar_t));
rectangle2d text_bounds;
draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0);
draw_unicode_string_compute_bounds(
&line_bounds,
text,
&text_bounds,
&line_cursor);
line_cursor.x0 -= 3;
text_bounds.x0 = line_bounds.x0;
rasterizer_draw_unicode_string(
&text_bounds,
NULL,
NULL,
0,
text);
line_bounds.y0 = line_cursor.y0;
text_position += element->data;
}
break;
case _hud_message_type_icon:
{
short icon_index = NONE;
if (element->data <= _icon_right_stick)
{
icon_index = element->data;
}
else if (element->data <= _icon_look)
{
if (element->data <= _icon_accept)
{
icon_index = preferences.game_control_to_xbox_buttons[
button_mappings[element->data - _icon_action]];
}
else
{
icon_index = button_mappings[element->data - _icon_action];
}
}
else if (hud_scripted_globals->show_hud_help_text)
{
error(_error_silent, "help text cannot use custom icons");
}
else
{
short custom_index = element->data - _icon_custom_1;
match_vassert(
"c:\\halo\\SOURCE\\interface\\hud_messaging.c",
0x457,
custom_index < NUMBER_OF_HUD_CUSTOM_ICONS,
"custom_index<NUMBER_OF_HUD_CUSTOM_ICONS");
if (TEST_FLAG(datum->state_message.is_text_flags, custom_index))
{
short string_index = datum->state_message.info[custom_index].text.string_index;
if (string_index == NONE)
{
render_state_text(
&line_bounds,
&line_cursor,
L"<unknown>",
FALSE);
}
else if (datum->state_message.info[custom_index].text.uses_scenario_names)
{
match_vassert(
"c:\\halo\\SOURCE\\interface\\hud_messaging.c",
0x460,
global_scenario_get()->custom_object_names.index != NONE,
"global_scenario_get()->custom_object_names.index!=NONE");
render_state_text(
&line_bounds,
&line_cursor,
unicode_string_list_get_string(
global_scenario_get()->custom_object_names.index,
string_index),
FALSE);
}
else
{
render_state_text(
&line_bounds,
&line_cursor,
unicode_string_list_get_string(
hud_globals->messaging.alternate_icon_text.index,
string_index),
FALSE);
}
}
else
{
struct icon_hud_element_definition const *icon =
datum->state_message.info[custom_index].icon;
if (icon)
{
render_state_bitmap(
&line_bounds,
&line_cursor,
pixel_color,
icon);
}
else
{
error(
_error_silent,
"help message using old code. get latest code and tags.");
}
}
break;
}
if (icon_index < hud_globals->messaging.button_icons.count)
{
struct icon_hud_element_definition const *icon;
icon_index = remap_sticks_for_local_player(
icon_index,
local_player_index);
icon = TAG_BLOCK_GET_ELEMENT(
&hud_globals->messaging.button_icons,
icon_index,
struct icon_hud_element_definition);
if (TEST_FLAG(icon->flags, _hud_icon_use_text_bit))
{
wchar_t const *icon_text;
rectangle2d text_bounds;
if (TEST_FLAG(icon->flags, _hud_icon_use_color_bit))
{
pixel32_to_real_argb_color(pixel_color, &text_color);
draw_string_set_draw_mode(
font_index,
NONE,
_text_justification_left,
0,
&text_color);
}
icon_text = unicode_string_list_get_string(
hud_globals->messaging.alternate_icon_text.index,
icon->text_index);
draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0);
draw_unicode_string_compute_bounds(
&line_bounds,
icon_text,
&text_bounds,
&line_cursor);
line_cursor.x0 -= 3;
text_bounds.x0 = line_bounds.x0;
rasterizer_draw_unicode_string(
&text_bounds,
NULL,
NULL,
0,
icon_text);
line_bounds.y0 = line_cursor.y0;
draw_string_set_draw_mode(
font_index,
NONE,
_text_justification_left,
0,
&state_color);
}
else
{
render_state_bitmap(
&line_bounds,
&line_cursor,
pixel_color,
icon);
}
}
else
{
rectangle2d text_bounds;
draw_string_set_indents(line_cursor.x0 - line_bounds.x0, 0);
draw_unicode_string_compute_bounds(
&line_bounds,
L"<no button icon>",
&text_bounds,
&line_cursor);
line_cursor.x0 -= 3;
text_bounds.x0 = line_bounds.x0;
rasterizer_draw_unicode_string(
&text_bounds,
NULL,
NULL,
0,
L"<no button icon>");
line_bounds.y0 = line_cursor.y0;
}
}
break;
default:
match_assert(
"c:\\halo\\SOURCE\\interface\\hud_messaging.c",
0x4A7,
!"unreachable");
break;
}
}
}
draw_string_set_indents(0, 0);
line_height = first_line_height;
line_top = line_cursor.y1;
}
if (!objective_active && !help_active &&
(datum->state_message.valid || datum->leave_first_line_blank))
{
if (split_screen)
{
line_top = (short)(
hud_msg_def->spacing * line_height +
screen_point.y - split_screen_hud_message_offset);
}
else
{
line_top = (short)(
hud_msg_def->spacing * line_height + screen_point.y);
}
maximum_message_count--;
}
qsort(
datum->messages,
NUMBEROF(datum->messages),
sizeof(datum->messages[0]),
(int (__cdecl *)(void const *, void const *))compare_messages);
if (maximum_message_count > 0)
{
wchar_t formatted[276];
short message_index;
for (message_index = 0;
message_index < maximum_message_count;
message_index++)
{
struct hud_message_definition *message = &datum->messages[message_index];
long now;
long elapsed;
rectangle2d message_bounds;
wchar_t const *item_text;
if (!message->valid)
break;
now = game_time_get();
elapsed = now - message->time;
text_color = hud_msg_def->text_color;
if ((real)elapsed > hud_msg_def->up_time * TICKS_PER_SECOND)
{
real fade = PIN(
1.0f -
((real)elapsed - hud_msg_def->up_time * TICKS_PER_SECOND) /
(hud_msg_def->fade_time * TICKS_PER_SECOND),
0.0f,
1.0f);
text_color.alpha *= (real)pow((double)fade, 1.9f);
}
message_bounds = render.camera.window_bounds;
message_bounds.y0 = line_top;
message_bounds.x0 = screen_point.x;
message_bounds.y1 = line_height + line_top;
message_bounds.x1 = render.camera.window_bounds.x1 -
render.camera.viewport_bounds.x0;
line_top = (short)(hud_msg_def->spacing * line_height + line_top);
draw_string_set_draw_mode(
font_index,
NONE,
_text_justification_left,
0,
&text_color);
if (message->item_definition_index == NONE)
{
rasterizer_draw_unicode_string(
&message_bounds,
NULL,
NULL,
0,
message->text);
}
else
{
char message_offset = message->message_offset;
struct item_definition *item = item_definition_get(
message->item_definition_index);
if (message_offset == NONE)
message_offset = message->quantity > 1;
item_text = hud_get_item_string(
item->item.hud_message_index + message_offset);
if ((message->message_offset == NONE && message_offset) ||
message->quantity)
{
usprintf(
formatted,
item_text,
message->quantity /
MAX(item->item.hud_message_value_scale, 1));
item_text = formatted;
}
rasterizer_draw_unicode_string(
&message_bounds,
NULL,
NULL,
0,
item_text);
}
message->valid = (real)(now - message->time) <
(hud_msg_def->fade_time + hud_msg_def->up_time) * TICKS_PER_SECOND;
if (!message->valid)
message->time = NONE;
}
}
}
return;
}
+466
View File
@@ -725,6 +725,472 @@ boolean convex_polygon3d_verify(
return TRUE;
}
boolean convex_hull3d_begin(
short point_count,
real_point3d const *points,
short vertex_count,
struct vertex3d *vertices,
short edge_count,
struct edge3d *edges,
short surface_count,
struct surface3d *surfaces)
{
short minimum_x_point_index = NONE;
short farthest_point_index = NONE;
short farthest_line_point_index = NONE;
short farthest_plane_point_index = NONE;
real minimum_x;
real maximum_distance_squared;
real maximum_line_distance_squared;
real maximum_plane_distance;
real_vector3d line_direction;
real_plane3d plane;
short point_index;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1710, points);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1711, vertices);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1712, edges);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1713, surfaces);
if (vertex_count < 4 || edge_count < 6 || surface_count < 4)
{
return FALSE;
}
minimum_x = REAL_MAX;
for (point_index = 0; point_index < point_count; point_index++)
{
if (points[point_index].x < minimum_x)
{
minimum_x = points[point_index].x;
minimum_x_point_index = point_index;
}
}
if (minimum_x_point_index == NONE)
{
return FALSE;
}
maximum_distance_squared = 0.f;
for (point_index = 0; point_index < point_count; point_index++)
{
if (distance_squared3d(points + point_index, points + minimum_x_point_index) > maximum_distance_squared)
{
farthest_point_index = point_index;
maximum_distance_squared = distance_squared3d(points + point_index, points + minimum_x_point_index);
}
}
if (farthest_point_index == NONE || maximum_distance_squared < global_convex_hull3d_delta)
{
return FALSE;
}
maximum_line_distance_squared = 0.f;
vector_from_points3d(points + minimum_x_point_index, points + farthest_point_index, &line_direction);
for (point_index = 0; point_index < point_count; point_index++)
{
real_vector3d offset;
real_vector3d projection = line_direction;
real t;
real distance_squared;
vector_from_points3d(points + minimum_x_point_index, points + point_index, &offset);
t = dot_product3d(&offset, &line_direction) / magnitude_squared3d(&line_direction);
scale_vector3d(&projection, t, &projection);
subtract_vectors3d(&offset, &projection, &offset);
distance_squared = magnitude_squared3d(&offset);
if (distance_squared > maximum_line_distance_squared)
{
maximum_line_distance_squared = distance_squared;
farthest_line_point_index = point_index;
}
}
if (farthest_line_point_index == NONE || maximum_line_distance_squared < global_convex_hull3d_delta)
{
return FALSE;
}
maximum_plane_distance = 0.f;
plane3d_from_points(&plane,
points + minimum_x_point_index,
points + farthest_point_index,
points + farthest_line_point_index);
for (point_index = 0; point_index < point_count; point_index++)
{
real distance = points[point_index].x*plane.n.i +
points[point_index].y*plane.n.j + points[point_index].z*plane.n.k - plane.d;
if (fabs(distance) > fabs(maximum_plane_distance))
{
maximum_plane_distance = distance;
farthest_plane_point_index = point_index;
}
}
if (farthest_plane_point_index == NONE || fabs(maximum_plane_distance) < global_convex_hull3d_delta)
{
return FALSE;
}
if (maximum_plane_distance > 0.f)
{
short swap = farthest_point_index;
farthest_point_index = farthest_line_point_index;
farthest_line_point_index = swap;
}
vertices[1].point_index = farthest_point_index;
vertices[2].point_index = farthest_line_point_index;
vertices[0].point_index = minimum_x_point_index;
vertices[3].edge_index = 3;
vertices[0].extant = TRUE;
vertices[0].edge_index = 0;
vertices[1].extant = TRUE;
vertices[1].edge_index = 0;
vertices[2].extant = TRUE;
vertices[2].edge_index = 1;
vertices[3].extant = TRUE;
vertices[3].point_index = farthest_plane_point_index;
edges[0].edge_indices[1] = 3;
edges[2].edge_indices[1] = 5;
edges[2].surface_indices[1] = 3;
edges[3].vertex_indices[1] = 3;
edges[3].surface_indices[1] = 3;
edges[4].vertex_indices[0] = 3;
edges[4].edge_indices[1] = 5;
edges[5].vertex_indices[0] = 3;
edges[5].edge_indices[1] = 3;
edges[5].surface_indices[1] = 3;
edges[0].extant = TRUE;
edges[0].vertex_indices[0] = 0;
edges[0].vertex_indices[1] = 1;
edges[0].edge_indices[0] = 1;
edges[0].surface_indices[0] = 0;
edges[0].surface_indices[1] = 1;
edges[1].extant = TRUE;
edges[1].vertex_indices[0] = 1;
edges[1].vertex_indices[1] = 2;
edges[1].edge_indices[0] = 2;
edges[1].edge_indices[1] = 4;
edges[1].surface_indices[0] = 0;
edges[1].surface_indices[1] = 2;
edges[2].extant = TRUE;
edges[2].vertex_indices[0] = 2;
edges[2].vertex_indices[1] = 0;
edges[2].edge_indices[0] = 0;
edges[2].surface_indices[0] = 0;
edges[3].extant = TRUE;
edges[3].vertex_indices[0] = 0;
edges[3].edge_indices[0] = 4;
edges[3].edge_indices[1] = 2;
edges[3].surface_indices[0] = 1;
edges[4].extant = TRUE;
edges[4].vertex_indices[1] = 1;
edges[4].edge_indices[0] = 0;
edges[4].surface_indices[0] = 1;
edges[4].surface_indices[1] = 2;
edges[5].extant = TRUE;
edges[5].vertex_indices[1] = 2;
edges[5].edge_indices[0] = 1;
edges[5].surface_indices[0] = 2;
surfaces[0].extant = TRUE;
plane3d_from_points(&surfaces[0].plane,
points + minimum_x_point_index,
points + farthest_point_index,
points + farthest_line_point_index);
surfaces[0].edge_index = 0;
surfaces[1].extant = TRUE;
plane3d_from_points(&surfaces[1].plane,
points + minimum_x_point_index,
points + farthest_plane_point_index,
points + farthest_point_index);
surfaces[1].edge_index = 0;
surfaces[2].extant = TRUE;
plane3d_from_points(&surfaces[2].plane,
points + farthest_point_index,
points + farthest_plane_point_index,
points + farthest_line_point_index);
surfaces[2].edge_index = 1;
surfaces[3].extant = TRUE;
plane3d_from_points(&surfaces[3].plane,
points + minimum_x_point_index,
points + farthest_line_point_index,
points + farthest_plane_point_index);
surfaces[3].edge_index = 2;
for (point_index = 4; point_index < vertex_count; point_index++)
{
vertices[point_index].extant = FALSE;
}
for (point_index = 6; point_index < edge_count; point_index++)
{
edges[point_index].extant = FALSE;
}
for (point_index = 4; point_index < surface_count; point_index++)
{
surfaces[point_index].extant = FALSE;
}
return TRUE;
}
boolean convex_hull3d_expand(
short point_count,
real_point3d const *points,
short vertex_count,
struct vertex3d *vertices,
short edge_count,
struct edge3d *edges,
short surface_count,
struct surface3d *surfaces,
short point_index)
{
real_point3d const *new_point;
boolean already_contained = TRUE;
short first_boundary_edge_index;
short surface_index;
short edge_index;
short vertex_index;
short new_vertex_index;
short previous_new_edge_index;
short first_new_edge_index;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1973, points);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1974, vertices);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1975, edges);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 1976, surfaces);
if (point_index < 0 || point_index >= point_count)
{
return FALSE;
}
new_point = points + point_index;
first_boundary_edge_index = NONE;
for (surface_index = 0; surface_index < surface_count; surface_index++)
{
struct surface3d const *surface = surfaces + surface_index;
if (surface->extant &&
surface->plane.n.i*new_point->x + surface->plane.n.j*new_point->y +
surface->plane.n.k*new_point->z - surface->plane.d > global_convex_hull3d_delta)
{
already_contained = FALSE;
break;
}
}
if (already_contained)
{
return TRUE;
}
for (surface_index = 0; surface_index < surface_count; surface_index++)
{
struct surface3d *surface = surfaces + surface_index;
if (surface->extant)
{
real distance = surface->plane.n.i*new_point->x +
surface->plane.n.j*new_point->y + surface->plane.n.k*new_point->z - surface->plane.d;
surface->extant = !(distance > -global_convex_hull3d_epsilon);
}
}
for (edge_index = 0; edge_index < edge_count; edge_index++)
{
struct edge3d *edge = edges + edge_index;
if (edge->extant)
{
struct surface3d *surface0;
struct surface3d *surface1;
if (edge->surface_indices[0] < 0 || edge->surface_indices[0] >= surface_count ||
edge->surface_indices[1] < 0 || edge->surface_indices[1] >= surface_count)
{
return FALSE;
}
surface0 = surfaces + edge->surface_indices[0];
surface1 = surfaces + edge->surface_indices[1];
edge->extant = surface0->extant || surface1->extant;
if (edge->extant && surface0->extant != surface1->extant)
{
if (!surface0->extant)
{
edge->surface_indices[0] = NONE;
}
else
{
edge->surface_indices[1] = NONE;
}
if (first_boundary_edge_index == NONE)
{
first_boundary_edge_index = edge_index;
}
}
}
}
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2076, first_boundary_edge_index!=NONE);
for (vertex_index = 0; vertex_index < vertex_count; vertex_index++)
{
struct vertex3d *vertex = vertices + vertex_index;
if (vertex->extant)
{
short first_edge_index = NONE;
short previous_edge_index = NONE;
short edge_index = (short)vertex->edge_index;
do
{
struct edge3d *edge;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2098, edge_index>=0 && edge_index<edge_count);
edge = edges + edge_index;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2099,
edge->vertex_indices[0]==vertex_index || edge->vertex_indices[1]==vertex_index);
if (edge->extant)
{
if (previous_edge_index == NONE)
{
first_edge_index = edge_index;
}
else
{
struct edge3d *previous_edge = edges + previous_edge_index;
previous_edge->edge_indices[previous_edge->vertex_indices[0]==vertex_index] = edge_index;
}
previous_edge_index = edge_index;
}
edge_index = (short)edge->edge_indices[edge->vertex_indices[0]==vertex_index];
}
while (edge_index != vertex->edge_index);
if (previous_edge_index == NONE)
{
vertex->extant = FALSE;
}
else
{
struct edge3d *previous_edge = edges + previous_edge_index;
vertex->edge_index = first_edge_index;
previous_edge->edge_indices[previous_edge->vertex_indices[0]==vertex_index] = first_edge_index;
}
}
}
new_vertex_index = 0;
first_new_edge_index = NONE;
previous_new_edge_index = NONE;
edge_index = first_boundary_edge_index;
while (new_vertex_index < vertex_count && vertices[new_vertex_index].extant)
{
new_vertex_index++;
}
if (new_vertex_index >= vertex_count)
{
return FALSE;
}
for (;;)
{
struct edge3d *edge;
struct vertex3d *vertex1;
struct vertex3d *vertex2;
struct surface3d *new_surface;
struct edge3d *new_edge;
short new_surface_index;
short new_edge_index;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2167, edge_index>=0 && edge_index<edge_count);
edge = edges + edge_index;
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2168, edge->extant);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2169,
edge->vertex_indices[0]>=0 && edge->vertex_indices[0]<vertex_count);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2170,
edge->vertex_indices[1]>=0 && edge->vertex_indices[1]<vertex_count);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2171,
(edge->surface_indices[0]==NONE)!=(edge->surface_indices[1]==NONE));
for (new_surface_index = 0;
new_surface_index < surface_count && surfaces[new_surface_index].extant;
new_surface_index++)
{
}
if (new_surface_index >= surface_count)
{
break;
}
for (new_edge_index = 0;
new_edge_index < edge_count && edges[new_edge_index].extant;
new_edge_index++)
{
}
if (new_edge_index >= edge_count)
{
break;
}
new_surface = surfaces + new_surface_index;
vertex1 = vertices + edge->vertex_indices[edge->surface_indices[0]!=NONE];
vertex2 = vertices + edge->vertex_indices[edge->surface_indices[1]!=NONE];
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2206, vertex1->extant);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2207,
vertex1->point_index>=0 && vertex1->point_index<point_count);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2209, vertex2->extant);
match_assert("c:\\halo\\SOURCE\\math\\geometry.c", 2210,
vertex2->point_index>=0 && vertex2->point_index<point_count);
new_surface->extant = TRUE;
plane3d_from_points(&new_surface->plane,
new_point,
points + vertex1->point_index,
points + vertex2->point_index);
new_surface->edge_index = edge_index;
new_edge = edges + new_edge_index;
new_edge->extant = TRUE;
new_edge->vertex_indices[0] = edge->vertex_indices[edge->surface_indices[1]!=NONE];
new_edge->vertex_indices[1] = new_vertex_index;
new_edge->edge_indices[0] = previous_new_edge_index;
new_edge->edge_indices[1] = edge->edge_indices[edge->surface_indices[0]!=NONE];
new_edge->surface_indices[0] = new_surface_index;
new_edge->surface_indices[1] = NONE;
edge->edge_indices[edge->surface_indices[0]!=NONE] = new_edge_index;
edge->surface_indices[edge->surface_indices[0]!=NONE] = new_surface_index;
if (previous_new_edge_index == NONE)
{
first_new_edge_index = new_edge_index;
}
else
{
edges[previous_new_edge_index].surface_indices[1] = new_surface_index;
}
previous_new_edge_index = new_edge_index;
edge_index = (short)new_edge->edge_indices[1];
if (edge_index == first_boundary_edge_index)
{
vertices[new_vertex_index].extant = TRUE;
vertices[new_vertex_index].point_index = point_index;
vertices[new_vertex_index].edge_index = first_new_edge_index;
edges[first_new_edge_index].edge_indices[0] = new_edge_index;
new_edge->surface_indices[1] = edges[first_new_edge_index].surface_indices[0];
return TRUE;
}
}
return FALSE;
}
boolean convex_hull3d(
short point_count,
real_point3d const *points,
+5 -1
View File
@@ -18,7 +18,11 @@ header included in hcex build.
void render_debug_string(boolean immediate, const char *string);
void render_debug_string_at_point(boolean immediate, real_point3d *point, const char *string, real_argb_color const *color);
void render_debug_string_at_point(
boolean immediate,
real_point3d const *point,
const char *string,
real_argb_color const *color);
void render_debug_point(boolean immediate, real_point3d const *point, real size, real_argb_color const *color);
void render_debug_line(boolean immediate, real_point3d const *point0, real_point3d const *point1, real_argb_color const *color);
+1 -6
View File
@@ -221,11 +221,6 @@ static void scripted_music_stop_all(
static void scripted_looping_sound_stop_internal(
long sound_index,
boolean fixed_fadeout);
static boolean track_object_impulse_sound(
long object_index,
void const *attachment_data,
struct sound_source *source);
/* ---------- globals */
struct game_sound_globals *game_sound_globals;
@@ -1006,7 +1001,7 @@ static boolean looping_sound_definition_is_music(
return FALSE;
}
static boolean track_object_impulse_sound(
boolean track_object_impulse_sound(
long object_index,
void const *attachment_data,
struct sound_source *source)
+7
View File
@@ -97,6 +97,13 @@ void compute_sound_obstruction(
short local_player_index,
struct sound_source *source,
real distance);
void game_sound_set_mouth_aperture(
long object_index,
real mouth_aperture);
boolean track_object_impulse_sound(
long object_index,
void const *attachment_data,
struct sound_source *source);
/* ---------- globals */
+1 -3
View File
@@ -115,9 +115,7 @@ symbols in this file:
enum
{
NUMBER_OF_SOUND_CLASSES = 51,
MAXIMUM_SOUND_INSTANCES_PER_DEFINITION = 16,
MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION = 16
NUMBER_OF_SOUND_CLASSES = 51
};
/* ---------- macros */
+6
View File
@@ -13,6 +13,12 @@ file has inline function assertions.
/* ---------- constants */
enum
{
MAXIMUM_SOUND_INSTANCES_PER_DEFINITION = 16,
MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION = 16
};
enum sound_class
{
_sound_class_unit_dialog = 19,
+10
View File
@@ -39,6 +39,13 @@ enum
MAXIMUM_DETAIL_SOUNDS_PER_LOOPING_SOUND = 32,
};
enum sound_definition_flags
{
_sound_definition_fit_to_compression_block_size_bit,
_sound_definition_linked_permutations_bit,
NUMBER_OF_SOUND_DEFINITION_FLAGS,
};
enum looping_sound_definition_flags
{
_looping_sound_deafening_bit,
@@ -194,6 +201,9 @@ real sound_definition_get_maximum_distance(
long sound_index);
real sound_definition_get_minimum_distance(
long sound_index);
real sound_permutation_get_real_mouth_aperture(
struct sound_permutation *permutation,
long tick_index);
short sound_definition_find_pitch_range_by_pitch(
struct sound_definition *definition,
+760 -25
View File
@@ -312,6 +312,14 @@ enum sound_channel_flags
_sound_channel_compressed_bit,
};
enum sound_channel_state
{
_sound_channel_idle,
_sound_channel_playing,
_sound_channel_queued,
NUMBER_OF_SOUND_CHANNEL_STATES,
};
enum looping_sound_track_flags
{
_fade_in_at_start_bit,
@@ -333,10 +341,11 @@ enum looping_sound_track_flags
struct sound_pitch_range)->permutations, \
permutation_index, \
struct sound_permutation)
#define sound_cache_sound_loaded(sound) \
_sound_cache_sound_request((sound), FALSE, FALSE, FALSE)
/* ---------- structures */
struct platform_sound_channel_properties;
struct platform_sound_listener_properties;
struct sound_location;
struct sound_permutation;
@@ -373,6 +382,28 @@ struct sound_channel_datum
struct sound_permutation *queued_permutation;
};
struct sound_channel_summary
{
short like_definition_count;
short like_definition_channels[MAXIMUM_SOUND_INSTANCES_PER_DEFINITION];
short maximum_instance_count;
short like_source_count;
short like_source_channels[MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION];
short maximum_source_instance_count;
};
struct platform_sound_channel_properties
{
real minimum_distance;
real maximum_distance;
real pitch;
real gain;
real cone_inside_angle;
real cone_outside_angle;
real cone_outside_gain;
real reverb_attenuation;
};
struct sound_datum
{
short identifier;
@@ -438,15 +469,9 @@ struct sound_platform_definition
void);
void (*queue_sound_to_channel)(
short channel_index,
struct sound_permutation const *permutation,
long hardware_format,
boolean looping,
short playback_rate,
boolean cache_miss);
struct sound_permutation *permutation);
void (*channel_update)(
short channel_index,
boolean force,
short playback_rate);
short channel_index);
void (*stop_channel)(
short channel_index);
short (*get_channel_state)(
@@ -457,24 +482,16 @@ struct sound_platform_definition
void);
void (*set_channel_location)(
short channel_index,
boolean valid,
boolean spatialized,
struct sound_location const *location,
real minimum_distance,
real maximum_distance,
boolean force,
short playback_rate);
real obstruction,
real occlusion,
boolean attenuate_direct_path);
void (*set_channel_properties)(
short channel_index,
struct platform_sound_channel_properties const *properties,
boolean gain_only,
boolean force,
short playback_rate);
void (*handle_audio_profile_changes)(
boolean surround_sound,
boolean headphones,
long speaker_configuration);
boolean (*hardware_reverb_enabled)(
void);
boolean gain_only);
real direct_path_gain;
};
struct sound_manager_globals
@@ -504,6 +521,10 @@ typedef char verify_sound_listener_size[
sizeof(struct sound_listener) == 0x44 ? 1 : -1];
typedef char verify_sound_channel_datum_size[
sizeof(struct sound_channel_datum) == 0x18 ? 1 : -1];
typedef char verify_sound_channel_summary_size[
sizeof(struct sound_channel_summary) == 0x48 ? 1 : -1];
typedef char verify_platform_sound_channel_properties_size[
sizeof(struct platform_sound_channel_properties) == 0x20 ? 1 : -1];
typedef char verify_sound_datum_size[
sizeof(struct sound_datum) == 0xAC ? 1 : -1];
typedef char verify_looping_sound_datum_size[
@@ -512,7 +533,7 @@ typedef char verify_looping_sound_datum_size[
typedef char verify_sound_manager_globals_size[
sizeof(struct sound_manager_globals) == 0x178 ? 1 : -1];
typedef char verify_sound_platform_definition_size[
sizeof(struct sound_platform_definition) == 0x40 ? 1 : -1];
sizeof(struct sound_platform_definition) == 0x3C ? 1 : -1];
typedef char verify_sound_platform_dispose_offset[
offsetof(struct sound_platform_definition, dispose) == 0x8 ? 1 : -1];
typedef char verify_sound_platform_pause_offset[
@@ -560,6 +581,16 @@ static long sound_travel_milliseconds(
real distance);
static boolean refresh_sound(
long sound_index);
static void sound_channel_summary_build(
struct sound_channel_summary *summary,
long sound_index);
static void channel_queue_sound(
short channel_index,
struct sound_permutation *permutation);
static void channel_set_properties_hardware(
short channel_index,
struct platform_sound_channel_properties *properties,
boolean gain_only);
static short channel_get_state(
short channel_index);
static boolean track_loop_track_sound(
@@ -575,6 +606,8 @@ static long looping_sound_new(
long definition_index,
long identifier,
struct sound_source const *source);
static void sound_set_definition_end(
long sound_index);
static long update_potentially_audible_looping_sound(
long definition_index,
long looping_sound_index,
@@ -584,6 +617,18 @@ static real limit_pitch(
real desired_pitch,
real old_pitch,
real maximum_bend);
static short sound_find_like_channel(
long sound_index,
short const *channel_indices,
short channel_count);
static void update_channel_for_impulse_sound(
short channel_index,
real fade);
static void update_channel_for_looping_sound(
short channel_index,
real fade);
static void update_channels(
void);
static void render_debug_sound(
long sound_index);
static void render_debug_looping_sound(
@@ -944,7 +989,7 @@ static void sound_update_time(
long render_time = system_milliseconds();
sound_manager_globals.ticks_elapsed =
(real)(render_time - sound_manager_globals.render_time) * 0.029999999f;
((real)render_time - sound_manager_globals.render_time) * 0.029999999f;
sound_manager_globals.render_time = render_time;
return;
@@ -1110,6 +1155,125 @@ static boolean refresh_sound(
return FALSE;
}
static void sound_channel_summary_build(
struct sound_channel_summary *summary,
long sound_index)
{
struct sound_datum *sound = sound_get(sound_index);
struct sound_definition *definition =
sound_definition_get(sound->definition_index);
short channel_index;
summary->like_definition_count = 0;
summary->like_source_count = 0;
summary->maximum_instance_count =
sound_class_get(definition->sound_class)->maximum_number_per_definition;
summary->maximum_source_instance_count =
sound_class_get(definition->sound_class)->maximum_number_per_object;
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x6A4,
summary->maximum_source_instance_count<=MAXIMUM_SOUND_INSTANCES_PER_DEFINITION);
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x6A5,
summary->maximum_instance_count<=MAXIMUM_SOUND_INSTANCES_PER_OBJECT_PER_DEFINITION);
for (
channel_index = 0;
channel_index < sound_manager_globals.channel_count;
channel_index++)
{
struct sound_channel_datum *channel = channel_get(channel_index);
if (channel->sound_index != NONE && channel->sound_index != sound_index)
{
struct sound_datum *other_sound = sound_get(channel->sound_index);
if (sound_valid_for_channel(
definition->compression,
definition->encoding,
definition->sample_rate,
sound->source.spatialization_mode,
channel->type_flags) &&
sound->definition_index == other_sound->definition_index)
{
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x6B5,
summary->like_definition_count<summary->maximum_instance_count);
summary->like_definition_channels[
summary->like_definition_count++] = channel_index;
if (sound->source_identifier != NONE &&
sound->source_identifier == other_sound->source_identifier)
{
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x6BB,
summary->like_source_count<summary->maximum_source_instance_count);
summary->like_source_channels[
summary->like_source_count++] = channel_index;
}
}
}
}
return;
}
static void channel_queue_sound(
short channel_index,
struct sound_permutation *permutation)
{
struct sound_channel_datum *channel = channel_get(channel_index);
if (channel->queued_permutation)
{
sound_cache_sound_finished(channel->queued_permutation);
}
sound_manager_globals.platform_definition->queue_sound_to_channel(
channel_index,
permutation);
if (channel->playing_permutation)
{
channel->queued_permutation = permutation;
}
else
{
channel->playing_permutation = permutation;
channel->estimated_tick_time = 0.f;
}
return;
}
static void channel_set_properties_hardware(
short channel_index,
struct platform_sound_channel_properties *properties,
boolean gain_only)
{
struct sound_channel_datum *channel = channel_get(channel_index);
if (!gain_only)
{
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x848,
properties->pitch>0.f);
channel->pitch = properties->pitch;
}
sound_manager_globals.platform_definition->set_channel_properties(
channel_index,
properties,
gain_only);
return;
}
static boolean track_loop_track_sound(
long looping_sound_index,
void const *track_data,
@@ -1231,6 +1395,61 @@ static long looping_sound_new(
return looping_sound_index;
}
static void sound_set_definition_end(
long sound_index)
{
struct sound_datum *sound = sound_get(sound_index);
struct sound_definition *definition =
sound_definition_get(sound->next_definition_index);
SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, TRUE);
sound->definition_index = sound->next_definition_index;
sound->next_definition_index = NONE;
sound->pitch_range_index = sound_definition_find_pitch_range_by_pitch(
definition,
sound->pitch,
(word)sound->pitch_range_index);
sound->permutation_index = sound_definition_next_permutation(
definition,
sound->pitch_range_index,
NONE);
if (sound->playing_channel_index != NONE)
{
struct sound_channel_summary summary;
short channel_index = NONE;
sound_channel_summary_build(&summary, sound_index);
if (summary.like_source_count >= summary.maximum_source_instance_count)
{
channel_index = sound_find_like_channel(
sound_index,
summary.like_source_channels,
summary.like_source_count);
}
else if (summary.like_definition_count >= summary.maximum_instance_count)
{
channel_index = sound_find_like_channel(
sound_index,
summary.like_definition_channels,
summary.like_definition_count);
}
else
{
return;
}
if (channel_index != NONE)
{
sound_index = channel_get(channel_index)->sound_index;
}
sound_stop(sound_index);
}
return;
}
static long update_potentially_audible_looping_sound(
long definition_index,
long looping_sound_index,
@@ -1773,6 +1992,104 @@ static real source_distance_squared(
return distance_squared;
}
static short sound_find_like_channel(
long sound_index,
short const *channel_indices,
short channel_count)
{
struct sound_datum *sound = sound_get(sound_index);
struct sound_definition *definition =
sound_definition_get(sound->definition_index);
real sound_distance_squared = source_distance_squared(
sound->listener_index,
&sound->source);
short index;
for (index = 0; index < channel_count; index++)
{
short channel_index = channel_indices[index];
struct sound_datum *other_sound =
sound_get(channel_get(channel_index)->sound_index);
struct sound_class_definition *sound_class =
sound_class_get(definition->sound_class);
if (sound_manager_globals.render_time - other_sound->start_time >=
sound_class->preemption_time)
{
real other_distance_squared = source_distance_squared(
other_sound->listener_index,
&other_sound->source);
if (sound_distance_squared - other_distance_squared < 1.f)
{
return channel_index;
}
}
}
return NONE;
}
static void update_channel_for_impulse_sound(
short channel_index,
real fade)
{
struct sound_channel_datum *channel = channel_get(channel_index);
struct sound_datum *sound = sound_get(channel->sound_index);
struct sound_definition *definition =
sound_definition_get(sound->definition_index);
real scale = sound->source.scale;
real master_gain = sound_manager_master_gain(definition->sound_class);
real scale_upper_gain = definition->one_gain_modifier;
real scale_lower_gain = definition->zero_gain_modifier;
real distance_gain =
(scale_upper_gain - scale_lower_gain) * scale + scale_lower_gain;
real gain = master_gain * sound->source.gain * fade * distance_gain;
struct platform_sound_channel_properties properties;
if (sound->playing_channel_index == NONE)
{
struct sound_pitch_range *pitch_range = TAG_BLOCK_GET_ELEMENT(
&definition->pitch_ranges,
sound->pitch_range_index,
struct sound_pitch_range);
struct sound_permutation *permutation = TAG_BLOCK_GET_ELEMENT(
&pitch_range->permutations,
sound->permutation_index,
struct sound_permutation);
properties.gain = permutation->gain * definition->gain_modifier * gain;
properties.pitch = sound->pitch * pitch_range->playback_rate;
properties.minimum_distance = sound_definition_get_minimum_distance(
sound->definition_index);
properties.cone_inside_angle = definition->inner_cone_angle;
properties.cone_outside_angle = definition->outer_cone_angle;
properties.cone_outside_gain = definition->outer_cone_gain;
properties.maximum_distance = FLT_MAX;
properties.reverb_attenuation =
sound_class_get(definition->sound_class)->wet_gain;
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x79A,
sound_cache_sound_loaded(permutation));
channel_set_properties_hardware(channel_index, &properties, FALSE);
channel_queue_sound(channel_index, permutation);
sound->playing_channel_index = channel_index;
}
else
{
properties.gain =
channel->playing_permutation->gain * definition->gain_modifier * gain;
channel_set_properties_hardware(channel_index, &properties, TRUE);
}
sound_manager_globals.platform_definition->channel_update(channel_index);
return;
}
void sound_stop_all(
void)
{
@@ -2256,6 +2573,223 @@ boolean sound_refresh_looping(
return result;
}
static void update_channel_for_looping_sound(
short channel_index,
real fade)
{
struct sound_channel_datum *channel = channel_get(channel_index);
struct sound_datum *sound = sound_get(channel->sound_index);
struct sound_definition *definition =
sound_definition_get(sound->definition_index);
struct looping_sound_datum *looping_sound =
looping_sound_get(sound->source_identifier);
long *primary_sound_index =
&looping_sound->tracks[sound->loop_track_index].primary_sound_index;
struct looping_sound_definition *looping_definition =
looping_sound_definition_get(looping_sound->definition_index);
struct looping_sound_track *track = TAG_BLOCK_GET_ELEMENT(
&looping_definition->tracks,
sound->loop_track_index,
struct looping_sound_track);
real scale = sound->source.scale;
real pitch = sound_scale_value(
sound->pitch,
definition->zero_pitch_modifier,
definition->one_pitch_modifier,
scale);
struct platform_sound_channel_properties properties;
struct sound_pitch_range *pitch_range;
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x9C4,
sound->type!=_sound_impulse);
properties.minimum_distance = sound_definition_get_minimum_distance(
sound->definition_index);
properties.maximum_distance = FLT_MAX;
properties.cone_inside_angle = definition->inner_cone_angle;
properties.cone_outside_angle = definition->outer_cone_angle;
properties.cone_outside_gain = definition->outer_cone_gain;
properties.reverb_attenuation =
sound_class_get(definition->sound_class)->wet_gain;
properties.gain = sound_manager_master_gain(definition->sound_class) *
track->gain *
sound_scale_value(
definition->gain_modifier,
definition->zero_gain_modifier,
definition->one_gain_modifier,
scale) *
sound->source.gain * fade;
if (sound->playing_channel_index == NONE)
{
struct sound_permutation *permutation;
pitch_range = TAG_BLOCK_GET_ELEMENT(
&definition->pitch_ranges,
sound->pitch_range_index,
struct sound_pitch_range);
permutation = TAG_BLOCK_GET_ELEMENT(
&pitch_range->permutations,
sound->permutation_index,
struct sound_permutation);
properties.gain *= permutation->gain;
properties.pitch = pitch * pitch_range->playback_rate;
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x9DA,
sound_cache_sound_loaded(permutation));
channel_set_properties_hardware(channel_index, &properties, FALSE);
channel_queue_sound(channel_index, permutation);
sound->playing_channel_index = channel_index;
}
else
{
real limited_pitch;
pitch_range = TAG_BLOCK_GET_ELEMENT(
&definition->pitch_ranges,
sound->pitch_range_index,
struct sound_pitch_range);
limited_pitch = limit_pitch(
pitch,
channel_get(sound->playing_channel_index)->pitch *
pitch_range->natural_pitch,
definition->maximum_bend_per_second);
properties.pitch = limited_pitch * pitch_range->playback_rate;
if (sound->type == _sound_loop_track &&
(sound->fade_start_time == sound->fade_stop_time ||
sound->fade_interpolation_end != 0.f) &&
sound_definition_find_pitch_range_by_pitch(
definition,
limited_pitch,
(word)sound->pitch_range_index) != sound->pitch_range_index &&
channel->sound_index == *primary_sound_index &&
!sound_manager_globals.idling)
{
long new_sound_index = update_potentially_audible_looping_sound(
sound->definition_index,
sound->source_identifier,
sound->loop_track_index,
_sound_loop_track);
if (new_sound_index != NONE)
{
sound_start_fade(
_sound_fade_mode_crossfade,
sound_pitch_range_fade_time,
new_sound_index,
channel->sound_index);
*primary_sound_index = new_sound_index;
}
}
if (sound->type != _sound_stop_track &&
(sound->type != _sound_start_track ||
!TEST_FLAG(track->flags, _fade_in_at_start_bit)) &&
(channel_get_state(sound->playing_channel_index) !=
_sound_channel_queued ||
TEST_FLAG(sound->flags, _sound_waiting_for_cache_bit) ||
(channel->playing_permutation->next_permutation_index == NONE &&
sound->next_definition_index != NONE)))
{
if (sound->next_definition_index != NONE)
{
if (!channel->playing_permutation ||
channel->playing_permutation->next_permutation_index == NONE)
{
sound_set_definition_end(channel->sound_index);
definition = sound_definition_get(sound->definition_index);
pitch_range = TAG_BLOCK_GET_ELEMENT(
&definition->pitch_ranges,
sound->pitch_range_index,
struct sound_pitch_range);
}
}
else if (!TEST_FLAG(sound->flags, _sound_waiting_for_cache_bit))
{
short permutation_index = sound_definition_next_permutation(
definition,
sound->pitch_range_index,
sound->permutation_index);
if (permutation_index == NONE)
{
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0xA1C,
TEST_FLAG(definition->flags, _sound_definition_linked_permutations_bit));
if (TEST_FLAG(
looping_definition->flags,
_looping_sound_fake_impulse_sound_bit))
{
sound->type = _sound_stop_track;
looping_sound->ordered_sounds_finished = TRUE;
}
else
{
permutation_index = sound_definition_next_permutation(
definition,
sound->pitch_range_index,
NONE);
}
}
if (permutation_index != NONE)
{
sound->permutation_index = permutation_index;
SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, TRUE);
}
}
{
struct sound_permutation *permutation = TAG_BLOCK_GET_ELEMENT(
&pitch_range->permutations,
sound->permutation_index,
struct sound_permutation);
if (sound->type != _sound_stop_track &&
_sound_cache_sound_request(
permutation,
FALSE,
TRUE,
TRUE))
{
SET_FLAG(sound->flags, _sound_waiting_for_cache_bit, FALSE);
channel_queue_sound(channel_index, permutation);
if (sound->next_definition_index == NONE &&
permutation->next_permutation_index == NONE)
{
if (sound->type == _sound_start_track)
{
sound->type = _sound_loop_track;
}
else if (sound->type == _sound_stopping_track)
{
sound->type = _sound_stop_track;
}
}
}
}
}
properties.gain *= TAG_BLOCK_GET_ELEMENT(
&pitch_range->permutations,
sound->permutation_index,
struct sound_permutation)->gain;
channel_set_properties_hardware(channel_index, &properties, FALSE);
}
sound_manager_globals.platform_definition->channel_update(channel_index);
return;
}
void sound_dispose_from_old_map(
void)
{
@@ -2491,3 +3025,204 @@ static void refresh_sounds(
return;
}
static void update_channels(
void)
{
short channel_index;
for (
channel_index = 0;
channel_index < sound_manager_globals.channel_count;
channel_index++)
{
struct sound_channel_datum *channel = channel_get(channel_index);
if (channel->sound_index != NONE)
{
struct sound_datum *sound = sound_get(channel->sound_index);
struct sound_definition *definition =
sound_definition_get(sound->definition_index);
real fade = sound_calculate_fade(channel->sound_index);
if (fade == 0.f && sound->fade_interpolation_end == 0.f)
{
sound_stop(channel->sound_index);
channel->sound_index = NONE;
continue;
}
if (TEST_FLAG(channel->type_flags, _sound_channel_3d_bit))
{
switch (sound->source.spatialization_mode)
{
case _sound_spatialization_mode_none:
match_vassert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x7D4,
FALSE,
NULL);
break;
case _sound_spatialization_mode_absolute:
{
struct sound_listener *listener =
listener_get(sound->listener_index);
struct sound_location location;
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x7DB,
listener->valid);
matrix4x3_inverse_transform_point(
&listener->matrix,
&sound->source.location.position,
&location.position);
matrix4x3_inverse_transform_normal(
&listener->matrix,
&sound->source.location.forward,
&location.forward);
matrix4x3_inverse_transform_vector(
&listener->matrix,
&sound->source.location.translational_velocity,
&location.translational_velocity);
location.translational_velocity.i =
TICKS_PER_SECOND * location.translational_velocity.i -
listener->velocity.i;
location.translational_velocity.j =
TICKS_PER_SECOND * location.translational_velocity.j -
listener->velocity.j;
location.translational_velocity.k =
TICKS_PER_SECOND * location.translational_velocity.k -
listener->velocity.k;
sound_manager_globals.platform_definition->set_channel_location(
channel_index,
TRUE,
&location,
sound->source.obstruction,
sound->source.occlusion,
listener->underwater);
}
break;
case _sound_spatialization_mode_relative:
sound_manager_globals.platform_definition->set_channel_location(
channel_index,
TRUE,
&sound->source.location,
0.f,
0.f,
FALSE);
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x7EC,
FALSE,
NULL);
break;
}
}
else
{
real_point3d relative_position = sound->source.location.position;
switch (sound->source.spatialization_mode)
{
case _sound_spatialization_mode_none:
break;
case _sound_spatialization_mode_absolute:
{
struct sound_listener *listener =
listener_get(sound->listener_index);
match_assert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x7FA,
listener->valid);
matrix4x3_inverse_transform_point(
&listener->matrix,
&sound->source.location.position,
&relative_position);
}
/* fall through */
case _sound_spatialization_mode_relative:
{
real minimum_distance =
sound_definition_get_minimum_distance(
sound->definition_index);
real maximum_distance =
sound_definition_get_maximum_distance(
sound->definition_index);
real distance = square_root(
relative_position.x * relative_position.x +
(relative_position.y * relative_position.y +
relative_position.z * relative_position.z));
real attenuation = 1.f -
(distance - minimum_distance) /
(maximum_distance - minimum_distance);
fade *= PIN(attenuation, 0.f, 1.f);
}
break;
default:
match_vassert(
"c:\\halo\\SOURCE\\sound\\sound_manager.c",
0x80A,
FALSE,
NULL);
break;
}
}
if (sound->type == _sound_impulse)
{
update_channel_for_impulse_sound(channel_index, fade);
}
else
{
update_channel_for_looping_sound(channel_index, fade);
}
if (sound_class_get(definition->sound_class)->speech &&
sound->track_proc == track_object_impulse_sound)
{
game_sound_set_mouth_aperture(
sound->source_identifier,
sound_permutation_get_real_mouth_aperture(
channel->playing_permutation,
(long)channel->estimated_tick_time));
}
}
}
return;
}
void sound_idle(
void)
{
sound_manager_globals.idling = TRUE;
if (sound_manager_globals.initialized && sound_manager_globals.enabled)
{
sound_manager_globals.platform_definition->begin_scene();
if (!sound_manager_globals.paused)
{
sound_update_time();
update_channels();
}
sound_manager_globals.platform_definition->end_scene();
}
sound_cache_idle();
sound_manager_globals.idling = FALSE;
return;
}