mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
120 lines
5.8 KiB
Bash
Executable File
120 lines
5.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Wrap a validated package_game_release.sh --stage-only Linux payload.
|
|
# No build, emitter or AOT invocation runs here. AppRun/desktop/PNG icon are
|
|
# caller-provided, fully rendered title metadata. AppRun must use usr/bin/<exe>
|
|
# and usr/share/<payload-name>, preserving user settings/cards in writable data.
|
|
# Usage: package_appimage.sh --payload DIR --exe-name NAME --payload-name NAME
|
|
# --app-run FILE --desktop-file FILE.desktop --icon FILE.png --output FILE.AppImage
|
|
# [--framework DIR] [--tool-cache DIR] [--jobs N]
|
|
set -euo pipefail
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
FRAMEWORK="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
|
PAYLOAD=""; EXE_NAME=""; PAYLOAD_NAME=""; APP_RUN=""; DESKTOP=""; ICON=""; OUTPUT=""
|
|
TOOLS="${XDG_CACHE_HOME:-${HOME:?}/.cache}/recomp-appimage-tools"
|
|
JOBS=2
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--payload) PAYLOAD="${2:?}"; shift 2;;
|
|
--exe-name) EXE_NAME="${2:?}"; shift 2;;
|
|
--payload-name) PAYLOAD_NAME="${2:?}"; shift 2;;
|
|
--app-run) APP_RUN="${2:?}"; shift 2;;
|
|
--desktop-file) DESKTOP="${2:?}"; shift 2;;
|
|
--icon) ICON="${2:?}"; shift 2;;
|
|
--output) OUTPUT="${2:?}"; shift 2;;
|
|
--framework) FRAMEWORK="${2:?}"; shift 2;;
|
|
--tool-cache) TOOLS="${2:?}"; shift 2;;
|
|
--jobs) JOBS="${2:?}"; shift 2;;
|
|
-h|--help) sed -n '2,8p' "$0"; exit 0;;
|
|
*) echo "unknown argument: $1" >&2; exit 2;;
|
|
esac
|
|
done
|
|
[[ "$(uname -s)" == Linux && "$(uname -m)" == x86_64 ]] || {
|
|
echo "AppImage packaging requires native Linux x86_64" >&2; exit 1;
|
|
}
|
|
for name in EXE_NAME PAYLOAD_NAME; do
|
|
[[ "${!name}" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]] || {
|
|
echo "invalid ${name}" >&2; exit 2;
|
|
}
|
|
done
|
|
[[ "$JOBS" =~ ^[1-9][0-9]*$ ]] || { echo "invalid jobs" >&2; exit 2; }
|
|
[[ -d "$PAYLOAD" && -n "$OUTPUT" && ! -e "$OUTPUT" && ! -L "$OUTPUT" ]] || {
|
|
echo "payload must exist and output must be a fresh path" >&2; exit 1;
|
|
}
|
|
PAYLOAD="$(cd "$PAYLOAD" && pwd)"
|
|
FRAMEWORK="$(cd "$FRAMEWORK" && pwd)"
|
|
for file in "$APP_RUN" "$DESKTOP" "$ICON" "$PAYLOAD/$EXE_NAME" "$PAYLOAD/$EXE_NAME.execution.json"; do
|
|
[[ -f "$file" ]] || { echo "missing ${file}" >&2; exit 1; }
|
|
done
|
|
[[ -d "$PAYLOAD/assets" && -f "$PAYLOAD/game.toml" ]] || {
|
|
echo "payload is missing staged runtime assets/config" >&2; exit 1;
|
|
}
|
|
DESKTOP_ID="$(basename "$DESKTOP" .desktop)"
|
|
[[ "$DESKTOP" == *.desktop && "$DESKTOP_ID" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]] || {
|
|
echo "invalid desktop metadata filename" >&2; exit 1;
|
|
}
|
|
PY="${PSX_RELEASE_STAGE_PYTHON:-python3}"
|
|
"$PY" - "$PAYLOAD/$EXE_NAME" "$ICON" <<'PY'
|
|
import sys
|
|
from pathlib import Path
|
|
with Path(sys.argv[1]).open('rb') as stream:
|
|
elf_magic = stream.read(4)
|
|
with Path(sys.argv[2]).open('rb') as stream:
|
|
png_magic = stream.read(8)
|
|
if elf_magic != b'\x7fELF':
|
|
raise SystemExit('Linux payload executable must be ELF')
|
|
if png_magic != b'\x89PNG\r\n\x1a\n':
|
|
raise SystemExit('AppImage icon must be PNG')
|
|
PY
|
|
if find "$PAYLOAD" -type f \( -iname '*.dll' -o -iname '*.exe' \) -print -quit | grep -q .; then
|
|
echo "Linux payload contains Windows binaries/cache" >&2; exit 1
|
|
fi
|
|
sh -n "$APP_RUN"
|
|
APPDIR="$(mktemp -d "${TMPDIR:-/tmp}/psx-appimage.XXXXXXXX")"
|
|
case "$(stat -f -c %T "$APPDIR")" in
|
|
9p|drvfs|ntfs*) echo "AppDir must be on the native Linux filesystem: $APPDIR" >&2; exit 1;;
|
|
esac
|
|
echo "AppDir: $APPDIR"
|
|
DATA="$APPDIR/usr/share/$PAYLOAD_NAME"
|
|
mkdir -p "$APPDIR/usr/bin" "$DATA" "$(dirname "$OUTPUT")" "$TOOLS"
|
|
cp -a "$PAYLOAD/." "$DATA/"
|
|
rm -f "$DATA/$EXE_NAME" "$DATA/$EXE_NAME.execution.json"
|
|
install -m 0755 "$PAYLOAD/$EXE_NAME" "$APPDIR/usr/bin/$EXE_NAME"
|
|
install -m 0755 "$APP_RUN" "$APPDIR/AppRun"
|
|
install -m 0644 "$DESKTOP" "$APPDIR/$DESKTOP_ID.desktop"
|
|
install -m 0644 "$ICON" "$APPDIR/$DESKTOP_ID.png"
|
|
ln -s "$DESKTOP_ID.png" "$APPDIR/.DirIcon"
|
|
# SDL resolves UI resources from the real ELF, rather than writable argv[0].
|
|
ln -s "../share/$PAYLOAD_NAME/assets" "$APPDIR/usr/bin/assets"
|
|
HELPER="$FRAMEWORK/tools/release_stage.py"
|
|
# Official GitHub asset digests checked 2026-10-07 UTC; versioned releases
|
|
# avoid drifting continuous URLs. Asset IDs313839329/324406736 respectively.
|
|
# These releases are not marked immutable; SHA verification remains mandatory.
|
|
LINUXDEPLOY_SHA=c20cd71e3a4e3b80c3483cef793cda3f4e990aca14014d23c544ca3ce1270b4d
|
|
APPIMAGETOOL_SHA=ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0
|
|
LINUXDEPLOY="$TOOLS/linuxdeploy-x86_64.AppImage"
|
|
APPIMAGETOOL="$TOOLS/appimagetool-x86_64.AppImage"
|
|
"$PY" "$HELPER" fetch-pinned --url https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20251107-1/linuxdeploy-x86_64.AppImage \
|
|
--sha256 "$LINUXDEPLOY_SHA" --destination "$LINUXDEPLOY"
|
|
"$PY" "$HELPER" fetch-pinned --url https://github.com/AppImage/appimagetool/releases/download/1.9.1/appimagetool-x86_64.AppImage \
|
|
--sha256 "$APPIMAGETOOL_SHA" --destination "$APPIMAGETOOL"
|
|
chmod 0755 "$LINUXDEPLOY" "$APPIMAGETOOL"
|
|
export NO_STRIP=1
|
|
"$LINUXDEPLOY" --appimage-extract-and-run --appdir "$APPDIR" \
|
|
--executable "$APPDIR/usr/bin/$EXE_NAME" --desktop-file "$APPDIR/$DESKTOP_ID.desktop" \
|
|
--icon-file "$APPDIR/$DESKTOP_ID.png"
|
|
# Deployment may rewrite RPATH. The original sidecar supplies the contract,
|
|
# while the helper verifies its embedded identity and hashes the FINAL ELF;
|
|
# a prior payload binary_sha256 is never accepted as the final-byte receipt.
|
|
"$PY" "$HELPER" stage-execution --binary "$APPDIR/usr/bin/$EXE_NAME" \
|
|
--manifest "$PAYLOAD/$EXE_NAME.execution.json" \
|
|
--output "$APPDIR/usr/bin/$EXE_NAME.execution.json"
|
|
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-0}"
|
|
[[ "$SOURCE_DATE_EPOCH" =~ ^[0-9]+$ ]] || { echo "invalid SOURCE_DATE_EPOCH" >&2; exit 2; }
|
|
export SOURCE_DATE_EPOCH
|
|
find "$APPDIR" -exec touch -h -d "@$SOURCE_DATE_EPOCH" {} +
|
|
ARCH=x86_64 "$APPIMAGETOOL" --appimage-extract-and-run \
|
|
--mksquashfs-opt -processors --mksquashfs-opt "$JOBS" "$APPDIR" "$OUTPUT"
|
|
chmod 0755 "$OUTPUT"
|
|
sha256sum "$OUTPUT" > "$OUTPUT.sha256"
|
|
echo "AppImage: $OUTPUT"
|