diff --git a/README.md b/README.md index e4999481..a47e3ccf 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,9 @@ SHA-1 before creating any game data. FireRed and LeafGreen support is in beta. **Platform notes:** [Linux](docs/platforms/linux.md), [iOS](docs/platforms/ios.md), [Xbox Dev Mode](docs/platforms/xbox.md), -[handhelds](docs/platforms/handhelds.md), and -[Nintendo Switch](docs/platforms/switch.md) each have their own install steps. +[handhelds](docs/platforms/handhelds.md), +[Nintendo Switch](docs/platforms/switch.md), and +[PlayStation 4](docs/platforms/ps4.md) each have their own install steps. **Windows Defender:** it sometimes flags the Windows build with a generic detection such as `Trojan:Win32/Wacatac!ml` (#621). This is a known false diff --git a/docs/README.md b/docs/README.md index b8a49b2d..860d2083 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,6 +20,7 @@ editor, and developer setup live on the - [Xbox Dev Mode](platforms/xbox.md) - [Handhelds](platforms/handhelds.md) - [Nintendo Switch](platforms/switch.md) +- [PlayStation 4](platforms/ps4.md) ## Modding diff --git a/docs/platforms/ps4.md b/docs/platforms/ps4.md new file mode 100644 index 00000000..ffa02144 --- /dev/null +++ b/docs/platforms/ps4.md @@ -0,0 +1,11 @@ +# PlayStation 4 + +Homebrew (GoldHEN) build: `gen1recomp-*-ps4.pkg`, the game fused with +[LÖVE for PS4](https://github.com/tomasmorello/love-ps4) (LÖVE 11.5), pinned +like love-nx is for the Switch. PS4 port by +[Tomas Morello](https://github.com/tomasmorello). Tested on hardware with +Red, Blue and Yellow; Gen 2 and Gen 3 are not tested yet. + +- Players: [ps4-install.md](../ps4-install.md). Install the `.pkg` from + USB, copy your own legal ROM over FTP into the inbox, Scan again. +- Builders: `scripts/build_ps4.sh --fetch --fused` (see the script header). diff --git a/docs/ps4-install.md b/docs/ps4-install.md new file mode 100644 index 00000000..b36335f9 --- /dev/null +++ b/docs/ps4-install.md @@ -0,0 +1,73 @@ +# Install Gen1Recomp on PlayStation 4 + +Releases that include PS4 support ship `gen1recomp-*-ps4.pkg`: the game and its +runtime in one installable package. Install it, then import your own legal +`.gb` / `.gbc` ROM. + +> You need a PS4 that runs homebrew (GoldHEN). This project does not help you +> set that up. + +PS4 port by [Tomas Morello](https://github.com/tomasmorello), on +[LÖVE for PS4](https://github.com/tomasmorello/love-ps4) (LÖVE 11.5). + +**Tested on real hardware:** Pokemon Red, Blue and Yellow (ROM import, play, +save and load, mods, returning to the launcher). **Not tested yet:** Gold, +Silver, Crystal, FireRed and LeafGreen. They may work; reports are welcome. + +## 1. Install the package + +1. Open [Releases](https://github.com/bryanthaboi/gen1recomp/releases) and + download `gen1recomp-*-ps4.pkg` (optionally check it against its `.sha256`). +2. Copy it to a USB drive formatted **exFAT** and plug it into the console. +3. **Settings → Debug Settings → Game → Package Installer**, pick the file. +4. Launch **Gen1Recomp** from the home screen. + +Updating: install the newer `.pkg` the same way; it installs over the old one. +Saves, imported ROMs and mods live in `/data/love/pokemon-love2d/`, outside the +package. **Do not delete that folder** when updating. + +## 2. Import your ROM + +The PS4 has no file picker, so the launcher reads an inbox folder, like the +Switch build: + +1. Enable GoldHEN's FTP server (GoldHEN menu), port **2121**. +2. With any FTP client, copy your `.gb` / `.gbc` into + `/data/love/pokemon-love2d/imports/`. +3. In the launcher, press **Scan again**. + +The other inboxes work the same way (copy over FTP, then **Scan again** in +that screen), all under `/data/love/pokemon-love2d/`: + +| What | Folder | +|---|---| +| Mods (`.zip`) | `imports/mods/` | +| Saves (`.sav`) | `imports/saves//`, e.g. `imports/saves/red/` | +| Custom Carts (`.g1rcart`) | `imports/carts/` | + +## 3. Controls + +The launcher uses **D-pad focus navigation**: the highlighted option is the +selected one; Cross confirms, Circle goes back. **Triangle** switches to the +virtual cursor and back. In game, the pad maps like on every other platform. + +To quit, use the **PS button** and close the application, like any PS4 app. + +⚠ **START + SHARE** belongs to GoldHEN (it opens its menu), so the "hold +START + SELECT" force-quit shortcut does not reach the game on PS4. + +## 4. Already have LÖVE for PS4? + +`scripts/build_ps4.sh --loose` produces a plain `game.love`: copy it to +`/data/love/game.love` and start **LÖVE for PS4** instead of installing the +standalone package. + +## Building + +```bash +scripts/build_ps4.sh --fetch --fused --version X.Y.Z +``` + +Needs the OpenOrbis PS4 Toolchain v0.5.4 packaging tools (`OO_PS4_TOOLCHAIN`) +and bash 4+. The runtime is downloaded from a pinned LÖVE for PS4 release and +verified against `scripts/ps4/love-ps4-runtime.sha256`. diff --git a/scripts/build_ps4.sh b/scripts/build_ps4.sh new file mode 100755 index 00000000..fdb32db1 --- /dev/null +++ b/scripts/build_ps4.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# PlayStation 4 packaging entry point (homebrew, GoldHEN). +# +# Usage: +# scripts/build_ps4.sh --fetch +# scripts/build_ps4.sh --loose +# scripts/build_ps4.sh --fused [--version X.Y.Z] +# scripts/build_ps4.sh --fetch --fused [--version X.Y.Z] +# +# Modes: +# --fetch Download the pinned LÖVE for PS4 runtime zip into +# .bazinga/love-ps4// and verify SHA-256 against +# scripts/ps4/love-ps4-runtime.sha256. +# +# --loose Pack game.love into dist/ps4/loose/ for players who already have +# the generic "LÖVE for PS4" package installed: they copy it to +# /data/love/game.love over FTP. Does not need the runtime. +# +# --fused Build gen1recomp--ps4.pkg: runtime + game.love in one +# installable package (title id GENR00001; its own tile and saves). +# Requires the pin (run --fetch or combine) and the OpenOrbis +# packaging tools: OO_PS4_TOOLCHAIN pointing at an unpacked +# OpenOrbis PS4 Toolchain v0.5.4 (bin/linux or bin/macos with +# PkgTool.Core and create-gp4), plus bash 4+. +# +# The runtime is LÖVE 11.5 for PS4 by Tomas Morello +# (https://github.com/tomasmorello/love-ps4), pinned the way love-nx is pinned +# for the Switch. +# +# Non-goals: installing on a console, FTP uploads, ROM handling. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +WORK="$ROOT/.bazinga/work" +DIST="$ROOT/dist/ps4" +MANIFEST="$ROOT/scripts/ps4/love-ps4-runtime.sha256" +TITLE_ID="${GEN1_PS4_TITLE_ID:-GENR00001}" +FETCH=0; LOOSE=0; FUSED=0 +VERSION="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo dev)" + +say() { printf '\033[1;32m==>\033[0m %s\n' "$*" >&2; } +fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } + +while [ $# -gt 0 ]; do + case "$1" in + --fetch) FETCH=1; shift ;; + --loose) LOOSE=1; shift ;; + --fused) FUSED=1; shift ;; + --version) VERSION="$2"; shift 2 ;; + -h|--help) sed -n '2,29p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + *) fail "unknown argument: $1" ;; + esac +done +[ $((FETCH + LOOSE + FUSED)) -gt 0 ] || fail "specify --fetch, --loose and/or --fused (see --help)" +[ $((LOOSE + FUSED)) -le 1 ] || fail "--loose and --fused cannot be combined" + +manifest_field() { # manifest_field + local v + v="$(awk -v k="$1" '$1 == k { print $2; exit }' "$MANIFEST")" + [ -n "$v" ] || fail "$MANIFEST has no '$1' entry" + printf '%s' "$v" +} +TAG="$(manifest_field tag)" +ZIP_NAME="love-ps4-${TAG#v}-runtime.zip" +RUNTIME_DIR="$ROOT/.bazinga/love-ps4/$TAG" +ZIP="$RUNTIME_DIR/$ZIP_NAME" +BASE_URL="${GEN1_LOVE_PS4_BASE_URL:-https://github.com/tomasmorello/love-ps4/releases/download/$TAG}" + +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}' + else shasum -a 256 "$1" | awk '{print $1}'; fi +} + +fetch_runtime() { + local want have + want="$(manifest_field "$ZIP_NAME")" + mkdir -p "$RUNTIME_DIR" + if [ -f "$ZIP" ] && [ "$(sha256_of "$ZIP")" = "$want" ]; then + say "runtime $TAG already fetched" + else + say "downloading $BASE_URL/$ZIP_NAME" + curl -fL --retry 3 --retry-delay 1 -o "$ZIP.part" "$BASE_URL/$ZIP_NAME" \ + || { rm -f "$ZIP.part"; fail "download failed: $BASE_URL/$ZIP_NAME"; } + have="$(sha256_of "$ZIP.part")" + [ "$have" = "$want" ] || { rm -f "$ZIP.part"; fail "SHA-256 mismatch for $ZIP_NAME: got $have, pinned $want"; } + mv "$ZIP.part" "$ZIP" + fi + rm -rf "$RUNTIME_DIR/unpacked" + mkdir -p "$RUNTIME_DIR/unpacked" + (cd "$RUNTIME_DIR/unpacked" && unzip -q "$ZIP") +} + +pack_game_love() { + mkdir -p "$WORK" + local love_out="$WORK/game.love" + # pack_love.sh only accepts X.Y.Z; dev builds (a commit hash) stay unstamped. + if printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + "$ROOT/scripts/pack_love.sh" --output "$love_out" --listing "$WORK/love-listing.txt" \ + --version "$VERSION" >/dev/null + else + "$ROOT/scripts/pack_love.sh" --output "$love_out" --listing "$WORK/love-listing.txt" >/dev/null + fi + printf '%s' "$love_out" +} + +# X.Y.Z -> PS4 APP_VER "NN.NN" as (10*X+Y).Z: 0.3.1 -> 03.01, 1.0.0 -> 10.00. +# Monotonic while Y < 10 and Z < 100, so a newer release installs over an older one. +app_ver() { + if printf '%s' "$1" | grep -Eq '^[0-9]+\.[0-9]\.[0-9]{1,2}$'; then + local x y z + IFS=. read -r x y z < copy to /data/love/game.love on the console" +fi + +if [ "$FUSED" -eq 1 ]; then + [ -f "$ZIP" ] || fail "runtime not fetched: run with --fetch" + [ -d "$RUNTIME_DIR/unpacked" ] || fetch_runtime + [ -n "${OO_PS4_TOOLCHAIN:-}" ] || fail "OO_PS4_TOOLCHAIN is not set (OpenOrbis PS4 Toolchain v0.5.4)" + fuse="$(ls "$RUNTIME_DIR"/unpacked/*/fuse-pkg.sh)" + love="$(pack_game_love)" + icon="$ROOT/scripts/ps4/icon0.png" + [ -f "$icon" ] || icon="" + mkdir -p "$DIST" + "$fuse" --love "$love" --title-id "$TITLE_ID" --title "Gen1Recomp" \ + --version "$(app_ver "$VERSION")" --content-label GEN1RECOMP --out-dir "$WORK/ps4-pkg" \ + ${icon:+--icon "$icon"} + cp "$WORK"/ps4-pkg/IV0000-"$TITLE_ID"_00-*.pkg "$DIST/gen1recomp-$VERSION-ps4.pkg" + (cd "$DIST" && sha256_of "gen1recomp-$VERSION-ps4.pkg" > "gen1recomp-$VERSION-ps4.pkg.sha256") + say "dist/ps4/gen1recomp-$VERSION-ps4.pkg" +fi diff --git a/scripts/ps4/icon0.png b/scripts/ps4/icon0.png new file mode 100644 index 00000000..7bca1258 Binary files /dev/null and b/scripts/ps4/icon0.png differ diff --git a/scripts/ps4/love-ps4-runtime.sha256 b/scripts/ps4/love-ps4-runtime.sha256 new file mode 100644 index 00000000..cdbf207b --- /dev/null +++ b/scripts/ps4/love-ps4-runtime.sha256 @@ -0,0 +1,8 @@ +# LÖVE for PS4 — pinned runtime (Tomas Morello) +# https://github.com/tomasmorello/love-ps4/releases +# +# scripts/build_ps4.sh --fetch downloads from the release and refuses +# it unless its SHA-256 matches. Bump both lines together. + +tag v0.9.3 +love-ps4-0.9.3-runtime.zip 8cc66a447738a6772fc79775d7306791fb63542582e420cf08db8cb0420eded7