# Static-translation research (ARM64 → WASM → native) > This is the earlier, function-by-function translation track. The playable port uses the > retargeted original binary instead; see [../native-port.md](../native-port.md). Commands below > are run from the repository root. The intended target is local single-player Factorio, without Steam integration or runtime JIT. Current deliverable: **two RNG functions, the original map-version and map-header constructors, and the MapDeserialiser/TargetDeserialiser/memory-pool initialization graphs translated to WebAssembly and compiled through wasm2c into a native iOS prototype**. The application can read version headers, initialize the original deserializer from a save's version prefix and, for a limited 2.0.77 profile, locate and decode map-header fields from save ZIPs. Full world loading, rendering, simulation and gameplay are not implemented. Pipeline: local ARM64 Mach-O → REA-inspected instructions → byte-guarded WASM translation → wasm2c → Clang → native iOS application. RNG functions use reviewed specialized translations; `MapVersion` uses the new bounded automatic integer lifter. The lifter now also supports bounded integer control flow, comparison flags and immediate branches, verified with separate native instruction fixtures. This is not yet a whole-program ARM64-to-WASM compiler. Glue5 supplies the model for the WASM-to-native half; it expects an existing WASM game build and does not supply the missing Factorio binary lifter. ## Reproduce on Apple Silicon macOS Requires Xcode, Python 3, CMake, Git and xcodegen. WABT is built locally at a pinned revision. The following commands fetch only tool sources: ```sh git clone --depth 1 --branch 1.0.42 https://github.com/WebAssembly/wabt.git third_party/wabt git -C third_party/wabt submodule update --init third_party/picosha2 ``` Then supply your installed game executable: ```sh python3 tools/build_rng.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tests/check_lift_guards.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tools/build_version.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tools/build_map_header.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tools/build_save_layout.py python3 tools/build_target_deserialiser.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tools/build_deserialiser.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' python3 tests/check_a64_guards.py python3 tests/check_a64_cfg.py python3 tests/check_a64_program.py python3 tests/check_a64_initialization.py python3 tests/check_a64_zero_vectors.py python3 tests/check_save_prefixes.py python3 tests/check_save_layout.py python3 tests/check_save_headers.py python3 tools/package_ios.py ``` The specialized lift rejects unrecognized function or constant bytes before producing artifacts. Supported inspected build: Factorio 2.0.77 ARM64. Original installation files are read only. Generated game code and evidence stay in ignored local directories. Outputs: - `build/rng/factorio_rng.wasm`, generated C/header and `libfactorio_rng_ios.a`. - `build/rng/manifest.json`: source identity, addresses and hashes. - `build/rng/verification.json`: native equivalence and Steam import audit. - `build/version/factorio_version.wasm`, generated C and `libfactorio_version_ios.a`. - `build/map-header/factorio_map_header.wasm`, generated C and `libfactorio_map_header_ios.a`. - `build/map-header/device-verification.json`: both version-dependent MapHeader paths pass on the iPhone using controlled payloads; same-launch version and real-save header checks also pass. - `build/save-layout/device-verification.json`: build 3 reads the actual save's MapHeader at decoded offset 333; its three raw fields match the Mac/original-constructor result. Supported envelope: 2.0.77.0, flag 0, empty startup settings. - `build/target-deserialiser/libfactorio_target_ios.a`: original loader initialization graph with a bounded guest heap. - `build/target-deserialiser/device-verification.json`: build 4 initializes 64 pool objects with four allocations on the iPhone; same-launch version, map-header and real-save regressions pass. - `build/deserialiser/device-verification.json`: build 5 initializes the original MapDeserialiser from the actual save's nine-byte prefix; 12 allocations, 10,712 guest heap bytes, same-launch regressions pass. - `build/FactorioRNG-unsigned.ipa`: historical filename; now includes the RNG and save-header prototype, requires signing to install. - `build/ios-verification.json` and `build/ios-build.log`. ## Verified scope `RandomGenerator::getInt()` and its unsigned-integer seed constructor are translated. The verification executable compares 876,544 generated values and their mutated state against original ARM64 instruction words, plus 1,024 constructor calls. It covers 6,848 starting states and five linear-memory offsets, including unaligned states and the end of memory. Guard bytes check for unintended writes. These finite comparisons are evidence, not proof of equivalence for every state. The native oracle is linked only into macOS verification executables. The iOS app links wasm2c output and runtime, with ordinary allocated data memory and explicit bounds checks. It does not load original machine code or compile WASM at runtime. The RNG prototype was installed and launched on the connected iPhone 15 Pro; the user confirmed it worked. The updated save-header prototype also runs on that device: its native constructor self-test and generated-archive probe pass. Results are in `build/version/iphone-self-test.json` and `build/version/iphone-save-probe.json`. The automatically lifted version constructor passes 20,000 native comparisons; ZIP/zlib header reading passes fifteen fixtures. It reads version 2.0.77 from the user's autosave locally on macOS. With explicit user approval, the actual autosave was also copied to the iPhone; its native header reader returned version 2.0.77 successfully. See [world-loading.md](world-loading.md) for exact scope, REA provenance, limitations and next entry points. ## REA workflow REA MCP performs artifact inspection and focused disassembly. Initial Mach-O evidence is in `rea-evidence.json`; subsequent local MCP responses are in `evidence/`. `evidence/factorio-functions-final.json` retains successful RNG disassembly, exact byte reads, and Steam/startup dossiers. LLVM's tools independently supplied the initial bounded instruction listings and debug UUID comparison. Evidence from these tools is kept distinct. The extracted ARM64 image is byte-identical to the original universal binary's ARM64 slice. After exporting the canonical REA bundle, validate its three byte ranges against the installed input: ```sh python3 tools/check_rea_rng.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' evidence/factorio-functions-final.json ``` This passes and writes `build/rng/rea-provenance.json`. The completed isolated REA session was exported to `build/factorio-rea.snapshot.json` and closed successfully. The installed Hopper vendor helper requires `/Applications/Hopper Disassembler.app` when Spotlight is unavailable. A symlink there pointing to the user-installed copy of Hopper (in `~/Applications`) resolves this. No Hopper binary was changed. The original full analysis took 5m27s, beyond REA's default 120s timeout; the demo also cannot save `.hop` databases. `tools/rea-session.mjs` starts a separate REA MCP process with a 600s startup timeout, without changing saved MCP client or REA configuration. Pass the vendor `Contents/MacOS/hopper` helper, not the GUI executable. ## Next engine milestones The detailed staged plan is in [implementation-plan.md](implementation-plan.md). Completed work and limits are in [translation-progress.md](translation-progress.md), [save-layout-progress.md](save-layout-progress.md), [initialization-progress.md](initialization-progress.md) and [deserialiser-progress.md](deserialiser-progress.md). 1. Lift the startup graph and replace its inlined Steam initialization with local services. REA identified the interface boundary and an inlined copy in `GlobalContext::init`; changing the factory alone is insufficient. 2. Extend the bounded integer lifter beyond straight-line code: full guest memory mapping, indirect-call dispatch, relocations, TLS, atomics, SIMD and C++ exception handling. Initial bounded direct guest calls, tail calls and known returns are implemented in `a64_program.py`. Initial bounded branches, NZCV support and original MapHeader constructor validation pass. 3. Translate world loading and simulation, then native graphics/audio/touch adapters. 4. Validate a small playable world and save/load on an actual iPhone before expanding compatibility. See [porting-notes.md](porting-notes.md) for target identity and [steam-boundary.md](steam-boundary.md) for Steam scope.