modemwars/extracted/vice/playSession.py
2026-08-23 02:09:40 -05:00

101 lines
4.3 KiB
Python

#!/usr/bin/env python3
# playSession.py - boot Modem Wars in VICE on a private Xvfb display and drive it with real key
# events (xdotool), taking screenshots through the remote monitor at each step.
#
# python3 playSession.py <disk.d64> <outDir> <script>
# where <script> is a comma separated list of steps:
# w<seconds> wait
# k<key>[:n] send key n times (xdotool keysyms, e.g. KP_2, KP_0, F1, space)
# s<name> take a screenshot called <name>.png
import os, socket, subprocess, sys, time, random
SCRATCH = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRATCH)
from viceDriver import pickFreePort, recvUntilPrompt
def findDisplay():
for n in range(90, 120):
if not os.path.exists(f"/tmp/.X11-unix/X{n}"):
return n
raise SystemExit("no free X display")
def main():
disk = os.path.abspath(sys.argv[1])
outDir = os.path.abspath(sys.argv[2])
steps = sys.argv[3].split(",")
os.makedirs(outDir, exist_ok=True)
display = f":{findDisplay()}"
port = pickFreePort()
env = dict(os.environ, DISPLAY=display)
xvfb = subprocess.Popen(["Xvfb", display, "-screen", "0", "800x600x24", "-nolisten", "tcp"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
time.sleep(2)
vice = subprocess.Popen(
["x64sc", "-remotemonitor", "-remotemonitoraddress", f"127.0.0.1:{port}",
"-drive8truedrive", "-drive8type", "1541", "-sounddev", "dummy", "-warp", "-jamaction", "2",
"-autostart-warp", "-model", "ntsc", "-joydev2", "1", "-chdir", SCRATCH, "-autostart", disk],
env=env, stdout=open(f"{outDir}/vice.log", "w"), stderr=subprocess.STDOUT)
try:
sock = None
for _ in range(120):
try:
sock = socket.create_connection(("127.0.0.1", port), timeout=1)
break
except OSError:
time.sleep(0.3)
if sock is None:
raise SystemExit("monitor did not open")
recvUntilPrompt(sock, 10)
sock.sendall(b"break 0800\n"); recvUntilPrompt(sock, 10)
sock.sendall(b"x\n"); recvUntilPrompt(sock, 5)
print("loading (warp) ...", flush=True)
print(recvUntilPrompt(sock, 400)[-80:], flush=True) # stops at $0800
sock.sendall(b"del\n"); recvUntilPrompt(sock, 5)
sock.sendall(b"x\n"); recvUntilPrompt(sock, 3) # run at full speed from here
# find the emulator window
winId = None
for _ in range(40):
out = subprocess.run(["xdotool", "search", "--name", "VICE"], env=env,
capture_output=True, text=True).stdout.split()
if out:
winId = out[-1]
break
time.sleep(0.5)
print("window:", winId, flush=True)
for step in steps:
kind, rest = step[0], step[1:]
if kind == "w":
time.sleep(float(rest))
elif kind == "k":
key, _, count = rest.partition(":")
for _ in range(int(count or 1)):
subprocess.run(["xdotool", "key", "--window", winId, "--clearmodifiers", key],
env=env, check=False)
time.sleep(0.25)
elif kind == "h":
# hold a key down long enough for the game to sample it (it polls once per frame)
key, _, ms = rest.partition(":")
subprocess.run(["xdotool", "keydown", "--window", winId, key], env=env, check=False)
time.sleep(int(ms or 300) / 1000.0)
subprocess.run(["xdotool", "keyup", "--window", winId, key], env=env, check=False)
time.sleep(0.3)
elif kind == "s":
sock.sendall(b"\n"); recvUntilPrompt(sock, 10) # enter monitor
path = f"{outDir}/{rest}.png"
sock.sendall(f'screenshot "{path}" 2\n'.encode()); recvUntilPrompt(sock, 10)
sock.sendall(b"x\n"); recvUntilPrompt(sock, 5) # resume
print("shot", path, flush=True)
sock.sendall(b"\n"); recvUntilPrompt(sock, 5)
sock.sendall(b"quit\n")
time.sleep(1)
finally:
vice.kill(); vice.wait()
xvfb.kill(); xvfb.wait()
main()