Add initial iOS port scaffolding

This commit is contained in:
Alejandro Pérez 2026-05-16 09:13:26 -05:00
parent 5e8695a157
commit c7fe1c11e7
5 changed files with 186 additions and 7 deletions

View file

@ -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"
}
}
]
}

View file

@ -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}

View file

@ -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()

42
thirdparty/nfd_ios_stub.c vendored Normal file
View file

@ -0,0 +1,42 @@
#include <nfd.h>
#include <stdlib.h>
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; }

53
tools/package_ios_ipa.sh Executable file
View file

@ -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"