#!/usr/bin/env bash # make-iigs-disk.sh - Build the JOEYLIB ProDOS disk image (joey.2mg) that # run-iigs-mame.sh and run-iigs.sh mount as the data disk (flop4). It holds # the launchable example binaries as ProDOS type $B3 (S16 / GS-OS apps) in a # volume named JOEYLIB, so GS/OS Finder lists and launches them. # # A 3.5" floppy caps at 800KB, which cannot hold every example at once. # Examples are added in priority order (small primitive demos first); any that # would overflow the volume are skipped with a warning rather than corrupting # the image. Override the set with JOEY_DISK_EXAMPLES="DRAW SPRITE ...". # # Usage: scripts/make-iigs-disk.sh [output.2mg] # Requires: toolchains/env.sh sourced (LLVM816_ROOT), built IIgs examples. set -euo pipefail repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) : "${LLVM816_ROOT:?make-iigs-disk.sh: source toolchains/env.sh first}" CADIUS="${CADIUS:-$LLVM816_ROOT/tools/cadius/cadius}" BINDIR="${BINDIR:-$repo/build/iigs/bin}" # The NTP replayer. DEFAULTED, not optional: without it on the volume every # IIgs example is silent except the ROM boot beep, because jlpAudioInit # fopen()s "ntpplayer" and returns before ntpArm() when that read fails -- # with no diagnostic. Callers used to have to pass NTP_BIN and one of them # passed the wrong path, which is exactly how Space Taxi ended up mute. NTP_BIN="${NTP_BIN:-$repo/build/iigs/audio/ntpplayer.bin}" OUT="${1:-$BINDIR/joey.2mg}" VOL=JOEYLIB # Volume size passed to cadius CREATEVOLUME. Defaults to an 800KB 3.5" # floppy (the classic bootable image); set JOEY_DISK_SIZE to a larger # ProDOS size (e.g. "5MB", "32MB") to build a hard-disk image that holds # the full example set, which does not fit an 800KB floppy. VOLSIZE="${JOEY_DISK_SIZE:-800KB}" [ -x "$CADIUS" ] || { echo "make-iigs-disk.sh: cadius not found at $CADIUS" >&2; exit 2; } # Priority order: small primitive demos first so DRAW/PATTERN/etc. always fit; # the large AGI build lands last and is dropped if the floppy is full. order=(${JOEY_DISK_EXAMPLES:-DRAW PATTERN KEYS JOY SPRITE UBER ADV2 ADV AGI}) work=$(mktemp -d -t joeylib-disk.XXXXXX) trap 'rm -rf "$work"' EXIT rm -f "$OUT" "$CADIUS" CREATEVOLUME "$OUT" "$VOL" "$VOLSIZE" >/dev/null # Writable save area for the save-file API (joey/file.h -> jlSave*). GS/OS's # libc has no mkdir, but the IIgs save HAL creates SAVES/ at runtime via GS/OS # Create; prestaging it here as well means saves work even before that call and # on a first run. Set JOEY_SKIP_SAVES_PRESTAGE=1 to omit it (tests runtime # creation in isolation). if [ -z "${JOEY_SKIP_SAVES_PRESTAGE:-}" ]; then "$CADIUS" CREATEFOLDER "$OUT" "/$VOL/SAVES" >/dev/null fi freeBlocks() { "$CADIUS" CATALOG "$OUT" 2>/dev/null \ | grep -oE 'Free : [0-9]+' | grep -oE '[0-9]+' | head -1 } added=() skipped=() for name in "${order[@]}"; do bin="$BINDIR/$name" [ -f "$bin" ] || continue sz=$(stat -c%s "$bin") # data blocks + one index block per 256 data blocks + a little dir slack need=$(( (sz + 511) / 512 + (sz + 131071) / 131072 + 2 )) if [ "$need" -gt "$(freeBlocks)" ]; then skipped+=("$name") continue fi # ProDOS type $B3 (S16/GS-OS application), aux $0000. cp "$bin" "$work/$name#B30000" "$CADIUS" ADDFILE "$OUT" "/$VOL" "$work/$name#B30000" >/dev/null rm -f "$work/$name#B30000" if "$CADIUS" CATALOG "$OUT" 2>/dev/null | grep -qE "^ $name +S16"; then added+=("$name") else skipped+=("$name") fi done # The NTP replayer binary (audio): jlpAudioInit fopen()s "ntpplayer" from # the volume at runtime (see src/iigs/audio_full.c NTP_PATH). Plain ProDOS # BIN ($06) file. Skipped with a warning if it doesn't fit or wasn't built. ntpOk=0 if [ -f "$NTP_BIN" ]; then ntpSz=$(stat -c%s "$NTP_BIN") ntpNeed=$(( (ntpSz + 511) / 512 + 2 )) if [ "$ntpNeed" -le "$(freeBlocks)" ]; then cp "$NTP_BIN" "$work/NTPPLAYER#060000" "$CADIUS" ADDFILE "$OUT" "/$VOL" "$work/NTPPLAYER#060000" >/dev/null rm -f "$work/NTPPLAYER#060000" # Never trust the exit code here (see the launch check below). if "$CADIUS" CATALOG "$OUT" 2>/dev/null | grep -qE '^ +NTPPLAYER +BIN'; then added+=(NTPPLAYER) ntpOk=1 fi fi fi if [ "$ntpOk" -eq 0 ]; then skipped+=(NTPPLAYER) cat >&2 </dev/null "$CADIUS" CREATEFOLDER "$OUT" "/$VOL/DATA/LEVELS" >/dev/null "$CADIUS" CREATEFOLDER "$OUT" "/$VOL/DATA/TILES" >/dev/null "$CADIUS" CREATEFOLDER "$OUT" "/$VOL/DATA/SPRITES" >/dev/null dataAdded=0 while IFS= read -r f; do rel=${f#"$staxiData/"} dir=$(dirname "$rel") base=$(basename "$rel") dst="/$VOL/DATA" if [ "$dir" != "." ]; then dst="/$VOL/DATA/${dir^^}" fi cp "$f" "$work/${base^^}#060000" "$CADIUS" ADDFILE "$OUT" "$dst" "$work/${base^^}#060000" >/dev/null rm -f "$work/${base^^}#060000" dataAdded=$((dataAdded + 1)) done < <(find "$staxiData" -type f -not -path '*/sprites/level*.spc' | sort) # The per-level hook-sprite banks are optional (the game JIT-compiles # a level's cels at entry when its bank is missing) and large, so # they go on last and only while the floppy has room for them. levelSkipped=0 while IFS= read -r f; do base=$(basename "$f") sz=$(stat -c%s "$f") need=$(( (sz + 511) / 512 + (sz + 131071) / 131072 + 2 )) if [ "$need" -gt "$(freeBlocks)" ]; then levelSkipped=$((levelSkipped + 1)) continue fi cp "$f" "$work/${base^^}#060000" "$CADIUS" ADDFILE "$OUT" "/$VOL/DATA/SPRITES" "$work/${base^^}#060000" >/dev/null rm -f "$work/${base^^}#060000" dataAdded=$((dataAdded + 1)) done < <(find "$staxiData" -type f -path '*/sprites/level*.spc' | sort) added+=("DATA[$dataAdded files]") if [ "$levelSkipped" -gt 0 ]; then echo " note: $levelSkipped level sprite bank(s) left off (floppy full; those levels JIT at entry)" >&2 fi else echo " WARNING: STAXI packed without its DATA tree ($staxiData missing)" >&2 fi fi echo "iigs-disk: $OUT (volume /$VOL)" echo " added: ${added[*]:-none}" if [ ${#skipped[@]} -gt 0 ]; then echo " SKIPPED ($VOLSIZE volume full): ${skipped[*]}" echo " -> mount a single-example disk via JOEY_DISK_EXAMPLES=\"NAME\" $0" fi