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

+67
View File
@@ -0,0 +1,67 @@
"""Exercise arbitrary bounded prefix lengths, outer/nested streaming and guards."""
import io
import hashlib
import json
import struct
import subprocess
import tempfile
import zipfile
import zlib
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / 'build/save-prefix'
OUT.mkdir(parents=True, exist_ok=True)
runtime = ROOT / 'third_party/wabt/wasm2c'
subprocess.run(['xcrun', 'clang', '-O2', '-DWASM_RT_USE_MMAP=0', '-DWASM_RT_MEMCHECK_BOUNDS_CHECK=1',
'-I'+str(ROOT/'runtime'), '-I'+str(ROOT/'build/version'), '-I'+str(runtime),
str(ROOT/'tests/save_prefix_probe.c'), str(ROOT/'runtime/save_header.c'),
str(ROOT/'runtime/version_bridge.c'), str(ROOT/'build/version/factorio_version.c'),
str(runtime/'wasm-rt-impl.c'), str(runtime/'wasm-rt-mem-impl.c'), '-lz', '-o', str(OUT/'probe')], check=True)
def archive(payload, method, streaming=False):
class NonSeeking(io.BytesIO):
def seek(self, *args): raise OSError('streaming')
output = NonSeeking() if streaming else io.BytesIO()
with zipfile.ZipFile(output, 'w', compression=method) as z:
z.writestr('fixture/level.dat0', zlib.compress(payload))
return output.getvalue()
checks = 0
def probe(data, length, expected, status=0):
global checks
with tempfile.TemporaryDirectory() as directory:
path = Path(directory)/'fixture.zip'
path.write_bytes(data)
result = subprocess.run([str(OUT/'probe'),str(path),str(length)],capture_output=True,text=True)
assert result.returncode == (1 if status else 0), result.stderr
report = json.loads(result.stdout)
assert report['status'] == status, report
assert bytes.fromhex(report['prefix_hex']) == expected
checks += 1
payload = struct.pack('<4HB',2,0,77,0,0) + bytes(range(256))*300
for method in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED):
for streaming in (False, True):
data = archive(payload,method,streaming)
for length in (1,8,9,10,33,4095,4096,4097,65536,len(payload)):
probe(data,length,payload[:length])
probe(data,len(payload)+1,b'',6)
probe(data,0,b'',7)
probe(data,1024*1024+1,b'',7)
probe(b'invalid',33,b'',2)
incompressible = struct.pack('<4HB',2,0,77,0,0)
incompressible += b''.join(hashlib.sha256(i.to_bytes(4,'little')).digest() for i in range(2048))
for method in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED):
for streaming in (False,True):
data=archive(incompressible,method,streaming)
for length in (4097,len(incompressible)):
probe(data,length,incompressible[:length])
over_budget=b''.join(hashlib.sha256(i.to_bytes(4,'little')).digest() for i in range(32768))
probe(archive(over_budget,zipfile.ZIP_STORED),1024*1024,b'',7)
print(f'PASS: {checks} bounded prefix cases, exact bytes, output guards and failure atomicity')
(OUT/'verification.json').write_text(json.dumps({'status':'pass','cases':checks,
'scope':'synthetic archives; bounded prefix only'},indent=2)+'\n')