Skip to content

Decompile ~30 level functions (census sweep: init/collide/spawn/state-machine handlers) - #136

Open
Tokatta007 wants to merge 39 commits into
MatBourgon:masterfrom
Tokatta007:decompile-batch-july20
Open

Decompile ~30 level functions (census sweep: init/collide/spawn/state-machine handlers)#136
Tokatta007 wants to merge 39 commits into
MatBourgon:masterfrom
Tokatta007:decompile-batch-july20

Conversation

@Tokatta007

Copy link
Copy Markdown
Contributor

Summary

Census-sweep batch stacked on the merged #134 — ~30 level functions across CIRCUIT, FINAL, GEXZIL, HORROR, KUNGFU, LOONEY, MOOSHU, REZOP, and RTA. The bulk are structural handlers (OnCreate/OnCollide/OnUpdate init, spawn, and state-machine functions), where the hit rate is much higher than the low-70s math/physics tier. All verified with a clean make build && make diff (No differences found).

Functions

CIRCUITfunc_8015D7F0 (spline object spawner), func_8015DB80, circuit_follow_OnUpdate
FINALfunc_8015AC70 (camera-lock), func_8015CDF0 / func_8015CF08 (debug HUD), func_8015D018 (particle spawner), final_lectro_OnUpdate
GEXZILfunc_8015C208 (anim state machine), func_8015E8C0 (bubble spawner), func_8015B220
HORRORhorror_skelh_OnUpdate, horror_drawer_OnUpdate, horror_polter_OnUpdate, horror_fltchst_OnCollide
KUNGFUkungfu_kbgen_OnUpdate, kungfu_swing_OnCreate, kungfu_joyride_OnCreate, kungfu_dragflm_OnCreate
LOONEYlooney_bullet_OnCollide, looney_trapmuv_OnCreate
MOOSHUfunc_8015B39C (spline midpoint check), func_8015B8B0, mooshu_moo_OnCollide, mooshu_moosprk_OnUpdate
REZOPfunc_8015ECB8 (particle RMW), rezop_crnkplt_OnUpdate
RTAfunc_8015E27C / func_8015E3AC (save/restore twins), func_8015E5E0, func_8015B4EC, rta_zwleak_OnCreate, rta_zswitch_OnCreate / _OnCollide

Techniques

  • Leftover-arg + unprototyped forward decl (func_8015B39C): callee reads a3 while callers pass 3 args; an unprototyped forward decl keeps the call sites warning-free.
  • Load-after-call to avoid an extra saved register — loading a value after an intervening call keeps it in a temp instead of forcing a callee-saved slot.
  • Comparison-operand order controls which operand's evaluation leads the block (both arms of a </> split).
  • unsigned short / short return-type matching to reproduce the target's lhu vs lh and per-arm promotions.
  • Chained assignment, postincrement-call-arg delay-slot store, increment-in-condition loops, and SVECTOR-vs-SVector copy sizing across the spawner/particle functions.

BSPTree event dispatch: outer switch on bsp->_06 (cases 4, 1), inner
switch on bsp->_04 (cases 2, 1, 5) with duplicated ApplyMatrixT arms
that cross-jump. Key derivations: the shared final store goes through
a z-value temp (m2c's var_v0) so it tail-merges across arms; the temp
is int (ushort narrows the LVECTOR load to the low half) with an
(unsigned short) cast on the globalOffset.z read for the lhu.
…unc_801616D4, 156 insns each)

Per-face particle init twins (the 7th-arg callback of func_800170E8):
copies the spawn position, builds two vertex-delta rows scaled by
D_80078244 with the third row zeroed, shares the AF30-family texture
flags block, and seeds rand tumble + a rotation matrix in data+0xC.
Key derivations: the three def indices load into int locals up front
(def[1] stays registered until vc), the posX store stretches past the
va address math via a ushort px0 local, and unk34=0 sits after unk30.
KUNGFU gains the ParticleData typedef from LOONEY.
- MOOSHU func_8015D190_C5B10 (44 insns): moodam intro-flag scan loop
- RTA func_8015B4EC_DBB5C (47 insns): particle ground-bounce callback.
  New GCC 2.7.2 recipes recorded in CLAUDE.md: store-before-reread
  forces a genuine field reload (cse conservative memory invalidation),
  inline compound RMWs evict the param to a2 via local-alloc pressure,
  and A += A*2 vs A *= 3 addu operand-order/dest differences.
