Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions SunPad.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
BB0000000000000000000202 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BB0000000000000000000203 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BB0000000000000000000204 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
BB0000000000000000000205 /* gGMSE01_recomp.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = gGMSE01_recomp.dylib; path = Provisioned/appletvos/gGMSE01_recomp.dylib; sourceTree = "<group>"; };
BB0000000000000000000205 /* gGMSE01_recomp.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = gGMSE01_recomp.dylib; path = "Provisioned/$(PLATFORM_NAME)/gGMSE01_recomp.dylib"; sourceTree = "<group>"; };
BB0000000000000000000206 /* SunPadTV.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SunPadTV.app; sourceTree = BUILT_PRODUCTS_DIR; };
BB0000000000000000000207 /* tvOS Sys */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Sys; path = build/tvos-deps/ModernGekko/vendor/dolphin/Data/Sys; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -598,7 +598,7 @@
"$(SRCROOT)/build/tvos-deps/ModernGekko/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/Source/Core",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/GXRuntime/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/build-tvos-appletvos-public",
"$(SRCROOT)/build/tvos-deps/ModernGekko/build-tvos-$(PLATFORM_NAME)-public",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/fmt/fmt/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/Externals/picojson",
);
Expand All @@ -609,7 +609,7 @@
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/apple/tvos/Provisioned/appletvos/libs",
"$(SRCROOT)/apple/tvos/Provisioned/$(PLATFORM_NAME)/libs",
);
MARKETING_VERSION = 0.1.0;
OTHER_LDFLAGS = (
Expand All @@ -632,7 +632,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.sunpad.SunPad.tv;
PRODUCT_NAME = SunPadTV;
SDKROOT = appletvos;
SUPPORTED_PLATFORMS = appletvos;
SUPPORTED_PLATFORMS = "appletvos appletvsimulator";
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 17.0;
};
Expand All @@ -653,7 +653,7 @@
"$(SRCROOT)/build/tvos-deps/ModernGekko/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/Source/Core",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/GXRuntime/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/build-tvos-appletvos-public",
"$(SRCROOT)/build/tvos-deps/ModernGekko/build-tvos-$(PLATFORM_NAME)-public",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/fmt/fmt/include",
"$(SRCROOT)/build/tvos-deps/ModernGekko/vendor/dolphin/Externals/picojson",
);
Expand All @@ -664,7 +664,7 @@
);
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/apple/tvos/Provisioned/appletvos/libs",
"$(SRCROOT)/apple/tvos/Provisioned/$(PLATFORM_NAME)/libs",
);
MARKETING_VERSION = 0.1.0;
OTHER_LDFLAGS = (
Expand All @@ -687,7 +687,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.sunpad.SunPad.tv;
PRODUCT_NAME = SunPadTV;
SDKROOT = appletvos;
SUPPORTED_PLATFORMS = appletvos;
SUPPORTED_PLATFORMS = "appletvos appletvsimulator";
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 17.0;
};
Expand Down
3 changes: 3 additions & 0 deletions apple/tvos/SunPadTVAppDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#import <GameController/GameController.h>
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#import <TargetConditionals.h>
#import <UIKit/UIKit.h>

