155 lines
5.5 KiB
Bash
Executable file
155 lines
5.5 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Behaviour 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, colour 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
|