- func_80162024/80162118/80162214 (61/63/52): rotation-transform trio,
  siblings of func_80161F4C with vec=(0,±0x40,0) and by-value SVECTOR offset
- func_801622E4 (62): camera-key switcher — ternary condition with
  per-arm flag tests, base pointer kept live across func_8000B054 call
- func_8016257C (52): camera-key prober — duplicated store tails,
  two-statement lock mask clears
- rta_zshark_OnCreate (58): flags-guarded matrix transform; 16-byte
  SharkVector struct copy, RotMatrixFull + ApplyMatrixLV into work6-8
- rta_zwleak_OnCollide (60): player-data impulse writeback with signed
  div-by-15 magic multiplies. Matched via extern void declarations for
  func_8000B054/func_800256A8 — implicit-int callees reserve a phantom
  v0 return pseudo that flips mflo temp coalescing ties (new recipe)
Target/Position HUD readouts (X/Y/Z via the 4-digit hex formatter).
Three linked recipes: D_800BDEE4 as a separate scalar extern (per-store
assembler-macro sw) alongside the D_800BDEE0 array (cached base);
pointer-deref arg spelling to keep the coordinate loads below the
global stores (array-index form hoists them and loses the hazard nop);
per-print constants routed through a rotating local
…atBourgon#132)

- final_finaltv_OnUpdate (44): abs() clamp condition
- looney_funguy_OnCreate (38): early-cached work0 pointer
- circuit_orbplat_OnCreate (60): plain inline transforms, ascending
  work-store order; (short) casts added for our implicit sin/cos decls
- func_8015CDF0 (70): Coordinates/Subject debug print (print-twin recipes)
- func_8015CF08 (68): debug rect frame draw; first store shares the
  call-arg address register, remaining stores via separate scalar externs
- func_8015D018 (116): 30x remsfx particle spawner. Dead reassignment
  of the lifetime local blocks loop-invariant motion from stealing a
  saved register from the callback addresses (commented in code).
  Added OBTABLE.h include with (int) casts at existing callers
Spline-driven skeleton hand (66 insns): parent-death kill, spline
process with plain-death + sound, and a work6 event-list scanner.
The event counter store rides the jal delay via a postincrement
call argument; work0 frame compare reads the short half
Spline segment-offset accumulator (67 insns): walks the intro group
writing per-instance work3 offsets from the multispline frame-length
table, with an introData skip-list. Outer goto-loop per the LIM rule;
inner accumulate loop uses while (++frame < end) so the increment
fills the lhu delay slot
- func_8015E5E0 (69): rotation-target smoother, p = (p*3 + offset)/4
  per axis through three LVECTOR locals; dead 16-byte local for the
  0x60 frame
- func_8015B220 (69): animation-frame advancer with wrap flags;
  short f = ++currentAnimFrame shares one sign-extension across both
  range compares
Waypoint trap mover init (70 insns, matched first try): default
intro/object-data blob fallbacks, waypoint index advance with wrap,
aim via the ratan2 SVECTOR-delta idiom with cached &work0 pointer
- func_8015B8B0 (72): mooelec spawner/attach with birth-and-link.
  Signed (Instance*, GameTracker*) with gameTracker unused to match
  the caller mooshu_moolevr_OnCollide, whose 2-arg call is part of
  its already-matched codegen (Object* typing on FindObject)
- rezop_crnkplt_OnUpdate (72): crank-plate spring physics. Intro
  unk14 is position.z; both clamp compares evaluate the pos+shift
  side first; shared int one for the state pair with a literal 1 in
  the spline compare to keep its register at t1
