Add macOS Support
This commit is contained in:
parent
db4d9c668d
commit
79d7503704
|
@ -0,0 +1,33 @@
|
|||
<?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>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Zelda64Recompiled</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.games</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11</string>
|
||||
<key>LSEnvironment</key>
|
||||
<dict>
|
||||
<key>MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS</key>
|
||||
<string>1</string>
|
||||
<key>MVK_CONFIG_USE_METAL_PRIVATE_API</key>
|
||||
<string>1</string>
|
||||
<key>MVK_CONFIG_RESUME_LOST_DEVICE</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"file_format_version": "1.0.0",
|
||||
"ICD": {
|
||||
"library_path": "../../../Frameworks/libMoltenVK.dylib",
|
||||
"api_version": "1.2.0",
|
||||
"is_portability_driver": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
include(BundleUtilities)
|
||||
|
||||
# Use generator expressions to get the absolute path to the bundle and frameworks
|
||||
set(APPS "Zelda64Recompiled.app/Contents/MacOS/Zelda64Recompiled")
|
||||
set(DIRS "Zelda64Recompiled.app/Contents/Frameworks")
|
||||
|
||||
# The fixup_bundle command needs an absolute path
|
||||
file(REAL_PATH ${APPS} APPS)
|
||||
file(REAL_PATH ${DIRS} DIRS)
|
||||
|
||||
fixup_bundle("${APPS}" "" "${DIRS}")
|
|
@ -0,0 +1,14 @@
|
|||
version: '2.9.3'
|
||||
prefix: '/opt/local'
|
||||
variants:
|
||||
select:
|
||||
- aqua
|
||||
- metal
|
||||
deselect: x11
|
||||
ports:
|
||||
- name: clang-18
|
||||
- name: llvm-18
|
||||
- name: libsdl2
|
||||
select: universal
|
||||
- name: freetype
|
||||
select: universal
|
|
@ -14,6 +14,14 @@ on:
|
|||
type: string
|
||||
required: false
|
||||
default: '4e6f4e52989aca69739880b40b9f988357f15d10ca03284377b81f1502463ff5'
|
||||
VULKAN_SDK_VERSION:
|
||||
type: string
|
||||
required: false
|
||||
default: '1.3.296.0'
|
||||
MOLTENVK_COMMIT:
|
||||
type: string
|
||||
required: false
|
||||
default: ''
|
||||
secrets:
|
||||
ZRE_REPO_WITH_PAT:
|
||||
required: true
|
||||
|
@ -95,7 +103,7 @@ jobs:
|
|||
rm -rf assets/scss
|
||||
tar -czf Zelda64Recompiled.tar.gz Zelda64Recompiled assets/ gamecontrollerdb.txt
|
||||
- name: Archive Zelda64Recomp
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-${{ runner.os }}-X64-${{ matrix.type }}
|
||||
path: Zelda64Recompiled.tar.gz
|
||||
|
@ -103,7 +111,7 @@ jobs:
|
|||
run: |-
|
||||
./.github/linux/appimage.sh
|
||||
- name: Zelda64Recomp AppImage
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-AppImage-X64-${{ matrix.type }}
|
||||
path: Zelda64Recompiled-*.AppImage
|
||||
|
@ -283,3 +291,119 @@ jobs:
|
|||
name: Zelda64Recompiled-PDB-${{ matrix.type }}
|
||||
path: |
|
||||
Zelda64Recompiled.pdb
|
||||
build-macos:
|
||||
runs-on: blaze/macos-14
|
||||
strategy:
|
||||
matrix:
|
||||
type: [ Debug, Release ]
|
||||
name: macos (x64, arm64, ${{ matrix.type }})
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.ref }}
|
||||
submodules: recursive
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ runner.os }}-z64re-ccache-${{ matrix.type }}
|
||||
- name: Homebrew Setup
|
||||
run: |
|
||||
brew install ninja
|
||||
brew uninstall --ignore-dependencies libpng freetype
|
||||
- name: MacPorts Setup
|
||||
uses: melusina-org/setup-macports@v1
|
||||
id: 'macports'
|
||||
with:
|
||||
parameters: '.github/macos/macports.yaml'
|
||||
- name: Prepare Build
|
||||
run: |-
|
||||
git clone ${{ secrets.ZRE_REPO_WITH_PAT }}
|
||||
./zre/process.sh
|
||||
cp ./zre/mm_shader_cache.bin ./shadercache/
|
||||
- name: Build N64Recomp & RSPRecomp
|
||||
run: |
|
||||
git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource
|
||||
cd N64RecompSource
|
||||
git checkout ${{ inputs.N64RECOMP_COMMIT }}
|
||||
git submodule update --init --recursive
|
||||
|
||||
# enable ccache
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
# Build N64Recomp & RSPRecomp
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build
|
||||
cmake --build cmake-build --config Release --target N64Recomp -j $(sysctl -n hw.ncpu)
|
||||
cmake --build cmake-build --config Release --target RSPRecomp -j $(sysctl -n hw.ncpu)
|
||||
|
||||
# Copy N64Recomp & RSPRecomp to root directory
|
||||
cp cmake-build/N64Recomp ..
|
||||
cp cmake-build/RSPRecomp ..
|
||||
- name: Run N64Recomp & RSPRecomp
|
||||
run: |
|
||||
./N64Recomp us.rev1.toml
|
||||
./RSPRecomp aspMain.us.rev1.toml
|
||||
./RSPRecomp njpgdspMain.us.rev1.toml
|
||||
- name: Cache Vulkan SDK
|
||||
id: cache-vulkan-sdk
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-vulkan-sdk-${{ inputs.VULKAN_SDK_VERSION }}
|
||||
- name: Install Vulkan SDK
|
||||
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
wget https://sdk.lunarg.com/sdk/download/${{ inputs.VULKAN_SDK_VERSION }}/mac/vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip
|
||||
unzip vulkansdk-macos-${{ inputs.VULKAN_SDK_VERSION }}.zip
|
||||
sudo InstallVulkan.app/Contents/MacOS/InstallVulkan --root ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }} --accept-licenses --default-answer --confirm-command install
|
||||
- name: Checkout MoltenVK
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
run: |
|
||||
git clone https://github.com/KhronosGroup/MoltenVK.git
|
||||
cd MoltenVK
|
||||
git checkout ${{ inputs.MOLTENVK_COMMIT }}
|
||||
- name: Cache MoltenVK Dependencies
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
id: cache-mvk-dependencies
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
MoltenVK/External/build
|
||||
!MoltenVK/External/build/Intermediates
|
||||
key: ${{ runner.os }}-${{ matrix.type }}-${{ hashFiles('MoltenVK/fetchDependencies','MoltenVK/ExternalRevisions/**','MoltenVK/ExternalDependencies.xcodeproj/**','MoltenVK/Scripts/**') }}
|
||||
- name: Fetch MVK Dependencies (Use Built Cache)
|
||||
if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
cd MoltenVK
|
||||
./fetchDependencies -v --none
|
||||
- name: Fetch MVK Dependencies
|
||||
if: inputs.MOLTENVK_COMMIT != '' && steps.cache-mvk-dependencies.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd MoltenVK
|
||||
./fetchDependencies -v --macos
|
||||
- name: Build MoltenVK
|
||||
if: inputs.MOLTENVK_COMMIT != ''
|
||||
run: |
|
||||
cd MoltenVK
|
||||
make macos
|
||||
sudo cp Package/Latest/MoltenVK/dylib/macOS/libMoltenVK.dylib ~/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS/lib/libMoltenVK.dylib
|
||||
- name: Build ZeldaRecomp
|
||||
run: |-
|
||||
# enable ccache
|
||||
export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_MAKE_PROGRAM=ninja -G Ninja -S . -B cmake-build \
|
||||
-DPATCHES_LD=/opt/local/bin/ld.lld-mp-18 -DPATCHES_OBJCOPY=/opt/local/bin/llvm-objcopy-mp-18 -DCMAKE_AR=/opt/local/bin/llvm-ar-mp-18 -DPATCHES_C_COMPILER=/opt/local/bin/clang-mp-18 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="x86_64;arm64"
|
||||
cmake --build cmake-build --config ${{ matrix.type }} --target Zelda64Recompiled -j $(sysctl -n hw.ncpu)
|
||||
env:
|
||||
VULKAN_SDK: /Users/runner/VulkanSDK/${{ inputs.VULKAN_SDK_VERSION }}/macOS
|
||||
- name: Prepare Archive
|
||||
run: |
|
||||
mv cmake-build/Zelda64Recompiled.app Zelda64Recompiled.app
|
||||
zip -r -y Zelda64Recompiled.zip Zelda64Recompiled.app
|
||||
- name: Archive Zelda64Recomp
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Zelda64Recompiled-${{ runner.os }}-${{ matrix.type }}
|
||||
path: Zelda64Recompiled.zip
|
||||
|
|
146
CMakeLists.txt
146
CMakeLists.txt
|
@ -1,5 +1,10 @@
|
|||
cmake_minimum_required(VERSION 3.20)
|
||||
project(Zelda64Recompiled)
|
||||
|
||||
if (APPLE) # has to be set before the first project() or enable_language()
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version")
|
||||
endif()
|
||||
|
||||
project(Zelda64Recompiled LANGUAGES C CXX)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
@ -15,6 +20,30 @@ if (WIN32)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
enable_language(OBJC OBJCXX)
|
||||
|
||||
# Check if VULKAN_SDK environment variable is set
|
||||
if(NOT DEFINED ENV{VULKAN_SDK})
|
||||
message(FATAL_ERROR "VULKAN_SDK environment variable is not set.")
|
||||
else()
|
||||
set(VULKAN_SDK $ENV{VULKAN_SDK})
|
||||
endif()
|
||||
|
||||
# Define paths to Vulkan loader and MoltenVK libraries
|
||||
set(VULKAN_LOADER_PATH "${VULKAN_SDK}/lib/libvulkan.dylib")
|
||||
set(MOLTENVK_PATH "${VULKAN_SDK}/lib/libMoltenVK.dylib")
|
||||
|
||||
# Ensure the Vulkan loader and MoltenVK libraries exist
|
||||
if(NOT EXISTS "${VULKAN_LOADER_PATH}")
|
||||
message(FATAL_ERROR "Vulkan loader not found at ${VULKAN_LOADER_PATH}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${MOLTENVK_PATH}")
|
||||
message(FATAL_ERROR "MoltenVK not found at ${MOLTENVK_PATH}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24:
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
|
@ -135,11 +164,12 @@ add_custom_target(DownloadGameControllerDB
|
|||
DEPENDS ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt)
|
||||
|
||||
# Main executable
|
||||
add_executable(Zelda64Recompiled)
|
||||
add_executable(Zelda64Recompiled MACOSX_BUNDLE)
|
||||
add_dependencies(Zelda64Recompiled DownloadGameControllerDB)
|
||||
|
||||
set (SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/main/main.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/support.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/register_overlays.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/register_patches.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/main/rt64_render_context.cpp
|
||||
|
@ -164,6 +194,10 @@ set (SOURCES
|
|||
${CMAKE_SOURCE_DIR}/lib/RmlUi/Backends/RmlUi_Platform_SDL.cpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND SOURCES ${CMAKE_SOURCE_DIR}/src/main/support_apple.mm)
|
||||
endif()
|
||||
|
||||
target_include_directories(Zelda64Recompiled PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/lib/concurrentqueue
|
||||
|
@ -243,6 +277,114 @@ if (WIN32)
|
|||
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
find_package(SDL2 REQUIRED)
|
||||
target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS})
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${CMAKE_DL_LIBS} Threads::Threads)
|
||||
|
||||
# Set bundle properties
|
||||
set_target_properties(Zelda64Recompiled PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Zelda64Recompiled"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.github.zelda64recompiled"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "1.0"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "1.0"
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon.icns"
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist
|
||||
)
|
||||
|
||||
set(ICON_SOURCE ${CMAKE_SOURCE_DIR}/icons/512.png)
|
||||
set(ICONSET_DIR ${CMAKE_BINARY_DIR}/AppIcon.iconset)
|
||||
set(ICNS_FILE ${CMAKE_BINARY_DIR}/resources/AppIcon.icns)
|
||||
|
||||
# Create iconset directory and add PNG file
|
||||
add_custom_command(
|
||||
OUTPUT ${ICONSET_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${ICONSET_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512.png
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${ICON_SOURCE} ${ICONSET_DIR}/icon_512x512@2x.png
|
||||
COMMAND touch ${ICONSET_DIR}
|
||||
COMMENT "Creating iconset directory and copying PNG file"
|
||||
)
|
||||
|
||||
# Convert iconset to icns
|
||||
add_custom_command(
|
||||
OUTPUT ${ICNS_FILE}
|
||||
DEPENDS ${ICONSET_DIR}
|
||||
COMMAND iconutil -c icns ${ICONSET_DIR} -o ${ICNS_FILE}
|
||||
COMMENT "Converting iconset to icns"
|
||||
)
|
||||
|
||||
# Custom target to ensure icns creation
|
||||
add_custom_target(create_icns ALL DEPENDS ${ICNS_FILE})
|
||||
|
||||
# Set source file properties for the resulting icns file
|
||||
set_source_files_properties(${ICNS_FILE} PROPERTIES
|
||||
MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
|
||||
# Add the icns file to the executable target
|
||||
target_sources(Zelda64Recompiled PRIVATE ${ICNS_FILE})
|
||||
|
||||
# Ensure Zelda64Recompiled depends on create_icns
|
||||
add_dependencies(Zelda64Recompiled create_icns)
|
||||
|
||||
# Configure Info.plist
|
||||
configure_file(${CMAKE_SOURCE_DIR}/.github/macos/Info.plist.in ${CMAKE_BINARY_DIR}/Info.plist @ONLY)
|
||||
|
||||
# Install the app bundle
|
||||
install(TARGETS Zelda64Recompiled BUNDLE DESTINATION .)
|
||||
|
||||
# Copy required frameworks to bundle
|
||||
target_link_libraries(Zelda64Recompiled PRIVATE ${MOLTENVK_PATH} ${VULKAN_LOADER_PATH})
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/.github/macos/fixup_bundle.cmake
|
||||
)
|
||||
|
||||
# Copy assets folder to the MacOS folder of the app bundle
|
||||
set(TEMP_ASSETS_DIR "${CMAKE_BINARY_DIR}/temp_assets")
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets ${TEMP_ASSETS_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR}/scss
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${TEMP_ASSETS_DIR} $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/assets
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${TEMP_ASSETS_DIR}
|
||||
)
|
||||
|
||||
# Copy ICD files to macOS Resources folder
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/vulkan/icd.d
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/.github/macOS/MoltenVK_icd.json $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/vulkan/icd.d/
|
||||
)
|
||||
|
||||
# Copy controller db file to macOS Resources folder
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/Resources/
|
||||
)
|
||||
|
||||
# Use install_name_tool to set the RPATH after the build
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND install_name_tool -add_rpath "@executable_path/../Frameworks/" $<TARGET_BUNDLE_DIR:Zelda64Recompiled>/Contents/MacOS/Zelda64Recompiled
|
||||
)
|
||||
|
||||
# Apply JIT compilation workaround
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Applying JIT compilation workaround"
|
||||
COMMAND /bin/bash -c "printf '\\x07' | dd of=$<TARGET_FILE:Zelda64Recompiled> bs=1 seek=160 count=1 conv=notrunc"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
# Code sign the app bundle with ad-hoc identity
|
||||
add_custom_command(TARGET Zelda64Recompiled POST_BUILD
|
||||
COMMAND codesign --deep --force --sign - $<TARGET_BUNDLE_DIR:Zelda64Recompiled>
|
||||
COMMENT "Code signing the app bundle with ad-hoc identity"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(X11 REQUIRED)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#ifndef __ZELDA_SUPPORT_H__
|
||||
#define __ZELDA_SUPPORT_H__
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace zelda64 {
|
||||
void dispatch_on_main_thread(std::function<void()> func);
|
||||
|
||||
#ifdef __APPLE__
|
||||
const char* get_bundle_resource_directory();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
2
lib/rt64
2
lib/rt64
|
@ -1 +1 @@
|
|||
Subproject commit 88c618c1f8d8089f94e78537e49e0d77798fc0be
|
||||
Subproject commit c648adae38ed8d0342c02ccf3a6063e99d746752
|
|
@ -13,6 +13,8 @@
|
|||
#elif defined(__linux__)
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include "common/rt64_apple.h"
|
||||
#endif
|
||||
|
||||
constexpr std::u8string_view general_filename = u8"general.json";
|
||||
|
@ -145,8 +147,8 @@ std::filesystem::path zelda64::get_app_folder_path() {
|
|||
}
|
||||
|
||||
CoTaskMemFree(known_path);
|
||||
#elif defined(__linux__)
|
||||
// check for APP_FOLDER_PATH env var used by AppImage
|
||||
#elif defined(__linux__) || defined(__APPLE__)
|
||||
// check for APP_FOLDER_PATH env var
|
||||
if (getenv("APP_FOLDER_PATH") != nullptr) {
|
||||
return std::filesystem::path{getenv("APP_FOLDER_PATH")};
|
||||
}
|
||||
|
@ -154,7 +156,11 @@ std::filesystem::path zelda64::get_app_folder_path() {
|
|||
const char *homedir;
|
||||
|
||||
if ((homedir = getenv("HOME")) == nullptr) {
|
||||
#if defined(__linux__)
|
||||
homedir = getpwuid(getuid())->pw_dir;
|
||||
#elif defined(__APPLE__)
|
||||
homedir = GetHomeDirectory();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (homedir != nullptr) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "zelda_config.h"
|
||||
#include "zelda_sound.h"
|
||||
#include "zelda_render.h"
|
||||
#include "zelda_support.h"
|
||||
#include "ovl_patches.hpp"
|
||||
#include "librecomp/game.hpp"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
@ -44,7 +45,7 @@ void exit_error(const char* str, Ts ...args) {
|
|||
// TODO pop up an error
|
||||
((void)fprintf(stderr, str, args), ...);
|
||||
assert(false);
|
||||
std::quick_exit(EXIT_FAILURE);
|
||||
ULTRAMODERN_QUICK_EXIT();
|
||||
}
|
||||
|
||||
ultramodern::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
|
@ -118,7 +119,12 @@ bool SetImageAsIcon(const char* filename, SDL_Window* window)
|
|||
SDL_Window* window;
|
||||
|
||||
ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::gfx_data_t) {
|
||||
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, SDL_WINDOW_RESIZABLE );
|
||||
uint32_t window_flags = SDL_WINDOW_RESIZABLE;
|
||||
#ifdef __APPLE__
|
||||
window_flags |= SDL_WINDOW_METAL;
|
||||
#endif
|
||||
|
||||
window = SDL_CreateWindow("Zelda 64: Recompiled", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1600, 960, window_flags );
|
||||
#if defined(__linux__)
|
||||
SetImageAsIcon("icons/512.png",window);
|
||||
if (ultramodern::renderer::get_graphics_config().wm_option == ultramodern::renderer::WindowMode::Fullscreen) { // TODO: Remove once RT64 gets native fullscreen support on Linux
|
||||
|
@ -146,6 +152,9 @@ ultramodern::renderer::WindowHandle create_window(ultramodern::gfx_callbacks_t::
|
|||
}
|
||||
|
||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.x11.display, wmInfo.info.x11.window };
|
||||
#elif defined(__APPLE__)
|
||||
SDL_MetalView view = SDL_Metal_CreateView(window);
|
||||
return ultramodern::renderer::WindowHandle{ wmInfo.info.cocoa.window, SDL_Metal_GetLayer(view) };
|
||||
#else
|
||||
static_assert(false && "Unimplemented");
|
||||
#endif
|
||||
|
@ -583,8 +592,13 @@ int main(int argc, char** argv) {
|
|||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
reset_audio(48000);
|
||||
|
||||
// Source controller mappings file
|
||||
if (SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt") < 0) {
|
||||
#if defined(__APPLE__)
|
||||
const char* resource_directory = zelda64::get_bundle_resource_directory();
|
||||
std::string mapping_file_path = std::string(resource_directory) + "gamecontrollerdb.txt";
|
||||
#else
|
||||
std::string mapping_file_path = "gamecontrollerdb.txt";
|
||||
#endif
|
||||
if (SDL_GameControllerAddMappingsFromFile(mapping_file_path.c_str()) < 0) {
|
||||
fprintf(stderr, "Failed to load controller mappings: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
|
|
|
@ -207,7 +207,7 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
|
|||
appCore.window.window = window_handle.window;
|
||||
#elif defined(__APPLE__)
|
||||
appCore.window.window = window_handle.window;
|
||||
appCore.window.view = window_handle.view;
|
||||
appCore.window.layer = window_handle.view;
|
||||
#endif
|
||||
|
||||
appCore.checkInterrupts = dummy_check_interrupts;
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef __APPLE__
|
||||
|
||||
#include "zelda_support.h"
|
||||
#include <functional>
|
||||
|
||||
void zelda64::dispatch_on_main_thread(std::function<void()> func) {
|
||||
func();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,13 @@
|
|||
#include "zelda_support.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
void zelda64::dispatch_on_main_thread(std::function<void()> func) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
func();
|
||||
});
|
||||
}
|
||||
|
||||
const char* zelda64::get_bundle_resource_directory() {
|
||||
NSString *bundlePath = [[NSBundle mainBundle] resourcePath];
|
||||
return strdup([bundlePath UTF8String]);
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
#include "recomp_input.h"
|
||||
#include "zelda_sound.h"
|
||||
#include "zelda_config.h"
|
||||
#include "zelda_support.h"
|
||||
#include "zelda_debug.h"
|
||||
#include "zelda_render.h"
|
||||
#include "promptfont.h"
|
||||
|
@ -519,6 +520,11 @@ public:
|
|||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String asset = "/assets/config_menu.rml";
|
||||
return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset);
|
||||
#endif
|
||||
|
||||
return context->LoadDocument("assets/config_menu.rml");
|
||||
}
|
||||
void register_events(recompui::UiEventListenerInstancer& listener) override {
|
||||
|
@ -725,7 +731,7 @@ public:
|
|||
throw std::runtime_error("Failed to make RmlUi data model for the controls config menu");
|
||||
}
|
||||
|
||||
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = recomp::get_num_inputs(); } );
|
||||
constructor.BindFunc("input_count", [](Rml::Variant& out) { out = static_cast<uint64_t>(recomp::get_num_inputs()); } );
|
||||
constructor.BindFunc("input_device_is_keyboard", [](Rml::Variant& out) { out = cur_device == recomp::InputDevice::Keyboard; } );
|
||||
|
||||
constructor.RegisterTransformFunc("get_input_name", [](const Rml::VariantList& inputs) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "recomp_ui.h"
|
||||
#include "zelda_config.h"
|
||||
#include "zelda_support.h"
|
||||
#include "librecomp/game.hpp"
|
||||
#include "ultramodern/ultramodern.hpp"
|
||||
#include "RmlUi/Core.h"
|
||||
|
@ -15,41 +16,43 @@ extern std::vector<recomp::GameEntry> supported_games;
|
|||
|
||||
void select_rom() {
|
||||
nfdnchar_t* native_path = nullptr;
|
||||
nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr);
|
||||
zelda64::dispatch_on_main_thread([&native_path] {
|
||||
nfdresult_t result = NFD_OpenDialogN(&native_path, nullptr, 0, nullptr);
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
std::filesystem::path path{native_path};
|
||||
if (result == NFD_OKAY) {
|
||||
std::filesystem::path path{native_path};
|
||||
|
||||
NFD_FreePathN(native_path);
|
||||
native_path = nullptr;
|
||||
NFD_FreePathN(native_path);
|
||||
native_path = nullptr;
|
||||
|
||||
recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id);
|
||||
switch (rom_error) {
|
||||
case recomp::RomValidationError::Good:
|
||||
mm_rom_valid = true;
|
||||
model_handle.DirtyVariable("mm_rom_valid");
|
||||
break;
|
||||
case recomp::RomValidationError::FailedToOpen:
|
||||
recompui::message_box("Failed to open ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotARom:
|
||||
recompui::message_box("This is not a valid ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectRom:
|
||||
recompui::message_box("This ROM is not the correct game.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotYet:
|
||||
recompui::message_box("This game isn't supported yet.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectVersion:
|
||||
recompui::message_box(
|
||||
"This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game.");
|
||||
break;
|
||||
case recomp::RomValidationError::OtherError:
|
||||
recompui::message_box("An unknown error has occurred.");
|
||||
break;
|
||||
recomp::RomValidationError rom_error = recomp::select_rom(path, supported_games[0].game_id);
|
||||
switch (rom_error) {
|
||||
case recomp::RomValidationError::Good:
|
||||
mm_rom_valid = true;
|
||||
model_handle.DirtyVariable("mm_rom_valid");
|
||||
break;
|
||||
case recomp::RomValidationError::FailedToOpen:
|
||||
recompui::message_box("Failed to open ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotARom:
|
||||
recompui::message_box("This is not a valid ROM file.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectRom:
|
||||
recompui::message_box("This ROM is not the correct game.");
|
||||
break;
|
||||
case recomp::RomValidationError::NotYet:
|
||||
recompui::message_box("This game isn't supported yet.");
|
||||
break;
|
||||
case recomp::RomValidationError::IncorrectVersion:
|
||||
recompui::message_box(
|
||||
"This ROM is the correct game, but the wrong version.\nThis project requires the NTSC-U N64 version of the game.");
|
||||
break;
|
||||
case recomp::RomValidationError::OtherError:
|
||||
recompui::message_box("An unknown error has occurred.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class LauncherMenu : public recompui::MenuController {
|
||||
|
@ -61,6 +64,11 @@ public:
|
|||
|
||||
}
|
||||
Rml::ElementDocument* load_document(Rml::Context* context) override {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String asset = "/assets/launcher.rml";
|
||||
return context->LoadDocument(zelda64::get_bundle_resource_directory() + asset);
|
||||
#endif
|
||||
|
||||
return context->LoadDocument("assets/launcher.rml");
|
||||
}
|
||||
void register_events(recompui::UiEventListenerInstancer& listener) override {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "librecomp/game.hpp"
|
||||
#include "zelda_config.h"
|
||||
#include "ui_rml_hacks.hpp"
|
||||
#include "zelda_support.h"
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
|
@ -1144,8 +1145,6 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
|||
Rml::Debugger::Initialise(ui_context->rml.context);
|
||||
|
||||
{
|
||||
const Rml::String directory = "assets/";
|
||||
|
||||
struct FontFace {
|
||||
const char* filename;
|
||||
bool fallback_face;
|
||||
|
@ -1162,7 +1161,13 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
|||
};
|
||||
|
||||
for (const FontFace& face : font_faces) {
|
||||
#if defined(__APPLE__)
|
||||
const Rml::String directory = "/assets/";
|
||||
Rml::LoadFontFace(zelda64::get_bundle_resource_directory() + directory + face.filename, face.fallback_face);
|
||||
#else
|
||||
const Rml::String directory = "assets/";
|
||||
Rml::LoadFontFace(directory + face.filename, face.fallback_face);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1506,6 +1511,9 @@ recompui::Menu recompui::get_current_menu() {
|
|||
}
|
||||
|
||||
void recompui::message_box(const char* msg) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), msg, nullptr);
|
||||
std::string message(msg);
|
||||
zelda64::dispatch_on_main_thread([message] {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, zelda64::program_name.data(), message.c_str(), nullptr);
|
||||
});
|
||||
printf("[ERROR] %s\n", msg);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue