7.1 KiB
SpriteNode Class Layout
Purpose
This note captures the current class-level read of the SpriteNode family so later Ghidra class work can move quickly and conservatively.
Compared with EntityDispatchEntry, this family has a cleaner explicit virtual/event-dispatch surface and a more bounded ownership model. That makes it an excellent second pilot for class lifting.
Current Best Class-Level Read
SpriteNode is a tree-based render/UI node family with:
- child-chain ownership
- accumulated position and bounds propagation
- dirty-state tracking
- central event dispatch through a small virtual surface
- destructor ownership over child nodes and global focus state
This already looks much closer to an ordinary C++ object family than many gameplay-side structures.
Strongest Evidence Anchors
Destructor
000b:326e sprite_node_destroy
Current best read:
- destructor-style path
- sets vtable ptr to
0x501a - clears global focus pointer
[0x4fd0:0x4fd2]ifself - releases child nodes
- frees object memory through
mem_free
This is the strongest current single proof that SpriteNode should be lifted as an owned object family.
Event dispatch
000b:3ab2 sprite_node_dispatch_event
Current best read:
- large event dispatch switch
- checks event types
2/4/8/0x100 - updates global focus pointer at
[0x4fd0:0x4fd2] - dispatches through virtual slots
+0x14,+0x18,+0x20,+0x24
This is the strongest current proof of a stable virtual method surface.
Dirty/update family
000b:3380 sprite_node_is_dirty000b:33a6 sprite_node_mark_dirty000b:40ee sprite_node_update_and_dispatch
These show that node state changes and redraw/update dispatch are methods on the same family, not just free helper functions wandering across unrelated data.
Recursive tree helpers
000a:b988 sprite_node_get_or_traverse000b:358d sprite_tree_accumulate_pos000b:3a00 sprite_tree_sum_x_offset000b:3a35 sprite_tree_sum_y_offset
These are strong evidence for a child-linked tree object with inherited coordinate accumulation.
Candidate Layout
This layout is intentionally conservative.
| Offset | Current name | Confidence | Current meaning |
|---|---|---|---|
+0x19/+0x1b |
child_or_next_ptr |
High | Child-chain pointer pair used by recursive traversal and offset accumulation. |
+0x21 |
local_x_offset |
High | Summed by sprite_tree_sum_x_offset / accumulate helpers. |
+0x23 |
local_y_offset |
High | Summed by sprite_tree_sum_y_offset / accumulate helpers. |
+0x29 |
dirty_flags |
High | Checked by sprite_node_is_dirty; manipulated by mark/update paths. |
+0x17e |
redraw_flag |
Medium | Cleared by sprite_clear_redraw_flag; likely a subtype or larger-object field tied to one SpriteNode family variant. |
Layout Caveat
The current notes likely mix a compact core SpriteNode with one or more larger derived UI/display objects. The evidence for +0x17e strongly suggests there are bigger family members or wrapper objects in the same virtual ecosystem.
So the safe future modeling strategy is:
- define a small
SpriteNodeBase - keep larger UI/display fields in derived or sibling structs until more offsets are closed
Candidate Method Map
Strong instance methods
| Address | Current function | Candidate method role |
|---|---|---|
000b:326e |
sprite_node_destroy |
Destroy() |
000b:3380 |
sprite_node_is_dirty |
IsDirty() |
000b:33a6 |
sprite_node_mark_dirty |
MarkDirty() |
000b:3ab2 |
sprite_node_dispatch_event |
DispatchEvent() |
000b:40ee |
sprite_node_update_and_dispatch |
UpdateAndDispatch() |
000a:b988 |
sprite_node_get_or_traverse |
GetOrTraverse() |
Strong family-local helpers that may remain free/static
| Address | Current function | Why it may stay non-method |
|---|---|---|
000b:3a00 |
sprite_tree_sum_x_offset |
Pure recursive accumulation helper; method status depends on later decompile readability. |
000b:3a35 |
sprite_tree_sum_y_offset |
Same as above. |
000b:330c |
sprite_tree_dispatch_wrapper |
Looks like a pure thunk wrapper rather than a meaningful source-level method. |
000b:3362 |
sprite_tree_unwind_check |
Stack-segment guard helper; probably not worth presenting as a class method. |
Candidate Virtual Slot Map
The currently verified slots are already good enough for a first typed vtable.
| Slot offset | Current best role | Evidence |
|---|---|---|
+0x14 |
event handler A | sprite_node_dispatch_event dispatches here for one event class |
+0x18 |
event handler B | same dispatcher |
+0x20 |
event handler C | same dispatcher |
+0x24 |
event handler D | same dispatcher |
The seg091 default-slot helpers are also useful evidence:
000a:7b44,000a:7b49,000a:7b53,000a:7b4e,000a:7b78,000a:7b7d,000a:7b30,000a:7b3f,000a:7b35,000a:7b3a000a:7b58returns zero and behaves like a default no-op boolean slot000a:7b5fis a forwarding trampoline slot
These likely belong to one or more shared/default node vtables and should be preserved as vtable evidence even if they never become pretty source-level methods.
Ownership And Global State
Focus/global state
Global focus pointer [0x4fd0:0x4fd2] is updated in the dispatch family and cleared in the destructor.
That gives the family a real interaction with global UI focus/state, but the key point for class work is simpler:
- focus ownership is tied to the node family itself
- this is not just an arbitrary free helper changing global UI state
Child ownership
The destructor and recursive sum/traverse helpers strongly suggest real child ownership or at least managed child linkage.
That means later class modeling should preserve a node/tree mental model rather than flattening everything into stand-alone display items.
Candidate Ghidra Modeling Plan
When class authoring begins, the safest sequence for this family is:
- create class namespace
SpriteNode - move
Destroy,IsDirty,MarkDirty,DispatchEvent,UpdateAndDispatch, andGetOrTraversefirst - create minimal
SpriteNodeBasestruct with the stable offsets around+0x19,+0x21,+0x23, and+0x29 - create provisional vtable with slots
+0x14,+0x18,+0x20,+0x24 - keep recursive tree helpers outside the class until decompiler output shows they benefit from becoming methods
Open Questions
- exact root vtable address or addresses for the main SpriteNode family
- whether the
+0x17eredraw flag belongs to a derived display node rather than the compact base node - which event-code cases map to which slot semantically beyond the current
A/B/C/Dplaceholder naming - whether
sprite_tree_accumulate_posshould become a class method, a static helper, or a separate geometry utility
Immediate Follow-Up Value
The most useful next companion work after this note is not more sprite detail by itself. It is the rebuild-ABI note, because once the first few class families are documented this well, the next real risk is drifting away from the original memory and calling-convention model before any code is emitted.