diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c111670d..c63c2576 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,9 @@ name: Release # Builds the macOS, Windows, and Linux desktop apps, an Android APK, an iOS -# IPA, a Nintendo Switch SD-ready zip (experimental), Xbox UWP, and the Anbernic -# RG34XXSP (Stock OS 64-bit MOD / PortMaster) port, then publishes them as a +# IPA, a Nintendo Switch SD-ready zip (experimental), Xbox UWP, the Anbernic +# RG34XXSP (Stock OS 64-bit MOD / PortMaster) and Linux ARM SBC PortMaster +# handheld ports on the self-hosted Mac runner, and publishes them as a # GitHub Release. # # Versioning: @@ -355,6 +356,20 @@ jobs: # runtime from PortMaster-GUI, so it needs no signing/notarization. ./build-rg34xxsp.sh --version "${{ needs.version.outputs.version }}" + - name: Build Linux ARM SBC PortMaster port + env: + # The release workflow must package the commit being released. The + # script defaults to the latest published release for standalone + # builds, while this explicit local override keeps CI source-aligned. + GEN1RECOMP_SOURCE_DIR: ${{ github.workspace }} + GEN1RECOMP_RELEASE_TAG: v${{ needs.version.outputs.version }} + run: | + set -euo pipefail + # Same aarch64 PortMaster-style pack for Linux ARM SBC PortMaster. The build + # keeps its own cache because the two scripts use different staging + # layouts and runtime package paths. + ./build-linux-arm-sbc.sh --version "${{ needs.version.outputs.version }}" + - name: Notarize & staple macOS app if: github.repository == 'bryanthaboi/gen1recomp' run: | @@ -449,6 +464,11 @@ jobs: [ -f "$rg34" ] || { echo "::error::$rg34 not found (expected from ./build-rg34xxsp.sh)"; exit 1; } cp "$rg34" "$outdir/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip" + # Linux ARM SBC PortMaster handheld port. + sbc="dist/linux-arm-sbc/gen1recomp-sbc-portmaster.zip" + [ -f "$sbc" ] || { echo "::error::$sbc not found (expected from ./build-linux-arm-sbc.sh)"; exit 1; } + cp "$sbc" "$outdir/gen1recomp-${v}-sbc-portmaster.zip" + # Platform-independent update payload, built alongside the desktop # apps above (same game.love that gets fused into each of them). love_file=".bazinga/work/game.love" @@ -564,6 +584,7 @@ jobs: "dist/release/gen1recomp-${v}-switch.zip" "dist/release/gen1recomp-${v}-xbox-uwp.zip" "dist/release/gen1recomp-${v}-rg34xxsp-stockos64-mod.zip" + "dist/release/gen1recomp-${v}-sbc-portmaster.zip" "dist/release/gen1recomp-${v}.love" "dist/release/sha256sums.txt" ) diff --git a/build-linux-arm-sbc.sh b/build-linux-arm-sbc.sh new file mode 100755 index 00000000..3da19638 --- /dev/null +++ b/build-linux-arm-sbc.sh @@ -0,0 +1,366 @@ +#!/usr/bin/env bash +# Build a PortMaster aarch64 port of gen1recomp for Linux ARM SBC handhelds. +# The package uses PortMaster control hooks and a self-contained LÖVE runtime, +# while keeping paths relative to the launcher for broad CFW compatibility. +# +# The launcher uses SHDIR-relative paths and bundles the LÖVE 11.5 aarch64 +# runtime so the device does not need a separate runtime download on first launch. +# +# Usage: +# ./build-linux-arm-sbc.sh [--version X.Y.Z] +# GEN1RECOMP_SOURCE_DIR="$PWD" ./build-linux-arm-sbc.sh --version X.Y.Z +# ./build-linux-arm-sbc.sh --source /path/to/gen1recomp --version X.Y.Z +# +# Output: +# dist/linux-arm-sbc/gen1recomp-sbc-portmaster.zip +# +# Install on device: +# 1. Install PortMaster for the handheld firmware. +# 2. Unzip into the device's PortMaster ports folder so you have: +# Roms/Ports (PORTS)/gen1recomp-sbc.sh +# Roms/Ports (PORTS)/gen1recomp-sbc/... +# 3. Copy a legal US Red or Blue .gb into Roms/Ports (PORTS)/gen1recomp-sbc/lovegame/ +# 4. Launch "gen1recomp-sbc" from the Ports list; press Choose ROM (scans that +# folder when zenity is missing). + +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +HERE="$ROOT/.bazinga" +CACHE="$HERE/cache/linux-arm-sbc" +WORK="$HERE/work/linux-arm-sbc" +DIST="$ROOT/dist/linux-arm-sbc" + +APP_NAME="gen1recomp-sbc" +# Artifact suffix identifies this as the generic PortMaster SBC package. +# Release uploads stage it as gen1recomp--sbc-portmaster.zip. +ARTIFACT_SUFFIX="portmaster" +PORT_DIR_NAME="gen1recomp-sbc" +LAUNCHER_NAME="gen1recomp-sbc.sh" +LOVE_VERSION="11.5" +# By default the pack is reproducible from the latest published GitHub release, +# not whatever happens to be in the caller's checkout. Development builds can +# point this at a local checkout with GEN1RECOMP_SOURCE_DIR=/path/to/repo. +SOURCE_DIR_OVERRIDE="${GEN1RECOMP_SOURCE_DIR:-}" +SOURCE_TAG_OVERRIDE="${GEN1RECOMP_RELEASE_TAG:-}" +VERSION="${GEN1RECOMP_VERSION:-}" + +# Official PortMaster LÖVE 11.5 aarch64 runtime (small love stub + liblove). +PM_RUNTIME_BASE="https://raw.githubusercontent.com/PortsMaster/PortMaster-GUI/main/PortMaster/runtimes/love_${LOVE_VERSION}" +RELEASES_LATEST_URL="https://github.com/bryanthaboi/gen1recomp/releases/latest" +RELEASE_TARBALL_BASE="https://github.com/bryanthaboi/gen1recomp/archive/refs/tags" + +say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } +warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; } +fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } + +while [ $# -gt 0 ]; do + case "$1" in + --version) [ $# -ge 2 ] || fail "--version needs X.Y.Z"; VERSION="$2"; shift ;; + --source) [ $# -ge 2 ] || fail "--source needs a directory"; SOURCE_DIR_OVERRIDE="$2"; shift ;; + --release-tag) [ $# -ge 2 ] || fail "--release-tag needs a tag"; SOURCE_TAG_OVERRIDE="$2"; shift ;; + -h|--help) + sed -n '2,24p' "$0" + exit 0 + ;; + *) fail "unknown argument: $1" ;; + esac + shift +done + +command -v curl >/dev/null || fail "curl is required" +command -v zip >/dev/null || fail "zip is required" +command -v unzip >/dev/null || fail "unzip is required" +command -v tar >/dev/null || fail "tar is required" + +mkdir -p "$CACHE" "$WORK" "$DIST" + +download() { + local url="$1" dest="$2" + if [ -f "$dest" ] && [ -s "$dest" ]; then + return 0 + fi + say "downloading $(basename "$dest")" + curl -fL --progress-bar "$url" -o "$dest.tmp" \ + || fail "download failed: $url" + mv "$dest.tmp" "$dest" +} + +# --------------------------------------------------------------- source + game tree +# Release builds use the latest published source archive. A local checkout is +# an explicit override for development and for CI's just-built release source. +if [ -n "$SOURCE_DIR_OVERRIDE" ]; then + SOURCE_DIR_OVERRIDE="$(cd "$SOURCE_DIR_OVERRIDE" 2>/dev/null && pwd)" \ + || fail "source directory does not exist: $SOURCE_DIR_OVERRIDE" + SOURCE_DIR="$SOURCE_DIR_OVERRIDE" + SOURCE_TAG="${SOURCE_TAG_OVERRIDE:-local}" + if [ "$SOURCE_TAG" != "local" ]; then + printf '%s' "$SOURCE_TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + || fail "release tag must look like vX.Y.Z: $SOURCE_TAG" + fi + if [ -z "$VERSION" ]; then + VERSION="$(git -C "$SOURCE_DIR" rev-parse --short HEAD 2>/dev/null || echo dev)" + fi +else + if [ -z "$SOURCE_TAG_OVERRIDE" ]; then + latest_location="$(curl -fsSI "$RELEASES_LATEST_URL" \ + | awk 'tolower($1) == "location:" { print $2 }' | tail -1 | tr -d '\r')" \ + || fail "could not resolve latest published release" + SOURCE_TAG_OVERRIDE="${latest_location##*/}" + fi + printf '%s' "$SOURCE_TAG_OVERRIDE" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + || fail "release tag must look like vX.Y.Z: $SOURCE_TAG_OVERRIDE" + SOURCE_TAG="$SOURCE_TAG_OVERRIDE" + SOURCE_ARCHIVE="$CACHE/gen1recomp-${SOURCE_TAG}.tar.gz" + download "$RELEASE_TARBALL_BASE/$SOURCE_TAG.tar.gz" "$SOURCE_ARCHIVE" + SOURCE_EXTRACT="$WORK/source-$SOURCE_TAG" + rm -rf "$SOURCE_EXTRACT" + mkdir -p "$SOURCE_EXTRACT" + tar -xzf "$SOURCE_ARCHIVE" -C "$SOURCE_EXTRACT" + SOURCE_DIR="$(find "$SOURCE_EXTRACT" -mindepth 1 -maxdepth 1 -type d -print -quit)" + [ -n "$SOURCE_DIR" ] || fail "release archive had no source directory" + if [ -z "$VERSION" ]; then VERSION="${SOURCE_TAG#v}"; fi +fi + +say "staging lovegame/ from $SOURCE_TAG" +GAME_SRC="$WORK/lovegame" +rm -rf "$GAME_SRC" +mkdir -p "$GAME_SRC" + +# Same payload as scripts/build.sh's game.love — never ship ROM-derived cache. +# tools/save-editor is part of that payload: the launcher's Edit button on a +# save row opens it in-process (main.lua). +(cd "$SOURCE_DIR" && zip -q -9 -r "$WORK/game-payload.zip" \ + main.lua conf.lua src libs data assets tools/save-editor \ + tools/rom_manifest.json tools/rom_manifest_blue.json \ + -x '*.DS_Store' 'data/generated/*' 'assets/generated/*') +if unzip -Z1 "$WORK/game-payload.zip" \ + | grep -Eq '^(data|assets)/generated/[^/]+|^(data|assets)/generated/.+/'; then + fail "payload unexpectedly contains generated ROM data" +fi +unzip -q "$WORK/game-payload.zip" -d "$GAME_SRC" +rm -f "$WORK/game-payload.zip" + +# Stamp release version into the staged tree only (never the working tree). +if printf '%s' "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + say "stamping engine version $VERSION" + sed -E "s/(engine[[:space:]]*=[[:space:]]*\")[^\"]*(\")/\1$VERSION\2/" \ + "$SOURCE_DIR/src/core/Version.lua" > "$GAME_SRC/src/core/Version.lua" + version_re="$(printf '%s' "$VERSION" | sed 's/\./\\./g')" + grep -Eq "engine[[:space:]]*=[[:space:]]*\"$version_re\"" \ + "$GAME_SRC/src/core/Version.lua" \ + || fail "version stamp failed" +else + say "version '$VERSION' is not X.Y.Z — shipping default engine (no stamp)" +fi + +# Portable marker: saves + ROM cache live next to the game on the SD card. +: > "$GAME_SRC/portable.txt" + +# --------------------------------------------------------------- love runtime +say "fetching LÖVE $LOVE_VERSION aarch64 runtime" +LOVE_BIN="$CACHE/love.aarch64" +LOVE_LIB="$CACHE/liblove-11.5.so" +LUAJIT_LIB="$CACHE/libluajit-5.1.so.2" +MODPLUG_LIB="$CACHE/libmodplug.so.1" +OGG_LIB="$CACHE/libogg.so.0" + +download "$PM_RUNTIME_BASE/love.aarch64" "$LOVE_BIN" +download "$PM_RUNTIME_BASE/libs.aarch64/liblove-11.5.so" "$LOVE_LIB" +download "$PM_RUNTIME_BASE/libs.aarch64/libluajit-5.1.so.2" "$LUAJIT_LIB" +download "$PM_RUNTIME_BASE/libs.aarch64/libmodplug.so.1" "$MODPLUG_LIB" +download "$PM_RUNTIME_BASE/libs.aarch64/libogg.so.0" "$OGG_LIB" + +# Sanity: love stub must be an aarch64 ELF. +file "$LOVE_BIN" | grep -qi 'aarch64\|ARM aarch64' \ + || fail "love.aarch64 does not look like an aarch64 ELF (got: $(file "$LOVE_BIN"))" + +# --------------------------------------------------------------- port tree +say "assembling port package" +PORT_ROOT="$WORK/port" +rm -rf "$PORT_ROOT" +mkdir -p "$PORT_ROOT/$PORT_DIR_NAME/bin" \ + "$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64" \ + "$PORT_ROOT/$PORT_DIR_NAME/licenses" \ + "$PORT_ROOT/$PORT_DIR_NAME/conf" + +cp -R "$GAME_SRC" "$PORT_ROOT/$PORT_DIR_NAME/lovegame" +cp "$LOVE_BIN" "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64" +chmod +x "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64" +cp "$LOVE_LIB" "$LUAJIT_LIB" "$MODPLUG_LIB" "$OGG_LIB" \ + "$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64/" + +# Drop a short license pointer for the bundled LÖVE bits. +cat > "$PORT_ROOT/$PORT_DIR_NAME/licenses/LICENSE.love2d.txt" <<'EOF' +This port bundles the LÖVE 11.5 aarch64 runtime from PortMaster +(https://github.com/PortsMaster/PortMaster-GUI). LÖVE is zlib-licensed; +see https://love2d.org/ for full terms. +EOF + +# --------------------------------------------------------------- launcher +# Resolve the game directory from the launcher so this works with both +# PortMaster-managed ports directories. +cat > "$PORT_ROOT/$LAUNCHER_NAME" <<'EOF' +#!/bin/bash +# gen1recomp-sbc — Linux ARM SBC / PortMaster launcher +# Uses SHDIR-relative paths so firmware-specific mount points do not matter. + +export HOME="${HOME:-/root}" +XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}" +SHDIR="$(cd "$(dirname "$0")" && pwd)" + +if [ -d "/mnt/SDCARD/Apps/PortMaster/PortMaster/" ]; then + controlfolder="/mnt/SDCARD/Apps/PortMaster/PortMaster" +elif [ -d "/mnt/SDCARD/Roms/ports/PortMaster" ]; then + controlfolder="/mnt/SDCARD/Roms/ports/PortMaster" +elif [ -d "/mnt/SDCARD/Data/PortMaster/" ]; then + controlfolder="/mnt/SDCARD/Data/PortMaster" +elif [ -d "$SHDIR/PortMaster" ]; then + controlfolder="$SHDIR/PortMaster" +elif [ -d "/opt/system/Tools/PortMaster/" ]; then + controlfolder="/opt/system/Tools/PortMaster" +elif [ -d "/opt/tools/PortMaster/" ]; then + controlfolder="/opt/tools/PortMaster" +elif [ -d "$XDG_DATA_HOME/PortMaster/" ]; then + controlfolder="$XDG_DATA_HOME/PortMaster" +elif [ -d "/roms/ports/PortMaster" ]; then + controlfolder="/roms/ports/PortMaster" +else + controlfolder="/mnt/SDCARD/Roms/PORTS/PortMaster" +fi + +if [ ! -f "$controlfolder/control.txt" ]; then + echo "PortMaster control.txt not found under $controlfolder" >&2 + exit 1 +fi +# shellcheck disable=SC1090 +source "$controlfolder/control.txt" +get_controls +if [ -n "${CFW_NAME:-}" ] && [ -f "${controlfolder}/mod_${CFW_NAME}.txt" ]; then + # shellcheck disable=SC1090 + source "${controlfolder}/mod_${CFW_NAME}.txt" +fi + +GAMEDIR="$SHDIR/gen1recomp-sbc" +CONFDIR="$GAMEDIR/conf" +mkdir -p "$CONFDIR" + +cd "$GAMEDIR" || exit 1 +> "$GAMEDIR/log.txt" && exec > >(tee "$GAMEDIR/log.txt") 2>&1 + +export XDG_DATA_HOME="$CONFDIR" +export XDG_CONFIG_HOME="$CONFDIR" +export LD_LIBRARY_PATH="$GAMEDIR/libs.aarch64:${LD_LIBRARY_PATH:-}" +export SDL_GAMECONTROLLERCONFIG="${sdl_controllerconfig:-}" +# GLES is the common path on ARM SBC handhelds; firmware may override it. +export LOVE_GRAPHICS_USE_OPENGLES="${LOVE_GRAPHICS_USE_OPENGLES:-1}" + +$ESUDO chmod a+x ./bin/love.aarch64 2>/dev/null || chmod a+x ./bin/love.aarch64 +$ESUDO chmod 666 /dev/uinput 2>/dev/null || true + +if [ -n "${GPTOKEYB:-}" ]; then + $GPTOKEYB "love.aarch64" & +fi +if type pm_platform_helper >/dev/null 2>&1; then + pm_platform_helper "$GAMEDIR/bin/love.aarch64" +fi + +./bin/love.aarch64 "$GAMEDIR/lovegame" + +if type pm_finish >/dev/null 2>&1; then + pm_finish +else + if [ -n "${ESUDO:-}" ]; then + $ESUDO kill -9 $(pidof gptokeyb) 2>/dev/null || true + else + kill -9 $(pidof gptokeyb) 2>/dev/null || true + fi +fi +EOF +chmod +x "$PORT_ROOT/$LAUNCHER_NAME" + +# --------------------------------------------------------------- metadata +cat > "$PORT_ROOT/port.json" < "$PORT_ROOT/gameinfo.xml" < + + + ./$LAUNCHER_NAME + gen1recomp-sbc + Native LÖVE2D recreation of Pokemon Red and Blue. Requires your own legal US Red or Blue ROM. + 20250101T000000 + the bois club + the bois club + RPG + + +EOF + +cat > "$PORT_ROOT/README.md" <<'EOF' +## gen1recomp-sbc (Linux ARM SBC / PortMaster) + +Native LÖVE 11.5 aarch64 PortMaster port of gen1recomp for compatible Linux ARM SBC handhelds, including H700-class devices. This pack was built from source release **__SOURCE_TAG__**. + +### Install + +1. Install PortMaster for your handheld firmware. +2. Unzip so `gen1recomp-sbc.sh` and the `gen1recomp-sbc/` folder are siblings in the device's PortMaster ports directory. +3. Copy a legal US Pokémon Red or Blue `.gb` into `gen1recomp-sbc/lovegame/`. +4. Refresh the launcher and launch **gen1recomp-sbc** from Ports. + +### Controls + +| Input | Action | +|--|--| +| D-pad | Move cursor | +| A | Click | +| L1 / R1 | Switch tabs | +| Start / Select | Play or choose ROM | + +Controls use the normal PortMaster / SDL pad map. Device-specific power/suspend behavior is supplied by the firmware and PortMaster runtime. + +### First run + +Put the `.gb` in `lovegame/`, then press **Choose ROM**. After import, the ROM-derived cache and saves stay beside the game (`portable.txt`). + +### Thanks + +LÖVE runtime binaries from [PortMaster](https://portmaster.games/). PortMaster device support and runtime integration are maintained by the PortMaster team. +EOF +sed -i.bak "s/__SOURCE_TAG__/$SOURCE_TAG/g" "$PORT_ROOT/README.md" +rm -f "$PORT_ROOT/README.md.bak" + +# --------------------------------------------------------------- zip +ZIP_OUT="$DIST/$APP_NAME-$ARTIFACT_SUFFIX.zip" +rm -f "$ZIP_OUT" +say "packing $ZIP_OUT" +(cd "$PORT_ROOT" && zip -q -9 -r "$ZIP_OUT" \ + "$LAUNCHER_NAME" "$PORT_DIR_NAME" port.json gameinfo.xml README.md) + +say "done." +say "artifact: $ZIP_OUT ($(du -h "$ZIP_OUT" | cut -f1))" +say "copy into the device PortMaster ports folder, then drop your .gb into gen1recomp-sbc/lovegame/" diff --git a/docs/linux-arm-sbc.md b/docs/linux-arm-sbc.md new file mode 100644 index 00000000..bd5157de --- /dev/null +++ b/docs/linux-arm-sbc.md @@ -0,0 +1,54 @@ +# Linux ARM SBC Handhelds (PortMaster) + +Download `gen1recomp-*-sbc-portmaster.zip` from the [Gen1Recomp releases](https://github.com/bryanthaboi/gen1recomp/releases). This build targets 64-bit Linux ARM handhelds with PortMaster, including compatible H700 devices. + +## Install + +1. Unzip the release. It contains `gen1recomp-sbc.sh` and a `gen1recomp-sbc/` folder. +2. Copy both as siblings into your device's PortMaster ports directory, commonly `Roms/Ports (PORTS)/` or `Roms/PORTS/`. +3. Install PortMaster for your firmware and refresh the Ports list. +4. Copy your legally owned canonical US Red or Blue `.gb` file into `gen1recomp-sbc/lovegame/`. +5. Launch **gen1recomp-sbc** from Ports and choose the ROM. + +The pack includes `portable.txt`, so saves and ROM-derived cache remain beside the game on the SD card. The build never ships ROM-derived bytes. + +Canonical US cart SHA-1 values: + +- Red: `ea9bcae617fdf159b045185467ae58b2e4a48b9a` +- Blue: `d7037c83e1ae5b39bde3c30787637ba1d4c48ce2` + +## Controls + +| Input | Action | +| --- | --- | +| D-pad | Move cursor | +| A | Click / confirm | +| L1 / R1 | Switch tabs | +| Start / Select | Play or choose ROM | + +In-game controls use the normal PortMaster/SDL mapping and can be rebound in **OPTIONS → CONTROLS**. + +## Runtime and suspend + +The package bundles PortMaster's LÖVE 11.5 aarch64 runtime. The launcher sources `control.txt`, calls `get_controls`, applies an optional CFW override, invokes `pm_platform_helper`, and calls `pm_finish` on exit. Paths are relative to the launcher, allowing different firmware mount points. + +Suspend/resume uses the existing LÖVE focus/visibility lifecycle: input is reset on focus loss and the game resumes when the window becomes visible again. Exact power-button behavior remains firmware-dependent; hardware validation has been performed on the TrimUI Brick, not every SBC or H700 device. + +## Building + +Release workflows build this automatically. Standalone builds resolve the latest published Gen1Recomp release by default: + +```sh +./build-linux-arm-sbc.sh --version 0.1.75 +``` + +For development, package a local checkout explicitly: + +```sh +GEN1RECOMP_SOURCE_DIR="$PWD" ./build-linux-arm-sbc.sh --version 0.1.0 +# or: ./build-linux-arm-sbc.sh --source "$PWD" --version 0.1.0 +``` + +The generated `port.json` records the source release tag. `install-linux-arm-sbc.sh` is a macOS helper for copying a built pack to a mounted SD card. + +PortMaster device support and runtime integration are maintained in the [PortMaster](https://github.com/PortsMaster/PortMaster-New) ecosystem. diff --git a/install-linux-arm-sbc.sh b/install-linux-arm-sbc.sh new file mode 100755 index 00000000..06b9a49c --- /dev/null +++ b/install-linux-arm-sbc.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# After first boot of a compatible Linux ARM handheld (or when PortMaster is installed), reinsert the +# SD card and run this to install gen1recomp-sbc + Red/Blue ROMs into Roms/PORTS. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")" && pwd)" +STAGE="$ROOT/.bazinga/work/linux-arm-sbc-install" +DECPREP="${DECPREP:-$ROOT/../decprep}" +ZIP="$ROOT/dist/linux-arm-sbc/gen1recomp-sbc-portmaster.zip" + +say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } +fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; } + +# Find a mounted handheld userdata volume with a ROMs or Apps directory. +find_roms_root() { + local v candidate + for v in /Volumes/*; do + [ -d "$v" ] || continue + # Prefer a volume that already has Roms/ or Apps/ + if [ -d "$v/Roms" ] || [ -d "$v/roms" ] || [ -d "$v/PORTS" ] || [ -d "$v/ports" ] || [ -d "$v/Apps" ]; then + echo "$v" + return 0 + fi + done + # Fallback: common removable-volume labels + for v in /Volumes/SDCARD /Volumes/sdcard /Volumes/NO\ NAME /Volumes/ROMS; do + if [ -d "$v" ]; then + echo "$v" + return 0 + fi + done + return 1 +} + +say "looking for handheld SD volume" +ROMS_ROOT="$(find_roms_root)" || fail "no SD volume mounted. boot the handheld once, power it off, reinsert the SD, then rerun." + +say "using: $ROMS_ROOT" +# Resolve the device PortMaster ports directory +if [ -d "$ROMS_ROOT/Roms/PORTS" ]; then + PORTS="$ROMS_ROOT/Roms/PORTS" +elif [ -d "$ROMS_ROOT/roms/PORTS" ]; then + PORTS="$ROMS_ROOT/roms/PORTS" +elif [ -d "$ROMS_ROOT/Roms/ports" ]; then + PORTS="$ROMS_ROOT/Roms/ports" +elif [ -d "$ROMS_ROOT/PORTS" ]; then + PORTS="$ROMS_ROOT/PORTS" +else + mkdir -p "$ROMS_ROOT/Roms/PORTS" + PORTS="$ROMS_ROOT/Roms/PORTS" +fi +say "PORTS: $PORTS" + +# Refresh staged payload +mkdir -p "$STAGE/PORTS" +if [ -f "$ZIP" ]; then + rm -rf "$STAGE/PORTS/gen1recomp-sbc.sh" "$STAGE/PORTS/gen1recomp-sbc" "$STAGE/PORTS/port.json" \ + "$STAGE/PORTS/gameinfo.xml" "$STAGE/PORTS/README.md" + unzip -q -o "$ZIP" -d "$STAGE/PORTS" +else + fail "missing $ZIP — run ./build-linux-arm-sbc.sh first" +fi + +# Ensure ROMs are in lovegame (Choose ROM scans this folder on minimal images) +[ -f "$DECPREP/Pokemon - Red Version.gb" ] || fail "missing Red ROM in $DECPREP" +[ -f "$DECPREP/Pokemon - Blue Version.gb" ] || fail "missing Blue ROM in $DECPREP" +cp -f "$DECPREP/Pokemon - Red Version.gb" "$STAGE/PORTS/gen1recomp-sbc/lovegame/" +cp -f "$DECPREP/Pokemon - Blue Version.gb" "$STAGE/PORTS/gen1recomp-sbc/lovegame/" + +say "copying gen1recomp port" +rm -rf "$PORTS/gen1recomp-sbc" "$PORTS/gen1recomp-sbc.sh" +cp -R "$STAGE/PORTS/gen1recomp-sbc" "$PORTS/" +cp -f "$STAGE/PORTS/gen1recomp-sbc.sh" "$PORTS/" +cp -f "$STAGE/PORTS/port.json" "$PORTS/" +cp -f "$STAGE/PORTS/README.md" "$PORTS/" +chmod +x "$PORTS/gen1recomp-sbc.sh" "$PORTS/gen1recomp-sbc/bin/love.aarch64" + +# Also drop carts in the stock GB folder for the emulator library +GB_DIR="" +for candidate in "$ROMS_ROOT/Roms/GB" "$ROMS_ROOT/roms/GB" "$ROMS_ROOT/Roms/gb"; do + if [ -d "$candidate" ]; then GB_DIR="$candidate"; break; fi +done +if [ -n "$GB_DIR" ]; then + say "copying .gb into $GB_DIR" + cp -f "$DECPREP/Pokemon - Red Version.gb" "$GB_DIR/" + cp -f "$DECPREP/Pokemon - Blue Version.gb" "$GB_DIR/" +fi + +sync +say "installed:" +ls -lh "$PORTS/gen1recomp-sbc.sh" +ls -lh "$PORTS/gen1recomp-sbc/lovegame/"*.gb +say "eject the SD, insert it in the handheld, open Ports → gen1recomp-sbc, Choose ROM."