Files
ps5upload/scripts/install-ps5-sdk.sh
phantomptrandClaude Opus 5.5 8d05f60a7e PS5 Payload SDK v0.44, and scripts/update-ps5-sdk.sh to bump it
Pin v0.44 (sha256 4d06eab7...). Its crt/ and prospero-nid are identical
to v0.43, so the vendored sdk-crt (+ ps5upload.patch) and the portable
prospero-nid stay as they are; the helper, installer and elfldr build to
the same code (only the build-id differs). v0.44 adds LLVM 23 to the
candidate list, so the Makefile comment and Homebrew fallbacks move too.

scripts/update-ps5-sdk.sh (make update-sdk): finds the latest release
(or --tag), checks the zip, rewrites the pin, re-vendors prospero-nid
and crt only when upstream changed them (stops if our crt patch no
longer applies), moves the "SDK vX.Y" doc mentions and installs with
install-ps5-sdk.sh, keeping the old SDK as a backup. --check only
reports. The installer names its backups after the release they hold.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub1mez9eaVtTF8pMEFXvA9
2026-10-10 11:14:44 -07:00

211 lines
8.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Install the repository-pinned PS5 Payload SDK release on POSIX hosts.
#
# The release archive is checksum-verified before extraction. Existing SDKs
# without the matching version marker are moved aside, not deleted, so an
# upgrade can be rolled back. The v0.42 archive was assembled on Linux and
# contains a Linux/x86-64 prospero-nid binary even though the upstream source
# is portable Python; install that exact tagged source implementation so the
# SDK also works on macOS and ARM Linux.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SDK_METADATA="$SCRIPT_DIR/ps5-sdk.env"
PORTABLE_NID="$SCRIPT_DIR/ps5-sdk/prospero-nid"
[ -f "$SDK_METADATA" ] || { echo "ERROR: missing $SDK_METADATA" >&2; exit 1; }
# shellcheck disable=SC1090
source "$SDK_METADATA"
SDK_DIR="${PS5_SDK_INSTALL_DIR:-$HOME/ps5-payload-sdk}"
SDK_URL="https://github.com/ps5-payload-dev/sdk/releases/download/${PS5_SDK_TAG}/ps5-payload-sdk.zip"
VERSION_MARKER=".ps5upload-sdk-version"
EXPECTED_NID="4J2sUJmuHZQ"
TMP_DIR=""
log() { printf '\n==> %s\n' "$*"; }
ok() { printf '✓ %s\n' "$*"; }
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
cleanup() {
if [ -n "$TMP_DIR" ] && [ -d "$TMP_DIR" ]; then
rm -rf -- "$TMP_DIR"
fi
}
trap cleanup EXIT
sha256_file() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$1" | awk '{print $1}'
else
die "need sha256sum or shasum to verify the SDK download"
fi
}
validate_metadata() {
case "$PS5_SDK_TAG" in
v[0-9]*.[0-9]*) ;;
*) die "invalid PS5_SDK_TAG in $SDK_METADATA: $PS5_SDK_TAG" ;;
esac
case "$PS5_SDK_SHA256" in
""|*[!0-9a-f]*) die "invalid PS5_SDK_SHA256 in $SDK_METADATA" ;;
esac
case "$PS5_SDK_NID_SHA256" in
""|*[!0-9a-f]*) die "invalid PS5_SDK_NID_SHA256 in $SDK_METADATA" ;;
esac
[ "${#PS5_SDK_SHA256}" -eq 64 ] || die "PS5_SDK_SHA256 must contain 64 hex characters"
[ "${#PS5_SDK_NID_SHA256}" -eq 64 ] || die "PS5_SDK_NID_SHA256 must contain 64 hex characters"
case "$PS5_SQLITE_VERSION" in
""|*[!0-9]*) die "invalid PS5_SQLITE_VERSION in $SDK_METADATA" ;;
esac
case "$PS5_SQLITE_YEAR" in
""|*[!0-9]*) die "invalid PS5_SQLITE_YEAR in $SDK_METADATA" ;;
esac
case "$PS5_SQLITE_SHA256" in
""|*[!0-9a-f]*) die "invalid PS5_SQLITE_SHA256 in $SDK_METADATA" ;;
esac
[ "${#PS5_SQLITE_SHA256}" -eq 64 ] || die "PS5_SQLITE_SHA256 must contain 64 hex characters"
[ -f "$PORTABLE_NID" ] || die "missing portable prospero-nid at $PORTABLE_NID"
[ "$(sha256_file "$PORTABLE_NID")" = "$PS5_SDK_NID_SHA256" ] || \
die "portable prospero-nid checksum does not match $SDK_METADATA"
}
install_portable_nid() {
local root="$1"
command -v python3 >/dev/null 2>&1 || die "python3 is required by the portable prospero-nid tool"
install -m 0755 "$PORTABLE_NID" "$root/bin/prospero-nid"
local actual
actual="$("$root/bin/prospero-nid" sceKernelGetProcessTime)"
[ "$actual" = "$EXPECTED_NID" ] || die "prospero-nid self-test failed (got $actual)"
}
# ── SQLite amalgamation ──────────────────────────────────────────────────
#
# The payload links a real SQLite so it can read (and repair) the console's
# content databases with the actual record parser instead of a hand-rolled
# b-tree walker. No firmware ships libSceSqlite.sprx, so this cannot come
# from the console — it is compiled into the payload from these sources.
#
# Sources are fetched rather than committed: the amalgamation is a 9 MB
# generated file, pinned by version and SHA-256 in ps5-sdk.env. Only
# sqlite3.c and sqlite3.h are kept; shell.c and sqlite3ext.h are dropped
# (a CLI shell and the extension ABI, neither of which the payload builds).
install_sqlite_amalgamation() {
local dir="$SCRIPT_DIR/../payload/third_party/sqlite3"
local stamp="$dir/.version"
if [ -f "$dir/sqlite3.c" ] && [ -f "$dir/sqlite3.h" ] \
&& [ -f "$stamp" ] \
&& [ "$(tr -d '[:space:]' < "$stamp")" = "$PS5_SQLITE_VERSION" ]; then
ok "SQLite $PS5_SQLITE_VERSION amalgamation already present"
return 0
fi
log "Downloading SQLite $PS5_SQLITE_VERSION amalgamation"
local tmp zip
tmp="$(mktemp -d)"
zip="$tmp/sqlite-amalgamation.zip"
curl --retry 3 --retry-delay 2 --retry-connrefused \
--fail --location --silent --show-error --output "$zip" \
"https://www.sqlite.org/${PS5_SQLITE_YEAR}/sqlite-amalgamation-${PS5_SQLITE_VERSION}.zip"
local actual
actual="$(sha256_file "$zip")"
if [ "$actual" != "$PS5_SQLITE_SHA256" ]; then
rm -rf -- "$tmp"
die "SQLite checksum mismatch: expected $PS5_SQLITE_SHA256, got $actual"
fi
ok "Verified SQLite archive SHA-256 $actual"
# Same archive-layout guard the SDK download uses: every entry must live
# under the single documented top-level directory, with no traversal.
local top="sqlite-amalgamation-${PS5_SQLITE_VERSION}"
while IFS= read -r entry; do
case "$entry" in
"$top"/*) ;;
*) rm -rf -- "$tmp"; die "unexpected SQLite archive entry: $entry" ;;
esac
case "/$entry/" in
*/../*|*/./*) rm -rf -- "$tmp"; die "unsafe SQLite archive entry: $entry" ;;
esac
done < <(unzip -Z1 "$zip")
unzip -q "$zip" -d "$tmp"
[ -f "$tmp/$top/sqlite3.c" ] || { rm -rf -- "$tmp"; die "SQLite archive is missing sqlite3.c"; }
[ -f "$tmp/$top/sqlite3.h" ] || { rm -rf -- "$tmp"; die "SQLite archive is missing sqlite3.h"; }
mkdir -p "$dir"
install -m 0644 "$tmp/$top/sqlite3.c" "$dir/sqlite3.c"
install -m 0644 "$tmp/$top/sqlite3.h" "$dir/sqlite3.h"
printf '%s\n' "$PS5_SQLITE_VERSION" > "$stamp"
chmod 0644 "$stamp"
rm -rf -- "$tmp"
ok "Installed SQLite $PS5_SQLITE_VERSION sources at $dir"
}
validate_metadata
install_sqlite_amalgamation
if [ -f "$SDK_DIR/toolchain/prospero.mk" ] \
&& [ -f "$SDK_DIR/$VERSION_MARKER" ] \
&& [ "$(tr -d '[:space:]' < "$SDK_DIR/$VERSION_MARKER")" = "$PS5_SDK_TAG" ]; then
install_portable_nid "$SDK_DIR"
ok "PS5 Payload SDK $PS5_SDK_TAG already installed at $SDK_DIR"
exit 0
fi
log "Downloading PS5 Payload SDK $PS5_SDK_TAG"
TMP_DIR="$(mktemp -d)"
ZIP_PATH="$TMP_DIR/ps5-payload-sdk.zip"
curl --retry 3 --retry-delay 2 --retry-connrefused \
--fail --location --silent --show-error \
--output "$ZIP_PATH" "$SDK_URL"
ACTUAL_SHA256="$(sha256_file "$ZIP_PATH")"
[ "$ACTUAL_SHA256" = "$PS5_SDK_SHA256" ] || \
die "SDK checksum mismatch: expected $PS5_SDK_SHA256, got $ACTUAL_SHA256"
ok "Verified official archive SHA-256 $ACTUAL_SHA256"
# Refuse unexpected archive layouts before invoking unzip. Every entry must be
# below the single documented ps5-payload-sdk/ top-level directory and may not
# contain traversal components.
while IFS= read -r entry; do
case "$entry" in
ps5-payload-sdk/*) ;;
*) die "unexpected SDK archive entry: $entry" ;;
esac
case "/$entry/" in
*/../*|*/./*) die "unsafe SDK archive entry: $entry" ;;
esac
done < <(unzip -Z1 "$ZIP_PATH")
unzip -q "$ZIP_PATH" -d "$TMP_DIR"
STAGED_SDK="$TMP_DIR/ps5-payload-sdk"
[ -f "$STAGED_SDK/toolchain/prospero.mk" ] || die "SDK archive is missing toolchain/prospero.mk"
[ -f "$STAGED_SDK/target/lib/crt1.o" ] || die "SDK archive is missing target/lib/crt1.o"
install_portable_nid "$STAGED_SDK"
printf '%s\n' "$PS5_SDK_TAG" > "$STAGED_SDK/$VERSION_MARKER"
mkdir -p "$(dirname "$SDK_DIR")"
BACKUP_DIR=""
if [ -e "$SDK_DIR" ]; then
# Name the backup after the release it holds when it says (backup-v0.43-...).
OLD_TAG="$(tr -d '[:space:]' < "$SDK_DIR/$VERSION_MARKER" 2>/dev/null || true)"
case "$OLD_TAG" in v[0-9]*) OLD_TAG="$OLD_TAG-" ;; *) OLD_TAG="" ;; esac
BACKUP_DIR="${SDK_DIR}.backup-${OLD_TAG}$(date +%Y%m%d-%H%M%S)-$$"
mv "$SDK_DIR" "$BACKUP_DIR"
ok "Moved the previous SDK to $BACKUP_DIR"
fi
if ! mv "$STAGED_SDK" "$SDK_DIR"; then
if [ -n "$BACKUP_DIR" ] && [ -e "$BACKUP_DIR" ] && [ ! -e "$SDK_DIR" ]; then
mv "$BACKUP_DIR" "$SDK_DIR"
fi
die "could not install the SDK at $SDK_DIR"
fi
ok "Installed PS5 Payload SDK $PS5_SDK_TAG at $SDK_DIR"