226 lines
7.9 KiB
Bash
226 lines
7.9 KiB
Bash
# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
|
|
# See LICENSE.md for license information.
|
|
# SPDX-License-Identifier: MIT
|
|
#
|
|
# 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 unpacked from DVX's dist/dvx.zip (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. A project built
|
|
# on make/project.mk sets DVX_BIN (and DVX_VENDOR, if it changed it) to its
|
|
# own before sourcing.
|
|
|
|
DVX_GATE="$(basename "$0" .sh)"
|
|
DVX_BIN="${DVX_BIN:-$JOEY_REPO/build/dvx/bin}"
|
|
# UBER's probe build (make dvx-probe), packaged apart from bin/.
|
|
DVX_PROBE_DIR="${DVX_PROBE_DIR:-$JOEY_REPO/build/dvx/probe}"
|
|
# The folder a program installs in under apps/ (make/dvx.mk's DVX_VENDOR), and
|
|
# the name of its log.
|
|
DVX_VENDOR="${DVX_VENDOR:-joeylib}"
|
|
DVX_LOG_NAME=joeylog.txt
|
|
# The port's own settings file in a program's settings folder, and its
|
|
# full-screen switch (src/dvx/hal.c).
|
|
DVX_SETTINGS_FILE=JOEYLIB.INI
|
|
DVX_SETTINGS_SECTION=display
|
|
DVX_SETTINGS_FULL_SCREEN=fullScreen
|
|
# DVX.LOG's line when a program's full-screen mode is set (the port asks for
|
|
# the stage's size at 8 bits a pixel).
|
|
DVX_FULL_SCREEN_LOG="Video: full screen 320x200 at 8 bpp"
|
|
# DVX.LOG's line when a program cannot be loaded (a missing file, a name
|
|
# that is not 8.3, an unresolved import).
|
|
DVX_LOAD_FAILED_LOG="DXE load failed"
|
|
# 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"
|
|
}
|
|
|
|
|
|
# dvxFullScreen DIR PROGRAM -- have PROGRAM start on the whole screen in the
|
|
# DVX staged in DIR: the port's own setting (JOEYLIB.INI, [display]
|
|
# fullScreen, in the program's settings folder), as Game > Full Screen leaves
|
|
# it. DOS names are case-blind, so an existing folder of any case is used.
|
|
dvxFullScreen() {
|
|
local conf
|
|
conf="$(find "$1/DVX" -ipath "*/config/apps/$DVX_VENDOR/$2" -type d -print -quit 2>/dev/null)"
|
|
if [ -z "$conf" ]; then
|
|
conf="$1/DVX/config/apps/$DVX_VENDOR/$2"
|
|
mkdir -p "$conf"
|
|
fi
|
|
printf '[%s]\r\n%s = true\r\n' "$DVX_SETTINGS_SECTION" "$DVX_SETTINGS_FULL_SCREEN" > "$conf/$DVX_SETTINGS_FILE"
|
|
}
|
|
|
|
|
|
# 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, or DVX.LOG says a program failed to load) 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
|
|
dvxDvxLog "$1" 400 | grep -q "$DVX_LOAD_FAILED_LOG" && return 1
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
|
|
# dvxKeysTyped LOG TARGET -- KEYS's typed line in LOG (its joeylog.txt: the
|
|
# hex of every character it received) must be TARGET's bytes.
|
|
dvxKeysTyped() {
|
|
local typed
|
|
local want
|
|
typed="$(tr -d '\r' < "$1" | sed -n 's/^typed=//p' | tail -1)"
|
|
want="$(printf '%s' "$2" | od -An -tx1 | tr -d ' \n' | tr 'a-f' 'A-F')"
|
|
echo "$DVX_GATE: log typed=$typed"
|
|
[ "$typed" = "$want" ]
|
|
}
|
|
|
|
|
|
# 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
|
|
}
|
|
|
|
|
|
# dvxDvxLogCopy DIR FILE -- the staged DVX's whole DVX.LOG as FILE (Unix line
|
|
# ends); FILE is left out when there is none.
|
|
dvxDvxLogCopy() {
|
|
local log
|
|
log="$(find "$1/DVX" -maxdepth 1 -iname dvx.log -print -quit)"
|
|
[ -n "$log" ] && tr -d '\r' < "$log" > "$2"
|
|
return 0
|
|
}
|