Files
factorio-ios/tools/record_target.py
T
SashaGoncharov19 51d085e865 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.
2026-10-23 15:27:58 +04:00

82 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Record exact local executable and installed data identities, without copying assets."""
import argparse
import hashlib
import json
import plistlib
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from lift_rng import arm64_image
ROOT = Path(__file__).resolve().parents[1]
EXPECTED_BINARY = '00b014d78df07e05ab1cee853904c8f7b3814ca652c043461a9d49fbad46cdf6'
EXPECTED_SLICE = '2cb4de127b50d54c6c5666acfef2263fae16d11a3361ad39353ecf8a66a4d9c7'
def digest(path):
result = hashlib.sha256()
with path.open('rb') as source:
for block in iter(lambda: source.read(1024 * 1024), b''):
result.update(block)
return result.hexdigest()
def command(*args):
return subprocess.check_output(args, cwd=ROOT, text=True).strip()
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('app', type=Path)
args = parser.parse_args()
app = args.app.resolve(strict=True)
binary = app / 'Contents/MacOS/factorio'
source = binary.read_bytes()
image, offset = arm64_image(source)
binary_hash, slice_hash = hashlib.sha256(source).hexdigest(), hashlib.sha256(image).hexdigest()
if binary_hash != EXPECTED_BINARY or slice_hash != EXPECTED_SLICE:
raise ValueError('Target differs from the inspected Factorio 2.0.77 build')
info = plistlib.loads((app / 'Contents/Info.plist').read_bytes())
assets = app / 'Contents/data'
entries = []
for path in sorted(assets.rglob('*')):
if path.is_symlink():
raise ValueError(f'Asset symlink requires explicit inventory policy: {path}')
if path.is_file():
entries.append({'path': path.relative_to(assets).as_posix(),
'bytes': path.stat().st_size, 'sha256': digest(path)})
if not entries:
raise ValueError('No installed data files found')
packages = []
for path in sorted(assets.glob('*/info.json')):
metadata = json.loads(path.read_text())
packages.append({'directory': path.parent.name, 'name': metadata.get('name'),
'version': metadata.get('version'), 'dependencies': metadata.get('dependencies')})
asset_manifest = {'scope': 'all regular files under installed Contents/data; no user mods or saves',
'files': entries, 'file_count': len(entries),
'total_bytes': sum(e['bytes'] for e in entries), 'packages': packages,
'gameplay_configuration': 'not selected; inventory does not prove save compatibility'}
out = ROOT / 'build/target'
out.mkdir(parents=True, exist_ok=True)
asset_path = out / 'assets.json'
asset_path.write_text(json.dumps(asset_manifest, indent=2) + '\n')
report = {'recorded_at': datetime.now(timezone.utc).isoformat(), 'app': str(app),
'version': info.get('CFBundleShortVersionString'), 'binary_sha256': binary_hash,
'arm64_sha256': slice_hash, 'arm64_slice_offset': offset, 'arm64_bytes': len(image),
'binary_uuids': command('xcrun', 'dwarfdump', '--uuid', str(binary)).splitlines(),
'xcode': command('xcodebuild', '-version'),
'iphoneos_sdk': command('xcrun', '--sdk', 'iphoneos', '--show-sdk-version'),
'wabt_revision': command('git', '-C', str(ROOT / 'third_party/wabt'), 'rev-parse', 'HEAD'),
'asset_manifest_sha256': digest(asset_path), 'asset_file_count': len(entries),
'source_files': {str(p.relative_to(ROOT)): digest(p) for base in ('tools', 'runtime', 'platform/ios', 'tests')
for p in sorted((ROOT / base).glob('*'))
if p.is_file() and p.suffix in ('.py', '.mjs', '.c', '.h', '.m', '.yml')}}
(out / 'manifest.json').write_text(json.dumps(report, indent=2) + '\n')
print(f'PASS: pinned executable and ARM64 slice; recorded {len(entries)} asset files ({asset_manifest["total_bytes"]} bytes)')
print(out / 'manifest.json')
if __name__ == '__main__':
main()