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.
This commit is contained in:
commit
51d085e865
89 files changed
+7999
No files matched your search
@@ -0,0 +1,55 @@
|
||||
# World-loading translation: first stage
|
||||
|
||||
The iOS prototype now runs an automatically translated `MapVersion::MapVersion(Deserialiser&)` constructor. It reads the save's version header. It does **not** load entities, surfaces, mods, prototypes, or a playable world.
|
||||
|
||||
## Translation and authority
|
||||
|
||||
`tools/a64_lift.py` decodes ARM64 instruction words and emits WAT for a bounded straight-line integer subset. `tools/build_version.py` compiles the original constructor at `0x10150bf28`, 192 bytes / 48 instructions, with body SHA-256 `b9254262cde89c9e79a9fdf6d700860ff9fd623d53c576efaf431600096c8a9b`. A changed body, unknown call, unsupported instruction, missing return, or extra bytes after return fails compilation.
|
||||
|
||||
Registers remain 64-bit. Guest addresses are checked before conversion to the prototype's 64 KiB WASM memory. The compiler supports only the declared execution model; it does not yet implement arbitrary branches, SIMD, native imports, global relocation, indirect calls, or exceptions. ARM encoding reference: [Arm A64 ISA](https://developer.arm.com/documentation/ddi0602/2026-03).
|
||||
|
||||
REA confirms the exact constructor bytes and instructions in `evidence/world-loading-final.json`:
|
||||
|
||||
- Constructor dossier: `ev_5b212f1169e5fff89ef9e6675af58a9df4616c5260c451a6d403a822935e36d0`.
|
||||
- Complete 192-byte read: `ev_e17c26cc53fcb5bc8e04b1342129594def2f2646cc186f462e281e394b436d45`.
|
||||
- `Deserialiser::readOrThrow` dossier: `ev_31eb669fe011e10190a6f9c1aa32ba33c2ad5b5a38cb37c46db03066d36f33b2`.
|
||||
|
||||
The constructor makes five reads of 2, 2, 2, 2 and 1 bytes. It packs four little-endian 16-bit components into a 64-bit field and stores the final byte separately. The final byte's meaning is not established. The original `readOrThrow` dispatches through a stream virtual method and throws a C++ exception on a short read. This bounded prototype replaces that host boundary with a memory stream and an explicit error status. It does not claim to preserve C++ exception unwinding for arbitrary game code.
|
||||
|
||||
## Archive adapter
|
||||
|
||||
`runtime/save_header.c` reads the ZIP central directory, locates `level.dat0` (or `level.dat`), handles stored or deflated ZIP entries, and decodes the nested zlib stream's first nine bytes. It streams bounded prefixes instead of extracting the save or loading the whole world into memory. It rejects ambiguous roots, encryption, unsupported central-directory layouts, invalid offsets and malformed compression. Each compressed/decoded prefix is limited to 1 MiB.
|
||||
|
||||
This is a **header probe**, not whole-archive validation: it does not verify the entire member CRC or the rest of the zlib/map stream. ZIP64 central directories and encrypted archives are unsupported. The generated test archive contains a header and synthetic text, not a real map.
|
||||
|
||||
## Checks completed
|
||||
|
||||
- 20,000 native comparisons against original constructor instruction words, with the same stream adapter on both sides; output bytes, five-read sequence, callee-saved registers, SP and memory guards match.
|
||||
- Short-header failures leave the public result unchanged.
|
||||
- Fifteen ZIP/zlib fixtures cover success, streaming descriptors, malformed/truncated inputs, unsupported flags, ambiguous roots and offset validation.
|
||||
- The local macOS probe reads version 2.0.77 from the installed `_autosave1.zip` without modifying it.
|
||||
- The updated iOS application builds with no Steam imports or dependency. It was signed, installed and launched on the connected iPhone 15 Pro. Its native constructor self-test passed, and it read version 2.0.77 from a generated header-only ZIP. Results are recorded under `build/version/iphone-*.json`.
|
||||
- After explicit user approval, `_autosave1.zip` was copied into the iPhone application’s `Documents/ProbeSave.zip`. The application was relaunched and returned status 0 and version 2.0.77. The actual-save result is retained in `build/version/iphone-real-save-probe.json`; `device-verification.json` records this test, and `generated-device-verification.json` retains the earlier synthetic test. This verifies the version header only; the world has not been loaded.
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
python3 tools/build_version.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio'
|
||||
python3 tests/check_a64_guards.py
|
||||
python3 tests/check_save_headers.py
|
||||
python3 tools/check_rea_rng.py '/absolute/path/to/factorio.app/Contents/MacOS/factorio' evidence/world-loading-final.json --include-version
|
||||
python3 tools/startup_inventory.py
|
||||
python3 tools/package_ios.py
|
||||
```
|
||||
|
||||
## Remaining startup and world work
|
||||
|
||||
The retained REA `GlobalContext::init` dossier observes 151 direct callees and 643 basic blocks, plus indirect calls with unresolved targets. `build/startup/frontier.json` lists this observed frontier; it is not a complete dependency graph or runnable startup implementation.
|
||||
|
||||
LLVM symbol inventory identifies the next candidate entry points (names/addresses, not recovered behavior): `Scenario::loadFactory` at `0x1000fa1d0`, `Map::load` at `0x1014c93f8`, and `Map::loadData` at `0x1014c9618`. Follow-up live REA callee queries became unavailable after the Hopper connection was lost; no graph completeness is claimed. The completed evidence was exported and a new snapshot retained in `build/world-loading-rea.snapshot.json`.
|
||||
|
||||
Next: recover those loader dependencies, add the required control-flow and memory/import support to the lifter, and replace graphical startup's inlined Steam block with a local optional-services context. Graphics and simulation remain unimplemented.
|
||||
|
||||
A subsequent REA session recovered direct callee frontiers for all three loader roots and translated the branching MapHeader constructor. See [translation-progress.md](translation-progress.md) for evidence, checks, and remaining scope.
|
||||
|
||||
The next batch adds a restricted scenario-envelope adapter and locates MapHeader at decoded offset 333 in the approved autosave. The original native constructor agrees with its translated output there. This is header decoding, not world loading; see [save-layout-progress.md](save-layout-progress.md) for profile restrictions and evidence.
|
||||
Reference in new issue
Block a user