#!/usr/bin/env bash # Behavior gate for the Space Taxi simulation: every screen's ride -- # A..X and the 25th, Y -- checked against the real C64. # # stsim rides a screen for up to 4000 ticks and prints one line of state # a tick: the cab, the fare machine, the demo playback, the RNG, the HUD, # the sprites shown and hashes of screen RAM, color RAM and the charset. # c64trace.py rides the same input on the original game in VICE and # prints the same line from the C64's memory. gateHashes.txt holds the # md5 of each C64 trace, so `check` -- which needs no emulator -- is a # byte-for-byte comparison with the real machine. # # H, W, T and X ride the C64's own attract recordings; the other screens # ride synthesised streams from gateStreams.h (tools/stsim/recorder). # # gate.sh check [dir] every ride's md5 against gateHashes.txt; a ride # that differs is diffed against its golden in dir, # if one was captured, to show where # gate.sh c64 [L...] ride the C64 too (VICE, c64trace.py) and compare # the two traces byte for byte, showing the first # fields that differ; every screen by default # gate.sh c64hashes the C64 traces' md5s in gateHashes.txt's form -- # the only way gateHashes.txt is made # gate.sh capture [dir] record goldens from the current tree # gate.sh hashes print the current tree's md5 per screen # # The rides run on the game's level bake, so build the game first # (gateLib.sh finds it). The c64 modes keep their traces in # build/c64trace/ and run C64_JOBS rides at once (default 4); they exit 2 # when VICE or the original disk is missing (see c64trace.py). set -u here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) mode=${1:-check} source "$here/gateLib.sh" traces=$here/../../build/c64trace/traces # simTrace L NN FILE: the current tree's ride. simTrace() { "$here/stsim" "$levels/level$2.dat" "$1" "$ticks" > "$3" 2>/dev/null } # c64Ride L NN: ride screen L on the C64 into $traces/L.jsonl and compare # it with stsim's; prints one verdict line, returns 1 on a difference. A # ride that could not be traced leaves $traces/L.failed behind. c64Ride() { local l=$1 n=$2 rm -f "$traces/$l.failed" STSIM_DEMOBUF=1 "$here/stsim" "$levels/level$n.dat" "$l" 0 > "$traces/$l.buf.json" simTrace "$l" "$n" "$traces/$l.sim.jsonl" if ! timeout -s KILL 900 python3 "$here/c64trace.py" "$l" "$ticks" "$traces/$l.buf.json" > "$traces/$l.jsonl"; then echo " $l: *** the C64 ride failed ***" touch "$traces/$l.failed" return 1 fi if cmp -s "$traces/$l.sim.jsonl" "$traces/$l.jsonl"; then printf ' %s: IDENTICAL to the C64 (%s lines)\n' "$l" "$(wc -l < "$traces/$l.jsonl")" return 0 fi echo " $l: *** DIFFERS from the C64 ***" python3 "$here/compare.py" "$traces/$l.sim.jsonl" "$traces/$l.jsonl" 3 | sed 's/^/ /' return 1 } # c64All [L...]: every named screen (all of them by default), C64_JOBS at # a time. c64All() { local want=" ${*:-} " fail=0 p l n python3 "$here/c64trace.py" --prepare || exit $? mkdir -p "$traces" for p in $pairs; do l=${p%%:*}; n=${p##*:} if [ -n "${*:-}" ] && [[ "$want" != *" $l "* ]]; then continue fi while [ "$(jobs -rp | wc -l)" -ge "${C64_JOBS:-4}" ]; do wait -n || fail=1 done c64Ride "$l" "$n" & done while [ "$(jobs -rp | wc -l)" -gt 0 ]; do wait -n || fail=1 done return $fail } case "$mode" in capture) dir=${2:-$here/golden} mkdir -p "$dir" for p in $pairs; do l=${p%%:*}; n=${p##*:} simTrace "$l" "$n" "$dir/demo$l.jsonl" printf ' %s: %s lines\n' "$l" "$(wc -l < "$dir/demo$l.jsonl")" done echo "gate: captured to $dir" ;; check) dir=${2:-$here/golden} fail=0 for p in $pairs; do l=${p%%:*}; n=${p##*:} got=$(mktemp) simTrace "$l" "$n" "$got" want=$(awk -v l="$l" '$1 == l { print $2 }' "$here/gateHashes.txt") have=$(md5sum < "$got" | cut -d' ' -f1) if [ "$have" = "$want" ]; then printf ' %s: IDENTICAL to the C64 (%s lines, md5 as gateHashes.txt)\n' "$l" "$(wc -l < "$got")" else echo " $l: *** DIFFERS from the C64 *** (md5 $have, gateHashes.txt ${want:-has none}; gate.sh c64 $l shows where)" if [ -f "$dir/demo$l.jsonl" ]; then python3 "$here/compare.py" "$dir/demo$l.jsonl" "$got" 3 | sed 's/^/ golden vs now: /' fi fail=1 fi rm -f "$got" done [ $fail -eq 0 ] && echo "gate: PASS" || echo "gate: FAIL" # The cel census rides the same streams: a cel a ride displays that its # manifest does not bake is a mid-ride interpreted sprite. "$here/celGate.sh" || fail=1 exit $fail ;; c64) shift c64All "$@" fail=$? [ $fail -eq 0 ] && echo "gate c64: PASS" || echo "gate c64: FAIL" exit $fail ;; c64hashes) c64All >&2 if compgen -G "$traces/*.failed" > /dev/null; then echo "gate c64hashes: a C64 ride failed; no hashes" >&2 exit 1 fi for p in $pairs; do l=${p%%:*} printf '%s %s\n' "$l" "$(md5sum < "$traces/$l.jsonl" | cut -d' ' -f1)" done ;; hashes) for p in $pairs; do l=${p%%:*}; n=${p##*:} got=$(mktemp) simTrace "$l" "$n" "$got" printf '%s %s\n' "$l" "$(md5sum < "$got" | cut -d' ' -f1)" rm -f "$got" done ;; *) sed -n '2,33p' "$0" exit 2 ;; esac