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:
SashaGoncharov19 committed 2026-10-23 15:27:58 +04:00
commit 51d085e865
89 files changed
+7999

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
"""Check build-5 constructor, actual-save and regression reports from the iPhone."""
import hashlib
import json
import subprocess
from datetime import datetime
from pathlib import Path
ROOT=Path(__file__).resolve().parents[1];OUT=ROOT/'build/deserialiser'
names=('deserialiser','save-initialization','target','layout','map-header','version','version-probe')
reports={name:json.loads((OUT/('iphone-'+name+'-result.json')).read_text()) for name in names}
assert all(r['app_build']=='5' and r['app_version']=='0.1.0' for r in reports.values())
assert len({r['run_id'] for r in reports.values()})==1 and len({r['build_id'] for r in reports.values()})==1
times=[datetime.fromisoformat(r['recorded_at'].replace('Z','+00:00')) for r in reports.values()]
assert (max(times)-min(times)).total_seconds()<30
constructor=reports['deserialiser'];actual=reports['save-initialization']
assert constructor['status']=='pass' and constructor['code']==0 and constructor['newer_version_status']==7
assert actual['status']==0 and actual['version']==[2,0,77,0,0]
for report in (constructor,actual):
for key,value in {'allocations':12,'reads':5,'target_capacity':64,'heap_bytes':10712,
'stream_cursor':9,'owns_target':1}.items():assert report[key]==value
assert reports['target']['status']=='pass' and reports['target']['initialized_targets']==64
assert reports['version']['status']=='pass' and reports['version-probe']['status']==0
assert reports['map-header']['status']=='pass' and reports['map-header']['modern_64_bit'] and reports['map-header']['legacy_32_bit_sentinel']
desktop=json.loads((ROOT/'build/save-layout/desktop-real-save.json').read_text())
for report in (actual,reports['layout'],reports['version-probe']):
assert report['status']==0 and report['input_sha256']==desktop['input_sha256']
assert len({reports[name]['probe_id'] for name in ('save-initialization','layout','version-probe')})==1
for key in ('header_offset','next_offset','mod_count','tick_fields_hex'):
assert reports['layout'][key]==desktop['decoded_layout'][key]
binary=ROOT/'build/ios-device/Build/Products/Release-iphoneos/FactorioPort.app/FactorioPort'
assert constructor['build_id'].encode() in binary.read_bytes()
dependencies=subprocess.check_output(['xcrun','otool','-L',str(binary)],text=True)
imports=subprocess.check_output(['xcrun','nm','-u',str(binary)],text=True)
symbols=subprocess.check_output(['xcrun','nm',str(binary)],text=True)
assert 'steam' not in (dependencies+imports).lower()
for symbol in ('_factorio_rng_original','_factorio_version_original','_factorio_map_header_original',
'_factorio_target_original','_factorio_deser_original','_native_deser_allocate'):
assert symbol not in symbols
result={'status':'pass','app_build':'5','run_id':constructor['run_id'],'build_id':constructor['build_id'],
'device':'iPhone 15 Pro','constructor':constructor,'actual_save_initialization':actual,
'same_launch_regressions':'pass','steam_dependency_or_import':False,'desktop_oracle_symbols':False,
'signed_binary_sha256':hashlib.sha256(binary.read_bytes()).hexdigest(),
'scope':'original constructor initialized from actual-save prefix; no loaded world or gameplay'}
(OUT/'device-verification.json').write_text(json.dumps(result,indent=2)+'\n')
print('PASS: build 5 MapDeserialiser, actual-save prefix, same-launch regressions and signed binary audit')