add macOS build and launch support
This commit is contained in:
parent
f896c6fade
commit
943d26e2d9
7 changed files with 271 additions and 27 deletions
|
|
@ -5,6 +5,9 @@ project(skate3 VERSION 1.0.0 LANGUAGES CXX)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
enable_language(RC)
|
enable_language(RC)
|
||||||
endif()
|
endif()
|
||||||
|
if(APPLE)
|
||||||
|
enable_language(OBJCXX)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
@ -16,24 +19,38 @@ skate3_resolve_version(SKATE3_FULL_VERSION
|
||||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
string(REGEX REPLACE "-.*$" "" SKATE3_NUMERIC_VERSION "${SKATE3_FULL_VERSION}")
|
string(REGEX REPLACE "-.*$" "" SKATE3_NUMERIC_VERSION "${SKATE3_FULL_VERSION}")
|
||||||
|
|
||||||
|
set(SKATE3_TARGET_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR}")
|
||||||
|
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||||
|
list(LENGTH CMAKE_OSX_ARCHITECTURES SKATE3_OSX_ARCH_COUNT)
|
||||||
|
if(SKATE3_OSX_ARCH_COUNT EQUAL 1)
|
||||||
|
set(SKATE3_TARGET_PROCESSOR "${CMAKE_OSX_ARCHITECTURES}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
if(SKATE3_TARGET_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
set(SKATE3_BUILD_PLATFORM "win-amd64")
|
set(SKATE3_BUILD_PLATFORM "win-amd64")
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64")
|
elseif(SKATE3_TARGET_PROCESSOR MATCHES "aarch64|ARM64")
|
||||||
set(SKATE3_BUILD_PLATFORM "win-arm64")
|
set(SKATE3_BUILD_PLATFORM "win-arm64")
|
||||||
else()
|
else()
|
||||||
set(SKATE3_BUILD_PLATFORM "win-${CMAKE_SYSTEM_PROCESSOR}")
|
set(SKATE3_BUILD_PLATFORM "win-${SKATE3_TARGET_PROCESSOR}")
|
||||||
|
endif()
|
||||||
|
elseif(APPLE)
|
||||||
|
if(SKATE3_TARGET_PROCESSOR MATCHES "aarch64|arm64|ARM64")
|
||||||
|
set(SKATE3_BUILD_PLATFORM "macos-arm64")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported macOS architecture: ${SKATE3_TARGET_PROCESSOR}. macOS builds are ARM64-only.")
|
||||||
endif()
|
endif()
|
||||||
elseif(UNIX AND NOT APPLE)
|
elseif(UNIX AND NOT APPLE)
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
if(SKATE3_TARGET_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
set(SKATE3_BUILD_PLATFORM "linux-amd64")
|
set(SKATE3_BUILD_PLATFORM "linux-amd64")
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|ARM64")
|
elseif(SKATE3_TARGET_PROCESSOR MATCHES "aarch64|ARM64")
|
||||||
set(SKATE3_BUILD_PLATFORM "linux-arm64")
|
set(SKATE3_BUILD_PLATFORM "linux-arm64")
|
||||||
else()
|
else()
|
||||||
set(SKATE3_BUILD_PLATFORM "linux-${CMAKE_SYSTEM_PROCESSOR}")
|
set(SKATE3_BUILD_PLATFORM "linux-${SKATE3_TARGET_PROCESSOR}")
|
||||||
endif()
|
endif()
|
||||||
else()
|
else()
|
||||||
set(SKATE3_BUILD_PLATFORM "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
|
set(SKATE3_BUILD_PLATFORM "${CMAKE_SYSTEM_NAME}-${SKATE3_TARGET_PROCESSOR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
string(TIMESTAMP SKATE3_BUILD_TIMESTAMP "%Y%m%d_%H%M")
|
string(TIMESTAMP SKATE3_BUILD_TIMESTAMP "%Y%m%d_%H%M")
|
||||||
|
|
@ -50,7 +67,7 @@ set(SKATE3_GAME_DATA_ROOT
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/game"
|
"${CMAKE_CURRENT_SOURCE_DIR}/game"
|
||||||
CACHE PATH "Path to an extracted Skate 3 retail game dump")
|
CACHE PATH "Path to an extracted Skate 3 retail game dump")
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
if(NOT APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64|x86_64")
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||||
add_compile_options(-march=x86-64-v3)
|
add_compile_options(-march=x86-64-v3)
|
||||||
set(SNAPPY_REQUIRE_AVX2 ON CACHE BOOL
|
set(SNAPPY_REQUIRE_AVX2 ON CACHE BOOL
|
||||||
|
|
@ -82,7 +99,7 @@ configure_file(
|
||||||
@ONLY
|
@ONLY
|
||||||
)
|
)
|
||||||
set(REXGLUE_ENABLE_TRACY OFF CACHE BOOL "Disable Tracy instrumentation for normal Skate 3 builds" FORCE)
|
set(REXGLUE_ENABLE_TRACY OFF CACHE BOOL "Disable Tracy instrumentation for normal Skate 3 builds" FORCE)
|
||||||
set(REXGLUE_ENABLE_PERF_COUNTERS OFF CACHE BOOL "Enable lightweight rexglue perf counter instrumentation" FORCE)
|
set(REXGLUE_ENABLE_PERF_COUNTERS OFF CACHE BOOL "Enable lightweight rexglue perf counter instrumentation")
|
||||||
|
|
||||||
include(cmake/RexGlue.cmake)
|
include(cmake/RexGlue.cmake)
|
||||||
include(cmake/CodegenTargets.cmake)
|
include(cmake/CodegenTargets.cmake)
|
||||||
|
|
@ -93,6 +110,9 @@ set(SKATE3_COMMON_SOURCES
|
||||||
src/skate3_iso_installer.cpp
|
src/skate3_iso_installer.cpp
|
||||||
src/skate3_user_settings.cpp
|
src/skate3_user_settings.cpp
|
||||||
)
|
)
|
||||||
|
if(APPLE)
|
||||||
|
list(APPEND SKATE3_COMMON_SOURCES src/skate3_iso_installer_macos.mm)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(SKATE3_SOURCES
|
set(SKATE3_SOURCES
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
|
@ -140,6 +160,11 @@ endif()
|
||||||
target_link_libraries(skate3 PRIVATE rex::runtime)
|
target_link_libraries(skate3 PRIVATE rex::runtime)
|
||||||
rexglue_configure_target(skate3)
|
rexglue_configure_target(skate3)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
target_link_options(skate3 PRIVATE -Wl,-stack_size,0x4000000)
|
||||||
|
target_link_libraries(skate3 PRIVATE "-framework Cocoa")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(skate3 PRIVATE comdlg32)
|
target_link_libraries(skate3 PRIVATE comdlg32)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,23 @@
|
||||||
"rhs": "Linux"
|
"rhs": "Linux"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-base",
|
||||||
|
"hidden": true,
|
||||||
|
"inherits": "base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_C_COMPILER": "/opt/homebrew/opt/llvm/bin/clang",
|
||||||
|
"CMAKE_CXX_COMPILER": "/opt/homebrew/opt/llvm/bin/clang++",
|
||||||
|
"CMAKE_AR": "/opt/homebrew/opt/llvm/bin/llvm-ar",
|
||||||
|
"CMAKE_RANLIB": "/opt/homebrew/opt/llvm/bin/llvm-ranlib",
|
||||||
|
"CMAKE_OSX_DEPLOYMENT_TARGET": "12.0"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "equals",
|
||||||
|
"lhs": "${hostSystemName}",
|
||||||
|
"rhs": "Darwin"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "debug",
|
"name": "debug",
|
||||||
"displayName": "Debug",
|
"displayName": "Debug",
|
||||||
|
|
@ -70,6 +87,22 @@
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"CMAKE_BUILD_TYPE": "Release"
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-relwithdebinfo",
|
||||||
|
"displayName": "macOS RelWithDebInfo",
|
||||||
|
"inherits": "macos-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-release",
|
||||||
|
"displayName": "macOS Release",
|
||||||
|
"inherits": "macos-base",
|
||||||
|
"cacheVariables": {
|
||||||
|
"CMAKE_BUILD_TYPE": "Release"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"buildPresets": [
|
"buildPresets": [
|
||||||
|
|
@ -92,6 +125,14 @@
|
||||||
{
|
{
|
||||||
"name": "linux-release",
|
"name": "linux-release",
|
||||||
"configurePreset": "linux-release"
|
"configurePreset": "linux-release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-relwithdebinfo",
|
||||||
|
"configurePreset": "macos-relwithdebinfo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macos-release",
|
||||||
|
"configurePreset": "macos-release"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
@ -25,8 +26,11 @@
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif
|
#endif
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
#include <spawn.h>
|
#include <spawn.h>
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <rex/cvar.h>
|
#include <rex/cvar.h>
|
||||||
|
|
@ -37,6 +41,7 @@
|
||||||
#include <rex/input/input_system.h>
|
#include <rex/input/input_system.h>
|
||||||
#include <rex/kernel/xam/module.h>
|
#include <rex/kernel/xam/module.h>
|
||||||
#include <rex/logging.h>
|
#include <rex/logging.h>
|
||||||
|
#include <rex/platform.h>
|
||||||
#include <rex/perf/counter.h>
|
#include <rex/perf/counter.h>
|
||||||
#include <rex/ppc/context.h>
|
#include <rex/ppc/context.h>
|
||||||
#include <rex/system/function_dispatcher.h>
|
#include <rex/system/function_dispatcher.h>
|
||||||
|
|
@ -49,7 +54,7 @@
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <toml++/toml.hpp>
|
#include <toml++/toml.hpp>
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
extern char** environ;
|
extern char** environ;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -61,10 +66,18 @@ extern "C" REX_FUNC(__restgprlr_19);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__APPLE__)
|
||||||
std::vector<std::string> CurrentProcessArgumentsForRestart(
|
std::vector<std::string> CurrentProcessArgumentsForRestart(
|
||||||
const std::filesystem::path& executable_path) {
|
const std::filesystem::path& executable_path) {
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
int argc = *_NSGetArgc();
|
||||||
|
char** argv = *_NSGetArgv();
|
||||||
|
args.reserve(static_cast<size_t>(argc > 0 ? argc : 1));
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
args.emplace_back(argv[i] ? argv[i] : "");
|
||||||
|
}
|
||||||
|
#else
|
||||||
std::ifstream cmdline("/proc/self/cmdline", std::ios::binary);
|
std::ifstream cmdline("/proc/self/cmdline", std::ios::binary);
|
||||||
std::string arg;
|
std::string arg;
|
||||||
char ch = 0;
|
char ch = 0;
|
||||||
|
|
@ -79,6 +92,7 @@ std::vector<std::string> CurrentProcessArgumentsForRestart(
|
||||||
if (!arg.empty()) {
|
if (!arg.empty()) {
|
||||||
args.push_back(std::move(arg));
|
args.push_back(std::move(arg));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const std::string executable = rex::path_to_utf8(executable_path);
|
const std::string executable = rex::path_to_utf8(executable_path);
|
||||||
if (args.empty()) {
|
if (args.empty()) {
|
||||||
|
|
@ -88,6 +102,28 @@ std::vector<std::string> CurrentProcessArgumentsForRestart(
|
||||||
}
|
}
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetRestartArgument(std::vector<std::string>& args, std::string name, std::string value) {
|
||||||
|
const std::string option = "--" + name;
|
||||||
|
const std::string option_with_equals = option + "=";
|
||||||
|
for (size_t i = 1; i < args.size(); ++i) {
|
||||||
|
if (args[i] == option) {
|
||||||
|
if (i + 1 < args.size()) {
|
||||||
|
args[i + 1] = std::move(value);
|
||||||
|
} else {
|
||||||
|
args.push_back(std::move(value));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (args[i].starts_with(option_with_equals)) {
|
||||||
|
args[i] = option_with_equals + value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args.push_back(std::move(option));
|
||||||
|
args.push_back(std::move(value));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr std::string_view kUserDirectoryName = "skate3";
|
constexpr std::string_view kUserDirectoryName = "skate3";
|
||||||
|
|
@ -190,6 +226,19 @@ void LoadAndNormalizeSimpleSettings(const std::filesystem::path& settings_path,
|
||||||
rex::ui::EnsureSimpleSettingsConfig(settings_path);
|
rex::ui::EnsureSimpleSettingsConfig(settings_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::filesystem::path ResolveRuntimeGameDataRoot(const rex::PathConfig& paths) {
|
||||||
|
if (!paths.game_data_root.empty()) {
|
||||||
|
return paths.game_data_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto working_directory_game = std::filesystem::current_path() / "game";
|
||||||
|
if (skate3::IsGameInstalled(working_directory_game)) {
|
||||||
|
return working_directory_game;
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths.config_path.parent_path() / "game";
|
||||||
|
}
|
||||||
|
|
||||||
const char* FirstExistingFontPath(std::initializer_list<const char*> paths) {
|
const char* FirstExistingFontPath(std::initializer_list<const char*> paths) {
|
||||||
for (const char* path : paths) {
|
for (const char* path : paths) {
|
||||||
if (std::filesystem::exists(path)) {
|
if (std::filesystem::exists(path)) {
|
||||||
|
|
@ -283,13 +332,19 @@ std::optional<rex::PathConfig> Skate3BaseApp::OnFinalizePaths(
|
||||||
skate3::SaveProfiles(profiles_path_, profiles);
|
skate3::SaveProfiles(profiles_path_, profiles);
|
||||||
}
|
}
|
||||||
auto runtime_paths = defaults;
|
auto runtime_paths = defaults;
|
||||||
if (runtime_paths.game_data_root.empty()) {
|
runtime_paths.game_data_root = ResolveRuntimeGameDataRoot(runtime_paths);
|
||||||
runtime_paths.game_data_root = config_path_.parent_path() / "game";
|
|
||||||
}
|
|
||||||
runtime_paths.config_path.clear();
|
runtime_paths.config_path.clear();
|
||||||
if (!skate3::IsGameInstalled(runtime_paths.game_data_root)) {
|
if (!skate3::IsGameInstalled(runtime_paths.game_data_root)) {
|
||||||
REXLOG_INFO("Game files not found at {}; launching rexglue ISO installer",
|
REXLOG_INFO("Game files not found at {}; launching rexglue ISO installer",
|
||||||
runtime_paths.game_data_root.string());
|
runtime_paths.game_data_root.string());
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
if (const char* automated_iso = std::getenv("SKATE3_INSTALL_ISO");
|
||||||
|
automated_iso == nullptr || *automated_iso == '\0') {
|
||||||
|
skate3::ShowRexglueIsoInstallWizard(imgui_drawer(), std::move(runtime_paths),
|
||||||
|
std::move(resume));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
rex::PathConfig installed_paths;
|
rex::PathConfig installed_paths;
|
||||||
const bool installed = skate3::RunRexglueIsoInstallWizardBlocking(
|
const bool installed = skate3::RunRexglueIsoInstallWizardBlocking(
|
||||||
app_context(), window(), imgui_drawer(), runtime_paths, installed_paths);
|
app_context(), window(), imgui_drawer(), runtime_paths, installed_paths);
|
||||||
|
|
@ -357,6 +412,7 @@ void Skate3BaseApp::OnPostSetup() {
|
||||||
|
|
||||||
void Skate3BaseApp::OnPostLaunchModule(rex::system::XThread* thread) {
|
void Skate3BaseApp::OnPostLaunchModule(rex::system::XThread* thread) {
|
||||||
(void)thread;
|
(void)thread;
|
||||||
|
|
||||||
if (std::getenv("SKATE3_INVALIDATE_GPU_MEMORY_EVERY_FRAME") == nullptr) {
|
if (std::getenv("SKATE3_INVALIDATE_GPU_MEMORY_EVERY_FRAME") == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -445,12 +501,23 @@ void Skate3BaseApp::ToggleSimpleSettings() {
|
||||||
};
|
};
|
||||||
auto close_settings = [this]() { ApplyGameplayCursorMode(); };
|
auto close_settings = [this]() { ApplyGameplayCursorMode(); };
|
||||||
auto close_game = [this]() {
|
auto close_game = [this]() {
|
||||||
|
#if REX_PLATFORM_MAC
|
||||||
|
std::thread([]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
|
REXLOG_WARN("macOS Close Game watchdog exiting process after shutdown timeout");
|
||||||
|
std::_Exit(EXIT_SUCCESS);
|
||||||
|
}).detach();
|
||||||
|
#endif
|
||||||
app_context().CallInUIThreadDeferred([this]() {
|
app_context().CallInUIThreadDeferred([this]() {
|
||||||
|
#if REX_PLATFORM_MAC
|
||||||
|
app_context().QuitFromUIThread();
|
||||||
|
#else
|
||||||
if (window()) {
|
if (window()) {
|
||||||
window()->RequestClose();
|
window()->RequestClose();
|
||||||
} else {
|
} else {
|
||||||
app_context().QuitFromUIThread();
|
app_context().QuitFromUIThread();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
auto restart_game = [this]() { RestartGame(); };
|
auto restart_game = [this]() { RestartGame(); };
|
||||||
|
|
@ -494,7 +561,7 @@ void Skate3BaseApp::RestartGame() {
|
||||||
}
|
}
|
||||||
CloseHandle(process_info.hThread);
|
CloseHandle(process_info.hThread);
|
||||||
CloseHandle(process_info.hProcess);
|
CloseHandle(process_info.hProcess);
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__) || defined(__APPLE__)
|
||||||
const auto executable_path = rex::filesystem::GetExecutablePath();
|
const auto executable_path = rex::filesystem::GetExecutablePath();
|
||||||
if (executable_path.empty()) {
|
if (executable_path.empty()) {
|
||||||
REXLOG_WARN("Restart requested, but the executable path could not be resolved");
|
REXLOG_WARN("Restart requested, but the executable path could not be resolved");
|
||||||
|
|
@ -502,6 +569,16 @@ void Skate3BaseApp::RestartGame() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto args = CurrentProcessArgumentsForRestart(executable_path);
|
auto args = CurrentProcessArgumentsForRestart(executable_path);
|
||||||
|
std::filesystem::path restart_game_data_root;
|
||||||
|
if (runtime()) {
|
||||||
|
restart_game_data_root = runtime()->game_data_root();
|
||||||
|
}
|
||||||
|
if (restart_game_data_root.empty()) {
|
||||||
|
restart_game_data_root = game_data_root();
|
||||||
|
}
|
||||||
|
if (!restart_game_data_root.empty()) {
|
||||||
|
SetRestartArgument(args, "game_data_root", rex::path_to_utf8(restart_game_data_root));
|
||||||
|
}
|
||||||
std::vector<char*> argv;
|
std::vector<char*> argv;
|
||||||
argv.reserve(args.size() + 1);
|
argv.reserve(args.size() + 1);
|
||||||
for (auto& arg : args) {
|
for (auto& arg : args) {
|
||||||
|
|
@ -523,11 +600,15 @@ void Skate3BaseApp::RestartGame() {
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if REX_PLATFORM_MAC
|
||||||
|
app_context().QuitFromUIThread();
|
||||||
|
#else
|
||||||
if (window()) {
|
if (window()) {
|
||||||
window()->RequestClose();
|
window()->RequestClose();
|
||||||
} else {
|
} else {
|
||||||
app_context().QuitFromUIThread();
|
app_context().QuitFromUIThread();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
@ -25,11 +26,17 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <rex/ui/window_win.h>
|
#include <rex/ui/window_win.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
#else
|
#else
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace skate3 {
|
namespace skate3 {
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
std::filesystem::path PickIsoFileMacOS();
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr uint64_t kSectorSize = 2048;
|
constexpr uint64_t kSectorSize = 2048;
|
||||||
|
|
@ -93,6 +100,10 @@ std::filesystem::path PickIsoFile() {
|
||||||
}
|
}
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
std::filesystem::path PickIsoFile() {
|
||||||
|
return skate3::PickIsoFileMacOS();
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
std::filesystem::path PickIsoFile() {
|
std::filesystem::path PickIsoFile() {
|
||||||
GtkWidget* dialog = gtk_file_chooser_dialog_new(
|
GtkWidget* dialog = gtk_file_chooser_dialog_new(
|
||||||
|
|
@ -352,6 +363,38 @@ bool IsGameInstalled(const std::filesystem::path& game_root) {
|
||||||
return std::filesystem::is_regular_file(game_root / std::string(kDefaultXex));
|
return std::filesystem::is_regular_file(game_root / std::string(kDefaultXex));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShowRexglueIsoInstallWizard(rex::ui::ImGuiDrawer* drawer, rex::PathConfig runtime_paths,
|
||||||
|
std::function<void(rex::PathConfig)> complete) {
|
||||||
|
auto pick_source = []() { return PickIsoFile(); };
|
||||||
|
auto install = [game_root = runtime_paths.game_data_root](
|
||||||
|
const std::filesystem::path& source, std::atomic<uint64_t>& copied_bytes,
|
||||||
|
std::atomic<uint64_t>& total_bytes, std::string& error) {
|
||||||
|
XboxIsoReader iso;
|
||||||
|
if (!iso.Open(source, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total_bytes = iso.TotalSize();
|
||||||
|
if (!iso.ExtractAll(game_root, copied_bytes, error)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!IsGameInstalled(game_root)) {
|
||||||
|
error = "Installation completed, but default.xex was not found in the install directory.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
new rex::ui::InstallWizardDialog(
|
||||||
|
drawer, "Skate 3 Setup",
|
||||||
|
"Skate 3 game files were not found. Select your Xbox 360 ISO to install them.",
|
||||||
|
runtime_paths.game_data_root.string(), std::move(pick_source), std::move(install),
|
||||||
|
[runtime_paths = std::move(runtime_paths), complete = std::move(complete)]() mutable {
|
||||||
|
if (complete) {
|
||||||
|
complete(std::move(runtime_paths));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context,
|
bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context,
|
||||||
rex::ui::Window* window,
|
rex::ui::Window* window,
|
||||||
rex::ui::ImGuiDrawer* drawer,
|
rex::ui::ImGuiDrawer* drawer,
|
||||||
|
|
@ -364,7 +407,6 @@ bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context
|
||||||
};
|
};
|
||||||
|
|
||||||
auto result = std::make_shared<InstallResult>();
|
auto result = std::make_shared<InstallResult>();
|
||||||
auto pick_source = []() { return PickIsoFile(); };
|
|
||||||
auto install = [game_root = runtime_paths.game_data_root](
|
auto install = [game_root = runtime_paths.game_data_root](
|
||||||
const std::filesystem::path& source, std::atomic<uint64_t>& copied_bytes,
|
const std::filesystem::path& source, std::atomic<uint64_t>& copied_bytes,
|
||||||
std::atomic<uint64_t>& total_bytes, std::string& error) {
|
std::atomic<uint64_t>& total_bytes, std::string& error) {
|
||||||
|
|
@ -398,11 +440,8 @@ bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
new rex::ui::InstallWizardDialog(
|
ShowRexglueIsoInstallWizard(drawer, runtime_paths,
|
||||||
drawer, "Skate 3 Setup",
|
[result](rex::PathConfig runtime_paths) mutable {
|
||||||
"Skate 3 game files were not found. Select your Xbox 360 ISO to install them.",
|
|
||||||
runtime_paths.game_data_root.string(), std::move(pick_source), std::move(install),
|
|
||||||
[result, runtime_paths = std::move(runtime_paths)]() mutable {
|
|
||||||
result->paths = std::move(runtime_paths);
|
result->paths = std::move(runtime_paths);
|
||||||
result->ok = true;
|
result->ok = true;
|
||||||
result->done = true;
|
result->done = true;
|
||||||
|
|
@ -442,9 +481,11 @@ bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context
|
||||||
if (window) {
|
if (window) {
|
||||||
window->RequestPaint();
|
window->RequestPaint();
|
||||||
}
|
}
|
||||||
|
#if !defined(__APPLE__)
|
||||||
while (gtk_events_pending()) {
|
while (gtk_events_pending()) {
|
||||||
gtk_main_iteration_do(FALSE);
|
gtk_main_iteration_do(FALSE);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
std::this_thread::sleep_for(std::chrono::milliseconds(16));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include <rex/rex_app.h>
|
#include <rex/rex_app.h>
|
||||||
|
|
||||||
namespace skate3 {
|
namespace skate3 {
|
||||||
|
|
||||||
bool IsGameInstalled(const std::filesystem::path& game_root);
|
bool IsGameInstalled(const std::filesystem::path& game_root);
|
||||||
|
void ShowRexglueIsoInstallWizard(rex::ui::ImGuiDrawer* drawer, rex::PathConfig runtime_paths,
|
||||||
|
std::function<void(rex::PathConfig)> complete);
|
||||||
bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context,
|
bool RunRexglueIsoInstallWizardBlocking(rex::ui::WindowedAppContext& app_context,
|
||||||
rex::ui::Window* window,
|
rex::ui::Window* window,
|
||||||
rex::ui::ImGuiDrawer* drawer,
|
rex::ui::ImGuiDrawer* drawer,
|
||||||
|
|
|
||||||
53
src/skate3_iso_installer_macos.mm
Normal file
53
src/skate3_iso_installer_macos.mm
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
|
||||||
|
|
||||||
|
namespace skate3 {
|
||||||
|
|
||||||
|
std::filesystem::path PickIsoFileMacOS() {
|
||||||
|
@autoreleasepool {
|
||||||
|
SDL_Window* sdl_window = SDL_GetKeyboardFocus();
|
||||||
|
NSWindow* ns_window = nil;
|
||||||
|
|
||||||
|
if (sdl_window) {
|
||||||
|
SDL_PropertiesID properties = SDL_GetWindowProperties(sdl_window);
|
||||||
|
ns_window = static_cast<NSWindow*>(
|
||||||
|
SDL_GetPointerProperty(properties, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr));
|
||||||
|
SDL_RaiseWindow(sdl_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
[NSApp activateIgnoringOtherApps:YES];
|
||||||
|
if (ns_window) {
|
||||||
|
[ns_window makeKeyAndOrderFront:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
||||||
|
[panel setTitle:@"Select Skate 3 Xbox 360 ISO"];
|
||||||
|
[panel setPrompt:@"Select ISO"];
|
||||||
|
[panel setCanChooseFiles:YES];
|
||||||
|
[panel setCanChooseDirectories:NO];
|
||||||
|
[panel setAllowsMultipleSelection:NO];
|
||||||
|
[panel setResolvesAliases:YES];
|
||||||
|
UTType* iso_type = [UTType typeWithFilenameExtension:@"iso"];
|
||||||
|
if (iso_type) {
|
||||||
|
[panel setAllowedContentTypes:@[ iso_type ]];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSInteger response = [panel runModal];
|
||||||
|
std::filesystem::path result;
|
||||||
|
if (response == NSModalResponseOK) {
|
||||||
|
NSURL* url = [[panel URLs] firstObject];
|
||||||
|
if (url && [url isFileURL]) {
|
||||||
|
result = std::string([[url path] UTF8String]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace skate3
|
||||||
2
third_party/rexglue-sdk
vendored
2
third_party/rexglue-sdk
vendored
|
|
@ -1 +1 @@
|
||||||
Subproject commit 028c1ada911f02d11d08a37ca18e9e99a1f5502e
|
Subproject commit 3184592404a70a4c1fcd07c8dd6f2494c4bd8b4f
|
||||||
Loading…
Add table
Reference in a new issue