#import "SunPadControllerMapping.h"
Expand Down Expand Up @@ -362,6 +363,7 @@ - (void)attemptStart {
buttons:@[]];
return;
}
#if !TARGET_OS_SIMULATOR
[self reconcileController];
if (!SunPadTVHasExtendedController()) {
UIButton *find = [self buttonWithTitle:@"Find Controller" action:^{
Expand All @@ -377,6 +379,7 @@ - (void)attemptStart {
buttons:@[find]];
return;
}
#endif
[NSFileManager.defaultManager createDirectoryAtPath:SunPadTVSupportRoot()
withIntermediateDirectories:YES attributes:nil error:nil];
SunPadSettings *settings = SunPadSettings.sharedSettings;
Expand Down
12 changes: 12 additions & 0 deletions docs/TVOS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ xcodebuild -project SunPad.xcodeproj -scheme SunPadTV -configuration Release \

Package and audit the unsigned preview only after the app build succeeds.

For the simulator gate, build and provision the same core and GMSE01 module for
the tvOS Simulator before building the `SunPadTV` target:

```sh
./scripts/tvos-build-core-simulator.sh
xcodebuild -project SunPad.xcodeproj -scheme SunPadTV -configuration Release \
-destination 'platform=tvOS Simulator,name=Apple TV 4K (3rd generation) (at 1080p)' \
-derivedDataPath /tmp/SunPadTVSimulatorDerivedData build
```

The simulator bypasses only the physical Extended Gamepad requirement. Device
builds still fail closed until an Extended Gamepad is connected.
## Physical acceptance still required

- Signed app installs and opens on a physical Apple TV.
Expand Down
1 change: 1 addition & 0 deletions scripts/check-repository.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ python3 tests/test_tvos_contract.py
test -x scripts/package-ios.sh
test -x scripts/audit-ios-package.sh
test -x scripts/tvos-build-core-device.sh
test -x scripts/tvos-build-core-simulator.sh
test -x scripts/prepare-tvos-dependencies.sh
test -x scripts/tvos-provision-device.sh
test -x scripts/stage-tvos-game-data.sh
Expand Down
13 changes: 9 additions & 4 deletions scripts/tvos-build-core-device.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@ ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
MG="${SUNPAD_TVOS_MODERNGEKKO_ROOT:-$ROOT/build/tvos-deps/ModernGekko}"
TPL="$ROOT/ref/ModernGekko-Template"
TOOLCHAIN="$ROOT/scripts/tvos-device-toolchain.cmake"
BUILD="$MG/build-tvos-appletvos-public"
SDK="${SUNPAD_TVOS_SDK:-appletvos}"
[[ "$SDK" = appletvos || "$SDK" = appletvsimulator ]] || {
echo "unsupported tvOS SDK: $SDK" >&2
exit 64
}
BUILD="$MG/build-tvos-$SDK-public"
MODULE_BUILD="${SUNPAD_TVOS_MODULE_BUILD:-/tmp/sunpad-module-tvos}"

"$ROOT/scripts/prepare-tvos-dependencies.sh"

CMAKE_COMMON=(
-DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN"
-DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_SYSTEM_PROCESSOR=arm64
-DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64
-DCMAKE_OSX_SYSROOT="$SDK" -DCMAKE_OSX_ARCHITECTURES=arm64
-DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 -DCMAKE_BUILD_TYPE=Release
-DUSE_SYSTEM_FMT=OFF -DENABLE_QT=OFF -DENABLE_TESTS=OFF
-DUSE_DISCORD_PRESENCE=OFF -DUSE_MGBA=OFF
-DUSE_RETRO_ACHIEVEMENTS=OFF -DENABLE_AUTOUPDATE=OFF
-DENABLE_ANALYTICS=OFF -DUSE_UPNP=OFF
-DMODERNGEKKO_ENABLE_DOLPHIN_TESTS=OFF
-DENABLE_CUBEB=OFF -DENABLE_VULKAN=OFF
-DUSE_SYSTEM_LZ4=OFF -DUSE_SYSTEM_ZSTD=OFF
-DUSE_SYSTEM_LZ4=OFF -DUSE_SYSTEM_XXHASH=OFF -DUSE_SYSTEM_ZSTD=OFF
-DHAVE_PIPE2=0 -DUSE_SANITIZERS=OFF
"-DCMAKE_C_FLAGS=-ffile-prefix-map=$ROOT=."
"-DCMAKE_CXX_FLAGS=-ffile-prefix-map=$ROOT=."
Expand Down Expand Up @@ -56,7 +61,7 @@ fi
cmake -S "$MG/vendor/dolphin/module-template" -B "$MODULE_BUILD" -G Ninja \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" \
-DCMAKE_SYSTEM_NAME=tvOS -DCMAKE_SYSTEM_PROCESSOR=arm64 \
-DCMAKE_OSX_SYSROOT=appletvos -DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_OSX_SYSROOT="$SDK" -DCMAKE_OSX_ARCHITECTURES=arm64 \
-DCMAKE_OSX_DEPLOYMENT_TARGET=17.0 -DGAME_ID=GMSE01 \
-DGENERATED_DIR="$GEN" -DGXRUNTIME_DIR="$MG/vendor/dolphin/GXRuntime" \
-DCHASSIS_ABI_DIR="$MG/vendor/dolphin/Source/Core/Core/PowerPC/StaticRecomp"
Expand Down
7 changes: 7 additions & 0 deletions scripts/tvos-build-core-simulator.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
export SUNPAD_TVOS_SDK=appletvsimulator
export SUNPAD_TVOS_MODULE_BUILD="${SUNPAD_TVOS_MODULE_BUILD:-/tmp/sunpad-module-tvos-appletvsimulator}"
exec "$ROOT/scripts/tvos-build-core-device.sh"
16 changes: 12 additions & 4 deletions scripts/tvos-provision-device.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@ set -euo pipefail

ROOT="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
MG="${SUNPAD_TVOS_MODERNGEKKO_ROOT:-$ROOT/build/tvos-deps/ModernGekko}"
TVOS_BUILD="${SUNPAD_TVOS_BUILD:-$MG/build-tvos-appletvos-public}"
MODULE="${SUNPAD_TVOS_MODULE_PATH:-/tmp/sunpad-module-tvos/gGMSE01_recomp.dylib}"
OUT="${SUNPAD_TVOS_PROVISIONED_OUT:-$ROOT/apple/tvos/Provisioned/appletvos}"
SDK="${SUNPAD_TVOS_SDK:-appletvos}"
[[ "$SDK" = appletvos || "$SDK" = appletvsimulator ]] || {
echo "unsupported tvOS SDK: $SDK" >&2
exit 64
}
TVOS_BUILD="${SUNPAD_TVOS_BUILD:-$MG/build-tvos-$SDK-public}"
MODULE_BUILD="${SUNPAD_TVOS_MODULE_BUILD:-/tmp/sunpad-module-tvos}"
MODULE="${SUNPAD_TVOS_MODULE_PATH:-$MODULE_BUILD/gGMSE01_recomp.dylib}"
OUT="${SUNPAD_TVOS_PROVISIONED_OUT:-$ROOT/apple/tvos/Provisioned/$SDK}"
LIBS_DIR="$OUT/libs"

[[ -d "$TVOS_BUILD" ]] || { echo "missing tvOS core build: $TVOS_BUILD" >&2; exit 1; }
Expand Down Expand Up @@ -60,5 +66,7 @@ done

libtool -static -o "$LIBS_DIR/libSunPadCore.a" "${LIBS[@]}"
cp "$MODULE" "$OUT/gGMSE01_recomp.dylib"
vtool -show-build "$OUT/gGMSE01_recomp.dylib" | grep -Eq 'platform +TVOS$'
platform=TVOS
[[ "$SDK" = appletvsimulator ]] && platform=TVOSSIMULATOR
vtool -show-build "$OUT/gGMSE01_recomp.dylib" | grep -Eq "platform +$platform$"
echo "tvOS core and GMSE01 module provisioned: $OUT"
10 changes: 9 additions & 1 deletion tests/test_tvos_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def test_target_and_metadata_are_project_owned(self):
with (ROOT / "apple/tvos/Info.plist").open("rb") as handle:
info = plistlib.load(handle)
self.assertIn("TARGETED_DEVICE_FAMILY = 3;", project)
self.assertIn('SUPPORTED_PLATFORMS = "appletvos appletvsimulator";', project)
self.assertIn("Provisioned/$(PLATFORM_NAME)/libs", project)
self.assertIn("Provisioned/$(PLATFORM_NAME)/gGMSE01_recomp.dylib", project)
self.assertTrue(info["GCSupportsControllerUserInteraction"])
self.assertEqual(
info["GCSupportedGameControllers"], [{"ProfileName": "ExtendedGamepad"}]
Expand Down Expand Up @@ -57,7 +60,12 @@ def test_build_produces_core_and_tvos_module(self):
self.assertIn("-DCMAKE_SYSTEM_NAME=tvOS", build)
self.assertIn("gGMSE01_recomp.dylib", provision)
self.assertNotIn("Xcode-27", build + provision)
self.assertIn("platform +TVOS", provision)
self.assertIn("platform=TVOS", provision)
simulator = self.text("scripts/tvos-build-core-simulator.sh")
self.assertIn("SUNPAD_TVOS_SDK=appletvsimulator", simulator)
self.assertIn('SDK="${SUNPAD_TVOS_SDK:-appletvos}"', build)
self.assertIn("TVOSSIMULATOR", provision)
self.assertIn("#if !TARGET_OS_SIMULATOR", self.text("apple/tvos/SunPadTVAppDelegate.mm"))

def test_device_workflow_preserves_scope(self):
stage = self.text("scripts/stage-tvos-game-data.sh")
Expand Down