Compare commits

...

4 Commits

Author SHA1 Message Date
David Chavez
d087dc9dae
Merge cae3c4bea9 into 208e3044fc 2024-06-22 23:04:03 -06:00
briaguya
208e3044fc
fix: use sdl userevent to set await_stick_return when scanning for input (#413) 2024-06-22 22:06:25 -04:00
danielryb
cbb5e8e7a4
Allow skipping Song of Soaring cutscene (#409) 2024-06-22 22:02:33 -04:00
dcvz
cae3c4bea9 Add macOS Support 2024-06-17 13:42:46 +02:00
17 changed files with 444 additions and 43 deletions

33
.github/macos/Info.plist.in vendored Normal file
View File

@ -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_SHOULD_MAXIMIZE_CONCURRENT_COMPILATION</key>
<string>1</string>
<key>MVK_CONFIG_USE_MTLHEAP</key>
<string>1</string>
</dict>
</dict>
</plist>

8
.github/macos/MoltenVK_icd.json vendored Normal file
View File

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

11
.github/macos/fixup_bundle.cmake vendored Normal file
View File

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

1
.github/macos/macports-deps.txt vendored Normal file
View File

@ -0,0 +1 @@
clang-18 llvm-18 libsdl2 +universal freetype +universal

View File

@ -268,3 +268,96 @@ jobs:
SDL2.dll
assets/
gamecontrollerdb.txt
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: Cache MacPorts
id: cache-macports
uses: actions/cache@v2
with:
path: /opt/local/
key: ${{ runner.os }}-macports-${{ hashFiles('.github/macos/macports-deps.txt') }}
restore-keys: |
${{ runner.os }}-macports
- name: Install MacPorts (if necessary)
run: |
if [ -d /opt/local/ ]; then
echo "MacPorts already installed"
else
wget https://github.com/macports/macports-base/releases/download/v2.9.3/MacPorts-2.9.3-14-Sonoma.pkg
sudo installer -pkg ./MacPorts-2.9.3-14-Sonoma.pkg -target /
fi
echo "/opt/local/bin:/opt/local/sbin" >> $GITHUB_PATH
- name: Install macOS Dependencies
run: |
brew uninstall --ignore-dependencies libpng freetype
sudo port install $(cat .github/macos/macports-deps.txt)
brew install ninja
- 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
if: runner.os != 'Windows'
run: |
git clone https://github.com/Mr-Wiseguy/N64Recomp.git --recurse-submodules N64RecompSource
cd N64RecompSource
git checkout 2a2df89349ff25a3afb3a09617deb3a166efe2f3
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: Install Vulkan SDK
run: |
wget https://sdk.lunarg.com/sdk/download/1.3.283.0/mac/vulkansdk-macos-1.3.283.0.dmg
hdiutil attach ./vulkansdk-macos-1.3.283.0.dmg
sudo /Volumes/vulkansdk-macos-1.3.283.0/InstallVulkan.app/Contents/MacOS/InstallVulkan --root ~/VulkanSDK/1.3.283.0 --accept-licenses --default-answer --confirm-command install
hdiutil detach /Volumes/vulkansdk-macos-1.3.283.0
- 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/1.3.283.0/macOS
- name: Prepare Archive
run: |
mv cmake-build/Zelda64Recompiled.app Zelda64Recompiled.app
zip -r Zelda64Recompiled.zip Zelda64Recompiled.app
- name: Archive Zelda64Recomp
uses: actions/upload-artifact@v4
with:
name: Zelda64Recompiled-${{ runner.os }}-${{ matrix.type }}
path: Zelda64Recompiled.zip

View File

@ -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)
@ -8,6 +13,30 @@ set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-everything -Wall -Wextra")
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)
@ -126,7 +155,7 @@ add_custom_target(DownloadGameControllerDB
DEPENDS ${CMAKE_SOURCE_DIR}/gamecontrollerdb.txt)
# Main executable
add_executable(Zelda64Recompiled)
add_executable(Zelda64Recompiled MACOSX_BUNDLE)
add_dependencies(Zelda64Recompiled DownloadGameControllerDB)
# Generate mm_shader_cache.c from the MM shader cache if it exists
@ -141,6 +170,7 @@ endif()
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
@ -169,6 +199,10 @@ if (HAS_MM_SHADER_CACHE)
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/mm_shader_cache.c)
endif()
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,101 @@ 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
)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
find_package(SDL2 REQUIRED)
find_package(X11 REQUIRED)

