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,94 @@
|
||||
"""Verify the bounded 2.0.77 scenario envelope, offsets and unsupported inputs."""
|
||||
import io
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
import tempfile
|
||||
import zipfile
|
||||
import zlib
|
||||
from pathlib import Path
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[1]
|
||||
PROBE=ROOT/'build/save-layout/probe'
|
||||
TICKS=(720001,42,0xffffffffffffffff)
|
||||
|
||||
|
||||
def compact(value,size=4):
|
||||
return bytes([value]) if value<255 else b'\xff'+value.to_bytes(size,'little')
|
||||
|
||||
|
||||
def string(value): return compact(len(value))+value
|
||||
def version(values=(2,0,77)): return b''.join(compact(v,2) for v in values)
|
||||
def localised(text=None,children=(),kind=1):
|
||||
return b'\0' if text is None else bytes([kind])+string(text)+bytes([len(children)])+b''.join(children)
|
||||
def ending(title=None,children=()):
|
||||
return localised(title,children)+localised()+compact(0)+localised()+string(b'')
|
||||
|
||||
|
||||
def fixture(long_string=b'freeplay',mod_count=1,extended_version=False,nested=False):
|
||||
result=bytearray(struct.pack('<4HB',2,0,77,0,0))
|
||||
positions={}
|
||||
result+=string(b'')+string(long_string)+string(b'base')
|
||||
positions['difficulty']=len(result);result+=b'\1'
|
||||
positions['boolean']=len(result);result+=b'\0\0'+string(b'')+b'\0\0\0\1'
|
||||
result+=version((65535,255,254) if extended_version else (2,0,77))
|
||||
result+=struct.pack('<I',1234)+b'\1'+struct.pack('<I',0xa00000)
|
||||
positions['mod_count']=len(result);result+=compact(mod_count)
|
||||
for i in range(mod_count):
|
||||
result+=string(b'base')+version((255,256,65535) if extended_version else (2,0,77))+struct.pack('<I',i)
|
||||
result+=struct.pack('<I',0)
|
||||
positions['property_type']=len(result);result+=b'\5\0'
|
||||
positions['property_count']=len(result);result+=struct.pack('<I',0)
|
||||
positions['ending']=len(result)
|
||||
children=(localised(b'child',(localised(),)),) if nested else ()
|
||||
result+=ending(b'victory',children)+ending(b'defeat')+b'\0'
|
||||
offset=len(result)
|
||||
result+=struct.pack('<3Q',*TICKS)
|
||||
return bytes(result),offset,positions
|
||||
|
||||
|
||||
checks=0
|
||||
def probe(data,status=0,mode='bytes',offset=None,mods=1):
|
||||
global checks
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
p=Path(directory)/'fixture';p.write_bytes(data)
|
||||
result=subprocess.run([str(PROBE),mode,str(p)],capture_output=True,text=True)
|
||||
assert result.returncode==(1 if status else 0),(result.returncode,result.stderr)
|
||||
report=json.loads(result.stdout)
|
||||
assert report['status']==status,report
|
||||
if not status:
|
||||
assert report['header_offset']==offset,report
|
||||
assert report['next_offset']==offset+24,report
|
||||
assert report['mod_count']==mods,report
|
||||
assert report['tick_fields_hex']==[f'{v:016x}' for v in TICKS],report
|
||||
elif status==6 and mode=='bytes':
|
||||
assert report['needed']>len(data),report
|
||||
checks+=1
|
||||
|
||||
|
||||
for length in (0,1,254,255,256,4097):
|
||||
data,offset,_=fixture(long_string=b'x'*length,extended_version=True,nested=True)
|
||||
probe(data,offset=offset)
|
||||
for count in (0,1,254,255):
|
||||
data,offset,_=fixture(mod_count=count)
|
||||
probe(data,offset=offset,mods=count)
|
||||
data,offset,positions=fixture()
|
||||
for length in range(len(data)):
|
||||
probe(data[:length],6)
|
||||
for index,value in [(4,78),(8,1),(positions['difficulty'],3),(positions['boolean'],2),
|
||||
(positions['property_type'],4),(positions['property_count'],1)]:
|
||||
changed=bytearray(data);changed[index]=value;probe(changed,4)
|
||||
probe(data[:9]+b'\xff'+struct.pack('<I',1024*1024+1),7)
|
||||
probe(data[:positions['mod_count']]+b'\xff'+struct.pack('<I',4097),7)
|
||||
deep=localised()
|
||||
for i in range(65): deep=localised(b'',(deep,))
|
||||
probe(data[:positions['ending']]+deep+data[positions['ending']:],7)
|
||||
|
||||
for method in (zipfile.ZIP_STORED,zipfile.ZIP_DEFLATED):
|
||||
output=io.BytesIO()
|
||||
with zipfile.ZipFile(output,'w',compression=method) as z:
|
||||
z.writestr('fixture/level.dat0',zlib.compress(data))
|
||||
probe(output.getvalue(),mode='zip',offset=offset)
|
||||
print(f'PASS: {checks} scenario-envelope cases, variable offsets, sentinel lengths, truncation, limits and failure atomicity')
|
||||
(ROOT/'build/save-layout/verification.json').write_text(json.dumps({'status':'pass','cases':checks,
|
||||
'scope':'2.0.77 envelope with empty startup settings; not world loading'},indent=2)+'\n')
|
||||
Reference in new issue
Block a user