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>
chore: fix clippy warnings, use windows-sys for Win32 types
Port BitGrid__BlitSpriteRect (0x4F6910) — the core sprite/bitmap
blitting function used for both rendering and collision mask
construction. Hooked via MinHook with fallthrough to original for
unsupported modes.
Sprite blit module (sprite_blit.rs):
- Direct copy (mode 0) and color-table/transparency (mode 1)
- 8 orientation modes (mirror X/Y/XY, 90° rotations)
- Destination clip rect support
- GIF test image loader with transparent index normalization
18 snapshot tests captured from WA's native function verify
pixel-perfect output. All 15 headless replay tests pass (simulation
checksums match), confirming correctness for collision masks.
Also ports DDDisplay::is_sprite_loaded (slot 32) and
DDDisplay::draw_via_callback (slot 21), bringing ported DDDisplay
methods to 13 of 38. Splits DisplayBase _buf_008 into named
sprite_array_0/1/2 fields.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: rename openwa-wormkit crate to openwa-dll, output openwa.dll
The project hasn't been a WormKit DLL for a long time — it requires a
custom launcher. Rename the crate to openwa-dll with output openwa.dll
to match the rest of the naming convention (openwa-core, openwa-launcher).
Updates all references across Cargo.toml, Rust sources, PowerShell scripts,
CLAUDE.md, skill files, and docs. Replaces stale "wormkit hook/shim"
comments with accurate descriptions. Three remaining "WormKit" references
are legitimate (refer to the actual WA modding framework).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add openwa-config crate with WA installation locator
Centralizes game path discovery into a new openwa-config crate.
find_wa_dir() checks OPENWA_WA_PATH env var then falls back to
HKCU\Software\Team17SoftwareLTD\WormsArmageddon (PATH value).
Removes all hardcoded Steam library paths from launcher, test runner,
and scheme_parse integration test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The offline harness (LoadLibraryExA-based WA.exe loading) was an early
experiment superseded by DLL injection + replay testing. It hasn't been
used since initial development and adds maintenance overhead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add #[derive(FieldRegistry)] proc macro for auto-generated field maps
New openwa-derive crate with FieldRegistry proc macro that generates
HasFieldRegistry impls for #[repr(C)] structs. Uses offset_of!() and
size_of() for compiler-verified offsets. Fields prefixed _unknown/_pad
are automatically excluded.
Applied to 5 key structs: DDGame, DDGameWrapper, GameSession, CTask,
CTaskWorm. Each now has a field_registry() -> &'static StructFields
method for runtime offset-to-name lookups.
This lays groundwork for typed vtable derive macros using the same
proc macro infrastructure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add structured address registry with runtime pointer identification
Replace flat pub const address definitions with define_addresses! macro
that generates both compile-time constants and a global queryable registry
via the inventory crate. ~200 addresses now have structured metadata
(kind, calling convention, class name).
Key additions:
- registry.rs: AddrEntry types, sorted lookup table, query API
(lookup_va, vtable_class_name, format_va, detect_pointer)
- define_addresses! macro: class blocks group vtable+ctor+vmethods,
standalone entries for functions/globals/data
- detect_pointer() in mem.rs: identifies static addresses by name,
heap objects by vtable class name
- classify_pointer() detail strings now include registry names
Eliminates three duplicated vtable→name mappings in snapshot.rs,
debugui/app.rs, and validation/mod.rs. All 488 existing va::* call
sites compile unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: use chrono for timestamp in test runner
New crate: openwa-test-runner — discovers and runs all headless replay
tests, compares output logs byte-for-byte, prints pass/fail summary.
Concurrency infrastructure:
- Per-instance event names (OpenWA_HooksReady_{pid}) in launcher + DLL
- Configurable OpenWA.log path via OPENWA_LOG_PATH env var
- Per-instance playback.thm paths to avoid replay map data races
- 1s launch stagger for concurrent mode stability
Usage: openwa-test [-j N] [--no-build] [filter]
Default is serial (j=1). Concurrent mode (-j 4) gives ~3x speedup
but has a known flaky race in WA.exe startup (needs procmon investigation).
Also normalizes CRLF in log comparison for cross-platform robustness.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
chore: update Cargo.lock for debug-cli/debug-proto crates
Steam registry lookup was unreliable from a 32-bit process (WoW64 redirection
issues, key may not exist). Replace with a short list of hardcoded common Steam
locations; OPENWA_WA_PATH and explicit CLI arg remain the preferred override.
Also add start.ps1 (build + launch) and update start-debug.ps1 to build with
--features openwa-wormkit/debug-ui, which is required to enable the debug UI
overlay at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: replace WormKit injection with custom launcher (openwa-launcher)
New binary crate `openwa-launcher` creates WA.exe in a suspended state,
injects openwa_wormkit.dll via CreateRemoteThread(LoadLibraryA), waits for
DllMain to complete (all hooks installed), then resumes the main thread.
This guarantees hooks are in place before a single WA instruction runs and
requires zero modifications to the game folder.
- WA.exe location: CLI arg > OPENWA_WA_PATH env var > Steam registry
- DLL location: always next to the launcher executable
- Forwards all CLI args (replay file etc.) to WA.exe
- Supports --minimized flag (used by replay-test.ps1)
- Remove deploy.ps1 / deploy-debug.ps1 (no longer needed)
- Update replay-test.ps1 to build + run via launcher; removes DLL copy step
- Update start-debug.ps1 to invoke launcher directly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat: add egui debug UI window with entity census, inspector, and log panel
Spawns a floating egui/eframe window in the same process as WA.exe (DLL
injection) when OPENWA_DEBUG_UI=1. Shows a live entity census from the
SharedData hash table, a struct inspector with navigation history, and a
scrollable log stream.
- New crate: openwa-debugui (egui 0.28 + glow backend, winit any-thread)
- Entity type names resolved via vtable map with ClassType fallback
- Children tree reads sparse slot array (children_size = high-watermark)
- Add CTASK_MINE/OILDRUM/CLOUD_VTABLE constants to address.rs (derived
from runtime vtables: delta 0x6C0000)
- debug-ui feature gate in openwa-wormkit; deploy-debug.ps1 / start-debug.ps1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor: merge openwa-types and openwa-lib into unified openwa-core crate
The split between openwa-types (pure types/addresses) and openwa-lib
(rebase/wa_call/typed wrappers) was arbitrary and added friction when
deciding where new code belongs. Merged both into openwa-core.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: replace PlayFeSfx and PlayFanfare_Default with full Rust implementations
Convert two speech passthrough hooks into full replacements that build
file paths in Rust via heapless::CString and delegate WAV playback to
WA's WavPlayer functions through naked asm wrappers (WavPlayerHandle).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: rename sprite.rs to render.rs, make install() consistently safe
Rename replacements/sprite.rs to render.rs to better reflect its scope
(RenderQueue enqueue, trail drawing, crosshair). Wrap unsafe hook calls
internally so all install() functions share a consistent safe signature.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: add configuration system design and implementation plan
Design doc and implementation plan for porting WA's configuration
subsystem (Theme I/O, Registry, GameInfo, CrashReportURL) to Rust.
These were used to guide the config.rs implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: add openwa-lib crate with typed WA function wrappers
Move rebase + wa_call primitives to shared openwa-lib crate. Add
zero-cost handle types (CWndHandle, CDialogHandle) with methods and
a wa::frontend module that encapsulates inline asm for PaletteAnimation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add openwa-wormkit DLL with FrontendChangeScreen replacement
New crate openwa-wormkit (wkOpenWA.dll) that replaces WA.exe functions
with Rust reimplementations, starting with FrontendChangeScreen (0x447A20).
- Naked ASM trampoline captures ESI (implicit dialog this pointer)
- Full reimplementation of both code paths (init + normal EndDialog)
- ScreenId enum expanded to 35 variants with correct semantics:
screen IDs (main-loop dispatch targets) vs result codes (sub-dialog
exit values like offline/online pairs 35/36 and 37/38)
- New globals: g_CurrentScreen (0x6B3504), g_SkipToMainMenu, etc.
- New addresses: Frontend__MainNavigationLoop, DoModal wrapper, etc.
- Validator: removed FrontendChangeScreen hook (conflicts with wormkit),
silenced constructor hooks to reduce log noise
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(validator): add function hooking via MinHook and vtable swapping
Two-layer hooking system replaces the failed retour-rs approach:
- VTable hooks: swap function pointers in .rdata for CTask virtual methods
- MinHook inline hooks: FrontendChangeScreen, CTask::Constructor,
CGameTask::Constructor — handles SEH prologues correctly
Also adds .cargo/config.toml to default target to i686-pc-windows-msvc.
Tested in-game: all hooks fire correctly, game runs normally.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add offline test harness that loads WA.exe and calls its functions
Loads WA.exe into a 32-bit test process via LoadLibraryExA with
DONT_RESOLVE_DLL_REFERENCES, patches the IAT (445 imports), initializes
the statically-linked MSVC CRT heap, and successfully calls WA.exe
functions including malloc and CTask::Constructor.
9 tests: PE loading, vtable validation, prologue checks, IAT patching,
malloc wrapper call, and CTask constructor call with SEH support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ASLR rebasing (GetModuleHandleA) — WA.exe loads at 0x90000, not 0x400000
- Replace retour constructor hooks with global pointer polling (10s delay)
Hooks crashed due to retour trampoline splitting SEH prologues
- Fix DDGameWrapper constructor: __stdcall with 7 params, not __thiscall
Confirmed via RET 0x1c and stack parameter access patterns
- Fix CTask constructor: __stdcall with 3 params, not __thiscall
Confirmed via RET 0xc
- Add G_GAME_SESSION (0x7A0884) global for polling DDGameWrapper at runtime
- Fix DDGameWrapper struct: +0x4C0 is not GfxHandler, +0x4D4 is sound ptr
- Remove retour dependency (no longer needed, builds on stable Rust)
- Add missing prologue bytes: 0x64 (FS: SEH), 0x85 (test), 0x8D (lea)
Results: 78/78 static + 6/6 deferred = 84/84 all passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bootstrap the Rust type foundation for WA 3.8.1 reverse engineering:
- Cargo workspace with openwa-types crate
- Enums translated from wkJellyWorm: ClassType (34), TaskMessage (83+),
Weapon (71), SoundId (126), SpriteId (696)
- Fixed-point math newtype (16.16 format, 0x10000 = 1.0)
- CTask and CGameTask struct stubs with compile-time size assertions
- 80+ known addresses: vtables, constructors, gameplay, rendering, sound
- RE documentation: task hierarchy, vtable layouts, memory map
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>