From 40ff6e2d15e29dbd66ade30e180c2be9a09ea35b Mon Sep 17 00:00:00 2001 From: moonpower Date: Sun, 23 Nov 2025 05:24:54 +0300 Subject: [PATCH] feat(android): add support for building SDL2/libarmsx for Android - Updated .gitignore to include Android native libraries and dependencies. - Modified android/app/build.gradle to register a new task for preparing native binaries. - Enhanced build.sh to support building SDL2/libarmsx for Android, including NDK setup and BIOS handling. - Refactored iOS AppDelegate to integrate RNOverlayController and EmulatorRunner for better overlay management. - Introduced EmulatorRunner class to handle the emulator lifecycle and argument passing. - Created RNOverlayController to manage the React Native overlay, including mounting and unmounting logic. - Updated Xcode project files to reflect new source files and dependencies. --- .gitignore | 10 +- android/app/build.gradle | 19 ++ build.sh | 110 +++++++++++ ios/HostApp/ARMSX.xcodeproj/project.pbxproj | 129 +++++++------ .../UserInterfaceState.xcuserstate | Bin 24174 -> 27303 bytes ios/HostApp/Sources/AppDelegate.mm | 176 ++---------------- ios/HostApp/Sources/EmulatorRunner.h | 13 ++ ios/HostApp/Sources/EmulatorRunner.mm | 128 +++++++++++++ ios/HostApp/Sources/RNOverlayController.h | 13 ++ ios/HostApp/Sources/RNOverlayController.mm | 66 +++++++ 10 files changed, 438 insertions(+), 226 deletions(-) create mode 100644 ios/HostApp/Sources/EmulatorRunner.h create mode 100644 ios/HostApp/Sources/EmulatorRunner.mm create mode 100644 ios/HostApp/Sources/RNOverlayController.h create mode 100644 ios/HostApp/Sources/RNOverlayController.mm diff --git a/.gitignore b/.gitignore index a236a90..2b88c02 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,9 @@ snap/ psxe.app .DS_Store */.DS_Store -node_modules -ios/HostApp/Pods/* -ios/HostApp/build/* -helpers/* \ No newline at end of file +node_modules +ios/HostApp/Pods/* +ios/HostApp/build/* +helpers/* +android/app/src/main/jniLibs/ +android/native-deps/ diff --git a/android/app/build.gradle b/android/app/build.gradle index 6a993e3..0b0f470 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -65,6 +65,7 @@ android { java.srcDirs += ['../../third_party/SDL/android-project/app/src/main/java'] res.srcDirs += ['src/main/res', '../../third_party/SDL/android-project/app/src/main/res'] assets.srcDirs += ['src/main/assets'] + jniLibs.srcDirs += ['src/main/jniLibs'] } } } @@ -87,3 +88,21 @@ dependencies { implementation 'com.facebook.react:react-android:0.76.0' implementation 'com.facebook.react:hermes-android:0.76.0' } + +def repoRootDir = rootProject.projectDir.parentFile +def nativeOutput = file("$projectDir/src/main/jniLibs/arm64-v8a/libarmsx.so") + +tasks.register("prepareNativeBinaries") { + group = "native" + description = "Build SDL2/libarmsx via build.sh android." + inputs.file(new File(repoRootDir, "build.sh")) + outputs.file(nativeOutput) + doLast { + exec { + workingDir repoRootDir + commandLine "./build.sh", "android" + } + } +} + +preBuild.dependsOn(tasks.named("prepareNativeBinaries")) diff --git a/build.sh b/build.sh index a864598..a8b4ac9 100755 --- a/build.sh +++ b/build.sh @@ -8,6 +8,7 @@ set -e # ./build.sh ios -> build iOS dylib using iPhone SDK + ios/Frameworks/SDL2.xcframework # ./build.sh macosapp -> build desktop exe and bundle armsx.app # ./build.sh wasm -> build WebAssembly target to bin/wasm using emscripten +# ./build.sh android -> build SDL2/libarmsx for Android and stage under android/app/src/main/jniLibs MODE="$1" @@ -57,6 +58,115 @@ elif [ "$MODE" = "shared" ]; then make clean SDL_STATIC=0 make shared +elif [ "$MODE" = "android" ]; then + ANDROID_NDK_ROOT="${ANDROID_NDK_ROOT:-${ANDROID_NDK_HOME:-${NDK_HOME:-}}}" + if [ -z "${ANDROID_NDK_ROOT}" ]; then + echo "ANDROID_NDK_ROOT (or ANDROID_NDK_HOME / NDK_HOME) must be set to a valid NDK path." + exit 1 + fi + if [ ! -d "${ANDROID_NDK_ROOT}" ]; then + echo "Android NDK path ${ANDROID_NDK_ROOT} does not exist." + exit 1 + fi + + ANDROID_ABI="${ANDROID_ABI:-arm64-v8a}" + ANDROID_API="${ANDROID_API:-26}" + ANDROID_PLATFORM="android-${ANDROID_API}" + + case "${ANDROID_ABI}" in + arm64-v8a) + ANDROID_TRIPLE="aarch64-linux-android" + ;; + armeabi-v7a) + ANDROID_TRIPLE="armv7a-linux-androideabi" + ;; + x86_64) + ANDROID_TRIPLE="x86_64-linux-android" + ;; + *) + echo "Unsupported ANDROID_ABI '${ANDROID_ABI}'." + exit 1 + ;; + esac + + HOST_OS="$(uname -s | tr '[:upper:]' '[:lower:]')" + HOST_ARCH="$(uname -m)" + case "${HOST_ARCH}" in + arm64|aarch64) + HOST_ARCH_TAG="arm64" + ;; + x86_64) + HOST_ARCH_TAG="x86_64" + ;; + *) + HOST_ARCH_TAG="${HOST_ARCH}" + ;; + esac + + HOST_TAG="${HOST_OS}-${HOST_ARCH_TAG}" + TOOLCHAIN_DIR="${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/${HOST_TAG}" + if [ ! -d "${TOOLCHAIN_DIR}" ]; then + # Fallback to the first available prebuilt toolchain + TOOLCHAIN_DIR="$(ls -d "${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/"* 2>/dev/null | head -n 1)" + if [ -z "${TOOLCHAIN_DIR}" ]; then + echo "Unable to locate LLVM toolchain inside ${ANDROID_NDK_ROOT}." + exit 1 + fi + HOST_TAG="$(basename "${TOOLCHAIN_DIR}")" + fi + + echo "Using Android NDK at ${ANDROID_NDK_ROOT} (toolchain ${HOST_TAG}, ABI ${ANDROID_ABI}, API ${ANDROID_API})" + + SDL_BUILD_ROOT="build/android/sdl" + SDL_INSTALL_DIR="${SDL_BUILD_ROOT}/install" + mkdir -p "${SDL_BUILD_ROOT}" + + cmake -S third_party/SDL -B "${SDL_BUILD_ROOT}" \ + -DANDROID=ON \ + -DANDROID_ABI="${ANDROID_ABI}" \ + -DANDROID_PLATFORM="${ANDROID_PLATFORM}" \ + -DANDROID_STL=c++_shared \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_ANDROID_NDK="${ANDROID_NDK_ROOT}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=ON \ + -DSDL_STATIC=OFF \ + -DSDL_TEST=OFF \ + -DCMAKE_INSTALL_PREFIX="${SDL_INSTALL_DIR}" + + cmake --build "${SDL_BUILD_ROOT}" --config Release + cmake --install "${SDL_BUILD_ROOT}" --config Release + + SDL_LIB_PATH="${SDL_INSTALL_DIR}/lib" + SDL_INCLUDE_PATH="${SDL_INSTALL_DIR}/include/SDL2" + SDL_SHARED_LIB="${SDL_LIB_PATH}/libSDL2.so" + if [ ! -f "${SDL_SHARED_LIB}" ]; then + echo "SDL shared library not found at ${SDL_SHARED_LIB}" + exit 1 + fi + + JNI_LIB_DIR="android/app/src/main/jniLibs/${ANDROID_ABI}" + SDL_HEADER_STAGE="android/native-deps/SDL2/include" + mkdir -p "${JNI_LIB_DIR}" + rm -rf "${SDL_HEADER_STAGE}" + mkdir -p "${SDL_HEADER_STAGE}" + cp -R "${SDL_INCLUDE_PATH}/." "${SDL_HEADER_STAGE}/" + + TOOLCHAIN_BIN="${TOOLCHAIN_DIR}/bin" + CC="${TOOLCHAIN_BIN}/${ANDROID_TRIPLE}${ANDROID_API}-clang" + CXX="${TOOLCHAIN_BIN}/${ANDROID_TRIPLE}${ANDROID_API}-clang++" + export CC + export CXX + + SDL_CFLAGS="-D_REENTRANT -DANDROID -I${SDL_INCLUDE_PATH}" + SDL_LIBS="-L${SDL_LIB_PATH} -lSDL2 -llog -landroid -lGLESv3 -lEGL -lOpenSLES -lm -lc++_shared" + + make clean + SDL_STATIC=0 SDL_CFLAGS="${SDL_CFLAGS}" SDL_LIBS_DYNAMIC="${SDL_LIBS}" make shared + + cp "${SDL_SHARED_LIB}" "${JNI_LIB_DIR}/" + cp bin/libarmsx.so "${JNI_LIB_DIR}/" + elif [ "$MODE" = "wasm" ]; then make clean if command -v emmake >/dev/null 2>&1; then diff --git a/ios/HostApp/ARMSX.xcodeproj/project.pbxproj b/ios/HostApp/ARMSX.xcodeproj/project.pbxproj index 28903ad..46322eb 100644 --- a/ios/HostApp/ARMSX.xcodeproj/project.pbxproj +++ b/ios/HostApp/ARMSX.xcodeproj/project.pbxproj @@ -7,16 +7,17 @@ objects = { /* Begin PBXBuildFile section */ - 1085901F2ED1CE5200D77FB1 /* bios.bin in Resources */ = {isa = PBXBuildFile; fileRef = 1085901E2ED1CE5200D77FB1 /* bios.bin */; }; + 0F9A2930A40B75845584BE6B /* RNOverlayController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0D4D51B594CEA0DA332E5D3E /* RNOverlayController.mm */; }; 3464725A44F4A69FBE2AB974 /* libarmsx.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 653A133BDB61BD1059CFAF23 /* libarmsx.dylib */; }; 3A83F5253BCB4AD47A6742B1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F686B87C80EB4D3204CDEEC0 /* main.m */; }; + 4193FB5FD92D4849FE747E3C /* libPods-ARMSX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6AC80B70529AD04509472C06 /* libPods-ARMSX.a */; }; 48352C131E8B0327373C61EC /* SDL2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 518B2258235B15B15373B04A /* SDL2.xcframework */; }; 5480B78F3661F4CB9446657C /* libarmsx.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 653A133BDB61BD1059CFAF23 /* libarmsx.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7435C97274E9B2668E6C9C86 /* SDL2.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 518B2258235B15B15373B04A /* SDL2.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 8BC157B9E48AA3D81793AA6A /* ARMSXModule.m in Sources */ = {isa = PBXBuildFile; fileRef = B35E8D4127C001F1E6F6F2AF /* ARMSXModule.m */; }; B16F42A290ECFF7DF154F24F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9484FEBF96C002F04D73B70C /* AppDelegate.mm */; }; - CB535A71335895F3B1AFB7E3 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EB192CCB1425D7B15B24E93A /* PrivacyInfo.xcprivacy */; }; - EFAA42EF186BC1A6B4315DDF /* libPods-ARMSX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B15244F288D2AB7C06E0752A /* libPods-ARMSX.a */; }; + D01D643FB3D4148C346B6200 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 150B82E62E95502476AFB049 /* PrivacyInfo.xcprivacy */; }; + E46372898534C89975BD3888 /* EmulatorRunner.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6CFA7D190974AD439774FB40 /* EmulatorRunner.mm */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -36,21 +37,24 @@ /* Begin PBXFileReference section */ 019479875FC7433ACB65A02E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 1085901E2ED1CE5200D77FB1 /* bios.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = bios.bin; path = ../../bios.bin; sourceTree = ""; }; + 03BD4A1BC72E3DE47140D96E /* EmulatorRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EmulatorRunner.h; sourceTree = ""; }; + 0D4D51B594CEA0DA332E5D3E /* RNOverlayController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RNOverlayController.mm; sourceTree = ""; }; + 150B82E62E95502476AFB049 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 1760EE6722F9E2BEDFDCCC6D /* armsx_bridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = armsx_bridge.h; sourceTree = ""; }; + 2EC8F1F7ACC46D5FABC3595C /* Pods-ARMSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ARMSX.release.xcconfig"; path = "Target Support Files/Pods-ARMSX/Pods-ARMSX.release.xcconfig"; sourceTree = ""; }; 518B2258235B15B15373B04A /* SDL2.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = SDL2.xcframework; path = ../Frameworks/SDL2.xcframework; sourceTree = ""; }; 64E02E161FEB892FC40F8BE1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 653A133BDB61BD1059CFAF23 /* libarmsx.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libarmsx.dylib; path = ../Frameworks/libarmsx.dylib; sourceTree = ""; }; - 67F12B2E9A249BA431A15C7C /* Pods-ARMSX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ARMSX.debug.xcconfig"; path = "Target Support Files/Pods-ARMSX/Pods-ARMSX.debug.xcconfig"; sourceTree = ""; }; + 6AC80B70529AD04509472C06 /* libPods-ARMSX.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ARMSX.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 6CFA7D190974AD439774FB40 /* EmulatorRunner.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = EmulatorRunner.mm; sourceTree = ""; }; 6E26D0ECD2CD966E5EA1C326 /* Pods-ARMSX.release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Pods-ARMSX.release.xcconfig"; sourceTree = ""; }; 87BA3EE4597141AA97A5557D /* ARMSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ARMSX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9484FEBF96C002F04D73B70C /* AppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AppDelegate.mm; sourceTree = ""; }; 996CB57F5DD4B88A0931BB95 /* ARMSXModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ARMSXModule.h; sourceTree = ""; }; - B15244F288D2AB7C06E0752A /* libPods-ARMSX.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ARMSX.a"; sourceTree = BUILT_PRODUCTS_DIR; }; B35E8D4127C001F1E6F6F2AF /* ARMSXModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ARMSXModule.m; sourceTree = ""; }; + C65B43834B690E6C938FFCEB /* Pods-ARMSX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ARMSX.debug.xcconfig"; path = "Target Support Files/Pods-ARMSX/Pods-ARMSX.debug.xcconfig"; sourceTree = ""; }; + D5E6D32A2F0405EA658F4F9F /* RNOverlayController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNOverlayController.h; sourceTree = ""; }; E3FAF87D7E4A2345FC43CA68 /* Pods-ARMSX.debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Pods-ARMSX.debug.xcconfig"; sourceTree = ""; }; - EB192CCB1425D7B15B24E93A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; - F150862C7055ADDBB5B57CF1 /* Pods-ARMSX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ARMSX.release.xcconfig"; path = "Target Support Files/Pods-ARMSX/Pods-ARMSX.release.xcconfig"; sourceTree = ""; }; F686B87C80EB4D3204CDEEC0 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -61,7 +65,7 @@ files = ( 48352C131E8B0327373C61EC /* SDL2.xcframework in Frameworks */, 3464725A44F4A69FBE2AB974 /* libarmsx.dylib in Frameworks */, - EFAA42EF186BC1A6B4315DDF /* libPods-ARMSX.a in Frameworks */, + 4193FB5FD92D4849FE747E3C /* libPods-ARMSX.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -73,7 +77,7 @@ children = ( 653A133BDB61BD1059CFAF23 /* libarmsx.dylib */, 518B2258235B15B15373B04A /* SDL2.xcframework */, - B15244F288D2AB7C06E0752A /* libPods-ARMSX.a */, + 6AC80B70529AD04509472C06 /* libPods-ARMSX.a */, ); name = Frameworks; sourceTree = ""; @@ -81,13 +85,12 @@ 48D57A41DAF59CF0D443A086 = { isa = PBXGroup; children = ( - 1085901E2ED1CE5200D77FB1 /* bios.bin */, B6E3FD0B464E641CA58C9EA3 /* Pods-ARMSX */, D0581165A1AB8CE875BD92E0 /* Sources */, 473DBC863A1B097C0DD3710F /* Frameworks */, 9B7D6BDFE7232B2E5D8A379F /* Products */, - EB192CCB1425D7B15B24E93A /* PrivacyInfo.xcprivacy */, - A221D24E0608A650E479FF55 /* Pods */, + 150B82E62E95502476AFB049 /* PrivacyInfo.xcprivacy */, + A03BFBC5506510D43087F66D /* Pods */, ); sourceTree = ""; }; @@ -99,12 +102,13 @@ name = Products; sourceTree = ""; }; - A221D24E0608A650E479FF55 /* Pods */ = { + A03BFBC5506510D43087F66D /* Pods */ = { isa = PBXGroup; children = ( - 67F12B2E9A249BA431A15C7C /* Pods-ARMSX.debug.xcconfig */, - F150862C7055ADDBB5B57CF1 /* Pods-ARMSX.release.xcconfig */, + C65B43834B690E6C938FFCEB /* Pods-ARMSX.debug.xcconfig */, + 2EC8F1F7ACC46D5FABC3595C /* Pods-ARMSX.release.xcconfig */, ); + name = Pods; path = Pods; sourceTree = ""; }; @@ -126,8 +130,12 @@ 1760EE6722F9E2BEDFDCCC6D /* armsx_bridge.h */, 996CB57F5DD4B88A0931BB95 /* ARMSXModule.h */, B35E8D4127C001F1E6F6F2AF /* ARMSXModule.m */, + 03BD4A1BC72E3DE47140D96E /* EmulatorRunner.h */, + 6CFA7D190974AD439774FB40 /* EmulatorRunner.mm */, 019479875FC7433ACB65A02E /* Info.plist */, F686B87C80EB4D3204CDEEC0 /* main.m */, + D5E6D32A2F0405EA658F4F9F /* RNOverlayController.h */, + 0D4D51B594CEA0DA332E5D3E /* RNOverlayController.mm */, ); path = Sources; sourceTree = ""; @@ -139,13 +147,13 @@ isa = PBXNativeTarget; buildConfigurationList = 1A8EEE030E970E82EC7B77F8 /* Build configuration list for PBXNativeTarget "ARMSX" */; buildPhases = ( - EFC8122B20E4CF7480A3496D /* [CP] Check Pods Manifest.lock */, + 317B2A99C6743282DB0B4378 /* [CP] Check Pods Manifest.lock */, 21709ABD74D1031F0389C4EC /* Sources */, D6910F21954EF81975654CFA /* Frameworks */, 8F7F79E31569D7CF1ACBC707 /* Embed Frameworks */, - 469D63DB0DB7A815B1CF8164 /* Resources */, - DBCAE27636921EA4F4D6F40B /* [CP] Embed Pods Frameworks */, - 15CF2083A041D419876ECE72 /* [CP] Copy Pods Resources */, + CC0F3B60AB5121E52FB88488 /* Resources */, + A1F84CFC47166FD1D4F87D5C /* [CP] Embed Pods Frameworks */, + A2B4EFBBBDD2A4D81A6CEE13 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -185,53 +193,18 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 469D63DB0DB7A815B1CF8164 /* Resources */ = { + CC0F3B60AB5121E52FB88488 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1085901F2ED1CE5200D77FB1 /* bios.bin in Resources */, - CB535A71335895F3B1AFB7E3 /* PrivacyInfo.xcprivacy in Resources */, + D01D643FB3D4148C346B6200 /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 15CF2083A041D419876ECE72 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - DBCAE27636921EA4F4D6F40B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - EFC8122B20E4CF7480A3496D /* [CP] Check Pods Manifest.lock */ = { + 317B2A99C6743282DB0B4378 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -253,6 +226,40 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + A1F84CFC47166FD1D4F87D5C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A2B4EFBBBDD2A4D81A6CEE13 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ARMSX/Pods-ARMSX-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -262,6 +269,8 @@ files = ( 8BC157B9E48AA3D81793AA6A /* ARMSXModule.m in Sources */, B16F42A290ECFF7DF154F24F /* AppDelegate.mm in Sources */, + E46372898534C89975BD3888 /* EmulatorRunner.mm in Sources */, + 0F9A2930A40B75845584BE6B /* RNOverlayController.mm in Sources */, 3A83F5253BCB4AD47A6742B1 /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -271,7 +280,7 @@ /* Begin XCBuildConfiguration section */ 6C84D25B5BF8B28E170A1AC3 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 67F12B2E9A249BA431A15C7C /* Pods-ARMSX.debug.xcconfig */; + baseConfigurationReference = C65B43834B690E6C938FFCEB /* Pods-ARMSX.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; @@ -438,7 +447,7 @@ }; EA79FDBEDBB17F1B501A7D1A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F150862C7055ADDBB5B57CF1 /* Pods-ARMSX.release.xcconfig */; + baseConfigurationReference = 2EC8F1F7ACC46D5FABC3595C /* Pods-ARMSX.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; diff --git a/ios/HostApp/ARMSX.xcworkspace/xcuserdata/mohammed.xcuserdatad/UserInterfaceState.xcuserstate b/ios/HostApp/ARMSX.xcworkspace/xcuserdata/mohammed.xcuserdatad/UserInterfaceState.xcuserstate index 9b606d664e4da91dcd79d7c6fa30bba645599f67..957d0867b441e719c48f1dd6eb624293f8aba378 100644 GIT binary patch delta 14724 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 delta 12517 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 #ifndef USE_HERMES #define USE_HERMES 1 #endif @@ -9,18 +8,12 @@ #else #import #endif -#import -#import -#import -#include -#include +#import "RNOverlayController.h" +#import "EmulatorRunner.h" -#import "armsx_bridge.h" - -@interface AppDelegate () -@property (nonatomic, assign) BOOL armsxRunning; -@property (nonatomic, strong) RCTBridge *reactBridge; -@property (nonatomic, strong) RCTRootView *reactRootView; +@interface AppDelegate () +@property (nonatomic, strong) RNOverlayController *overlayController; +@property (nonatomic, strong) EmulatorRunner *emulatorRunner; @end @implementation AppDelegate @@ -56,173 +49,32 @@ self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window.backgroundColor = [UIColor blackColor]; - UIViewController *controller = [UIViewController new]; - controller.view.backgroundColor = [UIColor blackColor]; - self.window.rootViewController = controller; + self.overlayController = [[RNOverlayController alloc] initWithLaunchOptions:launchOptions]; + self.window.rootViewController = self.overlayController; [self.window makeKeyAndVisible]; - [self attachReactNativeOverlay:launchOptions]; + [self attachReactNativeOverlay]; return YES; } -- (void)attachReactNativeOverlay:(NSDictionary *)launchOptions { - if (!self.window) { - return; - } - - if (!self.reactBridge) { - self.reactBridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; - } - - self.reactRootView = [[RCTRootView alloc] initWithBridge:self.reactBridge moduleName:@"ARMSXOverlay" initialProperties:nil]; - self.reactRootView.backgroundColor = [UIColor blackColor]; - self.reactRootView.frame = self.window.bounds; - self.reactRootView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - UIView *targetView = self.window.rootViewController.view ?: self.window; - [targetView addSubview:self.reactRootView]; +- (void)attachReactNativeOverlay { + [self.overlayController mountOverlayIfNeeded]; } - (void)teardownReactSurface { - if (self.reactRootView && self.reactRootView.superview) { - [self.reactRootView removeFromSuperview]; - } - self.reactRootView = nil; + [self.overlayController unmountOverlay]; } - (void)startSDLWithArgs:(NSArray *)args { - if (self.armsxRunning) { - return; - } - [self teardownReactSurface]; [self forceLandscapeIfNeeded]; - SDL_SetMainReady(); - SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); - - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { - NSLog(@"SDL_Init failed: %s", SDL_GetError()); - return; + if (!self.emulatorRunner) { + self.emulatorRunner = [EmulatorRunner new]; } - self.armsxRunning = YES; - - // Run the SDL/armsx entry on the main thread to satisfy UIKit threading requirements - dispatch_async(dispatch_get_main_queue(), ^{ - @autoreleasepool { - NSMutableArray *nativeArgs = [NSMutableArray arrayWithObject:@"armsx"]; - NSFileManager *fm = [NSFileManager defaultManager]; - - // Look for bios.bin packaged in the bundle (root, Contents/, or resource dir) - NSMutableArray *candidatePaths = [NSMutableArray array]; - NSBundle *bundle = [NSBundle mainBundle]; - - NSString *bundleRoot = bundle.bundlePath; - NSString *resourceRoot = bundle.resourcePath; - - // Common bundle layouts - if (bundleRoot.length) { - [candidatePaths addObject:[bundleRoot stringByAppendingPathComponent:@"bios.bin"]]; - [candidatePaths addObject:[bundleRoot stringByAppendingPathComponent:@"Contents/bios.bin"]]; - } - if (resourceRoot.length) { - [candidatePaths addObject:[resourceRoot stringByAppendingPathComponent:@"bios.bin"]]; - } - - // XcodeGen resources (if the optional BIOS is present) - NSString *biosPath = [bundle pathForResource:@"bios" ofType:@"bin"]; - if (biosPath.length) { - [candidatePaths insertObject:biosPath atIndex:0]; - } - - biosPath = nil; - - for (NSString *candidate in candidatePaths) { - if ([fm fileExistsAtPath:candidate]) { - biosPath = candidate; - break; - } - } - - // As a last resort, try SDL's base path - if (!biosPath.length) { - char *basePathC = SDL_GetBasePath(); - if (basePathC) { - NSString *basePath = [NSString stringWithUTF8String:basePathC]; - SDL_free(basePathC); - NSString *fallback = [basePath stringByAppendingPathComponent:@"bios.bin"]; - if ([fm fileExistsAtPath:fallback]) { - biosPath = fallback; - } - } - } - - // If found, copy to a writable pref path to avoid any translocation issues - if (biosPath.length && [fm fileExistsAtPath:biosPath]) { - char *prefPathC = SDL_GetPrefPath("nanodata", "armsx"); - NSString *prefBase = prefPathC ? [NSString stringWithUTF8String:prefPathC] : nil; - if (prefPathC) { - SDL_free(prefPathC); - } - - NSString *writableBios = prefBase ? [prefBase stringByAppendingPathComponent:@"bios.bin"] : nil; - NSError *copyErr = nil; - - // Clean existing copy to avoid stale/corrupt data - if (writableBios.length && [fm fileExistsAtPath:writableBios]) { - [fm removeItemAtPath:writableBios error:nil]; - } - - if (writableBios.length && [fm copyItemAtPath:biosPath toPath:writableBios error:©Err]) { - biosPath = writableBios; - NSLog(@"Using bundled BIOS copied to writable path %@", biosPath); - } else if (writableBios.length) { - NSLog(@"Failed to copy BIOS to writable location (%@). Using bundle path. Error: %@", writableBios, copyErr); - } - } - - if (biosPath.length && [fm fileExistsAtPath:biosPath]) { - [nativeArgs addObject:@"--bios"]; - [nativeArgs addObject:biosPath]; - NSLog(@"Passing BIOS path to libarmsx: %@", biosPath); - } else { - NSLog(@"No bundled BIOS found; libarmsx will rely on user-provided settings/CLI."); - } - - if (args.count) { - [nativeArgs addObjectsFromArray:args]; - } - - std::vector args; - std::vector argv; - - for (NSString *s in nativeArgs) { - args.emplace_back([s UTF8String]); - } - - for (const auto &s : args) - argv.push_back(s.c_str()); - - argv.push_back(nullptr); - - // Let the dylib create its own SDL window/renderer - external_main((int)args.size(), argv.data(), NULL, NULL); - } - }); -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -- (void)applicationWillTerminate:(UIApplication *)application { - self.armsxRunning = NO; + [self.emulatorRunner startWithArgs:args ?: @[]]; } @end diff --git a/ios/HostApp/Sources/EmulatorRunner.h b/ios/HostApp/Sources/EmulatorRunner.h new file mode 100644 index 0000000..5393e12 --- /dev/null +++ b/ios/HostApp/Sources/EmulatorRunner.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface EmulatorRunner : NSObject + +@property (nonatomic, assign, readonly, getter=isRunning) BOOL running; + +- (void)startWithArgs:(NSArray *)args; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/HostApp/Sources/EmulatorRunner.mm b/ios/HostApp/Sources/EmulatorRunner.mm new file mode 100644 index 0000000..95a563d --- /dev/null +++ b/ios/HostApp/Sources/EmulatorRunner.mm @@ -0,0 +1,128 @@ +#import "EmulatorRunner.h" + +#import + +#include +#include + +#import "armsx_bridge.h" + +@interface EmulatorRunner () +@property (nonatomic, assign, readwrite, getter=isRunning) BOOL running; +@end + +@implementation EmulatorRunner + +- (void)startWithArgs:(NSArray *)args { + if (self.running) { + return; + } + + SDL_SetMainReady(); + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "metal"); + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { + NSLog(@"SDL_Init failed: %s", SDL_GetError()); + return; + } + + self.running = YES; + + dispatch_async(dispatch_get_main_queue(), ^{ + @autoreleasepool { + NSMutableArray *nativeArgs = [NSMutableArray arrayWithObject:@"armsx"]; + NSFileManager *fm = [NSFileManager defaultManager]; + + NSMutableArray *candidatePaths = [NSMutableArray array]; + NSBundle *bundle = [NSBundle mainBundle]; + + NSString *bundleRoot = bundle.bundlePath; + NSString *resourceRoot = bundle.resourcePath; + + if (bundleRoot.length) { + [candidatePaths addObject:[bundleRoot stringByAppendingPathComponent:@"bios.bin"]]; + [candidatePaths addObject:[bundleRoot stringByAppendingPathComponent:@"Contents/bios.bin"]]; + } + if (resourceRoot.length) { + [candidatePaths addObject:[resourceRoot stringByAppendingPathComponent:@"bios.bin"]]; + } + + NSString *biosPath = [bundle pathForResource:@"bios" ofType:@"bin"]; + if (biosPath.length) { + [candidatePaths insertObject:biosPath atIndex:0]; + } + + biosPath = nil; + + for (NSString *candidate in candidatePaths) { + if ([fm fileExistsAtPath:candidate]) { + biosPath = candidate; + break; + } + } + + if (!biosPath.length) { + char *basePathC = SDL_GetBasePath(); + if (basePathC) { + NSString *basePath = [NSString stringWithUTF8String:basePathC]; + SDL_free(basePathC); + NSString *fallback = [basePath stringByAppendingPathComponent:@"bios.bin"]; + if ([fm fileExistsAtPath:fallback]) { + biosPath = fallback; + } + } + } + + if (biosPath.length && [fm fileExistsAtPath:biosPath]) { + char *prefPathC = SDL_GetPrefPath("nanodata", "armsx"); + NSString *prefBase = prefPathC ? [NSString stringWithUTF8String:prefPathC] : nil; + if (prefPathC) { + SDL_free(prefPathC); + } + + NSString *writableBios = prefBase ? [prefBase stringByAppendingPathComponent:@"bios.bin"] : nil; + NSError *copyErr = nil; + + if (writableBios.length && [fm fileExistsAtPath:writableBios]) { + [fm removeItemAtPath:writableBios error:nil]; + } + + if (writableBios.length && [fm copyItemAtPath:biosPath toPath:writableBios error:©Err]) { + biosPath = writableBios; + NSLog(@"Using bundled BIOS copied to writable path %@", biosPath); + } else if (writableBios.length) { + NSLog(@"Failed to copy BIOS to writable location (%@). Using bundle path. Error: %@", writableBios, copyErr); + } + } + + if (biosPath.length && [fm fileExistsAtPath:biosPath]) { + [nativeArgs addObject:@"--bios"]; + [nativeArgs addObject:biosPath]; + NSLog(@"Passing BIOS path to libarmsx: %@", biosPath); + } else { + NSLog(@"No bundled BIOS found; libarmsx will rely on user-provided settings/CLI."); + } + + if (args.count) { + [nativeArgs addObjectsFromArray:args]; + } + + std::vector argvStorage; + std::vector argv; + + for (NSString *stringArg in nativeArgs) { + argvStorage.emplace_back([stringArg UTF8String]); + } + + for (const auto &value : argvStorage) { + argv.push_back(value.c_str()); + } + + argv.push_back(nullptr); + + external_main((int)argvStorage.size(), argv.data(), NULL, NULL); + } + }); +} + +@end diff --git a/ios/HostApp/Sources/RNOverlayController.h b/ios/HostApp/Sources/RNOverlayController.h new file mode 100644 index 0000000..6124ca8 --- /dev/null +++ b/ios/HostApp/Sources/RNOverlayController.h @@ -0,0 +1,13 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface RNOverlayController : UIViewController + +- (instancetype)initWithLaunchOptions:(NSDictionary *)launchOptions; +- (void)mountOverlayIfNeeded; +- (void)unmountOverlay; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/HostApp/Sources/RNOverlayController.mm b/ios/HostApp/Sources/RNOverlayController.mm new file mode 100644 index 0000000..4f1deda --- /dev/null +++ b/ios/HostApp/Sources/RNOverlayController.mm @@ -0,0 +1,66 @@ +#import "RNOverlayController.h" + +#import +#import +#import + +@interface RNOverlayController () +@property (nonatomic, strong) NSDictionary *launchOptions; +@property (nonatomic, strong, nullable) RCTBridge *bridge; +@property (nonatomic, strong, nullable) RCTRootView *rootView; +@end + +@implementation RNOverlayController + +- (instancetype)initWithLaunchOptions:(NSDictionary *)launchOptions { + self = [super initWithNibName:nil bundle:nil]; + if (self) { + _launchOptions = launchOptions ?: @{}; + } + return self; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor blackColor]; + [self mountOverlayIfNeeded]; +} + +- (void)mountOverlayIfNeeded { + if (!self.bridge) { + self.bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions]; + } + + if (self.rootView) { + if (!self.rootView.superview) { + [self.view addSubview:self.rootView]; + } + return; + } + + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:self.bridge moduleName:@"ARMSXOverlay" initialProperties:nil]; + rootView.frame = self.view.bounds; + rootView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + rootView.backgroundColor = [UIColor blackColor]; + [self.view addSubview:rootView]; + self.rootView = rootView; +} + +- (void)unmountOverlay { + if (self.rootView && self.rootView.superview) { + [self.rootView removeFromSuperview]; + } + self.rootView = nil; +} + +#pragma mark - RCTBridgeDelegate + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +@end