#!/usr/bin/env bash
# Extracts the game data Shockolate needs out of a purchased System Shock:
# Enhanced Edition installer from gog.com, into ./ss_ee/{data,sound}.
#
# Usage:
#   1. Buy System Shock: Enhanced Edition on gog.com and download the
#      offline installer (a Windows .exe, e.g.
#      setup_system_shock_enhanced_edition_1.2.16_(64bit)_(44378).exe).
#   2. Drop it in this directory (res/assets/).
#   3. Run this script: ./extract_assets.sh
#
# The installer is an Inno Setup package; the actual game data lives
# inside it in sshock.kpf, which is just a zip file. Its res/pc/hd/ and
# res/pc/cdrom/ trees together hold every classic-game data/sound file
# Shockolate's res/ folder expects (res/pc/hd wins the couple of
# filenames present in both: intro.res, objprop.dat). Needs innoextract
# and unzip; if they're not installed locally, this script does the
# extraction inside a throwaway Ubuntu container instead (needs docker in
# that case, nothing else).
set -euo pipefail

cd "$(dirname "${BASH_SOURCE[0]}")"
ASSETS_DIR="$(pwd)"
OUT_DIR="$ASSETS_DIR/ss_ee"

fail() { echo "PREFLIGHT FAIL: $*" >&2; exit 1; }

shopt -s nullglob
installers=(setup_system_shock_enhanced_edition*.exe)
shopt -u nullglob
case "${#installers[@]}" in
    0) fail "no setup_system_shock_enhanced_edition*.exe found in $ASSETS_DIR - buy System Shock: Enhanced Edition on gog.com, download the offline installer, and drop it here" ;;
    1) ;;
    *) fail "multiple installer .exe files found in $ASSETS_DIR - keep only one: ${installers[*]}" ;;
esac
INSTALLER="${installers[0]}"

# Extracts sshock.kpf from $1 (an installer .exe) and assembles $2 (an
# ss_ee output directory) from its res/pc/{hd,cdrom} trees. Defined as a
# function so the exact same logic can run either natively or, via
# `declare -f`, inside a throwaway container.
run_extraction() {
    set -euo pipefail
    local installer="$1" out_dir="$2"
    # Deliberately not `local`: an EXIT trap set here fires when the whole
    # shell process exits, which is after this function - and this
    # function's own scope - has already returned, so a `local` var would
    # already be unset by then (unbound-variable error under `set -u`).
    work="$(mktemp -d)"
    trap 'rm -rf "${work:-}"' EXIT

    echo "== Extracting sshock.kpf from installer =="
    innoextract --silent --include sshock.kpf -d "$work/installer" "$installer"

    echo "== Unpacking sshock.kpf (data/sound trees only) =="
    mkdir -p "$work/kpf"
    unzip -q "$work/installer/sshock.kpf" \
        'res/pc/hd/data/*' 'res/pc/hd/sound/*' 'res/pc/cdrom/data/*' \
        -d "$work/kpf"

    echo "== Assembling $out_dir =="
    rm -rf "$out_dir"
    mkdir -p "$out_dir/data" "$out_dir/sound"
    # cdrom/data first (cutscenes, audio logs, and shared tables only it
    # has), then hd/data on top (wins the 2 overlapping filenames:
    # intro.res, objprop.dat - the hd install's own copies).
    cp -a "$work/kpf/res/pc/cdrom/data/." "$out_dir/data/"
    cp -a "$work/kpf/res/pc/hd/data/." "$out_dir/data/"
    cp -a "$work/kpf/res/pc/hd/sound/." "$out_dir/sound/"
}

if command -v innoextract >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1; then
    echo "== Using local innoextract/unzip =="
    run_extraction "$ASSETS_DIR/$INSTALLER" "$OUT_DIR"
else
    echo "== innoextract/unzip not found locally - using a throwaway docker container =="
    command -v docker >/dev/null 2>&1 \
        || fail "docker not found in PATH (needed since innoextract/unzip aren't installed locally)"
    docker run --rm \
        -v "$ASSETS_DIR:/assets" \
        -e HOST_UID="$(id -u)" \
        -e HOST_GID="$(id -g)" \
        ubuntu:22.04 \
        bash -c "
            set -euo pipefail
            apt-get update -qq >/dev/null
            apt-get install -y -qq innoextract unzip >/dev/null
            $(declare -f run_extraction)
            run_extraction '/assets/$INSTALLER' '/assets/ss_ee'
            chown -R \"\$HOST_UID:\$HOST_GID\" /assets/ss_ee
        "
fi

echo "== Done =="
echo "Assets extracted to $OUT_DIR"
echo "Note: mfdfrn.res/mfdger.res (French/German MFD art) are not present in this"
echo "installer and are only needed for those localizations - not for English play."
