A series of small type-safety improvements that compound to delete a
lot of raw byte arithmetic and ad-hoc transmutes:
- `LayerSpriteFrame.bitmap_vtable` retyped from `u32` to
`*const c_void`, and `LayerSpriteFrame.surface` from `u32` to
`*mut Surface`. The two existing consumers (the slot 0 destructor's
`layer_sprite_frame_destructor` and the `load_sprite_by_name` /
`free_layer_sprite` pair) now access these via named fields
instead of raw `elem.add(0x08)` / `elem.add(0x0C)` arithmetic.
- `SurfaceVtable::set_color_key` (slot 7) added as a typed binding,
matching the existing `lock_surface` / `unlock_surface` /
`init_surface` / `release` slots. `load_sprite_by_name`'s per-frame
surface init loop now uses the typed binding for slots 3, 4, 5, 6,
7 instead of hand-rolled raw transmutes — drops ~50 lines.
- `RenderContextVtable::lock_surface_write`'s `out_data` retyped
from `*mut u32` to `*mut *mut u8`, and `unlock_surface_write`'s
`p2: …
feat: port DisplayGfx::Destructor (slot 0) to pure Rust
Replaces vtable[0] of DisplayGfx (0x66A218) with a native Rust impl
that mirrors the original DestructorImpl (0x56A010) one-to-one.
The cleanup body lives in the new `display::destructor` module and
ports both helpers along with it:
- `free_layer_sprite_table` — Rust port of 0x56A280: iterates
`sprite_table[1..0x3FF]`, runs `LayerSpriteFrame::Destructor` on
every entry of each sprite's count-prefixed `frame_array`, then
frees the frame array and the LayerSprite itself.
- `layer_sprite_frame_destructor` — Rust port of 0x5732E0: rebinds
the embedded CBitmap vtable and dispatches the surface's
scalar deleting destructor (`vtable[0](1)`) via raw thiscall.
- `tile_bitmap_set_destructor` — Rust port of 0x569BC0 (originally
a usercall on EDI): iterates `bitmap_ptrs[0..count]`, calls
`vtable[0](1)` on each non-null `CBitmap*`, frees `bitmap_ptrs`.
- `free_cbitmap_vec` — the inline CBitmap vec teardown loop:
releases each non-null `CBitmap.surface` via …
feat: port DisplayGfx::DrawTiledBitmap (slot 11) to pure Rust
Last hot-path slot in the in-game render pipeline. The entire active
rendering pipeline of a normal Worms match now runs on Rust code; only
GetSpriteFrameForBlit (slot 33, called from blit_sprite) and the WA
wrapper backend (DDraw/D3D/OpenGL) remain as bridges.
Three phases per the existing slot docstring: lazy tile-cache
allocation, lazy populate, per-frame clipped tile blits. Bundles in
the leaf primitives blit_64byte_row_pattern (port of FUN_005B2A5E)
and blit_color_table_forward (port of BlitColorTable_Forward), plus
blit_bitmap_clipped_native (port of DisplayGfx::BlitBitmapClipped at
0x56A700) and cbitmap_blit_via_wrapper (port of FUN_00403c60).
Adds a 12-byte CBitmap struct (vtable 0x643F64) to sprite.rs, distinct
from but isomorphic to the trailing 12 bytes of LayerSpriteFrame, and
the SurfaceVtable / Surface stub in context.rs covering the lock /
unlock / init slots used by the populate phase. Renames
DisplayGfx._unknown_358c -> tile_cache_populated and retypes the
bitmap_vec…
refactor: reorganize display/ and render/ into unified render/ module
Merge the display/ and render/ sibling modules into a single render/
module with display/, sprite/, and ddraw/ sub-modules. Pure mechanical
refactor — file moves and import rewrites only, zero logic changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>