101 lines
4.3 KiB
Python
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()
|