Replace legacy DrawXxxCmd structs with RenderMessage variants for all
10 Rust-produced command types. Variable-length commands (LineStrip,
Polygon) use alloc_aux() for vertex data with explicit pointers in the
enum, avoiding layout coupling. Remove legacy structs and command_type
constants module.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: split render.rs, move game logic to core, type command structsβ
Replace raw u32 sprite_flags with typed SpriteOp (packed sprite index +
transform flags) and SpriteFlags (bitflags for the 13 known blit flags).
Follows the same pattern as SoundId/KnownSoundId in the audio system.
- SpriteId enum renamed to KnownSpriteId (non-exhaustive catalog)
- SpriteOp: #[repr(transparent)] newtype with index()/flags() accessors
- SpriteFlags: bitflags! type replacing raw 0x0080_0000-style masks
- Updated through the full pipeline: DrawSpriteCmd, RenderMessage,
vtable signature, dispatcher, and blit_sprite implementation
- Added bitflags 2 as direct dep (already transitive via windows crate)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: introduce RenderMessage enum for typed render queue commandsβ
Add a strongly-typed RenderMessage enum that can be stored inline in the
existing RenderQueue buffer arena (zero allocation). TypedRenderCmd wraps
it with the standard command_type/layer prefix so sorting works unchanged.
The dispatcher handles both legacy byte-format and typed commands.
Also rename DrawSpriteCmd fields sprite_id->sprite_flags, frame->palette
to match the vtable signature used by the dispatcher.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port RenderDrawingQueue dispatcher to pure Rustβ
Ports the per-frame render-queue dispatcher (0x542350, ~789k dispatches
per minute of gameplay) and its four leaf helpers β RQ_ClipCoordinates
(0x542BA0), RQ_ClipCoordinatesWithRef (0x542C70), RQ_TranslateCoordinates
(0x542B10, formerly mis-labelled RQ_GetCameraOffset_Maybe), and the
inline FixedDiv16_16 β to native Rust. The dispatcher's 14 cmd-type
cases dispatch through the DisplayGfx vtable to slots 11..22 via raw
vtable pointers, eliminating the last per-frame WAβDisplayGfx bridge.
Lesson #1 from the project memory bit again: the Ghidra decompile of
RQ_TranslateCoordinates lost the ECX register input entirely (only
showed in_EAX and unaff_EDI). Verified ECX = x_in from the prologue
disasm `AND ECX, 0xFFFF_0000` at 0x542B1A and from the dispatcher's
`MOV ECX, [ESI+0x8]` setup at every call site. Recorded as failure
mode #3 in feedback_ghidra_caller_frame_reads.md.
ClipContext, helpers, and all 14 case bodies use the existing Fixed
type (Fixed::floor / Fixed::div / Fixed::mul_raw)β¦
refactor: type FrameCache + Sprite/SpriteBank subframe-cache layoutsβ
Pre-work for porting slot 33 (get_sprite_frame_for_blit) to pure Rust.
All changes are type-only β no logic differences. Verified by all 26
unit tests + 15 headless replay tests passing.
- Rename `SpriteBufferCtrl` -> `FrameCache` with the LRU bookkeeping
fields typed (write_head/wrap_marker/tail_entry/head_entry/entry_count).
Add `FrameCacheEntry` (16-byte header). Note: `frame_idx@+8`,
`owner@+0xc` β the prep-session docstring blueprint had these swapped;
verified from the 0x4FA950 epilog and the eviction call's `MOV ECX,
[EAX+4]` (which loads the owner from `entry+0xc` for the thiscall).
- Rename `SpriteCache::buffer_ctrl` -> `frame_cache: *mut FrameCache`.
- Define `SpriteSubframeCache` (Sprite-side, decoded_ptr@+4) and
`SpriteBankSubframeCache` (bank-side, decoded_ptr@+8) as separate
structs with asserted offsets β sharing one type would corrupt one
of the two paths.
- Define `SpriteBankBboxEntry` (subframe_idx + decoded_offset + bbox).
- `Sprite::secondary_frame_β¦
refactor: extract font module, type GfxDir pointers, rename DisplayVtableβ
Three concurrent cleanups bundled together:
- Extract the font implementation (~830 lines) from display/vtable.rs
into a new display/font.rs module: Font struct (renamed from
FontObject), font_extend, font_set_palette_impl, font_get_info_impl,
font_get_metric_impl, font_set_param_impl, font_load_from_gfx, and
GlyphEntry. vtable.rs is now 870 lines lighter and only contains
vtable definitions and slot wrappers.
- Replace `*mut u8` with typed `*mut GfxDir` across the GfxDir call
graph: LayerSprite.gfx_dir, PCLandscape.{level,water}_gfx_dir,
DDGameWrapper.{primary,secondary}_gfx_dir, slot 31 load_sprite,
slot 34 load_font, slot 37 load_sprite_by_layer,
gfx_dir_find_entry, gfx_resource_create, call_gfx_find_and_load,
call_gfx_load_and_wrap, call_gfx_load_image, plus the matching DLL
trampolines and the inner transmuted vtable[2] cached-load function
pointer in three places. Also renames the misleading local names
`land_layer`/`water_layer` to `land_dir`/`water_dβ¦
refactor: rename mislabeled tile-bitmap queue command and slot 11β
Trying to trap DisplayGfx vtable slot 11 as dead code revealed it is in
fact reachable: RenderDrawingQueue case 0xD dispatches into it, and the
queue entries come from our own Rust trampoline for what was labelled
RQ_DrawPixel. Both names were wrong end-to-end β the function enqueues a
tile-cached bitmap draw (only known producer: CTaskLand::RenderLandscape),
not a single pixel.
Renames, byte-format unchanged:
WA 0x541D60: RQ_DrawPixel β RQ_EnqueueTiledBitmap
WA 0x56B8C0: DisplayGfx__CreateBitmap β DisplayGfx__DrawTiledBitmap
Rust DrawPixelCmd β DrawTiledBitmapCmd, with corrected field names
(x_pos/y_pos/color β y_fixed16/source_descriptor/x_fixed16)
command_type::DRAW_PIXEL β DRAW_TILED_BITMAP
trampoline_draw_pixel β trampoline_enqueue_tiled_bitmap
vtable slot 11 create_bitmap β draw_tiled_bitmap
The slot 11 docstring now captures the three-phase semantics (lazy
allocate tile surfaces, populate from source, clipped blit) so a future
port has a blueprint.
Co-Authoredβ¦
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>
feat: port DisplayGfx slots 4 (set_layer_color) and 6 (get_sprite_info)β
Command type 0xB is a crosshair draw, not a scaled sprite. The enqueue
function at 0x541ED0 writes [type, layer, color_fg, color_bg, x, y, 0],
dispatched by RenderDrawingQueue case 0xB to DDDisplay::draw_crosshair.
Also add DRAW_OUTLINED_PIXEL (0xC) constant.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port PaletteContext__MapColor to Rust with global hookβ
Pure Rust reimplementation of the palette color mapping function
(0x5412B0). All 10 callers now use the Rust version via MinHook detour.
Uses raw pointer ops to match WA's cache overflow behavior (cache_count
can reach 256, writing past the cache array into the dirty field) and
to avoid noalias miscompilation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>