14
include/zelda_support.h Normal file
View File

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

@ -1 +1 @@
Subproject commit e106c18965a89b26197bde972d9a83dd2dea1691
Subproject commit dcc79fa26caf5e36d412bee027f7072ac22d94b7

29
patches/skip_sos.c Normal file
View File

@ -0,0 +1,29 @@
#include "patches.h"
#include "overlays/actors/ovl_En_Test7/z_en_test7.h"
#define THIS ((EnTest7*)thisx)
void func_80AF118C(PlayState* play, OwlWarpFeather* feathers, EnTest7* this, s32 arg3, s32 arg4); // EnTest7_UpdateFeathers
void func_80AF2350(EnTest7* this, PlayState* play); // EnTest7_WarpCsWarp
void EnTest7_Update(Actor* thisx, PlayState* play) {
EnTest7* this = THIS;
this->actionFunc(this, play);
if (this->playerCamFunc != NULL) {
this->playerCamFunc(this, play);
}
this->timer++;
func_80AF118C(play, this->feathers, this, (this->flags & OWL_WARP_FLAGS_8) != 0,
(this->flags & OWL_WARP_FLAGS_10) != 0);
// @recomp Allow skipping the Song of Soaring cutscene.
Input* input = CONTROLLER1(&play->state);
if (CHECK_BTN_ANY(input->press.button, BTN_A | BTN_B) && (OWL_WARP_CS_GET_OCARINA_MODE(&this->actor) != ENTEST7_ARRIVE)) {
func_80AF2350(thisx, play);
}
}

View File

@ -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";
@ -146,8 +148,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")};
}
@ -155,7 +157,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) {

View File

@ -192,9 +192,23 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
SDL_ControllerAxisEvent* axis_event = &event->caxis;
float axis_value = axis_event->value * (1/32768.0f);
if (axis_value > axis_threshold) {
SDL_Event set_stick_return_event;
set_stick_return_event.type = SDL_USEREVENT;
set_stick_return_event.user.code = axis_event->axis;
set_stick_return_event.user.data1 = nullptr;
set_stick_return_event.user.data2 = nullptr;
recompui::queue_event(set_stick_return_event);
set_scanned_input({(uint32_t)InputType::ControllerAnalog, axis_event->axis + 1});
}
else if (axis_value < -axis_threshold) {
SDL_Event set_stick_return_event;
set_stick_return_event.type = SDL_USEREVENT;
set_stick_return_event.user.code = axis_event->axis;
set_stick_return_event.user.data1 = nullptr;
set_stick_return_event.user.data2 = nullptr;
recompui::queue_event(set_stick_return_event);
set_scanned_input({(uint32_t)InputType::ControllerAnalog, -axis_event->axis - 1});
}
} else {

View File

@ -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"
@ -46,7 +47,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() {
@ -120,7 +121,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
@ -148,6 +154,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, view };
#else
static_assert(false && "Unimplemented");
#endif
@ -456,8 +465,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());
}

10
src/main/support.cpp Normal file
View File

@ -0,0 +1,10 @@
#ifndef __APPLE__
#include "zelda_support.h"
#include <functional>
void zelda64::dispatch_on_main_thread(std::function<void()> func) {
func();
}
#endif

13
src/main/support_apple.mm Normal file
View File

@ -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]);
}

View File

@ -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"
@ -509,6 +510,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 {
@ -707,7 +713,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) {

View File

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

View File

@ -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
}
}
@ -1350,6 +1355,13 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
non_mouse_interacted = true;
kb_interacted = true;
break;
case SDL_EventType::SDL_USEREVENT:
if (cur_event.user.code == SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY) {
ui_context->rml.await_stick_return_y = true;
} else if (cur_event.user.code == SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) {
ui_context->rml.await_stick_return_x = true;
}
break;
case SDL_EventType::SDL_CONTROLLERAXISMOTION:
SDL_ControllerAxisEvent* axis_event = &cur_event.caxis;
if (axis_event->axis != SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTY && axis_event->axis != SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_LEFTX) {