Camera-follow state machine (73 insns): intro-flag gated flag toggle,
countdown, spline-signal dispatch on currentMainState. The trailing
state increment reads the field inline to reload into the right
register (a reused local kept its pseudo live)
Water-leak init (77 insns): memset the work block, sin/cos launch
vector from intro angle, three magic-mult offset writes. Compute work8
into an int first (front-loads its mult to match the ROM schedule) and
cast position.z unsigned for the lhu the truncated store allows
Bullet-vs-target collision (74 insns): player passthrough, parentName
G2String match, hit-state advance. Caches &work0 as long* for the
work1 store, casts the rand spread to short for func_80050508
Spline-ride mount init (76 insns): freeze-flag early-out, spline
setup with quaternion-to-matrix seeding, frame-count spline set.
Caches player->intro into a local to hoist its load ahead of the
flag store, matching the ROM schedule
Cow collision handler (77 insns): player-spline match sets flag bits,
then two state branches (moodam charge vs generic bump). Passes the
collision struct pointer to func_80022714/38 (keeps a3 live so the
byte checks reload), and int flags to coalesce onto the arg register
Switch collision handler (75 insns, leaf): player-hit gated state
machine with goto cross-jumps for the three activation paths, toggling
intro flag 0x800 on a match. D_8015EE00/EDFE typed unsigned short so
the reads emit lhu (a lh/lhu diff the reloc-masked per-function differ
missed; caught by the clean ROM)
Swing pendulum init (78 insns): randomized phase, amplitude from
object data with a signed short divide, an amplitude^2 rounding step,
and the position->work unaligned SVECTOR copy. int (not short) abs
temp negates in-register and avoids an 8-byte frame spill
horror_drawer_OnUpdate: goto cross-jump state machine (scale-pulse door),
duplicated shared tails into each arm per the cross-jump rule.
kungfu_kbgen_OnUpdate: OBTABLE/BirthObject spawner; in-place work0 increment
forwards without reload, cached unk8 load fills the branch delay slot.
Init handler: caches instance->parent, reloads parent->0xF6 for each
0x2000/0x1000 division into work1/scale, and a rand-seeded work9.
Scale-pulse boss state machine; states 1/2 share a scale.y/scale.z store
tail (block_9), reproduced by duplicating the tail into each arm per the
cross-jump rule. arg1->0xC is gameTracker->player.
Projectile: gravity clamp, ground check, cos/sin movement (state 0),
spawn + lifetime kill (state 1). Ground-check comparison written
position-first to match the target's load order. Corrected
func_8015D7B4_C6134 to 1-arg (its gameTracker param was unused and the
sole caller passes only instance->parent).
Switch-based sound state machine; the work0 respawn call (block_16) is
duplicated into both else-arms per the cross-jump rule, and GCC merges
them. intro->_2C gate sets state 2; case 0 falls through to case 1.
Switch init: shared-variable flag ORs (flags|0x400, flags|0x480), intro
flag gates, memset of the work block, and a D_8015EDFE sound-bit toggle.
soundBit is int (forces lh); storing to the global directly per arm
(no temp) lets GCC hoist the nor into the branch delay slot.
Player-collision handler: 4-clause guard (bsp->_06, instanceSpline==player,
currentMainState not in {1,2}, globalOffset.x), then swaps player position.y/x
with work1 by state, and fires COLLIDE_HandleSignal once (work4 latch).
bsp->_06 cached as the shared '1' value across the compares.
Save-state: copies 25 transform fields from arg0 into the D_8015EED0
buffer. Consolidated the consecutive destination globals into one array
(links identically to the separate symbols); last field (0x54) is an
int copy, the rest short.
Restore twin of func_8015E27C: copies the D_8015EED0 save buffer back
into arg0's 25 transform fields. Int field (0x54, D_8015EF08) goes last
in source (m2c ordered it from the schedule); rest are short via the
consolidated array.
Particle-vertex RMW: adjusts the 6 vertex deltas (or 3 positions by
velocity) on the particle and its linked twin (arg0->unk4). Reassigning
the same pointer (p = p->unk4) reuses s0 and blocks the early hoist of
the twin-pointer load.
Camera-lock setup (twin of func_8015ADC4): saves player state to
D_800785C8, forces state 5, CAMERA_SetMode, builds the mid/rot vectors,
sets the camera position, and mirrors the core vectors. gameTracker8
cached in a local for the position sets; rot.z written last to match
the load scheduling.
Bubble spawner: births a blububl_ when currentModelAnim is 0xB/0xC, sets
its rotation/position from oldMatrix[5] (euler + translation) or the
instance position (SVECTOR assignment for the unaligned copy). arg1->unk98
reloaded inline each store to match.
Animation state machine: frame advance (speed==0) or accumulator-driven
(acc<<16>>18) with a two-way ternary clamp. frame0 is int (forces lh);
clamp tail (flags2 |= 0x10) duplicated into both arms per cross-jump;
func_80050400 takes the 0x5F byte (currentAnimFrame high byte).
Spline object spawner: births a cached object, gates on the object name
(G2String vs ppathd__), then seeds position/scale from the two splines of
((Intro**)intro->_04)[1]->multiSpline via SplineGetFirstPoint. Cached
&_04[1] as the surviving base to match the strength-reduced addressing.
… position fields)

