Files
psxrecomp/tools/duckstation/setup.sh
Matthew Stanley 685687f36f Restructure duckstation as submodule + patch; salvage TCP docs + debug_client
Recovery + restructure after rm -rf destroyed duckstation/build/ earlier this
session. Adopts the nesrecomp pattern (see F:/Projects/nesrecomp): pristine
upstream as submodule, our changes as a patch file that gets auto-applied by a
setup script.

Submodule:
- duckstation/ now pins stenzek/duckstation@ffb33c281 (release-20260328 era)
- .gitmodules records the pin; .gitignore stops ignoring the submodule path but
  still ignores build/ and dep/prebuilt/ (the submodule's own .gitignore also
  handles these)

Patch + setup:
- tools/duckstation/psxrecomp_oracle.patch (1187 lines, 7 files) captures our
  TCP debug-server on port 4371 plus the unfinished pc_break family of commands
  (with the VirtualMemoryAddress compile fix)
- tools/duckstation/setup.sh clones submodule, fetches + verifies + extracts
  prebuilt Windows deps, rewrites stale absolute paths baked into the prebuilt
  CMake metadata, applies the oracle patch. Idempotent.
- tools/duckstation/build.sh configures CMake (VS 17 2022) + runs MSBuild, then
  provisions bin/ with portable marker, BIOS copy, and settings.ini post-fixes
- tools/duckstation/README.md documents the layout, the regeneration flow, and
  the known headless-boot configuration issue

TCP documentation (salvaged from v3):
- TCP_COMMANDS.md: full command table with native (4370) / duckstation (4371)
  / both columns, reflecting actual v4 implementation including wtrace_* and
  pc_break/pc_hit_last families
- tools/debug_client.py: unified CLI from v3 (ports 4370/4371 match v4)
- DEBUG.md: merged v3's END-TO-END COMPLETION RULE, Stale Hypothesis Rule,
  TOOLING RULE, and TOOL LIFECYCLE RULE sections into v4's existing loop

Phase 4 diagnostic artefacts from this session:
- generated/relocation_proofs/vblank_registrar/ — corrected VBlank-stall
  diagnosis. Prior handoff claimed the shell VBlank handler was never installed;
  live TCP inspection of both runtime and DuckStation oracle proved handler IS
  installed at 0x800DFEE0. Real gap is one level up in the kernel IRQ chain at
  *(0xA000E014).
- tools/duck_query*.py, duck_search.py, disasm_ram.py, scan_*.py — focused
  probes used to establish the above; reusable against either server

CLAUDE.md §2 and new §15 document the submodule-based oracle setup and the
"never edit submodule, regenerate patch instead" rule.

Known issues (tracked in tools/duckstation/README.md):
- Fresh settings.ini doesn't complete BIOS boot under -nogui; one-time launch
  with GUI is required to warm up the settings before headless mode works

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 01:34:27 -07:00

107 lines
4.2 KiB
Bash

