joeylib2/scripts/lib/dvxGate.sh
2026-09-29 19:31:34 -05:00

168 lines
5.5 KiB
Bash

# Shared by the DVX gates (scripts/verify-dvx-*.sh, scripts/bench-dvx.sh) and
# joey-run: a DVX installation staged in a scratch folder -- a copy of the
# runtime the installer took from a DVX tree (toolchains/opt/dvx, without
# its SDK) plus JoeyLib programs from build/dvx/bin -- whose DVX.INI makes
# one of them the desktop, so DVX boots straight into it; and the DOSBox
# Staging run of that folder as drive C:. Nothing here writes to the DVX
# project or to toolchains/opt/dvx. Source after joeyEnv.sh.
DVX_GATE="$(basename "$0" .sh)"
DVX_BIN="$JOEY_REPO/build/dvx/bin"
# The folder a program installs in under apps/, and the name of its log.
DVX_VENDOR=joeylib
DVX_LOG_NAME=joeylog.txt
# The runtime parts of toolchains/opt/dvx (all but the SDK).
DVX_RUNTIME="dvx.exe config system libs widgets apps"
# The desktop the gates run: 640x480 in 16-bit colour, DVX's own DOS test
# mode, where a program's window shows the stage at 1x. A caller may set
# others before dvxStage (joey-run shows 1024x768, the stage at 3x).
DVX_VIDEO_W=${DVX_VIDEO_W:-640}
DVX_VIDEO_H=${DVX_VIDEO_H:-480}
DVX_VIDEO_BPP=${DVX_VIDEO_BPP:-16}
# DOSBox Staging as DVX's harness runs it: an S3 card (VBE 2.0 with a linear
# frame buffer, which DVX needs) and the host's full speed.
DVX_DOSBOX_MACHINE=svga_s3
DVX_DOSBOX_MEM_MB=32
# dvxAppDir PROGRAM -- print the built folder of a program (its make/dvx.mk
# name: uber, keys, ...): the .app, its help and its DATA.
dvxAppDir() {
printf '%s\n' "$DVX_BIN/apps/$DVX_VENDOR/$1"
}
# dvxNeedBuild PROGRAM... -- exit 2 unless each program is built.
dvxNeedBuild() {
local prog
for prog in "$@"; do
if [ ! -f "$(dvxAppDir "$prog")/$prog.app" ]; then
echo "$DVX_GATE: $(dvxAppDir "$prog")/$prog.app is missing; run: make dvx" >&2
exit 2
fi
done
}
# dvxStage DIR APPDIR... -- stage DVX in DIR/DVX with the programs in the
# APPDIRs (each a folder holding NAME.app, from dvxAppDir or anywhere else),
# installed as apps/joeylib/NAME. The first one is the desktop. DIR may hold
# an earlier staging: the runtime and the programs are copied over it, and
# what DVX wrote there (its settings, the programs' CONFIG folders) stays.
dvxStage() {
local dir="$1"
local part
local app
local name
shift
mkdir -p "$dir/DVX/apps/$DVX_VENDOR"
for part in $DVX_RUNTIME; do
cp -a "$DVX_ROOT/$part" "$dir/DVX/"
done
for app in "$@"; do
name="$(basename "$app")"
rm -rf "${dir:?}/DVX/apps/$DVX_VENDOR/$name"
cp -a "$app" "$dir/DVX/apps/$DVX_VENDOR/"
done
name="$(basename "$1")"
dvxIniSet "$dir/DVX/config/DVX.INI" "apps/$DVX_VENDOR/$name/$name.app"
}
# dvxIniSet INI DESKTOP -- DVX.INI for a run: DESKTOP as the desktop program,
# no first-run Welcome window, the gate video mode, and no desktop sounds (a
# recording gate must hear only the program). DOS line ends are kept.
dvxIniSet() {
python3 - "$1" "$2" "$DVX_VIDEO_W" "$DVX_VIDEO_H" "$DVX_VIDEO_BPP" <<'PY'
import sys
path, desktop, width, height, bpp = sys.argv[1:]
sets = {("shell", "desktop"): desktop, ("video", "width"): width, ("video", "height"): height, ("video", "bpp"): bpp}
lines = open(path, newline="").read().split("\r\n")
out = []
section = ""
for line in lines:
stripped = line.strip()
if stripped.startswith("["):
section = stripped.strip("[]").lower()
elif "=" in stripped and not stripped.startswith(";"):
key = stripped.split("=")[0].strip()
if section == "shell" and key.lower() == "welcome":
continue
if section == "sounds":
line = "%s =" % key
elif (section, key.lower()) in sets:
line = "%s = %s" % (key, sets[(section, key.lower())])
out.append(line)
open(path, "w", newline="").write("\r\n".join(out))
PY
}
# dvxDosboxConf FILE DIR [SBTYPE] -- a DOSBox Staging configuration that boots
# DIR/DVX as C:\DVX. SBTYPE (e.g. sb16) adds a Sound Blaster; without it the
# machine is silent (DVX then runs without sound, as on a PC without a card).
dvxDosboxConf() {
local sbType="${3:-none}"
local nosound=true
[ "$sbType" = none ] || nosound=false
cat > "$1" <<EOF
[sdl]
fullscreen = false
[dosbox]
machine = $DVX_DOSBOX_MACHINE
memsize = $DVX_DOSBOX_MEM_MB
[cpu]
cpu_cycles = max
[mixer]
nosound = $nosound
[sblaster]
sbtype = $sbType
[autoexec]
mount c "$2"
c:
cd \\DVX
DVX.EXE
EOF
}
# dvxProgramLog DIR PROGRAM -- print the path of PROGRAM's log in the staged
# DVX: in its settings folder, CONFIG\ plus the folder the .app is in (DOS
# names are upper case on the host). Prints nothing while there is none.
dvxProgramLog() {
find "$1/DVX" -ipath "*/config/apps/$DVX_VENDOR/$2/$DVX_LOG_NAME" -print -quit 2>/dev/null
}
# dvxAwaitLog DIR PROGRAM REGEX SECONDS PID -- wait up to SECONDS (or until PID
# exits) for PROGRAM's log to hold a line matching REGEX; print its path if
# it does.
dvxAwaitLog() {
local log
local waited
for ((waited = 0; waited < $4; waited++)); do
log="$(dvxProgramLog "$1" "$2")"
if [ -n "$log" ] && tr -d '\r' < "$log" | grep -qE "$3"; then
printf '%s\n' "$log"
return 0
fi
joeyAlive "$5" || return 1
sleep 1
done
return 1
}
# dvxDvxLog DIR -- print the tail of the staged DVX's own log (DVX.LOG), the
# place a program that failed to load is explained.
dvxDvxLog() {
local log
log="$(find "$1/DVX" -maxdepth 1 -iname dvx.log -print -quit)"
[ -n "$log" ] && tr -d '\r' < "$log" | tail -"${2:-15}"
return 0
}