diff --git a/CMakePresets.json b/CMakePresets.json index b64ca36a..cbd2260a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -165,6 +165,65 @@ "CMAKE_BUILD_TYPE": "Release", "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true } + }, + { + "name": "ios-simulator-debug", + "displayName": "iOS Simulator Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "iOS", + "CMAKE_OSX_SYSROOT": "iphonesimulator", + "CMAKE_OSX_ARCHITECTURES": "arm64", + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + }, + "VCPKG_TARGET_TRIPLET": { + "value": "arm64-ios-simulator", + "type": "STRING" + } + }, + "environment": { + "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } + }, + { + "name": "ios-device-release", + "displayName": "iOS Device Release", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_SYSTEM_NAME": "iOS", + "CMAKE_OSX_SYSROOT": "iphoneos", + "CMAKE_OSX_ARCHITECTURES": "arm64", + "CMAKE_BUILD_TYPE": "Release", + "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true, + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + }, + "VCPKG_TARGET_TRIPLET": { + "value": "arm64-ios", + "type": "STRING" + } + }, + "environment": { + "VCPKG_ROOT": "${sourceDir}/thirdparty/vcpkg" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } } ] } diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 01e040ab..d22da7b9 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -97,9 +97,9 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") "os/linux/user_linux.cpp" "os/linux/version_linux.cpp" ) -elseif (APPLE) - set(UNLEASHED_RECOMP_OS_CXX_SOURCES - "os/macos/logger_macos.cpp" +elseif (APPLE) + set(UNLEASHED_RECOMP_OS_CXX_SOURCES + "os/macos/logger_macos.cpp" "os/macos/media_macos.cpp" "os/macos/process_macos.cpp" "os/macos/user_macos.cpp" @@ -292,7 +292,26 @@ if (WIN32) if (${CMAKE_BUILD_TYPE} MATCHES "Release") target_link_options(UnleashedRecomp PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") endif() -elseif (APPLE) +elseif (CMAKE_SYSTEM_NAME STREQUAL "iOS") + CreateVersionString( + VERSION_TXT ${VERSION_TXT} + OUTPUT_VAR IOS_BUNDLE_VERSION + ) + + add_executable(UnleashedRecomp MACOSX_BUNDLE + ${UNLEASHED_RECOMP_CXX_SOURCES} + ) + set_target_properties(UnleashedRecomp PROPERTIES + OUTPUT_NAME "Unleashed Recompiled" + MACOSX_BUNDLE_GUI_IDENTIFIER hedge-dev.UnleashedRecomp + MACOSX_BUNDLE_BUNDLE_NAME "Unleashed Recompiled" + MACOSX_BUNDLE_BUNDLE_VERSION ${IOS_BUNDLE_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${IOS_BUNDLE_VERSION} + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "hedge-dev.UnleashedRecomp" + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + ) +elseif (APPLE) # Create version number for app bundle. CreateVersionString( VERSION_TXT ${VERSION_TXT} diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index d5c08581..d5e749a9 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -15,7 +15,7 @@ set(SDL2MIXER_OPUS OFF) set(SDL2MIXER_VORBIS "VORBISFILE") set(SDL2MIXER_WAVPACK OFF) -if (CMAKE_SYSTEM_NAME MATCHES "Linux") +if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "iOS") set(SDL_VULKAN_ENABLED ON CACHE BOOL "") endif() @@ -24,12 +24,18 @@ if (WIN32) endif() add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/msdf-atlas-gen") -add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended") +if (CMAKE_SYSTEM_NAME STREQUAL "iOS") + add_library(nfd STATIC "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nfd_ios_stub.c") + add_library(nfd::nfd ALIAS nfd) + target_include_directories(nfd PUBLIC "${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended/src/include") +else() + add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/nativefiledialog-extended") +endif() add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/o1heap") add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/SDL") add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/SDL_mixer") add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/plume") -if (APPLE) +if (APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "iOS") add_subdirectory("${UNLEASHED_RECOMP_THIRDPARTY_ROOT}/MoltenVK") endif() diff --git a/thirdparty/nfd_ios_stub.c b/thirdparty/nfd_ios_stub.c new file mode 100644 index 00000000..05476b3e --- /dev/null +++ b/thirdparty/nfd_ios_stub.c @@ -0,0 +1,42 @@ +#include +#include + +static const char* g_nfd_ios_error = "Native file dialogs are not implemented for iOS yet."; + +nfdresult_t NFD_Init(void) { return NFD_OKAY; } +void NFD_Quit(void) {} +const char* NFD_GetError(void) { return g_nfd_ios_error; } + +void NFD_FreePathN(nfdnchar_t* filePath) { free(filePath); } +void NFD_FreePathU8(nfdu8char_t* filePath) { free(filePath); } + +nfdresult_t NFD_OpenDialogN(nfdnchar_t** outPath, const nfdnfilteritem_t* filterList, nfdfiltersize_t filterCount, const nfdnchar_t* defaultPath) { (void)outPath; (void)filterList; (void)filterCount; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogU8(nfdu8char_t** outPath, const nfdu8filteritem_t* filterList, nfdfiltersize_t filterCount, const nfdu8char_t* defaultPath) { (void)outPath; (void)filterList; (void)filterCount; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogN_With_Impl(nfdversion_t version, nfdnchar_t** outPath, const nfdopendialognargs_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogU8_With_Impl(nfdversion_t version, nfdu8char_t** outPath, const nfdopendialogu8args_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogMultipleN(const nfdpathset_t** outPaths, const nfdnfilteritem_t* filterList, nfdfiltersize_t filterCount, const nfdnchar_t* defaultPath) { (void)outPaths; (void)filterList; (void)filterCount; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogMultipleU8(const nfdpathset_t** outPaths, const nfdu8filteritem_t* filterList, nfdfiltersize_t filterCount, const nfdu8char_t* defaultPath) { (void)outPaths; (void)filterList; (void)filterCount; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogMultipleN_With_Impl(nfdversion_t version, const nfdpathset_t** outPaths, const nfdopendialognargs_t* args) { (void)version; (void)outPaths; (void)args; return NFD_ERROR; } +nfdresult_t NFD_OpenDialogMultipleU8_With_Impl(nfdversion_t version, const nfdpathset_t** outPaths, const nfdopendialogu8args_t* args) { (void)version; (void)outPaths; (void)args; return NFD_ERROR; } +nfdresult_t NFD_SaveDialogN(nfdnchar_t** outPath, const nfdnfilteritem_t* filterList, nfdfiltersize_t filterCount, const nfdnchar_t* defaultPath, const nfdnchar_t* defaultName) { (void)outPath; (void)filterList; (void)filterCount; (void)defaultPath; (void)defaultName; return NFD_ERROR; } +nfdresult_t NFD_SaveDialogU8(nfdu8char_t** outPath, const nfdu8filteritem_t* filterList, nfdfiltersize_t filterCount, const nfdu8char_t* defaultPath, const nfdu8char_t* defaultName) { (void)outPath; (void)filterList; (void)filterCount; (void)defaultPath; (void)defaultName; return NFD_ERROR; } +nfdresult_t NFD_SaveDialogN_With_Impl(nfdversion_t version, nfdnchar_t** outPath, const nfdsavedialognargs_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_SaveDialogU8_With_Impl(nfdversion_t version, nfdu8char_t** outPath, const nfdsavedialogu8args_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_PickFolderN(nfdnchar_t** outPath, const nfdnchar_t* defaultPath) { (void)outPath; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_PickFolderU8(nfdu8char_t** outPath, const nfdu8char_t* defaultPath) { (void)outPath; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_PickFolderN_With_Impl(nfdversion_t version, nfdnchar_t** outPath, const nfdpickfoldernargs_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_PickFolderU8_With_Impl(nfdversion_t version, nfdu8char_t** outPath, const nfdpickfolderu8args_t* args) { (void)version; (void)outPath; (void)args; return NFD_ERROR; } +nfdresult_t NFD_PickFolderMultipleN(const nfdpathset_t** outPaths, const nfdnchar_t* defaultPath) { (void)outPaths; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_PickFolderMultipleU8(const nfdpathset_t** outPaths, const nfdu8char_t* defaultPath) { (void)outPaths; (void)defaultPath; return NFD_ERROR; } +nfdresult_t NFD_PickFolderMultipleN_With_Impl(nfdversion_t version, const nfdpathset_t** outPaths, const nfdpickfoldernargs_t* args) { (void)version; (void)outPaths; (void)args; return NFD_ERROR; } +nfdresult_t NFD_PickFolderMultipleU8_With_Impl(nfdversion_t version, const nfdpathset_t** outPaths, const nfdpickfolderu8args_t* args) { (void)version; (void)outPaths; (void)args; return NFD_ERROR; } +nfdresult_t NFD_PathSet_GetCount(const nfdpathset_t* pathSet, nfdpathsetsize_t* count) { (void)pathSet; if (count) *count = 0; return NFD_OKAY; } +nfdresult_t NFD_PathSet_GetPathN(const nfdpathset_t* pathSet, nfdpathsetsize_t index, nfdnchar_t** outPath) { (void)pathSet; (void)index; (void)outPath; return NFD_ERROR; } +nfdresult_t NFD_PathSet_GetPathU8(const nfdpathset_t* pathSet, nfdpathsetsize_t index, nfdu8char_t** outPath) { (void)pathSet; (void)index; (void)outPath; return NFD_ERROR; } +void NFD_PathSet_FreePathN(const nfdnchar_t* filePath) { free((void*)filePath); } +void NFD_PathSet_FreePathU8(const nfdu8char_t* filePath) { free((void*)filePath); } +void NFD_PathSet_Free(const nfdpathset_t* pathSet) { (void)pathSet; } +void NFD_PathSet_FreeEnum(nfdpathsetenum_t* enumerator) { (void)enumerator; } +nfdresult_t NFD_PathSet_GetEnum(const nfdpathset_t* pathSet, nfdpathsetenum_t** outEnumerator) { (void)pathSet; (void)outEnumerator; return NFD_ERROR; } +nfdresult_t NFD_PathSet_EnumNextN(nfdpathsetenum_t* enumerator, nfdnchar_t** outPath) { (void)enumerator; (void)outPath; return NFD_ERROR; } +nfdresult_t NFD_PathSet_EnumNextU8(nfdpathsetenum_t* enumerator, nfdu8char_t** outPath) { (void)enumerator; (void)outPath; return NFD_ERROR; } diff --git a/tools/package_ios_ipa.sh b/tools/package_ios_ipa.sh new file mode 100755 index 00000000..becb4455 --- /dev/null +++ b/tools/package_ios_ipa.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +BUILD_DIR="$ROOT/out/build/ios-device-release" +IPA_DIR="$ROOT/out/ipa" +PAYLOAD_DIR="$IPA_DIR/Payload" +APP_PATH="$BUILD_DIR/Unleashed Recompiled.app" +IPA_PATH="$IPA_DIR/UnleashedRecompiled.ipa" + +missing=0 +for file in \ + "$ROOT/UnleashedRecompLib/private/default.xex" \ + "$ROOT/UnleashedRecompLib/private/default.xexp" \ + "$ROOT/UnleashedRecompLib/private/shader.ar"; do + if [[ ! -f "$file" ]]; then + printf 'Missing required file: %s\n' "$file" >&2 + missing=1 + fi +done + +if [[ "$missing" -ne 0 ]]; then + exit 1 +fi + +cmake --preset ios-device-release +cmake --build "$BUILD_DIR" --target UnleashedRecomp -j "${JOBS:-8}" + +if [[ ! -d "$APP_PATH" ]]; then + printf 'Expected app bundle was not produced: %s\n' "$APP_PATH" >&2 + exit 1 +fi + +rm -rf "$IPA_DIR" +mkdir -p "$PAYLOAD_DIR" +cp -R "$APP_PATH" "$PAYLOAD_DIR/" + +if [[ -n "${MOBILEPROVISION:-}" ]]; then + cp "$MOBILEPROVISION" "$PAYLOAD_DIR/Unleashed Recompiled.app/embedded.mobileprovision" +fi + +if [[ -n "${CODESIGN_IDENTITY:-}" ]]; then + codesign --force --sign "$CODESIGN_IDENTITY" \ + ${ENTITLEMENTS:+--entitlements "$ENTITLEMENTS"} \ + "$PAYLOAD_DIR/Unleashed Recompiled.app" +fi + +( + cd "$IPA_DIR" + zip -qry "$IPA_PATH" Payload +) + +printf '%s\n' "$IPA_PATH"