[iOS] Add XeniOS app layer

This commit is contained in:
XeniOS
2026-06-03 23:21:47 +09:00
parent 0ccfa7981c
commit 0e6c9a9b88
256 changed files with 43958 additions and 446 deletions
+3
View File
@@ -88,6 +88,8 @@ node_modules/
/build-vs/
/build-arm64/
/build-x64/
/build-ios/
/build-ios-xcode/
# CMake user-specific overrides + compile commands export
CMakeUserPresets.json
@@ -105,6 +107,7 @@ CMakeUserPresets.json
/third_party/binutils/bin/
/third_party/binutils/powerpc-none-elf/
/third_party/binutils/share/
/third_party/MoltenVK/
/third_party/vasm/
/tools/shader-playground/*.dll
/profile_print_times.py
+41 -16
View File
@@ -1,15 +1,30 @@
cmake_minimum_required(VERSION 3.20)
set(CMAKE_TRY_COMPILE_CONFIGURATION Release)
# Must be set before project() so the toolchain bakes -mmacosx-version-min
# into every compile/link; setting it later only updates the variable.
if(APPLE)
# Keep Apple platform selection explicit. CMake sets APPLE for both macOS and
# iOS, but the two targets need different bundles, frameworks and dependencies.
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(XE_PLATFORM_IOS TRUE)
set(XE_PLATFORM_MACOS FALSE)
elseif(APPLE)
set(XE_PLATFORM_IOS FALSE)
set(XE_PLATFORM_MACOS TRUE)
else()
set(XE_PLATFORM_IOS FALSE)
set(XE_PLATFORM_MACOS FALSE)
endif()
# Must be set before project() so the toolchain bakes the deployment target into
# every compile/link; setting it later only updates the variable.
if(XE_PLATFORM_IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET "18.0" CACHE STRING "Minimum iOS version")
elseif(XE_PLATFORM_MACOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "Minimum macOS version")
endif()
# Suppress macOS ranlib warnings for empty object files. wxWidgets's vendored
# libpng compiles Intel/MIPS/PowerPC sources that are empty on ARM64.
if(APPLE)
if(APPLE OR XE_PLATFORM_IOS)
set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
set(CMAKE_CXX_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
set(CMAKE_OBJC_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>")
@@ -35,8 +50,8 @@ set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
# Detect target architecture.
# - VS generator: CMAKE_GENERATOR_PLATFORM (from -A flag) takes priority.
# - macOS: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds
# would need both backends compiled, which we don't support).
# - Apple: CMAKE_OSX_ARCHITECTURES — single-arch only (universal builds would
# need both backends compiled, which we don't support).
# - Ninja/Makefiles: CMAKE_SYSTEM_PROCESSOR (set via -DCMAKE_SYSTEM_NAME
# + -DCMAKE_SYSTEM_PROCESSOR for cross-compile, or auto-detected natively).
if(CMAKE_GENERATOR_PLATFORM)
@@ -82,6 +97,7 @@ option(XENIA_ENABLE_PROFILER "Enable profiler (UI is Debug-only)" OFF)
option(XENIA_ENABLE_ITRACE "Enable JIT per-instruction tracing to the log (slow)" OFF)
option(XENIA_ENABLE_DTRACE "Enable JIT per-operation data tracing to the log (very slow)" OFF)
option(XENIA_ENABLE_FTRACE "Enable JIT per-function-call tracing to the log" OFF)
option(XENIA_ENABLE_IOS_MOLTENVK "Enable the optional MoltenVK backend in iOS builds" OFF)
if(XENIA_ENABLE_PROFILER)
# UI is Debug-only — shutdown dump spams the log otherwise.
@@ -113,7 +129,9 @@ set(CMAKE_STATIC_LINKER_FLAGS_CHECKED "${CMAKE_STATIC_LINKER_FLAGS_DEBUG}" CACHE
if(WIN32)
set(XE_PLATFORM_NAME "Windows")
elseif(APPLE)
elseif(XE_PLATFORM_IOS)
set(XE_PLATFORM_NAME "iOS")
elseif(XE_PLATFORM_MACOS)
set(XE_PLATFORM_NAME "macOS")
else()
set(XE_PLATFORM_NAME "Linux")
@@ -327,7 +345,7 @@ else()
)
if(APPLE)
# macOS-specific settings
# Apple-specific settings.
add_compile_options(
-Wno-unknown-warning-option
$<$<COMPILE_LANGUAGE:CXX>:-Wno-shorten-64-to-32>
@@ -336,20 +354,27 @@ else()
-fno-common
)
add_link_options(-Wl,-no_warn_duplicate_libraries)
if(XE_TARGET_X86_64)
if(XE_PLATFORM_MACOS AND XE_TARGET_X86_64)
# macOS 64-bit binaries default to a 4GB __PAGEZERO, which makes every
# address below 4GB unmapped. The x64 backend places its constant
# table (PlaceConstData) sub-2GB and its guest trampolines sub-4GB,
# so we shrink __PAGEZERO to one page to free up that range.
add_link_options(-Wl,-pagezero_size,0x1000)
endif()
link_libraries(
"-framework CoreFoundation"
"-framework Foundation"
"-framework IOKit"
dl
pthread
)
if(XE_PLATFORM_MACOS)
link_libraries(
"-framework CoreFoundation"
"-framework Foundation"
"-framework IOKit"
dl
pthread
)
elseif(XE_PLATFORM_IOS)
link_libraries(
"-framework CoreFoundation"
"-framework Foundation"
)
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-x11-3.0)
+91 -11
View File
@@ -1,20 +1,100 @@
<p align="center">
<a href="https://github.com/xenia-canary/xenia-canary/tree/canary_experimental/assets/icon">
<img height="256px" src="https://raw.githubusercontent.com/xenia-canary/xenia/master/assets/icon/256.png" />
</a>
<img height="256px" src="assets/apple/xenios-readme-icon.png" alt="XeniOS app icon" />
</p>
<h1 align="center">Xenia Edge - Xbox 360 Emulator</h1>
<h1 align="center">XeniOS - Xbox 360 Emulator</h1>
Xenia Edge is yet another experimental fork of the Xenia emulator, originally based on [Xenia Canary](https://github.com/xenia-canary/xenia-canary). The focus is
on faster iteration, higher default game compatibility, usability and platform support.
XeniOS is an experimental Apple-focused fork of Xenia, currently based on
[Xenia Edge](https://github.com/has207/xenia-edge). It exists as a fast-moving
place to develop, test, and ship iOS and macOS work while also carrying
platform changes that benefit ARM64 Windows, Linux, and Android. Relevant
improvements are intended to flow back upstream over time.
## Status
<p align="center">
<a href="https://xenios.jp">Website</a> ◦
<a href="https://github.com/xenios-jp/XeniOS/releases">Releases</a> ◦
<a href="https://xenios.jp/docs">Docs</a> ◦
<a href="https://xenios.jp/faq">FAQ</a> ◦
<a href="https://xenios.jp/compatibility">Compatibility</a> ◦
<a href="https://discord.gg/QwcTtNKTGf">Discord</a> ◦
<a href="https://github.com/xenios-jp/XeniOS/issues">Issues</a>
</p>
Build (Windows / Linux / macOS arm64 / macOS x86_64): [![CI](https://github.com/has207/xenia-edge/actions/workflows/CI.yml/badge.svg?branch=edge)](https://github.com/has207/xenia-edge/actions/workflows/CI.yml) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/cd506034fd8148309a45034925648499)](https://app.codacy.com/gh/has207/xenia-edge/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
## Current Focus
Releases
--------
[Latest](https://github.com/has207/xenia-edge/releases/latest) ◦ [All](https://github.com/has207/xenia-edge/releases)
- iOS
- macOS (Apple Silicon)
- macOS (Intel)
Current published releases focus on iOS and macOS.
For Windows or Linux builds, use [Xenia Edge](https://github.com/has207/xenia-edge) or [Xenia Canary](https://github.com/xenia-canary/xenia-canary).
## Why This Fork Exists
Xenia development is relatively thinly staffed right now, and upstream is not
set up for fast iteration on Apple-specific packaging, documentation, release
flow, and user experience. XeniOS exists so that work can move faster in a
repository where the full product experience can be shaped directly, from the
app itself to releases, docs, compatibility reporting, and the public website
at [xenios.jp](https://xenios.jp).
The goal is not to keep good work siloed here forever. The goal is to iterate
quickly, build a more polished user-facing experience for Apple platforms, and
then contribute the useful technical improvements back upstream into the
broader Xenia community, especially
[Xenia Canary](https://github.com/xenia-canary/xenia-canary).
## Downloads
Download XeniOS from
[GitHub Releases](https://github.com/xenios-jp/XeniOS/releases).
- [Latest GitHub release](https://github.com/xenios-jp/XeniOS/releases/latest)
- [All GitHub releases](https://github.com/xenios-jp/XeniOS/releases)
## Quickstart
Start with the public docs at [xenios.jp/docs](https://xenios.jp/docs).
## FAQ
See the public FAQ at [xenios.jp/faq](https://xenios.jp/faq).
## Game Compatibility
Browse currently tracked games on
[xenios.jp/compatibility](https://xenios.jp/compatibility).
To file a compatibility report, use the
[GitHub compatibility tracker](https://github.com/xenios-jp/game-compatibility/issues/new/choose).
## Building
See [building.md](docs/building.md) for setup and information about the
`xb` script. When writing code, check the [style guide](docs/style_guide.md)
and be sure to run clang-format!
## Contributors Wanted!
Have some spare time, know advanced C++, and want to write an emulator?
Contribute! There's a ton of work that needs to be done, a lot of which
is wide open greenfield fun.
**For general rules and guidelines please see [CONTRIBUTING.md](.github/CONTRIBUTING.md).**
Fixes and optimizations are always welcome, especially around Apple platform
performance, UI polish, compatibility coverage, packaging, and tooling.
Start with the
[XeniOS issue tracker](https://github.com/xenios-jp/XeniOS/issues),
join the [XeniOS Discord](https://discord.gg/QwcTtNKTGf), check
[CONTRIBUTING.md](.github/CONTRIBUTING.md), and coordinate before starting
larger work.
## Disclaimer
The goal of this project is to experiment, research, and educate on the topic
of emulation of modern devices and operating systems. **It is not for enabling
illegal activity**. All information is obtained via reverse engineering of
legally purchased devices and games and information made public on the internet
(you'd be surprised what's indexed on Google...).
+19 -3
View File
@@ -3,7 +3,7 @@
include(CMakeParseArguments)
set(XE_PLATFORM_SUFFIXES
_win _linux _posix _gnulinux _x11 _gtk _android _mac _amd64 _x64 _arm64
_win _linux _posix _gnulinux _x11 _gtk _android _mac _ios _amd64 _x64 _arm64
)
# xe_platform_sources(target base_path [RECURSIVE])
@@ -55,7 +55,14 @@ function(xe_platform_sources target base_path)
"${base_path}/*_win.h"
"${base_path}/*_win.cc"
)
elseif(APPLE)
elseif(XE_PLATFORM_IOS)
file(${glob_mode} _plat_sources
"${base_path}/*_posix.h"
"${base_path}/*_posix.cc"
"${base_path}/*_ios.h"
"${base_path}/*_ios.cc"
)
elseif(XE_PLATFORM_MACOS)
file(${glob_mode} _plat_sources
"${base_path}/*_posix.h"
"${base_path}/*_posix.cc"
@@ -106,6 +113,8 @@ function(xe_target_defaults target)
)
if(MSVC)
target_compile_options(${target} PRIVATE /WX)
elseif(XCODE AND XE_PLATFORM_IOS)
target_compile_options(${target} PRIVATE -w)
elseif(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${target} PRIVATE -Werror)
endif()
@@ -327,6 +336,13 @@ function(xe_shader_rules_metal target shader_dir)
set(_generated_root "${PROJECT_BINARY_DIR}/generated")
set(_bytecode_dir "${_generated_root}/${_rel_dir}/bytecode/metal")
set(_valid_stages vs ps cs)
set(_metal_args --msl)
if(XE_PLATFORM_IOS)
list(APPEND _metal_args
--metal-sdk iphoneos
--metal-std ios-metal2.3
--metal-min-version-flag "-miphoneos-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif()
set(_outputs)
file(MAKE_DIRECTORY "${_bytecode_dir}")
foreach(src ${_sources})
@@ -350,7 +366,7 @@ function(xe_shader_rules_metal target shader_dir)
list(APPEND _outputs "${_out}")
add_custom_command(
OUTPUT "${_out}"
COMMAND $<TARGET_FILE:xenia-shader-cc> --msl --depfile "${_dep}"
COMMAND $<TARGET_FILE:xenia-shader-cc> ${_metal_args} --depfile "${_dep}"
"${src}" "${_out}"
DEPENDS "${src}" xenia-shader-cc
DEPFILE "${_dep}"
+8 -2
View File
@@ -28,19 +28,25 @@ if(APPLE)
endif()
add_subdirectory(helper/sdl)
add_subdirectory(hid)
add_subdirectory(hid/touch)
add_subdirectory(hid/nop)
add_subdirectory(hid/sdl)
add_subdirectory(hid/portal)
add_subdirectory(kernel)
add_subdirectory(patcher)
add_subdirectory(ui)
if(XE_PLATFORM_IOS)
add_subdirectory(ui/ios)
endif()
add_subdirectory(ui/vulkan)
if(APPLE)
add_subdirectory(ui/metal)
endif()
add_subdirectory(vfs)
add_subdirectory(debug/ui)
add_subdirectory(app/discord)
if(NOT XE_PLATFORM_IOS)
add_subdirectory(debug/ui)
add_subdirectory(app/discord)
endif()
add_subdirectory(app)
if(WIN32)
+166
View File
@@ -1,3 +1,169 @@
if(XE_PLATFORM_IOS)
set(_xe_ios_entitlements "${PROJECT_SOURCE_DIR}/xenia_ios.entitlements")
add_executable(xenia-app MACOSX_BUNDLE
${CMAKE_CURRENT_SOURCE_DIR}/xenia_main_ios.mm
${PROJECT_SOURCE_DIR}/src/xenia/ui/ios/app/windowed_app_main_ios.mm
)
set_target_properties(xenia-app PROPERTIES
OUTPUT_NAME "XeniOS"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info_ios.plist"
MACOSX_BUNDLE_BUNDLE_NAME "XeniOS"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.xenios"
MACOSX_BUNDLE_BUNDLE_VERSION "1"
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.xenios"
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS
"${_xe_ios_entitlements}")
target_sources(xenia-app PRIVATE "${_xe_ios_entitlements}")
target_compile_definitions(xenia-app PRIVATE
XBYAK_NO_OP_NAMES
XBYAK_ENABLE_OMITTED_OPERAND
)
target_link_libraries(xenia-app PRIVATE
xenia-apu
xenia-apu-nop
xenia-apu-sdl
xenia-base
xenia-core
xenia-cpu
xenia-cpu-backend-a64
xenia-gpu
xenia-gpu-metal
xenia-helper-sdl
xenia-hid
xenia-hid-nop
xenia-hid-sdl
xenia-hid-touch
xenia-kernel
xenia-patcher
xenia-ui
xenia-ui-ios
xenia-ui-metal
xenia-vfs
aes_128
capstone
dxbc
fmt
imgui
libavcodec
libavformat
libavutil
mspack
snappy
xxhash
"-framework AVFoundation"
"-framework AudioToolbox"
"-framework CoreFoundation"
"-framework Foundation"
"-framework GameController"
"-framework Metal"
"-framework MetalKit"
"-framework QuartzCore"
"-framework Security"
"-framework UIKit"
"-framework UniformTypeIdentifiers"
)
if(XENIA_ENABLE_IOS_MOLTENVK)
target_link_libraries(xenia-app PRIVATE
xenia-gpu-vulkan
xenia-ui-vulkan
xenia-third-party-moltenvk
glslang-spirv
)
endif()
target_link_options(xenia-app PRIVATE
"-Wl,-u,___clear_cache"
"-Wl,-rpath,@executable_path/Frameworks"
"-Wl,-rpath,@loader_path/Frameworks"
)
set(_xe_ios_embedded_dylibs)
if(XENIA_ENABLE_IOS_MOLTENVK)
set(_xe_moltenvk_ios_framework
"${PROJECT_SOURCE_DIR}/third_party/MoltenVK/ios/MoltenVK.framework")
if(XCODE)
list(APPEND _xe_ios_embedded_dylibs "${_xe_moltenvk_ios_framework}")
else()
add_custom_command(TARGET xenia-app POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks"
COMMAND "${CMAKE_COMMAND}" -E copy_directory
"${_xe_moltenvk_ios_framework}"
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks/MoltenVK.framework"
VERBATIM)
endif()
endif()
foreach(_xe_app_dylib xenia-third-party-metal-shader-converter
xenia-third-party-dxilconv)
if(TARGET ${_xe_app_dylib})
if(XCODE)
get_target_property(_xe_app_dylib_path ${_xe_app_dylib}
IMPORTED_LOCATION_RELEASE)
if(NOT _xe_app_dylib_path)
get_target_property(_xe_app_dylib_path ${_xe_app_dylib}
IMPORTED_LOCATION)
endif()
if(_xe_app_dylib_path)
list(APPEND _xe_ios_embedded_dylibs "${_xe_app_dylib_path}")
endif()
else()
add_custom_command(TARGET xenia-app POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E make_directory
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks"
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
"$<TARGET_FILE:${_xe_app_dylib}>"
"$<TARGET_BUNDLE_DIR:xenia-app>/Frameworks/$<TARGET_FILE_NAME:${_xe_app_dylib}>"
VERBATIM)
endif()
endif()
endforeach()
if(XCODE AND _xe_ios_embedded_dylibs)
set_target_properties(xenia-app PROPERTIES
XCODE_EMBED_FRAMEWORKS "${_xe_ios_embedded_dylibs}"
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY YES)
endif()
target_include_directories(xenia-app PRIVATE
"${PROJECT_SOURCE_DIR}/third_party/rapidjson/include"
)
xe_embed_binary_assets(xenia-app "${PROJECT_SOURCE_DIR}/assets/icons" icons)
xe_embed_compressed_bundle(xenia-app
"${PROJECT_SOURCE_DIR}/build/data_repos/xenia-manager-database/data/game-compatibility"
game_compat)
set(_xe_ios_ad_hoc_sign_command
codesign --force --deep --sign -
--entitlements "${_xe_ios_entitlements}"
"$<TARGET_BUNDLE_DIR:xenia-app>")
if(NOT XCODE)
add_custom_command(TARGET xenia-app POST_BUILD
COMMAND ${_xe_ios_ad_hoc_sign_command}
VERBATIM)
endif()
set(_xe_ios_package_command
/bin/bash "${PROJECT_SOURCE_DIR}/tools/package_ios_ipa.sh"
"$<TARGET_BUNDLE_DIR:xenia-app>"
"${_xe_ios_entitlements}"
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.ipa"
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.requested-entitlements.plist"
"$<TARGET_BUNDLE_DIR:xenia-app>/../XeniOS.signed-entitlements.plist")
add_custom_command(TARGET xenia-app POST_BUILD
COMMAND ${_xe_ios_package_command}
VERBATIM)
if(XCODE)
add_custom_target(xenia-app-ios-package
COMMAND ${_xe_ios_package_command}
DEPENDS xenia-app
VERBATIM)
else()
add_custom_target(xenia-app-ios-package
COMMAND ${_xe_ios_ad_hoc_sign_command}
COMMAND ${_xe_ios_package_command}
DEPENDS xenia-app
VERBATIM)
endif()
xe_target_defaults(xenia-app)
return()
endif()
if(WIN32)
add_executable(xenia-app WIN32)
elseif(APPLE)
+127
View File
@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>Xbox 360 Disc Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Default</string>
<key>LSItemContentTypes</key>
<array>
<string>public.iso-image</string>
<string>public.disk-image</string>
</array>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>xex</string>
<string>zar</string>
</array>
<key>CFBundleTypeName</key>
<string>Xbox 360 Executable</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSHandlerRank</key>
<string>Default</string>
<key>LSItemContentTypes</key>
<array>
<string>public.data</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>jp.xenios.xenios.launch</string>
<key>CFBundleURLSchemes</key>
<array>
<string>xenios</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>stikjit</string>
</array>
<key>LSSupportsGameMode</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>XeniaSceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchScreen</key>
<dict/>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
<string>metal</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportsDocumentBrowser</key>
<true/>
</dict>
</plist>
+9
View File
@@ -19,6 +19,7 @@
#include "third_party/stb/stb_image.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
#include "xenia/vfs/iso_metadata.h"
#include "xenia/vfs/stfs_metadata.h"
#include "xenia/vfs/xex_metadata.h"
@@ -173,6 +174,10 @@ size_t DirectoryScanner::CountResultsWithTitleId(
}
void DirectoryScanner::Run(std::filesystem::path root) {
#if XE_PLATFORM_IOS
xe::threading::set_name("Directory Scanner");
xe::threading::set_current_thread_qos(xe::threading::ThreadQoS::kUtility);
#endif
XELOGI("DirectoryScanner: scan start, root='{}'", xe::path_to_utf8(root));
std::error_code ec;
if (!std::filesystem::is_directory(root, ec) || ec) {
@@ -219,6 +224,10 @@ void DirectoryScanner::Run(std::filesystem::path root) {
}
void DirectoryScanner::WorkerLoop() {
#if XE_PLATFORM_IOS
xe::threading::set_name("Directory Scanner Worker");
xe::threading::set_current_thread_qos(xe::threading::ThreadQoS::kUtility);
#endif
while (true) {
std::filesystem::path dir;
{
File diff suppressed because it is too large Load Diff
+57 -6
View File
@@ -9,6 +9,8 @@
#include "xenia/apu/audio_system.h"
#include <cstring>
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/audio_driver.h"
#include "xenia/apu/xma_decoder.h"
@@ -35,7 +37,21 @@
// and let the normal AudioSystem handling take it, to prevent duplicate
// implementations. They can be found in xboxkrnl_audio_xma.cc
DEFINE_uint32(apu_max_queued_frames, 8,
namespace {
#if XE_PLATFORM_IOS
constexpr uint32_t kApuQueuedFramesDefault = 32;
constexpr uint32_t kApuQueuedFramesMinimum = 32;
bool IsTitleStopRequested(xe::cpu::Processor* processor) {
return processor && processor->title_stop_requested_ios();
}
#else
constexpr uint32_t kApuQueuedFramesDefault = 8;
constexpr uint32_t kApuQueuedFramesMinimum = 4;
#endif // XE_PLATFORM_IOS
} // namespace
DEFINE_uint32(apu_max_queued_frames, kApuQueuedFramesDefault,
"Allows changing max buffered audio frames to reduce audio "
"delay. Lowering this value might cause performance issues. "
"Value range: [4-64]",
@@ -97,11 +113,26 @@ X_STATUS AudioSystem::Setup(kernel::KernelState* kernel_state) {
}
void AudioSystem::WorkerThreadMain() {
#if XE_PLATFORM_IOS
if (xe::threading::set_current_thread_qos(
xe::threading::ThreadQoS::kUserInteractive)) {
XELOGI("iOS: Audio Worker QoS set to user-interactive");
} else {
XELOGW("iOS: Audio Worker QoS request failed");
}
#endif // XE_PLATFORM_IOS
// Initialize driver and ringbuffer.
Initialize();
// Main run loop.
while (worker_running_) {
#if XE_PLATFORM_IOS
if (IsTitleStopRequested(processor_)) {
break;
}
#endif // XE_PLATFORM_IOS
// These handles signify the number of submitted samples. Once we reach
// 64 samples, we wait until our audio backend releases a semaphore
// (signaling a sample has finished playing)
@@ -112,10 +143,14 @@ void AudioSystem::WorkerThreadMain() {
continue;
}
if (!worker_running_) {
break;
}
if (result.first == threading::WaitResult::kSuccess &&
result.second == kMaximumClientCount) {
// Shutdown event signaled.
if (paused_) {
if (paused_.load(std::memory_order_acquire)) {
pause_fence_.Signal();
threading::Wait(resume_event_.get(), false);
}
@@ -141,6 +176,12 @@ void AudioSystem::WorkerThreadMain() {
}
}
#if XE_PLATFORM_IOS
if (IsTitleStopRequested(processor_)) {
break;
}
#endif // XE_PLATFORM_IOS
if (client_callback) {
SCOPE_profile_cpu_i("apu", "xe::apu::AudioSystem->client_callback");
uint64_t args[] = {client_callback_arg};
@@ -148,6 +189,12 @@ void AudioSystem::WorkerThreadMain() {
args, xe::countof(args));
}
#if XE_PLATFORM_IOS
if (IsTitleStopRequested(processor_)) {
break;
}
#endif // XE_PLATFORM_IOS
pumped = true;
}
@@ -181,6 +228,12 @@ void AudioSystem::Initialize() {}
void AudioSystem::Shutdown() {
worker_running_ = false;
shutdown_event_->Set();
#if XE_PLATFORM_IOS
resume_event_->Set();
for (size_t i = 0; i < kMaximumClientCount; ++i) {
client_semaphores_[i]->Release(1, nullptr);
}
#endif // XE_PLATFORM_IOS
if (worker_thread_) {
worker_thread_->Wait(0, 0, 0, nullptr);
worker_thread_.reset();
@@ -411,10 +464,9 @@ bool AudioSystem::Restore(ByteStream* stream) {
}
void AudioSystem::Pause() {
if (paused_) {
if (paused_.exchange(true, std::memory_order_acq_rel)) {
return;
}
paused_ = true;
// Kind of a hack, but it works.
shutdown_event_->Set();
@@ -424,10 +476,9 @@ void AudioSystem::Pause() {
}
void AudioSystem::Resume() {
if (!paused_) {
if (!paused_.exchange(false, std::memory_order_acq_rel)) {
return;
}
paused_ = false;
resume_event_->Set();
+2 -2
View File
@@ -68,7 +68,7 @@ class AudioSystem {
bool Save(ByteStream* stream);
bool Restore(ByteStream* stream);
bool is_paused() const { return paused_; }
bool is_paused() const { return paused_.load(std::memory_order_acquire); }
void Pause();
void Resume();
@@ -116,7 +116,7 @@ class AudioSystem {
std::unique_ptr<xe::threading::Event> shutdown_event_;
xe::threading::WaitHandle* wait_handles_[kMaximumClientCount + 1];
bool paused_ = false;
std::atomic<bool> paused_ = false;
threading::Fence pause_fence_;
std::unique_ptr<threading::Event> resume_event_;
};
+2 -2
View File
@@ -35,8 +35,8 @@ static void _generic_sequential_6_BE_to_interleaved_6_LE(
}
}
}
#if XE_COMPILER_CLANG_CL != 1 && !XE_PLATFORM_LINUX && !XE_PLATFORM_MAC
// load_be_u32 unavailable on clang-cl, clang on Linux, or clang on macOS
#if XE_COMPILER_CLANG_CL != 1 && !XE_PLATFORM_LINUX && !XE_PLATFORM_APPLE
// load_be_u32 unavailable on clang-cl, clang on Linux, or clang on Apple
XE_NOINLINE
static void _movbe_sequential_6_BE_to_interleaved_6_LE(
float* XE_RESTRICT output, const float* XE_RESTRICT input,
+73 -2
View File
@@ -22,6 +22,20 @@ namespace xe {
namespace apu {
namespace sdl {
namespace {
bool ShouldReportUnderrun(uint64_t count, uint64_t last_reported_count) {
const uint64_t next_report =
last_reported_count == 0 ? 1 : last_reported_count * 2;
return count >= next_report;
}
bool ShouldReportAudioStats(uint64_t submitted_count) {
return (submitted_count & 1023) == 0;
}
} // namespace
SDLAudioDriver::SDLAudioDriver(xe::threading::Semaphore* semaphore,
uint32_t frequency, uint32_t channels,
bool need_format_conversion)
@@ -115,7 +129,57 @@ void SDLAudioDriver::SubmitFrame(float* frame) {
{
std::unique_lock<std::mutex> guard(frames_mutex_);
frames_queued_.push(output_frame);
const size_t queue_depth = frames_queued_.size();
size_t previous_max = max_queue_depth_.load(std::memory_order_relaxed);
while (queue_depth > previous_max &&
!max_queue_depth_.compare_exchange_weak(previous_max, queue_depth,
std::memory_order_relaxed,
std::memory_order_relaxed)) {
}
if (queue_depth <= 2) {
low_queue_count_.fetch_add(1, std::memory_order_relaxed);
}
}
const uint64_t submitted =
frames_submitted_.fetch_add(1, std::memory_order_relaxed) + 1;
const uint64_t underruns = underrun_count_.load(std::memory_order_relaxed);
uint64_t last_reported =
last_reported_underrun_count_.load(std::memory_order_relaxed);
if (underruns != 0 && ShouldReportUnderrun(underruns, last_reported) &&
last_reported_underrun_count_.compare_exchange_strong(
last_reported, underruns, std::memory_order_relaxed,
std::memory_order_relaxed)) {
XELOGW(
"SDL audio underrun: count={}, submitted={}, played={}, "
"max_queue_depth={}",
underruns, frames_submitted_.load(std::memory_order_relaxed),
frames_played_.load(std::memory_order_relaxed),
max_queue_depth_.load(std::memory_order_relaxed));
}
#if XE_PLATFORM_IOS
const uint64_t low_queue = low_queue_count_.load(std::memory_order_relaxed);
uint64_t last_low_queue =
last_reported_low_queue_count_.load(std::memory_order_relaxed);
if (low_queue != 0 && ShouldReportUnderrun(low_queue, last_low_queue) &&
last_reported_low_queue_count_.compare_exchange_strong(
last_low_queue, low_queue, std::memory_order_relaxed,
std::memory_order_relaxed)) {
XELOGW(
"SDL audio queue running low: count={}, submitted={}, played={}, "
"underruns={}, max_queue_depth={}",
low_queue, submitted, frames_played_.load(std::memory_order_relaxed),
underruns, max_queue_depth_.load(std::memory_order_relaxed));
}
if (ShouldReportAudioStats(submitted)) {
XELOGI(
"SDL audio stats: submitted={}, played={}, underruns={}, low_queue={}, "
"max_queue_depth={}",
submitted, frames_played_.load(std::memory_order_relaxed), underruns,
low_queue, max_queue_depth_.load(std::memory_order_relaxed));
}
#endif // XE_PLATFORM_IOS
}
void SDLAudioDriver::Pause() {
@@ -169,6 +233,10 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
{
std::unique_lock<std::mutex> guard(driver->frames_mutex_);
if (driver->frames_queued_.empty()) {
driver->underrun_count_.fetch_add(1, std::memory_order_relaxed);
if (driver->semaphore_) {
driver->semaphore_->Release(1, nullptr);
}
return;
}
buffer = driver->frames_queued_.front();
@@ -213,8 +281,11 @@ void SDLAudioDriver::SDLCallback(void* userdata, SDL_AudioStream* stream,
driver->frames_unused_.push(buffer);
}
auto ret = driver->semaphore_->Release(1, nullptr);
assert_true(ret);
driver->frames_played_.fetch_add(1, std::memory_order_relaxed);
if (driver->semaphore_) {
auto ret = driver->semaphore_->Release(1, nullptr);
assert_true(ret);
}
}
}
} // namespace sdl
+9
View File
@@ -10,6 +10,7 @@
#ifndef XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
#define XENIA_APU_SDL_SDL_AUDIO_DRIVER_H_
#include <atomic>
#include <mutex>
#include <queue>
#include <stack>
@@ -58,6 +59,14 @@ class SDLAudioDriver : public AudioDriver {
std::queue<float*> frames_queued_ = {};
std::stack<float*> frames_unused_ = {};
std::mutex frames_mutex_ = {};
std::atomic<uint64_t> frames_submitted_{0};
std::atomic<uint64_t> frames_played_{0};
std::atomic<uint64_t> underrun_count_{0};
std::atomic<uint64_t> low_queue_count_{0};
std::atomic<uint64_t> last_reported_underrun_count_{0};
std::atomic<uint64_t> last_reported_low_queue_count_{0};
std::atomic<size_t> max_queue_depth_{0};
};
} // namespace sdl
+72
View File
@@ -9,13 +9,46 @@
#include "xenia/apu/sdl/sdl_audio_system.h"
#include <atomic>
#include "xenia/apu/apu_flags.h"
#include "xenia/apu/sdl/sdl_audio_driver.h"
#include "xenia/base/logging.h"
#include "xenia/base/threading.h"
namespace xe {
namespace apu {
namespace sdl {
namespace {
#if XE_PLATFORM_IOS
// Fallback used when SDL/CoreAudio device creation fails on iOS.
// Keeps guest audio callback flow alive without real audio output.
class SilentAudioDriver final : public AudioDriver {
public:
explicit SilentAudioDriver(xe::threading::Semaphore* semaphore)
: semaphore_(semaphore) {}
bool Initialize() override { return true; }
void Shutdown() override {}
void SubmitFrame(float* samples) override {
(void)samples;
if (semaphore_) {
semaphore_->Release(1, nullptr);
}
}
void Pause() override {}
void Resume() override {}
void SetVolume(float volume) override { (void)volume; }
private:
xe::threading::Semaphore* semaphore_ = nullptr;
};
#endif // XE_PLATFORM_IOS
} // namespace
std::unique_ptr<AudioSystem> SDLAudioSystem::Create(cpu::Processor* processor) {
return std::make_unique<SDLAudioSystem>(processor);
}
@@ -31,10 +64,32 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
xe::threading::Semaphore* semaphore,
AudioDriver** out_driver) {
assert_not_null(out_driver);
#if XE_PLATFORM_IOS
// iOS/SDL audio path is single-output in practice here. Additional guest
// clients should not attempt to open another device because SDL reports
// "Audio device already open" and we fall back to silence anyway.
if (index > 0) {
static std::atomic<bool> logged_secondary_fallback{false};
if (!logged_secondary_fallback.exchange(true, std::memory_order_relaxed)) {
XELOGW(
"SDLAudioSystem: iOS secondary audio clients use silent fallback; "
"keeping primary SDL device on client 0");
}
*out_driver = new SilentAudioDriver(semaphore);
return X_STATUS_SUCCESS;
}
#endif
auto driver = std::make_unique<SDLAudioDriver>(semaphore);
if (!driver->Initialize()) {
driver->Shutdown();
#if XE_PLATFORM_IOS
XELOGW("SDLAudioSystem: SDL audio init failed, using silent fallback");
*out_driver = new SilentAudioDriver(semaphore);
return X_STATUS_SUCCESS;
#else
return X_STATUS_UNSUCCESSFUL;
#endif
}
*out_driver = driver.release();
@@ -44,12 +99,29 @@ X_STATUS SDLAudioSystem::CreateDriver(size_t index,
AudioDriver* SDLAudioSystem::CreateDriver(xe::threading::Semaphore* semaphore,
uint32_t frequency, uint32_t channels,
bool need_format_conversion) {
#if XE_PLATFORM_IOS
static std::atomic<bool> logged_independent_fallback{false};
if (!logged_independent_fallback.exchange(true, std::memory_order_relaxed)) {
XELOGW(
"SDLAudioSystem: iOS independent audio drivers use silent fallback; "
"secondary SDL/CoreAudio devices are not opened");
}
return new SilentAudioDriver(semaphore);
#else
return new SDLAudioDriver(semaphore, frequency, channels,
need_format_conversion);
#endif // XE_PLATFORM_IOS
}
void SDLAudioSystem::DestroyDriver(AudioDriver* driver) {
assert_not_null(driver);
#if XE_PLATFORM_IOS
if (auto* silent_driver = dynamic_cast<SilentAudioDriver*>(driver)) {
silent_driver->Shutdown();
delete silent_driver;
return;
}
#endif
auto sdldriver = dynamic_cast<SDLAudioDriver*>(driver);
assert_not_null(sdldriver);
sdldriver->Shutdown();
+6
View File
@@ -10,6 +10,12 @@ if(_base_exclude AND _base_srcs)
list(REMOVE_ITEM _base_srcs ${_base_exclude})
set_target_properties(xenia-base PROPERTIES SOURCES "${_base_srcs}")
endif()
if(XE_PLATFORM_IOS)
# This file is named for the original macOS implementation, but the
# Objective-C autorelease pool runtime is Apple-wide.
target_sources(xenia-base PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/autorelease_pool_mac.cc)
endif()
target_link_libraries(xenia-base PUBLIC fmt)
target_link_libraries(xenia-base PRIVATE zlib-ng boost_context)
xe_target_defaults(xenia-base)
+2 -2
View File
@@ -9,7 +9,7 @@
#include "xenia/base/autorelease_pool_mac.h"
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
#include <pthread.h>
#include <chrono>
@@ -174,4 +174,4 @@ ScopedAutoreleasePool::~ScopedAutoreleasePool() {
} // namespace xe
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
+3 -3
View File
@@ -16,7 +16,7 @@
#include "xenia/base/platform.h"
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
namespace xe {
@@ -94,13 +94,13 @@ class ScopedAutoreleasePool {
#define XE_SCOPED_AUTORELEASE_POOL(name) \
xe::ScopedAutoreleasePool _autorelease_pool_##__LINE__(name)
#else // !XE_PLATFORM_MAC
#else // !XE_PLATFORM_APPLE
#define XE_AUTORELEASE_POOL_PUSH(location) nullptr
#define XE_AUTORELEASE_POOL_POP(pool, location) (void)0
#define XE_AUTORELEASE_POOL_CHECK_LEAKS() (void)0
#define XE_SCOPED_AUTORELEASE_POOL(name) (void)0
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#endif // XENIA_BASE_AUTORELEASE_POOL_MAC_H_
+23
View File
@@ -0,0 +1,23 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2026 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <cstddef>
#include <cstdint>
#include <libkern/OSCacheControl.h>
extern "C" void __clear_cache(void* start, void* end) {
auto* start_bytes = static_cast<uint8_t*>(start);
auto* end_bytes = static_cast<uint8_t*>(end);
if (end_bytes <= start_bytes) {
return;
}
sys_icache_invalidate(start, static_cast<size_t>(end_bytes - start_bytes));
}
+12 -12
View File
@@ -39,7 +39,7 @@ std::pair<ExceptionHandler::Handler, void*> handlers_[kMaxHandlerCount];
static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
void* signal_context) {
#if XE_PLATFORM_MAC && XE_ARCH_ARM64
#if XE_PLATFORM_APPLE && XE_ARCH_ARM64
// The Darwin kernel may pass an unaligned ucontext_t pointer to signal
// handlers; copy into an aligned local before reading. mcontext_t is a
// pointer on Mac, so writes still reach kernel storage via the pointer.
@@ -54,7 +54,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
HostThreadContext thread_context;
#if XE_ARCH_AMD64
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
// Darwin: mcontext is a pointer; integer state in __ss, FP/XMM in __fs.
// __fpu_xmm0..__fpu_xmm15 are laid out contiguously in
// __darwin_x86_float_state64.
@@ -101,9 +101,9 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
thread_context.r15 = uint64_t(mcontext.gregs[REG_R15]);
std::memcpy(thread_context.xmm_registers, mcontext.fpregs->_xmm,
sizeof(thread_context.xmm_registers));
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#elif XE_ARCH_ARM64
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
// Darwin: mcontext is a pointer, registers in __ss and __ns.
for (int i = 0; i < 29; ++i) {
thread_context.x[i] = mcontext->__ss.__x[i];
@@ -150,7 +150,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
std::memcpy(thread_context.v, mcontext_fpsimd->vregs,
sizeof(thread_context.v));
}
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#endif // XE_ARCH
Exception ex;
@@ -164,7 +164,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
#if XE_ARCH_AMD64
// x86_pf_error_code::X86_PF_WRITE
constexpr uint64_t kX86PageFaultErrorCodeWrite = UINT64_C(1) << 1;
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
access_violation_operation =
(uint64_t(mcontext->__es.__err) & kX86PageFaultErrorCodeWrite)
? Exception::AccessViolationOperation::kWrite
@@ -176,7 +176,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
: Exception::AccessViolationOperation::kRead;
#endif
#elif XE_ARCH_ARM64
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
{
// On Darwin, determine access direction from the faulting instruction.
uint64_t fault_pc = mcontext->__ss.__pc;
@@ -242,7 +242,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
Exception::AccessViolationOperation::kUnknown;
}
}
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#else
access_violation_operation =
Exception::AccessViolationOperation::kUnknown;
@@ -260,7 +260,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
// Exception handled.
#if XE_ARCH_AMD64
uint32_t modified_register_index;
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
mcontext->__ss.__rip = thread_context.rip;
mcontext->__ss.__rflags = thread_context.eflags;
// Pointer-to-member map; order must match X64Register.
@@ -327,10 +327,10 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
&thread_context.xmm_registers[modified_register_index],
sizeof(vec128_t));
}
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#elif XE_ARCH_ARM64
uint32_t modified_register_index;
#if XE_PLATFORM_MAC
#if XE_PLATFORM_APPLE
uint32_t modified_x_registers_remaining = ex.modified_x_registers();
while (xe::bit_scan_forward(modified_x_registers_remaining,
&modified_register_index)) {
@@ -386,7 +386,7 @@ static void ExceptionHandlerCallback(int signal_number, siginfo_t* signal_info,
thread_context.x[modified_register_index];
}
}
#endif // XE_PLATFORM_MAC
#endif // XE_PLATFORM_APPLE
#endif // XE_ARCH
return;
}

Some files were not shown because too many files have changed in this diff Show More