Implement detached object animation control

This commit is contained in:
gamer147
2026-07-20 14:53:34 -04:00
parent d740c27b4a
commit 6aede1e525
10 changed files with 231 additions and 29 deletions

View File

@@ -116,7 +116,7 @@ note = "retained_gfx_manager+0xb530 selected surface slot; -1 denotes device bac
offset = 0x51b6c
name = "gfx_dirty_a"
type = "int"
note = "retained_gfx_manager+0xb558 dirty flag; op 0x1f6 clears it"
note = "retained_gfx_manager+0xb558 redraw-dirty flag; active finite object channels set it even when detached by 0x242; op 0x1f6 clears it"
[[field]]
offset = 0x51b70
name = "gfx_force_complete"
@@ -126,7 +126,12 @@ note = "retained_gfx_manager+0xb55c requests forced completion of one-shot chann
offset = 0x51b74
name = "gfx_dirty_b"
type = "int"
note = "retained_gfx_manager+0xb560 dirty flag"
note = "retained_gfx_manager+0xb560 blocking-presentation dirty flag; active finite object channels set it unless obj+0x2d0 bit 0 detaches them"
[[field]]
offset = 0x51b80
name = "gfx_animation_service_flags"
type = "uint"
note = "retained_gfx_manager+0xb56c service flags; bit 1 suppresses op 0x243 force-complete/clock-reset request"
[[field]]
offset = 0x52bd4
name = "surfaces"

View File

@@ -6440,19 +6440,20 @@ observed_types = ["l-int"]
[[opcode]]
op = 0x242
label = "set-gfx-field2d0"
label = "set-object-animation-detached"
argc = 2
abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "set-gfx-field2d0"
name = "set-object-animation-detached"
category = "draw"
summary = "0x242 (handle)(value) — get-or-create the retained gfx object and write value to obj+0x2d0; the generic instruction length is 5 dwords. SC0000's common CG loader passes zero after draw binding. This field does not reset transform or color channels; its downstream purpose remains unknown."
summary = "Replace the retained object's animation-control word at obj+0x2d0. Bit 0 detaches finite one-shot channels from blocking presentation and protects them from 0x243 forced completion until they finish naturally."
details = "The setter get-or-creates the object and writes the complete operand. During retained composition, bit 0 makes the one-shot color/scale/rotation/translation/source-rectangle group ignore the manager's force-complete request and suppresses the blocking-dirty flag while leaving the redraw-dirty flag active. When no finite channel remains, native clears bit 0 and the shared start time. SC0000's two common CG loaders explicitly write zero. Across DATA1, 302 calls write zero and the sole nonzero call is BTL.BIN's animated battle-object setup, which writes one."
noop_headless = false
source = "investigation"
confidence = "high"
depends_on = []
evidence = "Ghidra /v2: op_0x242_set_object_field2d0@0x4249d0 fetches operands 2 and 1 and calls gfx_object_set_field2d0@0x47f1a0; the worker calls gfx_object_get_or_create then stores operand 2 at returned object+0x2d0."
evidence = "Ghidra /v2: op_0x242_set_object_animation_detached@0x4249d0 calls gfx_object_set_animation_control@0x47f1a0. Consumer gfx_object_apply_transform_channels@0x472f00 tests obj+0x2d0 bit 0 to suppress manager+0xb55c forced completion and manager+0xb560 blocking-dirty publication, while still setting manager+0xb558 redraw dirty; it clears bit 0 when the finite group completes. DATA1: 303 calls; values zero x302, one x1."
[[opcode.semantics.args]]
i = 1
@@ -6461,7 +6462,7 @@ observed_types = ["l-int", "l-ptr"]
[[opcode.semantics.args]]
i = 2
role = "field2d0_value"
role = "animation control flags; bit 0 = detached/nonblocking"
observed_types = ["imm"]
[[opcode]]
@@ -6473,12 +6474,12 @@ abi_source = "kelebek+decode-validated"
[opcode.semantics]
name = "reset-anim-clock"
category = "draw"
summary = "Reset the native global animation-service elapsed and duration fields to zero when service flag bit 1 is clear."
summary = "When animation-service flag bit 1 is clear, request forced completion of ordinary finite one-shot object channels and reset the global animation-service elapsed/duration fields. Objects detached by 0x242 bit 0 ignore the force request."
noop_headless = false
source = "investigation"
confidence = "high"
depends_on = []
evidence = "Ghidra handler 0x4182d0: if !(ctx+0x51b80 & 2), set ctx+0x51b70=1 and zero ctx+0x51b78/+0x51b7c. Normal SC0000 label_1235a calls it before present-frame."
depends_on = [0x242]
evidence = "Ghidra handler 0x4182d0: if !(ctx+0x51b80 & 2), set retained-gfx owner+0xb55c (EngineCtx+0x51b70)=1 and zero owner+0xb564/+0xb568. gfx_object_apply_transform_channels treats force value 1 as immediate completion unless obj+0x2d0 bit 0 is set. SC0000 label_1235a calls it before present-frame."
[[opcode]]
op = 0x248