joeylib2/scripts/run-agi.sh

277 lines
8.9 KiB
Bash
Executable file

#!/usr/bin/env bash
# Launch the AGI interpreter in the named platform's emulator with a
# staged AGI v2 game directory. The game's data files are copied flat
# alongside the binary so AGI's CWD-relative default ("./LOGDIR" etc.)
# works on platforms whose autostart can't pass argv.
#
# scripts/run-agi.sh <platform> [game] [room]
# platform : dos | amiga | atarist | iigs
# game : subdirectory under examples/agi/gamedata/ (default kq3)
# room : starting room number 1..255. Skips logic.0's hardcoded
# title-room target and jumps straight to the named room.
# Only forwarded to DOS today -- the Atari ST / Amiga
# autostart paths can't pass argv to the binary; their
# AGI starts at the title screen until we wire a stage-
# time config-file fallback or implement enough title
# opcodes for the title to advance on its own.
#
# Required files in the game directory (others are ignored):
# LOGDIR PICDIR VIEWDIR SNDDIR VOL.0 [VOL.1 ...] [OBJECT] [WORDS.TOK]
#
# IIgs is not yet wired (needs the disk packager extended to a larger
# 2mg volume plus case-folded ProDOS file names); the script reports
# what's missing rather than launching.
set -euo pipefail
if [[ $# -lt 1 || $# -gt 3 ]]; then
echo "usage: $0 <dos|amiga|atarist|iigs> [game-name] [starting-room]" >&2
exit 2
fi
platform=$1
game=${2:-kq3}
room=${3:-0}
if ! [[ $room =~ ^[0-9]+$ ]] || (( room > 255 )); then
echo "starting-room must be 0..255 (got '$room')" >&2
exit 2
fi
repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
gamedata=$repo/examples/agi/gamedata/$game
support=$repo/toolchains/emulators/support
if [[ ! -d $gamedata ]]; then
echo "$gamedata: not an AGI v2 game directory" >&2
if [[ -d $repo/examples/agi/gamedata ]]; then
echo "available games:" >&2
find "$repo/examples/agi/gamedata" -maxdepth 1 -mindepth 1 -type d -printf ' %f\n' >&2
fi
exit 1
fi
if [[ ! -f $gamedata/LOGDIR ]]; then
echo "$gamedata: no LOGDIR -- not an AGI v2 game directory" >&2
exit 1
fi
# stageGameData WORKDIR
# Copies the subset of files AGI v2 reads (DIR files, VOL.x, OBJECT,
# WORDS.TOK) into WORKDIR. Leaves Sierra's original AGI binary, OVL
# graphics drivers, and platform-specific launchers behind so a flat
# stage doesn't clash with our binary's name on case-insensitive
# filesystems (e.g. Amiga FFS: "AGI" vs our "Agi").
stageGameData() {
local destDir=$1
local f
for f in LOGDIR PICDIR VIEWDIR SNDDIR OBJECT WORDS.TOK; do
if [[ -f $gamedata/$f ]]; then
cp "$gamedata/$f" "$destDir/$f"
fi
done
for f in "$gamedata"/VOL.*; do
[[ -f $f ]] && cp "$f" "$destDir/$(basename "$f")"
done
}
runDos() {
local bin=$repo/build/dos/bin/AGI.EXE
local conf=$repo/scripts/dosbox-386sx16.conf
local work
if [[ ! -f $bin ]]; then
echo "$bin not built. Run 'make -f make/dos.mk EXAMPLE=agi' first." >&2
exit 1
fi
# Sweep any leftover staging dirs from previous runs that
# didn't reach their trap (DOSBox window close, SIGKILL, etc.).
# We're about to make a new one anyway; freshly leftover dirs
# serve no purpose and just leak ~1 MB each.
rm -rf /tmp/joeylib-agi-dos.* 2>/dev/null || true
rm -f /tmp/joeylib-agi-dos-last.log 2>/dev/null || true
work=$(mktemp -d -t joeylib-agi-dos.XXXXXX)
# Cleanup trap: copy DJGPP's diagnostic log out (DOS uppercases
# so the file lands as JOEYLOG.TXT, not joeylog.txt), then nuke
# the staging dir. Hook EXIT plus the common interrupt signals
# so the cleanup fires even when the user closes the DOSBox
# window or Ctrl-Cs the terminal -- both bypass plain EXIT in
# some bash configurations.
cleanupDos() {
local log
for log in "$work/JOEYLOG.TXT" "$work/joeylog.txt"; do
if [[ -f $log ]]; then
cp "$log" /tmp/joeylib-agi-dos-last.log
break
fi
done
rm -rf "$work"
}
trap cleanupDos EXIT INT TERM HUP
cp "$bin" "$work/AGI.EXE"
stageGameData "$work"
# AGI.EXE reads game data from its CWD (we mount $work as C: and
# cd there first), so argv[1] is "." -- the starting-room override
# lives in argv[2]. Skip the argv when room=0 so the binary
# exercises its default code path.
local agi_cmd="AGI.EXE"
if (( room != 0 )); then
agi_cmd="AGI.EXE . $room"
fi
cat <<EOF
DOSBox: mounting $work as C:, running '$agi_cmd' for $game.
ESC quits the demo, then 'pause' waits for a keypress in the shell.
EOF
local dosboxArgs=(
-conf "$conf"
-set "mouse_capture=seamless"
)
# CYCLES=max (or any other DOSBox cycles value) overrides the conf's
# period-correct 386SX-16 cap. Use this to A/B test whether perf
# ceilings are coming from the simulated CPU or the code itself.
if [[ -n "${CYCLES:-}" ]]; then
dosboxArgs+=(-set "cpu cycles=$CYCLES")
fi
dosboxArgs+=(
-c "C:"
-c "$agi_cmd"
-c "pause"
--exit "$work"
)
# Avoid `exec`: we need bash to stay alive so the EXIT trap above
# actually runs and removes the staged $work dir. With `exec`,
# dosbox replaces the shell and the trap never fires, leaking
# ~1 MB of game data into /tmp on every launch.
dosbox "${dosboxArgs[@]}"
}
runAtariST() {
local bin=$repo/build/atarist/bin/AGI.PRG
local tos=$support/emutos-512k.img
local work
if [[ ! -f $bin ]]; then
echo "$bin not built. Run 'make -f make/atarist.mk EXAMPLE=agi' first." >&2
exit 1
fi
if [[ ! -f $tos ]]; then
echo "TOS ROM missing: $tos" >&2
echo "Run ./toolchains/install.sh (EmuTOS should have been staged)." >&2
exit 1
fi
rm -rf /tmp/joeylib-agi-atarist.* 2>/dev/null || true
work=$(mktemp -d -t joeylib-agi-atarist.XXXXXX)
trap 'rm -rf "$work"' EXIT INT TERM HUP
cp "$bin" "$work/AGI.PRG"
stageGameData "$work"
cat <<EOF
Hatari: mounting $work as GEMDOS C:, autostarting AGI.PRG for $game.
EOF
# Run hatari as a child so the EXIT trap removes the staged
# game data dir; `exec` would have hatari replace bash and
# leak $work into /tmp on every launch.
hatari \
--tos "$tos" \
--harddrive "$work" \
--gemdos-drive C \
--auto "C:\\AGI.PRG"
}
runAmiga() {
local bin=$repo/build/amiga/bin/Agi
local kickstart=$support/kickstart.rom
local workbench=$support/workbench.adf
local work
if [[ ! -f $bin ]]; then
echo "$bin not built. Run 'make -f make/amiga.mk EXAMPLE=agi' first." >&2
exit 1
fi
rm -rf /tmp/joeylib-agi-amiga.* 2>/dev/null || true
work=$(mktemp -d -t joeylib-agi-amiga.XXXXXX)
trap 'rm -rf "$work"' EXIT INT TERM HUP
mkdir -p "$work/s"
cp "$bin" "$work/Agi"
stageGameData "$work"
# ':' prefix anchors to volume root so AmigaDOS doesn't search C:.
echo ":Agi" > "$work/s/startup-sequence"
local fsargs=(
--amiga_model=A500
--fast_memory=2048
--hard_drive_0="$work"
--hard_drive_0_label=JOEYLIB
--automatic_input_grab=0
--middle_click_ungrab=1
--mouse_integration=1
--floppy_drive_speed=800
)
if [[ -f $kickstart ]]; then
fsargs+=(--kickstart_file="$kickstart")
fi
if [[ -f $workbench ]]; then
# If a Workbench floppy is staged it boots from DF0: instead of
# the HD, so the startup-sequence never fires. Skip Workbench
# for AGI to keep the launch one-shot; user can still launch
# manually with run-amiga.sh if they want the GUI.
echo "(skipping workbench.adf so the JOEYLIB HD boots Agi directly)"
fi
cat <<EOF
FS-UAE booting JOEYLIB hard drive ($work) and auto-running Agi for $game.
EOF
fs-uae "${fsargs[@]}"
}
runIigs() {
cat <<EOF >&2
IIgs AGI run is not wired up yet.
The existing IIgs disk packager (toolchains/iigs/package-disk.sh) builds
joey.2mg from build/iigs/bin/* only; it doesn't accept arbitrary game
data, the disk size is fixed at 800K (KQ3's ~1.5 MB of resources won't
fit), and ProDOS case-folding for game-data file names has not been
audited.
To wire this up the package-disk.sh tool needs a larger volume option
and an "extra data" path argument, and run-iigs.sh needs an AGI mode
that points GSplus at the resulting 2mg. Tracked separately.
Workaround for now: build with 'make -f make/iigs.mk EXAMPLE=agi' to
prove the IIgs link still works, but launch via run-iigs.sh and pick
JOEYLIB:AGI in Finder -- it will fail to find LOGDIR (no game data on
disk), which is the expected current state.
EOF
exit 1
}
case "$platform" in
dos) runDos ;;
atarist) runAtariST ;;
amiga) runAmiga ;;
iigs) runIigs ;;
*)
echo "$platform: unknown platform (expected dos, amiga, atarist, or iigs)" >&2
exit 2
;;
esac