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.
68 lines
2.9 KiB
Python
68 lines
2.9 KiB
Python
"""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')
|