As of: 2026-04-22
Branch: 2026-ios
Eleven days of work (2026-04-12 – 2026-04-22). Newest first:
iOS port Phase 2D steps 1+2 verified — snowgoons renders correctly on iOS (2026-04-22) — log cabin (stone base + wood walls + blue windows + snowy roof), tree properly occluded by hedge, snowgoon/player character in the center, snowy ground at correct depth relative to the cabin, all visible on the iPhone 17 Pro Simulator. Step 1 (textures): PixelMap::GetRoot() + GetRootPixelBuffer() inline accessors expose raw RGBA to backend_metal.mm's per-root MTLTexture cache, MTLSamplerState linear/linear + repeat/repeat matching GL, 1×1 white fallback so [[texture(0)]] is always bound, MSL fragment samples when u.use_tex != 0. Step 2 (z-buffer): original "12 tris flat" read was a z-sort artifact (no depth attachment in the render pass + GL-style [-1, 1] NDC.z against Metal's [0, 1] expected range clipping near-plane geometry), not a camera/frustum issue; added Depth32Float MTLTexture sized to drawable and reused across frames, MTLDepthStencilState (LessEqual, writes enabled), depthAttachmentPixelFormat in the pipeline state, flipped Mat4Perspective to Metal's [0, 1] convention. Phase 2D step 3: lockstep render parity diff vs. Linux. Phase 3 queued: touch input + on-screen HUD.
iOS port Phase 2C-B verified + landscape orientation fixed (2026-04-22) — WF's geometry renders through Metal on iOS in landscape; engine thread owns the frame end-to-end (WFMetalView is a pure layer host, backend_metal.mm's WFIosRenderBegin(r,g,b)/WFIosRenderEnd() bridge wraps nextDrawable → cmdbuf → encoder → present+commit), Info.plist is landscape-only and WFIosSetSurfaceSize confirms 2622x1206 (aspect=2.174), but xcrun simctl io screenshot captures the hardware framebuffer in portrait so codemagic.yaml post-processes with sips -r 270. Seven commits past 2C-A: engine-thread-owns-frame rewrite, QuartzCore/CAMetalLayer.h import, one-shot fprintf tracers (diagnosed cpu=0 tris, encoder=live root cause), replace renderpoly3d_stubs.cc Phase-2B2 no-ops with real gfx/glpipeline/rend{f,g}{c,t}{l,p}.cc impls, surface-size tracer + periodic tris-per-frame tracer, sim-verify screenshot rotation. Steady-state is only ~12 triangles/frame; most geometry out of the camera's current view.
iOS port Phase 2C-A verified — engine main loop boots on iOS for the first time (2026-04-22) — snowgoons loads end-to-end on iPhone 17 Pro Simulator (37 objects, 37 textures, actors, zforth camshot module), game loop runs silently emitting normal BungeeCameraHandler::GetWatchObject chatter; seven bring-up commits past Phase 2B3: GL-stub TU for pixelmap.cc's 7 GLES symbols (no OpenGLES.framework link), stdio dup2 to NSTemporaryDirectory()/wf.log + signal trap (Android-parallel), HAL-boundary fprintf(stderr) tracers, ma_engine_config::noDevice=MA_TRUE to unstick the CoreAudio RPC deadlock on Codemagic's headless Mac mini, Display::PageFlip timeval-delta fix, and codemagic-setup.md capturing the GitHub App + webhook wiring (the ~2-week manual-trigger era is over). Sim-verify still cornflower blue because Phase 2C-B hasn't wired SetCurrentEncoder per-frame yet.
iOS port Phase 2B3 verified — Metal RendererBackend compiles and links (2026-04-22) — new hal/ios/backend_metal.mm implements the RendererBackend vtable method-for-method against backend_modern.cc, with MSL vertex+fragment shaders inline and runtime-compiled via [MTLDevice newLibraryWithSource:] (no .metal file + Xcode build phase, build stays Codemagic-native). backend_factory.cc routes RendererBackendGet() through MetalBackendInstance() on iOS via WF_TARGET_IOS ifdef; CMake pulls that one file in as a one-off since the rest of gfx/glpipeline/ hard-depends on GLES. Single BGRA8Unorm pipeline state, per-vertex float3 pos/color/normal + float2 uv matching the GL Vert layout. Nothing drives the backend yet — lazy init + MSL compile run only when something calls DrawTriangle — so the Codemagic sim-verify still shows cornflower blue and no MetalBackend ready log line. Phase 2C next: wire MetalView's CADisplayLink tick to hand the backend a live MTLRenderCommandEncoder via SetCurrentEncoder/ClearCurrentEncoder, and boot the engine's main loop.
iOS port Phase 2B2 verified — full engine links on iOS (2026-04-22) — all ~120 engine sources now compile and link for arm64 iOS Simulator; wf_game.app launches on iPhone 17 Pro Sim with MetalView init, device=Apple iOS simulator GPU and cd.iff opened, size=174080 in the unified log, cornflower blue clear still on-screen. Three bring-up commits past Phase 2A: widen iOS WF_DIRS to the full engine minus X11/GL dirs (gfx/gl, gfx/glpipeline stay out, hal/linux replaced by hal/ios); compile audio/linux/*.cc as Obj-C++ since miniaudio on Apple unconditionally includes AVFoundation.h (documented integration pattern, not a workaround); add AVFoundation/AudioToolbox/CoreAudio frameworks plus an iOS-only stub file for the 8 GL-backend RenderObject3D::RenderPoly3D* methods that Material::Get3DRenderObjectPtr's dispatch table references. Phase 2B3 next: real RendererBackend subclass in backend_metal.mm + MSL port of the phong+fog shaders.
iOS port Phase 2A verified — Metal is alive on Sim (2026-04-22) — WFMetalView (new hal/ios/metal_view.mm) hosts a CAMetalLayer as the root view, owns a MTLDevice + command queue, and a CADisplayLink ticks a clear-to-color render pass every frame. Codemagic's sim-verify screenshot confirms (99, 148, 237) cornflower-blue across 3.1M of 3.16M pixels — the MTLLoadActionClear color reached the Simulator's framebuffer via a real present-drawable loop. MTLCreateSystemDefaultDevice() returns "Apple iOS simulator GPU" at @3x scale. Metal + MetalKit frameworks added to iOS link libs. Phase 2B next: port GLSL shader to MSL, real RendererBackend subclass with vertex batching, pull gfx/ + game/ into iOS WF_DIRS, first snowgoons triangle.
iOS port Phase 1 verified end-to-end (2026-04-22) — Codemagic now not only builds the wf_game.app but boots it on an iPhone 17 Pro Simulator inside the same Mac runner and grep-confirms HALGetAssetAccessor().OpenForRead("cd.iff") succeeded via a log show-captured NSLog; zero user Macs involved in the verify path. Three follow-ups on top of the initial sim-verify step nailed down Apple-specific quirks: xcrun simctl device picker returning iPhone 17 Pro UDID from list -j JSON, log show --last 60s instead of log stream | timeout (macOS has no GNU timeout), and moving artifact files into a sim-artifacts/ subdir so Codemagic's artifact resolver picks them up.
iOS port Phase 1 build green (2026-04-22) — Codemagic ships a wf_game.app bundle for iOS Simulator arm64; entire HAL-plus-core build compiles + links under Apple clang and bundles cd.iff + level0.mid as resources. Four bring-up commits past Phase 0: route pigsys through cf_linux.h on iOS (Darwin is POSIX), widen -Wno-register to iOS (C++17 removed the keyword, endian.cc still uses it), drop extern "C" from _PlatformSpecificInit forward-declaration to match Linux/Android C++ linkage, and retire two wfWindowWidth/Height references in platform.mm since gfx/ is excluded from the Phase 1 iOS build.
iOS port Phase 0 complete (2026-04-21) — Codemagic cloud-Mac pipeline end-to-end green on 2026-ios, Xcode build now reaches per-source compilation and stops at the expected iOS HAL gap (GL/gl.h not found in gfx/renderer.hp); five bring-up commits unblocked it: codemagic.yaml + two schema fixes, force WF_PHYSICS_ENGINE=legacy on Xcode (Jolt-on-Xcode deferred), restore zforth sources lost to a bare-zforth gitignore pattern + drop the local-path python-tui-lib submodule, extend the Forth-only mobile policy to iOS so WAMR's x86_64 invokeNative_em64.s stops being in the build graph.
Snowgoons renders fully via textile-rs + levcomp-rs pipeline (2026-04-19) — two user-visible regressions closed this session: textile-rs's 24-bit TGA path was routing rgba_555(r,g,b,255) into its a > 170 → return 0 translucent-black sentinel, blanking the House shake-roof texture (11cbca7 added a direct BGR555 fast path matching C++ BR_COLOUR_BGRA(r,g,b,0)); and levcomp-rs's earlier 21ca707 audit-fix was too eager — unconditionally preferring nested STR over DATA for any I32 field with a pipe-separated enum list — demoting Omni01/Omni02 lightType from DIRECTIONAL (DATA=0) to AMBIENT (STR="Ambient"→1 due to a Blender-exporter bug at export_level.py:1077), removing both directional lights at runtime and dimming the scene (4c3e652 gated the STR-lookup on ShowAs ∈ {DROPMENU, RADIOBUTTONS} per iff2lvl's oad.cc:1245-1276); LVL content-diff is now 3 uninitialized room-struct pad bytes (same new char[] family as the _PathOnDisk.base.rot "Euler garbage" earlier in the day), shadows + textured House roof both restored in-game.
levcomp-rs LVL diff down to 5 bytes (2026-04-19) — the 78-byte player-script leading-newline source mismatch (closed by 88b9df7 prepending \n to the joystick-input Script STR in snowgoons.lev to match oracle byte-for-byte) dropped the LVL delta from 83 → 5 byte-diffs, a further 94% reduction on top of the 97% the two-phase refactor already achieved; remaining 5 bytes are the last unpredictable heap-garbage pads (3B rooms + 2B stray common-block unknowns) that no single mirror rule fixes consistently across objects — see the updated scorecard in levcomp-common-block-two-phase.
textile-rs wired into snowgoons.iff.txt (2026-04-19) — edaffb3 replaces the oracle-extracted perm.bin / rm0.bin / rm1.bin stopgaps in the LVAS text-IFF with explicit { 'ASS' $<packedID>l [ "<file>" ] } slots for each palette/texture/UV/cycle/mesh asset (palPerm.tga, Perm.tga/.ruv/.cyc, pal0.tga, Room0.tga/.ruv/.cyc, pal1.tga, Room1.tga/.ruv/.cyc, plus per-mesh .iff files); also a 24-bit TGA fast-path in textile-rs (11cbca7) fixed an invisible-roof renderer bug — see textile-rs-validation.
levcomp-rs LVL diff down 97% to 83 bytes (2026-04-19) — three commits on top of the two-phase refactor push cmp vs oracle from 2,772 → 83 bytes: (a) 8e2f244 — two-phase common-block emission + ObjectOnDisk pad heap-garbage mirror (2,772 → 141); (b) 21ca707 — OAD audit fix 1 (field_str_child_only accessor so I32 enum-label fields pick up the nested STR label instead of short-circuiting on a binary DATA's empty C-string, closing the CamShot Rotation/Position X/Y/Z 7-byte diff at obj[12]/obj[35]); (c) 0a37e20 — swap Actboxor01/02 OBJ chunks in snowgoons.lev to match oracle's .lvl order, closing the 40B obj-data diff plus 11B room-entries cascade and dislodging oracle-deps plan item 3 from deferred — remaining 83 bytes are 78B player-script leading-newline .lev-source mismatch, 3B room-pad heap garbage, 2B stray common-block unknowns; full scorecard in levcomp-common-block-two-phase.
OAD ButtonType audit written (2026-04-19) — cross-referenced all 29 wf_oad::ButtonType variants against iff2lvl's SaveStruct / SizeOfOnDisk / CreateCommonData to catch per-type emission bugs: found three (Int32 enum-label STR miss — landed 21ca707; ClassReference routes through object-name table instead of class table — latent; String emits raw bytes instead of 4-byte packed asset ID — latent) and four permissive-where-iff2lvl-asserts spots (Fixed16/Int16/Int8); 21 types already aligned — see oad-buttontype-audit.
levcomp-rs two-phase common-block refactor landed (2026-04-19) — executed the plan above: oad_loader::precompute_xdata_offsets runs Pass 1 across all objects × all OAD entries to seed the common area with XDATA blobs before any per-object COMMONBLOCK section gets added, matching iff2lvl's emission order; also mirrored two _ObjectOnDisk heap-garbage pad bytes (0x0B 0x08 and 0xBF) the oracle carries, so LVL payload length is now byte-identical to oracle (8628/8628, was −4) and total diff bytes dropped from 2,772 → 141 (94.9% reduction), with the remaining 141 all orthogonal to common-block emission (Actboxor01/02 swap 40B, player-script leading-newline source mismatch 78B, class defaults 7B, Room 1 heap-garbage pad 2B) — see plan scorecard in levcomp-common-block-two-phase.
levcomp-rs common-block two-phase plan written (2026-04-19) — levcomp-rs's .lvl output is 4 bytes short of oracle and only ~1352/3632 common-block bytes byte-identical, traced via iff2lvl code review to a phase-ordering mismatch (iff2lvl does all-objects XDATA → all-objects per-object COMMONBLOCK; we do single-per-object interleaved, so the greedy dedup scan finds different matches) — fix is a Pass-1/Pass-2 refactor, ~half-day, detailed in plan levcomp-common-block-two-phase.
textile-rs validation plan written (2026-04-19) — Rust port of the texture-atlas compositor wftools/textile/ already exists (~1.8K LOC, builds, CLI-parity) but has zero tests and no byte-identity verification; new plan lays out a snowgoons validation harness, gap closure against oracle PERM/RM1, and round-trip wiring to retire swap_lvl.py's texture dependency — see plan textile-rs-validation.
python-tui-lib extracted + embedded markdown help in git-branch-browser (2026-04-19) — Stood up ~/python-tui-lib (tuilib package, ~27K LOC copied from parking-space: ui primitives, widgets, doc_viewer, llm_picker, framework, panes, scripting DSL, games), rewrote 73 imports to the tuilib.* root, parameterized every parkingspace / parking-space-tui hardcoded path behind a new tuilib.APP_NAME symbol with app_cache_dir / app_config_dir / app_ssm_prefix helpers; vendored into WorldFoundry at vendor/python-tui-lib/ and wired ? in git-branch-browser.py to open git-branch-browser.help.md rendered via tuilib.ui.doc_viewer.renderer.DocViewer — see plan python-tui-lib-extraction.
snowgoons.iff.txt round-trips byte-identical (2026-04-19) — iffcomp-rs snowgoons.iff.txt now produces md5-identical bytes to the oracle snowgoons.iff (hash 3a3a985c…), which matters less as a one-off achievement and more as a regression anchor: every future iffcomp-rs / iffdump-rs / levcomp-rs change now has a single-byte-drift detector built in. The fix touched four layers — iffcomp-rs gained real top-level +/- arithmetic plus expression-accepting .offsetof 2nd-arg; iffdump-rs default wrappers match the oracle chunks.txt (plus L4–L9); two mis-nested chunks in snowgoons.iff.txt got relocated (LVL's final 48 bytes and the trailing ALGN were one level too shallow); and iffcomp-rs now scans file-include first-chunk headers so .offsetof(::'GAME'::'L4') resolves when L4 sits inside a [ "snowgoons.iff" ] include, which unblocks cd_snowgoons.iff.txt / cd_full.iff.txt. Side benefit: fetching the SourceForge wf-gdk CVS snapshot resolved the "how did the oracle get those bytes?" mystery — the historical iff.prp used .offsetof(X, -2048) for a pre-L4 file layout where LVAS sat at file offset 0x800, and binary +/- in the Bison grammar was never actually wired to the output stream (an empty printf trace), so the iffcomp-rs arithmetic fix is a new extension rather than a restoration — see iffcomp-offsetof-arithmetic.
git-branch-browser v2 shipped (2026-04-19) — Curses TUI now renders the repo's branch topology as a chronological waypoint pipeline with per-waypoint strata bars, sideways-fork detection, and three diff modes (vs parent / vs master / compare), with clean Ctrl+C handling in every input loop — see plan git-branch-browser.
Blender round-trip plays continuously, untextured (2026-04-19) — Nine exporter/compiler fixes (BOX3 frame, Mesh Name gating, authored BOX3 preservation, enum-label import, path+channel serialization, oadFlags MovesBetweenRooms bit, mesh-bbox extension disable, first-keyframe t=0 export, _RoomOnDisk 36-byte struct-alignment padding) take snowgoons-blender.iff through a continuous per-frame loop with audio + camera + no assertions; next-up oracle dependencies (texture atlases, MeshName asset-ID packing, CamShot BOX3 gating) tracked in blender-roundtrip-oracle-dependencies.
Android port closure (2026-04-18) — Branch hits its close criterion (polished sideloadable APK) with only launcher icons + one stale build.gradle.kts comment left and Play Store / keystore / R8 / splash explicitly out of scope — see closure audit.
Android audio — Für Elise on snowgoons (2026-04-18) — Desktop miniaudio + TinySoundFont ports to Android as-is via an HALGetAssetAccessor() memory loader (audio_stub.cc retired, music.cc reworked); follow-up plan audio-assets-from-iff routes audio through cd.iff like every other asset class.
Android post-boot polish (2026-04-18) — Snowgoons is a fully playable APK on stock arm64: viewport/projection aspect from real EGL surface (aebbb15), pause/resume preserving EGL context + events-during-suspend (0b19119), zForth here bootstrap fix for if/else/then (62451a1), and an on-screen d-pad + A/B HUD (c20e56e, TV-mode suppressed).
Snowgoons rendering on Android phone (2026-04-18) — Phase 3 step 7 ✅: sideloaded debug APK boots snowgoons on physical arm64 via NativeActivity + EGL 3.0 + AAssetManager-backed cd.iff, unblocked by four pre-flight fixes (Forth shell bootstrap, graceful missing-engine no-op, 4096² framebuffer cap, GLSL ES int precision) and on-device wf.log since adb logcat wasn't reachable.
Snowgoons joystick control restored (2026-04-17) — On-disk snowgoons.iff/cd.iff still had the pre-671de1e ?cam-helper director that zForth's minimal bootstrap couldn't compile; byte-preserving re-patch (a7ef46e) landed the inlined three-block form the current patch_snowgoons_forth.py produces.
Window-close shutdown stability (2026-04-17) — mesa.cc now handles WM_DELETE_WINDOW and rest_api.cc registers RestApi_Stop via sys_atexit; X11 close button exits cleanly instead of aborting.
Graphics — retire immediate-mode GL / Android Phase 0 (2026-04-18, complete) — Modern VBO + GLSL 330 / GLES 300 es shader backend is the sole GL path on Linux and Android (legacy fixed-function retired at ff589c8, −541 LOC net across 16 files; tag pre-legacy-gl-retire at 807d1ea preserves the last backend_legacy.cc commit).
Audio (Phases 1–5 complete) (2026-04-17) — miniaudio + TinySoundFont vendored with per-level level<N>.mid music, fire-and-forget SFX, and 3D positional playback audible in snowgoons, but only via scripting_lua.cc closures — mailbox-wired audio API for the other seven engines is deferred.
Android port (Phases 0+1+2 complete; Phase 3 steps 1–6 done) (2026-04-18) — Phase 0 retired legacy GL, Phases 1+2 landed CMake+NDK build / HAL lifecycle seam / AssetAccessor, and Phase 3 added NativeActivity + EGL 3.0, a Gradle project (AGP 8.5.2, leanback manifest, arm64-v8a, min 21 / target 34), gamepad + touch with TV-mode detection, and AAssetManager-backed cd.iff — only step 7 (device smoke test) remained at the time, since closed.
Blender ↔ level round-trip (2026-04-17) — levcomp-rs compiles .lev → .lvl end-to-end and the Blender plugin round-trips 152/152 OAD fields with Phase 2c mesh bboxes / packed asset IDs / asset.inc landed — real path/channel keyframes are the last remaining piece.
Level pipeline proof (2026-04-17, in progress) — Phases A+B+C done (primitives.lev/whitestar.lev compile through the pipeline; wf_oad has a common.oad fixture test; levcomp decompile round-trips snowgoons' 36 objects with an 8-byte common-block delta), with D–E (decompile 4 source-less levels, multi-level cd.iff) gating the common.inc rearrangement the deferred ScriptLanguage OAD plan needs.
Tooling and plans (2026-04-17) — engine/ reorganised to top-level; REST API box PoC landed; iOS plan written (blocked on Android); CLI level override (-L<path>) confirmed; IFF lineage + MIDI-source investigations filed.
Scripting system (2026-04-16) — Seven engines smoke-tested end-to-end in snowgoons (Lua 5.4, Fennel, QuickJS, JerryScript, WAMR, Wren, zForth) with Lua optional (WF_LUA_ENGINE=lua54|none) and wasm3 retired in favour of WAMR — five alternate Forth backends build+link but aren't end-to-end tested, WAMR AOT deferred.
Dead-code removal (2026-04-15, closed 2026-04-18) — Batches 1–7 complete (wfsource/source/ 64,252 → 36,199 lines, −43.7%) with Batch 8 (physics/wf/, ~1,700 LOC) deferred until Jolt parity on a second level and hal/_list / _mempool migration left as future opt-in.
Jolt Physics (2026-04-14) — Integrated as default (WF_PHYSICS_ENGINE=jolt) with the five-step plan complete (SIGABRT, zombie bodies, authority model, vertical pop, 60 s soak); legacy physics/wf/ retained until parity on a second level.
Steam (Phases 1+2) (2026-04-12) — Steamworks SDK lifecycle wired into HAL + PageFlip with Steam Input ORing into _JoystickButtonsF each frame (WF_ENABLE_STEAM=1; SDK not committed); Phases 3 (depot) and 4 (store page) deferred.
| Date | Plan | Status | Summary |
|---|---|---|---|
| 2026-04-16 | Plan: Blender ↔ Level Round-Trip | In progress — step 6 🟡 (plays, untextured) | Steps 1–5 complete. 2026-04-19 (c1550f7 et al.): nine exporter/compiler fixes took snowgoons-blender.iff from "asserts at frame 0" to "runs continuously with audio + camera, no assertions" — notably real path+channel serialization, the oadFlags MovesBetweenRooms bit, and the _RoomOnDisk 36-byte struct-alignment padding. Remaining oracle dependencies (texture atlases, MeshName asset-ID packing, etc.) are a separate plan. |
| 2026-04-19 | Plan: Blender round-trip — oracle dependencies | Not started — inventory only | Enumerates what my pipeline currently reuses verbatim from the oracle snowgoons.iff: LVAS wrapper + RAM + ASMP + PERM + RMn asset chunks, plus a handful of within-LVL diffs (MeshName asset-ID packing, CamShot BOX3 defaults, Actboxor01/02 index order, NULL-entry sentinel, _PathOnDisk.base.rot uninit bytes). Policy: mirror the oracle exactly before deviating. Texture atlas pipeline (textile port) is the biggest piece and will be a follow-up plan. |
| 2026-04-19 | Plan: textile-rs validation & round-trip integration | Phase 1 done — end-to-end pipeline working | Seven fixes landed: 16-bit BGR555 TGA fast path (f3da913); 24-bit TGA fast path matching C++ BR_COLOUR_BGRA — also fixes invisible-roof renderer bug (11cbca7); align_to_size_multiple unit mismatch that made -alignx=w -aligny=h always fail (a45194c); paly=-1 for 16-bit textures matching C++'s unconditional division (72a4af9); levcomp-rs --textile-ini flag replicating prep ini.prp (a45194c); per-asset ASS expansion in lvas_writer replacing [ "perm.bin" ] placeholder with individual { 'ASS' $<id>l [ "file" ] } includes per iff.prp semantics (a45194c); textile-rs output files replace the oracle-extracted .bin placeholders (edaffb3). PERM chunk byte-identical (29492/29492); RM1 atlas content 31/31 textures byte-identical per-texture extraction. Game runs with textile-rs outputs + levcomp-rs .lvl, House roof + directional shadows all rendering correctly. |
| 2026-04-19 | Plan: levcomp-rs two-phase common-block emission | Phase A + follow-ups done — 3 heap-pad bytes remain (99.9% closed, content-diff zero) | Five commits land the refactor plus four follow-up fixes: (a) 8e2f244 — two-phase emission + _ObjectOnDisk heap-garbage type/rot pads (141 diffs); (b) 21ca707 — audit-fix 1, field_str_child_only accessor for I32 enum-label lookup (134 diffs; closed CamShot Rotation/Position X/Y/Z per obj[12]/obj[35]); (c) 0a37e20 — Actboxor01/02 OBJ-chunk swap in the .lev (83 diffs); (d) 88b9df7 — prepend \n to joystick-input Script STR in snowgoons.lev to match oracle byte-for-byte (5 diffs); (e) 4c3e652 — gate I32 STR-lookup on ShowAs ∈ {DROPMENU, RADIOBUTTONS} per iff2lvl's oad.cc:1245-1276 (3 diffs; also the fix that restored directional lighting in-game — 21ca707's too-eager heuristic had demoted Omni01/Omni02 to AMBIENT). Remaining 3 bytes are all uninitialized _RoomOnDisk pad from iff2lvl's new char[] allocator (Room 0 trailing pad + Room 1 struct-alignment pad) — same new char[size] family as the _PathOnDisk.base.rot Euler garbage; no deterministic mirror rule possible. Content-diff is effectively zero; structural identity achieved. |
| 2026-04-17 | Plan: Prove all 7 level pipelines before breaking common.inc | In progress — Phases A+B done | Phase A (534ead7): primitives.lev + whitestar.lev compile through iffcomp-rs → levcomp-rs (skips OBJ chunks with no Class Name — Max aim-point helpers). Phase B: wf_oad/tests/fixtures/common.oad committed; parse_common_oad test asserts 14 entries + Script field; 6 tests pass. Phases C (decompile subcommand), D (4 source-less levels), E (multi-level cd.iff) remaining before the gated common.inc rearrangement that unblocks the ScriptLanguage OAD plan. |
| 2026-04-21 | Plan: iOS port (via Codemagic) | Phase 2D step 1 — textures on iOS | Snowgoons renders with textures on iOS: snowy ground + green hedges + bark tree branches + scene accents all visible. PixelMap::GetRoot/GetRootPixelBuffer accessors, per-root MTLTexture cache, MTLSamplerState (linear/repeat matching GL), 1×1 white fallback for untextured draws, MSL fragment now samples when u.use_tex != 0. 2 textures uploaded (#0, #1 256x256). ~12 tris/frame from narrow camera frustum — scene is 2 rooms with a few dozen objects, most not currently in view. Phase 2D step 2: camera-reach + full-scene parity vs. Linux. |
| Date | Plan | Status | Summary |
|---|---|---|---|
| 2026-04-18 | Plan: Android launcher polish — adaptive-icon XML | Not started | Goal: Layer res/mipmap-anydpi-v26/ic_launcher.xml adaptive-icon XML on top of the legacy mipmap PNGs that just landed, so Android 8+ renders rounded / themed / dynamic-shape forms via foreground + background drawables. Carry-over from the Android port closure audit. |
| 2026-04-18 | Plan: audio assets from iff | Not started | Goal: retire every filesystem / loose-file audio loader — MIDI, soundfont, SFX all come through cd.iff / level<N>.iff chunks like meshes and textures already do. Current loadAssetBytes(path) path stays behind a -DWF_AUDIO_DEV_LOOSE_FILES=1 opt-in for iteration. Requires new IFF chunk tags (MIDI/SFNT/SFX) + iffcomp-rs + levcomp-rs + Blender plugin updates. Unblocks the iOS port from needing its own asset-bundling pipeline. |
| 2026-04-17 | Plan: Steam release | In progress — Phases 1+2 done | Steamworks SDK lifecycle wired into HAL + PageFlip. Steam Input → EJ_BUTTONF_* merged in _JoystickButtonsF. WF_ENABLE_STEAM=1 build flag; SDK not committed (see vendor README). Phases 3 (depot) and 4 (store page) deferred. |
| 2026-04-17 | Plan: Mailbox-wired audio API | Not started | Goal: Every scripting engine can trigger music + SFX via mailbox writes. EMAILBOX_SOUND=3017 enum and OAD sfx0..sfx127 asset slots still exist; handler + slot loader were deleted in the 460a3fd dead-code sweep (pre-cleanup impl was Linux-stubbed). Phase A: restore _sfx[128] loader + EMAILBOX_SOUND handler that plays at the actor's position. Phase B: MUSIC_PLAY/MUSIC_STOP/MUSIC_VOLUME mailboxes; Lua closures become forwarders. Phase C (opt): named SFX_* constants. |
| 2026-04-22 | Plan: WF streams → platform filesystem sinks | Not started | Goal: give cbinstrm/cstreaming/ctest a real per-platform sink so iOS/Android DBSTREAM traffic lands in a file like Linux's terminal does today. Add HALOpenDiagnosticStream; linux returns std::cerr, iOS returns std::ofstream at NSTemporaryDirectory()/wf.log, Android returns one at externalDataPath/wf.log. Retires the raw-fd dup2 shim in the two native_app_entry.* files (crash handlers stay). Deferred behind the Phase 2C boot-and-debug loop. |
| 2026-04-16 | ScriptLanguage OAD field | Deferred — blocked on Blender+levcomp-rs level round-trip | Field added then reverted from common.oad to restore binary layout compat with existing compiled levels. Dispatch table + language param threading remain in engine (passing 0=Lua). Will re-introduce once all levels compile through Blender+levcomp-rs. |
| Date | Plan | Status | Summary |
|---|---|---|---|
| 2026-04-19 | Plan: python-tui-lib extraction | Closed 2026-04-19 | Goal: carve the reusable TUI subset out of parking-space into a standalone python-tui-lib repo and consume it from WorldFoundry. Four phases all landed same-day: tuilib repo stood up at /home/will/python-tui-lib (~27K LOC, 68 .py files, commits 2695044 / 1942141 / 82764dd), imports rewritten to tuilib.*, parkingspace-hardcoded paths parameterized via tuilib.APP_NAME, and WorldFoundry's git-branch-browser.py submodules it at vendor/python-tui-lib/ with a ?-key help overlay rendered by DocViewer (commit f75e7c7). Follow-on plans 2/3/4 (parking-space migration, logs.py + LogSource, worker-pool) remain separate. |
| 2026-04-16 | Plan: git-branch-browser — curses TUI for browsing a branch pipeline | Closed 2026-04-19 | Goal: A Python curses program at scripts/git-branch-browser.py that surfaces branch topology as a chronological waypoint pipeline with strata bars, sideways-fork detection, and three diff modes (vs parent, vs master, compare). v2 (~1260 LOC) shipped 2026-04-19; clean Ctrl+C handling in main loop, diff pager, and compare view verified under a pty. |
| 2026-04-16 | Plan: Android port | Closed 2026-04-18 | Phases 0+1+2 + Phase 3 steps 1–7 all landed: legacy GL retired (ff589c8, pre-legacy-gl-retire tag at 807d1ea), CMake+NDK build, HAL lifecycle seam, AssetAccessor, NativeActivity + EGL 3.0, Gradle project (AGP 8.5.2, leanback, arm64-v8a, min 21 / target 34), gamepad + touch with TV-mode detection, AAssetManager-backed cd.iff, on-device smoke test on arm64 phone. Post-boot polish shipped (viewport aspect, pause/resume EGL preservation, zForth here director fix, on-screen touch HUD). Remaining polish tracked as separate plans: android-launcher-polish and audio-assets-from-iff. |
| 2026-04-15 | Dead-code removal | Closed 2026-04-18 | Batches 1–7 landed (64,252 → 36,199 LOC, −43.7%). LOC claims verified against git history — Batch 5 03211f9 shows −20,967 across 208 files; e2dcc98 milestone records the 36,199 total. Batch 8 (physics/wf/ deletion, ~1,700 LOC) + hal/_list/_mempool migration (stretch) accepted at their estimates, deferred to future opportunistic commits. |
| 2026-04-16 | Plan: Lua engine is not special — make it optional | Done | scripting_lua.cc/hp extracted; WF_LUA_ENGINE=lua54|none added to build_game.sh; all lua_engine:: calls guarded; Fennel+none warns and forces lua54; stale scripting_wasm3.hp include removed. |
| 2026-04-16 | Engine directory reorganization | Complete | engine/ is now a top-level directory. wftools/wf_engine/ → engine/, wftools/vendor/ → engine/vendor/, wf_viewer/stubs/ → engine/stubs/, wf_viewer/include/ → engine/include/. wftools/ is now strictly dev tooling. |
| 2026-04-16 | Finish Jolt physics integration | Complete | Five-step plan: fix SIGABRT (JoltSyncFromCharacter), eliminate zombie kinematic bodies, lock WF↔Jolt authority model, fix 3 m vertical pop (feet vs centre offset), 60 s soak. Player walks on snowgoons floor. |
| 2026-04-15 | Lua engine fixes (#1–#6) | Complete | All 6 fixes: script cache, per-actor envs, Fennel precompile, debug gating, stdlib sandbox, coroutine continuations. Smoke-tested 2026-04-16. |
| 2026-04-15 | Align scripting plans to ScriptRouter | Complete | Phases A–E complete: all plan docs updated, JS/wasm3 renamed to js_engine/wasm3_engine namespaces, WAMR/Wren/Forth landed. All engine smoke tests passed 2026-04-16. |
| 2026-04-14 | WAMR (dev interp + AOT ship) | Complete | Phase 1 (classic interpreter) landed 2026-04-15; smoke-tested 2026-04-16 (GROUND, no crashes). Phase 2 (AOT) and Phase 3 (w2c2) deferred until ship targets are concrete. |
| 2026-04-14 | Forth scripting engine | Complete | All seven phases landed 2026-04-16. All six backends build and link; snowgoons.iff + cd.iff carry Forth scripts (\ wf sigil) via byte-preserving patcher. zForth is the default and smoke-tested; the five alternates (ficl, atlast, embed, libforth, pforth) are build-verified but not yet end-to-end smoke-tested. |
| 2026-04-14 | Pluggable JS engines (QuickJS / JerryScript) | Complete | Both engines landed 2026-04-14 with js_engine namespace. QuickJS and JerryScript both smoke-tested 2026-04-16 (snowgoons passes). |
| 2026-04-14 | Wren scripting engine | Complete | All phases complete: vendor, plug, dispatch, build, docs, patcher. Smoke-tested 2026-04-16 (GROUND, no crashes). |
| 2026-04-14 | WebAssembly (wasm3) | Retired 2026-04-16 | Initial wasm spike. WAMR reached parity; engine/vendor/wasm3-v0.5.0/ + scripting_wasm3.{hp,cc} deleted. WF_WASM_ENGINE=wamr is now the only wasm option. |
| 2026-04-14 | Fennel on Lua | Complete | Landed 2026-04-14. ; sigil, sub-dispatch inside lua_engine, vendored Fennel 1.6.1, minifier, codegen, snowgoons Fennel scripts. |
| 2026-04-14 | Vendor Lua 5.4 | Complete | Landed 2026-04-14. Lua 5.4.8 in engine/vendor/lua-5.4.8/, compiled directly from source, no system liblua5.4 dependency. |
| 2026-04-13 | Lua interpreter spike | Complete | Landed 2026-04-13; refactored 2026-04-15 to lua_engine namespace in ScriptRouter. Snowgoons player + director ported to Lua; player moves, cameras cut. |
| Date | Investigation | Status | Summary |
|---|---|---|---|
| 2026-04-23 | World Foundry / Project Velocity — Public History | In progress | An aggregation of externally available references to the World Foundry GDK and its predecessor game projects. |
| 2026-04-19 | Snowgoons build pipeline — Blender to running game | Working end-to-end. Validated 2026-04-19: ./wftools/wf_blender/build_level_binary.sh snowgoons produces wflevels/snowgoons.iff (md5 010b6b97a9019fdc14f07ad9127a9cf9, reproducible); wf_game -L wflevels/snowgoons-standalone.iff boots and plays. |
One-stop reference for every tool in the snowgoons build chain, in the order they actually run, with inputs, outputs, and what each one does. Useful when onboarding, debugging a mid-chain … |
| 2026-04-19 | OAD ButtonType audit — iff2lvl vs levcomp-rs | In progress | Purpose: cross-reference every ButtonType variant from wf_oad::ButtonType (29 total) against how wftools/iff2lvl emits it and how wftools/levcomp-rs/src/oad_loader.rs emits it, to catch … |
| 2026-04-19 | .offsetof arithmetic in iffcomp — oracle vs current behavior |
Recommendation accepted; iffcomp-rs arithmetic implementation in | Context: Reconstructing wflevels/snowgoons.iff.txt as a proper compile-source text-IFF file (mirror-first, deviate-later), chunk-by-chunk, so iffcomp-rs can produce byte-identical output … |
| 2026-04-19 | Email: _PathOnDisk.base.rot mystery bytes in oracle snowgoons.iff |
In progress | To: Kevin T. Seghetti (cc: original WF team — this is about wftools/iff2lvl/path.cc) From: Will, via Claude-assisted archaeology Subject: Do you remember what _PathOnDisk.base.rot … |
| 2026-04-18 | Android Port — Executable Size and RAM Usage | In progress | Branch: 2026-android Artifact: android/app/build/outputs/apk/debug/worldfoundry-debug.apk NDK: 26.2.11394342, arm64-v8a, -DCMAKE_BUILD_TYPE=RelWithDebInfo, then stripped by AGP … |
| 2026-04-18 | Closing the Android Port — Remaining Work | Playable APK shipped; this doc lists what's left to call it "done." | Branch: 2026-android |
| 2026-04-17 | IFF format lineage — EA IFF 85, AIFF, RIFF, WorldFoundry IFF | Complete | Traces all four formats from the common 1985 ancestor. Key findings: AIFF and WF IFF independently arrived at the same solution (bake navigational offsets at write time for slow-media random access); WF uniquely separates interchange (text source) from execution (platform binary); .align(2048) maps CD-ROM sectors. Side-by-side comparison table included. |
| 2026-04-16 | Reverse-engineering the WF binary level format for levcomp-rs |
Phase 2c complete | Binary format fully mapped; Phase 2c (2026-04-17): mesh bbox from MODL/VRTX, packed asset IDs, asset.inc output, 37 objects validated. Remaining: real path/channel keyframes. |
| 2026-04-16 | Coding-conventions remediation | In progress | Audit of 2026-authored code in wfsource/source/ against docs/coding-conventions.md. Honest accounting of where recent additions don't yet follow the rules they propose. |
| 2026-04-15 | LOC tracking | Ongoing | Tracks code line count over time. Dead-code removal took the tree from 64,252 (baseline 74d1a47) to 36,199 (−43.7%, e2dcc98 after Batch 7); retiring the legacy physics/wf/ backend is projected to land the reduction at ~35,113 (−45.3%). Tool: scripts/loc_report.py. |
| 2026-04-14 | Scripting language replacement | Complete | Comprehensive survey that recommended Lua 5.4 as the primary engine. Spawned all scripting plans. Decision: Lua won. |
| 2026-04-14 | Physics engine survey | Complete | Surveyed Bullet, PhysX, Rapier, Jolt, and others. Recommended Jolt Physics (MIT, ~300 KB, CharacterVirtual, active upstream). Spawned Jolt integration plan. |
| 2026-04-14 | Jolt Physics integration | Functional | Jolt is the default (WF_PHYSICS_ENGINE=jolt); snowgoons is playable. Runtime init/shutdown moved to WFGame (b5dc7fe). Legacy physics/wf/ retained pending parity on a second level. |
| 2026-04-14 | Remove audio subsystem | Complete | Implemented 2026-04-15. wfsource/source/audio/ and wfsource/source/audiofmt/ deleted. Stub audio stubs were non-functional on Linux. To be replaced by miniaudio (see audio investigation). |
| 2026-04-13 | ButtonType × showAs coverage audit | Complete | Audited all OAD field type × showAs combinations against the Blender plugin. Gaps identified and fixed. |
| 2026-04-11 | iffcomp — Rust rewrite | Complete | Rust port in wftools/iffcomp-rs/. Byte-exact against C++ oracle. Includes comprehensive all_features.iff.txt torture test shared with Go port. |
| 2026-04-11 | iffcomp — Go rewrite | Complete | Go port in wftools/iffcomp-go/. Byte-exact against C++ oracle (both binary and text output). Passes shared torture fixture. Go is primary; C++ kept as oracle. |
| 2026-04-11 | iffcomp — C++ modernization | Complete | Modernized the 1996 flex/bison C++ iffcomp to build on GCC 15 / Clang under C++17. Now serves as byte-exact oracle for Go and Rust ports. |
| 2026-04-14 | Audio: sound effects, music, positional sound | Active — Phases 1–5 complete | Phase 1: miniaudio SFX one-shots. Phase 2: TinySoundFont MIDI player audible. Phase 3: per-level level<N>.mid + load/stop hooks. Phase 4: play_music/stop_music/set_music_volume Lua closures (Lua-only, not mailbox). Phase 5: 3D positional SFX + listener tracking from camera (three miniaudio gotchas surfaced and fixed). Phases 6 (mobile) and 7 (docs) deferred; mailbox-wired audio API for non-Lua engines deferred. |
| 2026-04-14 | Constraint-based props | Deferred | Doors, chains, pulleys, elevators via Jolt constraints. Hard prerequisite: Jolt integration must land first; also requires IFF binary chunk support. Not yet scheduled. |
| 2026-04-14 | Multiplayer, voice chat, mobile input | Deferred | Surveyed multiplayer sync models, voice (LKWS/Agora/LiveKit), mobile input (touch/gyro/haptics). Depends on mobile port landing first. Not yet scheduled. |
| 2026-04-14 | REST API box PoC | Complete | cpp-httplib embedded server in wf_game; create/recolor/resize/delete GL wireframe boxes via HTTP at runtime. Landed 7e690e1. |
| Date | Document | Summary |
|---|---|---|
| 2026-04-16 | Scripting languages in WF | Survey of all supported engines (Lua, Fennel, JS, wasm, Wren, Forth). Covers integration surface, binary cost (.text, -O2), RAM footprint, compile-time switches, and reference scripts for each language. |
| 2026-04-16 | Coding conventions | Authoritative C++ style guide for WF runtime code. Subsumes wfsource/source/codingstandards.txt. Covers target envelope, naming, Validate() discipline, assert family, no-fallback policy, lookup tables, OAS/OAD decision tree, mailboxes, streams, and foreign-library interop. |
| 2026-04-15 | JerryScript GCC 14 build fixes | Documents 7 GCC 14 build failures in JerryScript v3.0.0 with wf-minimal profile and how they were fixed. Applied as part of the JS engine landing. |
| 2026-04-14 | Compile-time switches | Generated catalogue of 929 unique #ifdef switches across the codebase. Informational. See also docs/compile-time-switches.md (live version). |
| 2026-04-13 | Blender → cd.iff pipeline | Maps the existing pipeline and proposes the Blender-native replacement for the 3DS Max content path. Key follow-up: no automated .lev → .iff path from Blender yet. |
| 2026-04-13 | OAS / OAD format | Documents the OAS (object attribute source) and OAD (compiled descriptor) binary format. Used by wf_blender and oas2oad-rs. |
| 2026-04-12 | Steam shipping plan | Comprehensive plan for shipping a WF-based game on Steam. Enumerates runtime blockers (build system, C++ dialect, HAL, graphics, scripting). Most blockers are now resolved or being resolved; Steam packaging itself not yet started. |
| 2026-04-11 | wftools rewrite analysis | Analyzes all ~23 wftools/ directories; recommends which to drop, rewrite (Go/Rust), or replace with off-the-shelf tools. |
| (early) | WF viewer design notes | Describes the standalone wftools/wf_viewer/ approach for rendering level geometry without the full engine stack. Superseded by wf_game running end-to-end. |
| (early) | Production pathway diagram | Mermaid diagram of the original pipeline from .oas / 3D editor → cd.iff. Useful map of where each tool fits. |
No hard blockers. Jolt is functional and all scripting engines are smoke-tested. Active areas of ongoing work are listed in Open follow-up work below.
- WAMR Phase 2 (AOT) — deferred;
wamrccompiles.wasm→ native machine code offline; output is ISA-specific (x86_64, arm32, arm64 each need a separate.aotblob); ~10 KB AOT loader vs. ~107 KB classic interp. Revisit when ship targets are concrete. - wasm3 retired — done 2026-04-16;
engine/vendor/wasm3-v0.5.0/+scripting_wasm3.{hp,cc}removed;WF_WASM_ENGINE=wamris the only wasm option. - Lua remote step debugger — explicit user request for "later": MobDebug/DBG.lua/LuaLS-DAP into LuaInterpreter for in-game step debugging.
- Fennel macros /
require—fennel.searcher/package.searchers;.fnlbuild step. - Coroutine smoke test — fix #6 landed but untested end-to-end with a real yielding script.
- wasm module cache — hash source pointer + size, reuse compiled modules across
RunScriptcalls (needed before wasm in hot-loop scripts). - Binary IFF chunk types —
WSM/AOTtags with explicit length; drop base64 for ~33% asset shrink. - Cross-language API parity audit —
read_actor/read_fixed/read_color/read_flagstyped accessors need to be consistent across all engines when added. No canonical IDL yet. WF_DEFAULT_ENGINEknob — for Lua-off builds, needs a way to select the sigil-less fallthrough engine. Currently undefined behavior.- Lua → JS / Lua → Wren script converters — mirroring
tcl_to_lua_in_dump.py. - Load level by filename from CLI — done:
-L<path>flag inmain.cc:167;gLevelOverridePathingame.cc:140.wf_game -L<level.iff>bypassescd.iff.
- Remove
physics/wf/— kept until Jolt passes snowgoons parity on at least one other level; removal is a separate reviewable commit. - Constraint-based props — doors/chains/pulleys via Jolt; blocked on Jolt parity + IFF binary chunks.
- Batch 8 — Jolt replaces WF physics (in progress).
hal/_list+hal/_mempoolmigration — refactorMsgPortto usecpplib/minlist.hp+memory/mempool.hp, then delete the HAL remnants.eval/(120 LOC) — tool-side callers (wftools/prep,wftools/iff2lvl) need porting to Blender plugin first.
- Blender →
cd.iffpipeline — Phases 2a + 2b + 2c landed; snowgoons loads 37 objects end-to-end, all Jolt bodies valid. Remaining: real path/channel keyframes. - iffcomp: Rust is primary — Decision: tools in Rust. Four implementations exist (C++ modernized oracle, Go, Node.js, Rust); all pass
all_features.iff.txt. Rust port (iffcomp-rs/) is the going-forward implementation. C++ kept as byte-exact oracle; Go and Node.js ports are superseded.
- Audio (miniaudio) — Phases 1–5 complete (SFX one-shots, MIDI MusicPlayer, per-level music, Lua scripting surface, 3D positional SFX + listener tracking). Phases 6 (mobile backends) and 7 (docs) deferred. Gap: audio API is Lua-only (C closures in
scripting_lua.cc), not mailbox-wired; the other seven scripting engines currently can't trigger music or SFX. - Mobile port — Android arm64 / iOS arm64; plans written (Android, iOS). Android Phases 1+2 done; Phase 0 (immediate-mode GL retirement) in progress — steps 1/2/4a/4b landed 2026-04-17, step 4c remaining is proper shader ports of directional lighting + linear fog + matte triangles, not Android-only stubs. iOS still blocked on Android.
- Multiplayer / voice / mobile input — blocked on mobile port.
- Steam packaging — Phases 1+2 done: Steamworks SDK lifecycle and Steam Input are wired in. Phases 3 (SteamPipe depot build + upload) and 4 (store page assets) deferred; blocked on Steamworks partner account and store capsule art.
2026-04-23 10:46 — docs/investigations/2026-04-23-world-foundry-history.md: World Foundry / Project Velocity — Public History