|
Download docs/re-notes/sprites.md from nmarshall/wormz-a: direct link, hf CLI and curl.
- Browser
- Download file 7.35 kB
-
https://huggingface.co/nmarshall/wormz-a/resolve/main/docs/re-notes/sprites.md
- Command line
-
hf download hf://nmarshall/wormz-a/docs/re-notes/sprites.md
-
curl -L -o sprites.md https://huggingface.co/nmarshall/wormz-a/resolve/main/docs/re-notes/sprites.md
7.35 kB
| # Sprite System | |
| WA.exe uses a custom `.spr` format for paletted animated sprites. The DD_Display object | |
| manages a 1024-slot sprite table, loading sprites from `.dir` archive files via the | |
| GfxDir subsystem. | |
| ## DD_Display Sprite Storage | |
| DD_Display contains four arrays for sprite management: | |
| | Byte Offset | Size | Description | | |
| |---|---|---| | |
| | +0x0008 | 4096 (1024 Γ u32) | `sprite_palette[1024]` β palette ID (1-3) per slot | | |
| | +0x1008 | 4096 (1024 Γ Ptr32) | `sprite_ptrs[1024]` β Sprite object pointer per slot | | |
| | +0x311C | 12 (3 Γ Ptr32) | `gfx_dirs[3]` β GfxDir pointer per palette | | |
| | +0x3530 | 12 (3 Γ u32) | `sprite_counts[3]` β loaded sprite count per palette | | |
| Sprite IDs range from 1 to 1023. Bit 0x800000 on the ID signals "already loaded" and | |
| causes LoadSprite to return immediately with success. | |
| ## Sprite Object (0x70 bytes) | |
| Vtable: `0x66418C`. Created by ConstructSprite (0x4FAA30), populated by ProcessSprite | |
| (0x4FAB80). | |
| | Offset | Size | Type | Name | Source | | |
| |---|---|---|---|---| | |
| | 0x00 | 4 | Ptr32 | vtable | ConstructSprite | | |
| | 0x04 | 4 | Ptr32 | context_ptr | ConstructSprite (ECX param) | | |
| | 0x08 | 2 | u16 | _unknown_08 | wkJellyWorm | | |
| | 0x0A | 2 | u16 | fps | wkJellyWorm | | |
| | 0x0C | 2 | u16 | width | wkJellyWorm, ProcessSprite | | |
| | 0x0E | 2 | u16 | height | wkJellyWorm, ProcessSprite | | |
| | 0x10 | 2 | u16 | flags | wkJellyWorm, ProcessSprite | | |
| | 0x12 | 2 | u16 | frame_count | ProcessSprite (can be overwritten) | | |
| | 0x14 | 2 | u16 | header_flags | ProcessSprite (from .spr raw+8) | | |
| | 0x16 | 2 | u16 | max_frames | wkJellyWorm, ProcessSprite | | |
| | 0x18 | 2 | u16 | _unknown_18 | | | |
| | 0x1A | 2 | u16 | _unknown_1a | | | |
| | 0x1C | 4 | u32 | scale_x | ProcessSprite (negative frame count) | | |
| | 0x20 | 4 | u32 | scale_y | ProcessSprite (negative frame count) | | |
| | 0x24 | 4 | u32 | is_scaled | ProcessSprite (1 if scaled, 0 otherwise) | | |
| | 0x28 | 4 | Ptr32 | frame_meta_ptr | SpriteFrame* array | | |
| | 0x2C | 4 | Ptr32 | secondary_frame_ptr | 0x4000 flag in header_flags | | |
| | 0x30 | 2 | u16 | secondary_frame_count | | | |
| | 0x32 | 2 | u16 | _pad_32 | | | |
| | 0x34 | 4 | Ptr32 | display_gfx | DisplayGfx vtable (0x664144) | | |
| | 0x38 | 40 | [u8; 0x28] | _unknown_38 | | | |
| | 0x60 | 4 | Ptr32 | raw_frame_header_ptr | ProcessSprite | | |
| | 0x64 | 4 | Ptr32 | bitmap_data_ptr | ProcessSprite | | |
| | 0x68 | 4 | Ptr32 | palette_data_ptr | ProcessSprite | | |
| | 0x6C | 4 | u32 | _unknown_6c | | | |
| ### SpriteFrame (0x0C bytes) | |
| Per-frame metadata, pointed to by `Sprite::frame_meta_ptr`. One entry per animation frame. | |
| | Offset | Size | Type | Name | | |
| |---|---|---|---| | |
| | 0x00 | 4 | u32 | bitmap_offset | | |
| | 0x04 | 2 | u16 | start_x | | |
| | 0x06 | 2 | u16 | start_y | | |
| | 0x08 | 2 | u16 | end_x | | |
| | 0x0A | 2 | u16 | end_y | | |
| Source: wkJellyWorm `Sprites.h::SpriteFrame`. | |
| ## Sprite Vtable (0x66418C, 8 entries) | |
| | Slot | Address | Name | | |
| |---|---|---| | |
| | 0 | 0x4FAA80 | Destructor | | |
| | 1 | 0x4FAAD0 | Unknown | | |
| | 2 | 0x4FB5C0 | Unknown | | |
| | 3 | 0x4FE550 | Unknown | | |
| | 4 | 0x4FE2F0 | Unknown | | |
| | 5 | 0x4FE9C0 | Unknown | | |
| | 6 | 0x5613D0 | CTask common stub | | |
| | 7 | 0x5613D0 | CTask common stub | | |
| ## Key Functions | |
| ### LoadSprite (0x523400) | |
| `__thiscall` on DD_Display, `RET 0x14` (5 stack params). Loads a sprite into the | |
| DD_Display sprite table. | |
| Parameters: | |
| 1. `palette` (1-3) β which GfxDir palette to use | |
| 2. `sprite_id` (1-1023) β slot index | |
| 3. `size_override` β packed DWORD: low16 = width override, high16 = height override (0 = no override) | |
| 4. `file_archive` β PC_FileArchive* | |
| 5. `filename` β const char* sprite filename | |
| Returns: 1 on success, 0 on failure. | |
| Flow: | |
| 1. If `sprite_id & 0x800000`, return 1 (already loaded) | |
| 2. Validate `palette` in [1,3] and `gfx_dirs[palette]` is non-null | |
| 3. Validate `sprite_id` in [1,1023] | |
| 4. Call vtable[0x20] to check if sprite already exists at this slot | |
| 5. Allocate sprite memory via `WA_MallocMemset` (0x53E910) | |
| 6. Call `ConstructSprite` (0x4FAA30) to initialize | |
| 7. Call `LoadSpriteFromVfs` (0x4FAAF0) to load data | |
| 8. Store sprite pointer at `this[sprite_id + 0x402]` (byte offset +0x1008) | |
| 9. Store palette at `this[sprite_id + 2]` (byte offset +0x8) | |
| 10. Increment `sprite_counts[palette]` | |
| 11. Apply `size_override` to sprite +0x16 (width) and +0x18 (height) if non-zero | |
| ### ConstructSprite (0x4FAA30) | |
| `__usercall` β EAX = sprite pointer, ECX = context pointer. Initializes a freshly | |
| allocated 0x70-byte sprite struct: sets vtable to 0x66418C, DisplayGfx vtable at +0x34, | |
| zeroes all data fields. | |
| ### LoadSpriteFromVfs (0x4FAAF0) | |
| `__usercall` β EAX = filename string, ECX = PC_FileArchive pointer, 2 stack params | |
| (sprite pointer, GfxDir pointer). Tries `GfxDir::FindEntry` (0x566520) for a cached | |
| entry first; on miss, calls `GfxDir::LoadImage` (0x5666D0) to read from the archive. | |
| Allocates a buffer for raw sprite data, then calls `ProcessSprite` (0x4FAB80). | |
| ### ProcessSprite (0x4FAB80) | |
| `__usercall` β EAX = sprite pointer, 1 stack param (raw data pointer from .spr file). | |
| Parses the .spr binary format and populates the Sprite struct fields. See the | |
| .spr File Format section below. | |
| ### DrawSpriteGlobal (0x541FE0) | |
| `__thiscall` + EAX = y_pos (usercall). 4 stack params: layer, x_pos, sprite_id, frame. | |
| Enqueues a draw command (type 4) to the rendering queue. World-space coordinates. | |
| ### DrawSpriteLocal (0x542060) | |
| Identical to DrawSpriteGlobal but enqueues command type 5. Screen-space coordinates. | |
| ### DestroySprite (0x4FAA80) | |
| `__thiscall`, sprite vtable slot 0. Frees sprite resources. | |
| ## .spr File Format | |
| Binary format parsed by ProcessSprite (0x4FAB80). Data layout: | |
| ``` | |
| Offset Size Field | |
| +0x00 4 (unused / magic) | |
| +0x04 4 (u32) data_size β added to global counter at 0x7A0864 | |
| +0x08 2 (u16) header_flags β sprite+0x14 | |
| +0x0A 2 (u16) palette_entry_count | |
| +0x0C 3 Γ N palette entries (3 bytes each, likely RGB) | |
| ... varies frame metadata (SpriteFrame, 0x0C bytes each) | |
| ... varies bitmap pixel data | |
| ``` | |
| Special flags: | |
| - **0x4000** in header_flags: secondary frame table present before the primary frames. | |
| Count at first word after palette, followed by 0x0C-byte entries. | |
| - **Negative frame count** (bit 15 set in the frame count word at the frame header): | |
| high 7 bits β scale_x, low 7 bits β scale_y. Actual frame_count set to 1. | |
| Scale values stored at sprite +0x1C and +0x20 as `(value << 16) >> 5`. | |
| ## Global Counters | |
| Four u32 globals tracking total sprite resource usage, accumulated by ProcessSprite: | |
| | Address | Description | | |
| |---|---| | |
| | 0x7A0864 | Total sprite data bytes loaded | | |
| | 0x7A0868 | Total sprite frame count | | |
| | 0x7A086C | Total sprite pixel area (sum of frame wΓh) | | |
| | 0x7A0870 | Total palette entries Γ 3 | | |
| ## GfxDir System | |
| Sprites are stored in `.dir` archive files loaded by the GfxDir subsystem: | |
| - **GfxHandler::LoadDir** (0x5663E0): Loads a `.dir` archive. Reads `"DIR\x1A"` | |
| header (0x1A524944), parses 0x1000-byte entries, performs pointer relocation. | |
| - **GfxDir::FindEntry** (0x566520): Looks up a cached entry by name. | |
| - **GfxDir::LoadImage** (0x5666D0): Reads image data from archive, returns through | |
| vtable with 0x0C-byte descriptor (vtable 0x66A1C0). | |
| ## Sources | |
| - Ghidra decompilation of LoadSprite, ConstructSprite, ProcessSprite, DrawSpriteGlobal | |
| - wkJellyWorm `Sprites.h` / `Sprites.cpp` β struct layout, function hooks, calling conventions | |
| - Runtime analysis via OpenWA validator DLL | |