Pre-emptively applies PR MatBourgon#134 review feedback to july20's debug-print
functions (func_8015CDF0 new here), which had independently reproduced the
D_800BDEE4 split and raw position-deref patterns the leader corrected.
Spline midpoint/direction check. Reads arg3 via leftover a3 (callers pass 3
args); unprototyped forward decl keeps the 3-arg call sites clean. Key levers:
load dir after the count call (avoids an extra saved reg), unsigned short
frame, short count for per-arm re-extension, and (short)frame-first operand
order in both comparison arms.
…uly20

# Conflicts:
#	src/level/HORROR.c
#	src/level/KUNGFU.c
#	src/level/LOONEY.c
#	src/level/MOOSHU.c
#	src/level/RTA.c
splat64 0.50.0 stopped generating nonmatchings .s files for c segments,
breaking INCLUDE_ASM builds in CI (requirements.txt was unpinned at >=0.38.0).
0.41.1 (what master's passing CI used) generates them correctly.
Comment thread src/level/CIRCUIT.c
func_8015D780_84960(instance, gameTracker);
} else if (state == -1) {
if (func_800257B4(gameTracker->player) != 0) {
v = ((int*)instance->introData);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v -> intro

Comment thread src/level/CIRCUIT.c
obj->position.y = pt->y;
obj->position.z = pt->z;
}
spline2 = ((int*)v1[0]->multiSpline)[0x8/4];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be v1[0]->multiSpline->scaling

Comment thread src/level/CIRCUIT.c
INSTANCE_InsertInstanceWithFlagsCleared(obj, 0xF000);
}
obj->work4 = 0;
pt = SplineGetFirstPoint(((Spline**)v1[0]->multiSpline)[0], (SplineDef*)&obj->work0);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be v1[0]->multiSpline->positional

Comment thread src/level/CIRCUIT.c
sum = 0;
n = d[0] - 1;
if (((Intro**)d)[1]->multiSpline != 0) {
sd = ((int**)((Intro**)d)[1]->multiSpline)[0];

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multiSpline->positional

Comment thread src/level/CIRCUIT.c
void func_8015DB80_84D60(Instance* instance) {
int* d;
int* sd;
int* intro2;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intro2 but no intro or intro1?

Comment thread src/level/CIRCUIT.c
}

INCLUDE_ASM("asm/nonmatchings/level/CIRCUIT", func_8015DB80_84D60);
void func_8015DB80_84D60(Instance* instance) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is kind of a mess, especially with the not really obviously named variables, but at least it matches.

Comment thread src/level/MOOSHU.c
Instance* born;
short count;

intro = instance->object->data;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not an intro object

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants