Files
verstack/scripts/emulator_preflight.py
Verstack Local f73167de85 Add downloadable VMs and improve catalog processing
Package Apple Silicon VM exports and cached runtime switching with local cabinet controls and conagent provisioning. Preserve experimental SPIKE 2 emulation.

Improve preparation concurrency, Ghidra checkpoints, signature importing, sound indexing and client-side spectrograms. Include regression tests and validation notes.
2026-09-14 20:03:59 -05:00

97 lines
3.9 KiB
Python

#!/usr/bin/env python3
"""Read-only inventory for the supplied SPIKE 3 process emulator.
Does not execute donor code, prepare images, install tools, or read credentials.
Passing this inventory does not establish that a game can boot.
"""
import argparse
import json
import platform
import shutil
from pathlib import Path
SOURCE_FILES = (
"emulation/spike3-emu",
"emulation/process/spike3_emu.py",
"emulation/process/config.default.json",
"emulation/machine/service.py",
"emulation/machine/profiles/pokemon-pro.json",
"emulation/dashboard/server.py",
"emulation/vpx/layout.json",
)
RUNTIME_FILES = (
"games/pokemon_pro/game",
"games/pokemon_pro/image.bin",
"games/pokemon_pro/assets/godot/main.pck",
"games/pokemon_pro/spike3/spike_menu/game",
"games/pokemon_pro/spike3/spike_menu/image.bin",
"games/pokemon_pro/spike3/bin/boot_display",
"lib/ld-linux-aarch64.so.1",
)
def inventory(source: Path, runtime: Path) -> dict:
machine = platform.machine().lower()
native = machine in ("aarch64", "arm64")
checks = []
for category, root, paths in (
("source", source, SOURCE_FILES), ("runtime", runtime, RUNTIME_FILES)
):
for relative in paths:
path = root / relative
checks.append({"category": category, "name": relative,
"present": path.is_file(), "path": str(path)})
commands = [(name,) for name in (
"python3", "bwrap", "ffmpeg", "ffplay", "Xvfb", "x11vnc", "websockify"
)]
commands += [("readelf", "objdump")]
commands += [("cc", "gcc")] if native else [
("aarch64-linux-gnu-gcc",), ("qemu-aarch64-static", "qemu-aarch64")
]
for alternatives in commands:
found = next((path for command in alternatives
if (path := shutil.which(command))), None)
checks.append({"category": "tool", "name": " or ".join(alternatives),
"present": found is not None, "path": found})
return {
"schema": 1,
"platform": platform.system(),
"architecture": machine,
"suggested_engine": "native" if native else "qemu-user",
"inventory_complete": platform.system() == "Linux" and all(
check["present"] for check in checks
),
"boot_verified": False,
"checks": checks,
"limitations": [
"Inventory targets the donor's default Linux process/display path.",
"Library closure, ELF architecture, Mesa, noVNC assets, namespace permissions, and boot still require launcher validation.",
"The current launcher expects source and runtime in one workspace; a separate runtime root here is inventory only.",
"This inventories the standalone donor, not the Verstack Docker worker. Use emulator/README.md to test the integrated screen/audio and session controls.",
],
}
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--source", type=Path, default=Path.home() / "pokemon_emulator")
parser.add_argument("--runtime", type=Path, help="Extracted runtime root; defaults to source")
parser.add_argument("--json", action="store_true", help="Print structured inventory")
args = parser.parse_args()
source = args.source.expanduser().resolve()
result = inventory(source, (args.runtime or source).expanduser().resolve())
if args.json:
print(json.dumps(result, indent=2))
else:
print(f"SPIKE 3 inventory: {result['platform']} {result['architecture']} ({result['suggested_engine']})")
for check in result["checks"]:
print(f"{'FOUND' if check['present'] else 'MISSING'} {check['category']}: {check['name']}")
for limitation in result["limitations"]:
print(limitation)
return 0 if result["inventory_complete"] else 1
if __name__ == "__main__":
raise SystemExit(main())