From f0e88aa581f3e711eb034ae73ab5e8255c2751bc Mon Sep 17 00:00:00 2001 From: Andrew Quenehen Date: Sat, 1 Aug 2026 12:48:04 -0300 Subject: [PATCH] feat(switch): native-or-Docker fused NRO packaging with clear errors Co-authored-by: Cursor --- scripts/switch/build_fused.sh | 103 ++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/scripts/switch/build_fused.sh b/scripts/switch/build_fused.sh index 246dd22c..eddb4193 100755 --- a/scripts/switch/build_fused.sh +++ b/scripts/switch/build_fused.sh @@ -2,24 +2,28 @@ # Build fused gen1recomp Switch NRO (romfs game.love + nacp + icon). # # Usage: scripts/switch/build_fused.sh GAME_LOVE VERSION OUT_NRO +# +# Prefers native nacptool/elf2nro (PATH or $DEVKITPRO/tools/bin). +# Falls back to Docker using GEN1_DKP_IMAGE or scripts/switch/dkp-docker.image. set -euo pipefail -ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=common.sh +. "$SCRIPT_DIR/common.sh" + LOVE_NX_TAG="11.5-nx1" LOVE_NX_DIR="$ROOT/.bazinga/love-nx/$LOVE_NX_TAG" LOVE_ELF="$LOVE_NX_DIR/love.elf" ICON="$ROOT/assets/switch/icon.jpg" APP_NAME="gen1recomp" BUNDLE_ID="com.theboisclub.pokemonred" +DKP_IMAGE_FILE="$ROOT/scripts/switch/dkp-docker.image" GAME_LOVE="${1:-}" VERSION="${2:-}" OUT_NRO="${3:-}" -fail() { printf 'error: %s\n' "$*" >&2; exit 1; } -say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } - [ -n "$GAME_LOVE" ] && [ -n "$VERSION" ] && [ -n "$OUT_NRO" ] \ || fail "usage: $0 GAME_LOVE VERSION OUT_NRO" @@ -27,30 +31,83 @@ say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } "$ROOT/scripts/switch/verify_love_nx.sh" -command -v nacptool >/dev/null \ - || fail "nacptool not found (install devkitPro switch-dev; never download love-nx latest)" -command -v elf2nro >/dev/null \ - || fail "elf2nro not found (install devkitPro switch-dev; never download love-nx latest)" - [ -f "$ICON" ] || fail "missing Switch icon at $ICON" -WORK="$(mktemp -d "${TMPDIR:-/tmp}/gen1recomp-fused.XXXXXX")" -trap 'rm -rf "$WORK"' EXIT +ensure_dkp_tools_path -ROMFS_DIR="$WORK/romfs" -mkdir -p "$ROMFS_DIR" -cp "$GAME_LOVE" "$ROMFS_DIR/game.love" +resolve_dkp_image() { + if [ -n "${GEN1_DKP_IMAGE:-}" ]; then + printf '%s' "$GEN1_DKP_IMAGE" + return 0 + fi + [ -f "$DKP_IMAGE_FILE" ] || fail "missing Docker image pin: $DKP_IMAGE_FILE" + local line + line="$(grep -v '^[[:space:]]*#' "$DKP_IMAGE_FILE" | grep -v '^[[:space:]]*$' | head -1 || true)" + [ -n "$line" ] || fail "empty Docker image pin: $DKP_IMAGE_FILE" + printf '%s' "$line" +} -NACP="$WORK/control.nacp" -nacptool --create "$APP_NAME" "$BUNDLE_ID" "$VERSION" "$NACP" +run_fused_native() { + local work romfs_dir nacp + work="$(mktemp -d "${TMPDIR:-/tmp}/gen1recomp-fused.XXXXXX")" + # shellcheck disable=SC2064 + trap "rm -rf '$work'" EXIT -say "building fused NRO with pinned love.elf" -elf2nro "$LOVE_ELF" "$OUT_NRO" \ - --icon="$ICON" \ - --nacp="$NACP" \ - --romfsdir="$ROMFS_DIR" + romfs_dir="$work/romfs" + mkdir -p "$romfs_dir" "$(dirname "$OUT_NRO")" + cp "$GAME_LOVE" "$romfs_dir/game.love" -mkdir -p "$(dirname "$OUT_NRO")" -shasum -a 256 "$OUT_NRO" | awk '{print $1}' > "${OUT_NRO}.sha256" + nacp="$work/control.nacp" + nacptool --create "$APP_NAME" "$BUNDLE_ID" "$VERSION" "$nacp" + + say "building fused NRO with pinned love.elf (native)" + elf2nro "$LOVE_ELF" "$OUT_NRO" \ + --icon="$ICON" \ + --nacp="$nacp" \ + --romfsdir="$romfs_dir" +} + +run_fused_docker() { + local image stage out_dir out_base + image="$(resolve_dkp_image)" + command -v docker >/dev/null 2>&1 || fail_fused_toolchain + + # Stage under ROOT so a single repo bind-mount covers love.elf, icon, and romfs. + stage="$ROOT/.bazinga/work/fused-docker-$$" + mkdir -p "$stage/romfs" "$(dirname "$OUT_NRO")" + cp "$GAME_LOVE" "$stage/romfs/game.love" + # shellcheck disable=SC2064 + trap "rm -rf '$stage'" EXIT + + out_dir="$(cd "$(dirname "$OUT_NRO")" && pwd)" + out_base="$(basename "$OUT_NRO")" + + say "building fused NRO with pinned love.elf (Docker: $image)" + docker run --rm \ + -v "$ROOT:/src:ro" \ + -v "$stage:/work" \ + -v "$out_dir:/out" \ + -w /work \ + "$image" \ + bash -c " + set -euo pipefail + nacptool --create '$APP_NAME' '$BUNDLE_ID' '$VERSION' /work/control.nacp + elf2nro /src/.bazinga/love-nx/$LOVE_NX_TAG/love.elf /out/$out_base \ + --icon=/src/assets/switch/icon.jpg \ + --nacp=/work/control.nacp \ + --romfsdir=/work/romfs + " +} + +if command -v nacptool >/dev/null 2>&1 && command -v elf2nro >/dev/null 2>&1; then + run_fused_native +elif command -v docker >/dev/null 2>&1; then + run_fused_docker +else + fail_fused_toolchain +fi + +[ -f "$OUT_NRO" ] || fail "fused NRO was not produced at $OUT_NRO" +sha256_file "$OUT_NRO" > "${OUT_NRO}.sha256" say "fused NRO: $OUT_NRO" say "sha256: $(cat "${OUT_NRO}.sha256")"