#!/usr/bin/env bash
# Set up the DuckStation oracle build:
# 1. Ensure duckstation/ submodule is initialized and at the pinned upstream commit
# 2. Fetch + extract Windows prebuilt deps if missing
# 3. Rewrite stale absolute paths inside the extracted deps (artefact of how
# upstream packages them — the prefix is baked in at pack time)
# 4. Apply the PSXRecomp oracle patch (psxrecomp_oracle.patch) if not applied
#
# After setup, run `tools/duckstation/build.sh` (or the commands in its tail) to
# compile duckstation-qt.exe into duckstation/build/bin/.
#
# Safe to run repeatedly — each step checks for "already done" and skips.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
DUCK="$REPO_ROOT/duckstation"
PATCH="$REPO_ROOT/tools/duckstation/psxrecomp_oracle.patch"
UPSTREAM_BASE="ffb33c281d196eb8ee0f559085ca285de7cdd51b" # stenzek/master at time of daa1808
log() { echo "[duckstation-setup] $*"; }
# ---- Step 1: submodule init + checkout upstream base ----
if [ ! -f "$DUCK/CMakeLists.txt" ]; then
log "initializing duckstation submodule..."
cd "$REPO_ROOT"
git submodule update --init --recursive duckstation
fi
cd "$DUCK"
CUR_SHA="$(git rev-parse HEAD)"
if [ "$CUR_SHA" != "$UPSTREAM_BASE" ]; then
log "checking out upstream base $UPSTREAM_BASE (was $CUR_SHA)..."
git fetch origin "$UPSTREAM_BASE" 2>/dev/null || git fetch origin
git checkout --detach "$UPSTREAM_BASE"
fi
# ---- Step 2: prebuilt deps ----
DEPS_ARCHIVE="$DUCK/dep/prebuilt/deps-windows-x64.7z"
DEPS_DIR="$DUCK/dep/prebuilt/windows-x64"
PREBUILT_SHA256="f3473f18504b8a2d3ae32f007a575839288380503584d7867135da894ac8c129"
PREBUILT_VERSION="$(cat "$DUCK/dep/PREBUILT-VERSION")"
PREBUILT_URL="https://github.com/duckstation/dependencies/releases/download/${PREBUILT_VERSION}/deps-windows-x64.7z"
if [ ! -f "$DEPS_ARCHIVE" ]; then
log "downloading prebuilt deps ($PREBUILT_VERSION) from $PREBUILT_URL..."
mkdir -p "$(dirname "$DEPS_ARCHIVE")"
if command -v curl >/dev/null 2>&1; then
curl -L --fail -o "$DEPS_ARCHIVE" "$PREBUILT_URL"
elif command -v wget >/dev/null 2>&1; then
wget -O "$DEPS_ARCHIVE" "$PREBUILT_URL"
else
log "ERROR: neither curl nor wget found — fetch $PREBUILT_URL manually and place at $DEPS_ARCHIVE"
exit 1
fi
fi
if [ -f "$DEPS_ARCHIVE" ]; then
ACTUAL_SHA="$(sha256sum "$DEPS_ARCHIVE" | awk '{print $1}')"
if [ "$ACTUAL_SHA" != "$PREBUILT_SHA256" ]; then
log "ERROR: prebuilt archive sha256 mismatch"
log " expected: $PREBUILT_SHA256"
log " actual: $ACTUAL_SHA"
exit 1
fi
fi
if [ ! -d "$DEPS_DIR" ] || [ -z "$(ls -A "$DEPS_DIR" 2>/dev/null || true)" ]; then
log "extracting prebuilt deps..."
cd "$DUCK/dep/prebuilt"
if command -v 7z >/dev/null 2>&1; then
7z x -y deps-windows-x64.7z >/dev/null
elif [ -x "/c/Program Files/7-Zip/7z.exe" ]; then
"/c/Program Files/7-Zip/7z.exe" x -y deps-windows-x64.7z >/dev/null
else
log "ERROR: 7z not found — extract $DEPS_ARCHIVE manually"
exit 1
fi
fi
# ---- Step 3: rewrite stale absolute paths in extracted CMake/pkg-config metadata ----
# Upstream bakes in whatever prefix the archive was built at. Replace with this
# checkout's real path. Idempotent: no-op once rewritten.
log "normalizing absolute paths in prebuilt deps metadata..."
python3 "$REPO_ROOT/tools/fix_duckstation_deps_paths.py" "$DEPS_DIR" >/dev/null
# ---- Step 4: apply PSXRecomp oracle patch ----
cd "$DUCK"
if git apply --check "$PATCH" >/dev/null 2>&1; then
log "applying PSXRecomp oracle patch..."
git apply "$PATCH"
else
# Either already applied, or conflicts. Distinguish:
if git apply --reverse --check "$PATCH" >/dev/null 2>&1; then
log "oracle patch already applied"
else
log "ERROR: oracle patch does not apply cleanly and is not already applied."
log " - upstream commit may have moved past the pinned base"
log " - or the patch itself was regenerated against a different base"
log " patch: $PATCH"
log " duck HEAD: $(git rev-parse HEAD)"
exit 1
fi
fi
log "setup complete — run tools/duckstation/build.sh to compile"