Files
factorio-ios/docs/research/translation-progress.md
T
SashaGoncharov19 51d085e865 Native iPhone port of Factorio 2.0.77
Run the original macOS ARM64 game code on iOS:
- tools/retarget.py: header-only Mach-O retargeting to an iOS dylib
- platform/native: UIKit host and macOS API shims (AppKit for SDL's
  Cocoa backend, CoreGraphics displays, CoreAudio device, Metal device
  properties, Wuffs PNG decoding, Steam and peripheral stubs)
- touch, overlay, iOS keyboard and hardware keyboard input
- tools/build_native.py: build, sign, install, data copy, launch
- docs: build guide, controls, architecture

Also includes the earlier static-translation research (ARM64 -> WASM ->
native) under docs/research, runtime/, tests/ and tools/.

No game files are included; the build reads them from a local install.
2026-10-23 15:27:58 +04:00

65 lines
6.7 KiB
Markdown

# Translation batch: branching map-header constructor
2026-10-20. The next engine primitive is implemented and passes desktop native equivalence tests. This document distinguishes constructor tests from loading a real world.
## Implemented
- `tools/record_target.py` rejects a different executable/slice and records hashes of all 8,673 installed data files (1,498,094,877 bytes). It inventories base/core metadata and tool/source identities without copying assets. Outputs: `build/target/manifest.json` and `assets.json`. Save compatibility and the gameplay configuration remain unselected.
- `tools/a64_lift.py` now handles bounded immediate branches, loops, CBZ/CBNZ, TBZ/TBNZ, B.cond, integer ADD/SUB with NZCV, conditional selects, and selected unscaled/indexed memory operations. It also recognizes an adjacent 128-bit load/store copy idiom; this is not general SIMD support.
- CFG exports use `run(context_offset, instruction_budget)`. Straight-line exports retain `run(context_offset)`. Status 3 aborts on budget exhaustion with partial state; it is not resumable. Unknown targets, unsupported instructions, undefined incoming flags, and entry into the middle of a copy are rejected.
- `tools/build_map_header.py` automatically translates the original 332-byte / 83-instruction `MapHeader::MapHeader(MapDeserialiser&)` at `0x1014c6aac`. It has six direct read call sites but performs three reads per successful invocation. The native memory-stream boundary replaces `readOrThrow`; arbitrary C++ unwinding remains unimplemented.
- `runtime/map_header_bridge.c` exposes this constructor with an explicitly supplied version and a payload already positioned at the constructor's input. It does not assume the payload follows byte nine in the save. Recovering `MapDeserialiser` initialization/stream positioning is still required before probing these fields in a real archive.
- The constructor copies the version object and reads three MapTick-sized fields. For packed versions at or below `0x0001000200000175`, it reads three 32-bit values and widens `0xffffffff` to `UINT64_MAX`; above that cutoff, it reads three 64-bit values. The individual field meanings are not established.
- Device reports now include run ID, timestamp, app version/build and compilation identity. Save probes stream a SHA-256 of the entire input. A fresh device report's hash matched the actual approved Mac autosave, and its run ID matched the version self-test.
## Checks
- 1,860,000 comparisons of hand-authored ARM64 branch/loop/flag/select fixtures against native execution, including edge values, preserved integer registers, memory guards, execution-budget exhaustion, and six invalid CFG cases. These fixtures are compiler validation, not recovered game code.
- 20,000 comparisons of the original MapHeader instruction words against the generated WASM→C implementation, covering old/new version paths, boundary versions, legacy sentinels, guarded public output, unchanged input, and short-stream failure without public output mutation.
- Existing 20,000 MapVersion comparisons and fifteen archive-prefix fixtures pass.
- Nine unsupported-instruction/CFG/copy guards reject invalid inputs.
- Exact REA byte provenance passes for the two RNG functions, their constants, MapVersion, and MapHeader.
- Unsigned iOS build and Steam dependency/import audit pass. Signed build 2 was installed and launched on the iPhone 15 Pro; both modern 64-bit and legacy 32-bit/sentinel MapHeader self-tests passed. The version constructor self-test and actual-save version probe passed in the same launch, with a matching input SHA-256. `build/map-header/device-verification.json` retains these reports and the signed binary hash. These MapHeader tests use controlled payloads; the actual-save check still reads only its version.
Reproduce the new checks after the existing RNG/version builds:
```sh
python3 tools/record_target.py '/absolute/path/to/factorio.app'
python3 tests/check_a64_cfg.py
python3 tests/check_a64_guards.py
python3 tools/build_map_header.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio'
python3 tools/check_rea_rng.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' evidence/loader-cfg-final.json --include-version --include-map-header
python3 tools/loader_inventory.py evidence/loader-cfg-final.json
python3 tools/package_ios.py
```
## New loader evidence
`evidence/loader-cfg-final.json` retains the complete exported records. REA was closed successfully with `build/loader-cfg-rea.snapshot.json`; the live Hopper session took roughly ten minutes to prepare this time. Snapshot replay did not eliminate native analysis for these new queries.
| Root | Observed direct callees | Observed basic blocks |
| --- | ---: | ---: |
| `Map::load`, `0x1014c93f8` | 3 | Not queried |
| `Map::loadData`, `0x1014c9618` | 82 | 227 |
| `Scenario::loadFactory`, `0x1000fa1d0` | 87 | 702 |
| `MapHeader` constructor, `0x1014c6aac` | 1 | 8 |
The Map::load assembly allocates an object, calls the Map constructor, copies initialization state, then calls loadData. Its vector copies, allocator and constructor dependencies remain unsupported by the runnable engine prototype. Map::loadData includes indirect calls and many subsystems; its observed direct call set does not establish complete dependency closure. `build/startup/loader-frontier.json` records the immediate frontier and observed indirect call sites available in retained assembly.
REA evidence IDs:
- Map::load direct calls: `ev_9de8048a446bfa9212d8be5bd14ca915af239bde3fefe0005c4d635624fcdd35`.
- Map::loadData direct calls: `ev_d8182d6051b428439fae8688802fd80ded2f7a4b88e4b121da8c85c2f015a3ea`.
- Scenario::loadFactory direct calls: `ev_3272507fc8a450275baa7dcdba5330225b12cb7df300791ace8a663dd9da9833`.
- MapHeader assembly: `ev_23fbedc8f2cdb2aa700d457c1395d090c9af691160a01e3398330fcc740031c8`.
- Complete MapHeader bytes: `ev_edc9090e494bf4c92f5c0e5d9f4366272062c6d2d3d758442448ab720bf4e422`.
## Next batch
1. Recover MapDeserialiser construction, its version object/layout, and stream position at MapHeader entry. Establish a real-save fixture for this constructor before interpreting actual tick fields.
2. Add general guest-to-guest calls so translated constructors compose without implementing each callee as a native bridge.
3. Recover the Map constructor's required allocations/global references and translate its smallest reachable dependencies.
4. Continue the runtime memory/import model and optional Steam-free startup. Full archive loading, world reconstruction, simulation, rendering and gameplay are still absent.
The subsequent stream-positioning batch locates MapHeader in the actual save, adds bounded direct guest calls and enters MapHeader through its original C1 tail branch. See [save-layout-progress.md](save-layout-progress.md). The original batch status above is historical.