diff --git a/librecomp/CMakeLists.txt b/librecomp/CMakeLists.txt index 596e588..78c5c5d 100644 --- a/librecomp/CMakeLists.txt +++ b/librecomp/CMakeLists.txt @@ -1,14 +1,6 @@ cmake_minimum_required(VERSION 3.20) project(librecomp) -# Check for headers -include(CheckIncludeFile) -check_include_file("malloc.h" HAVE_MALLOC_H) - -if(HAVE_MALLOC_H) - add_compile_definitions(HAVE_MALLOC_H) -endif() - # Define the library add_library(librecomp STATIC "${CMAKE_CURRENT_SOURCE_DIR}/src/ai.cpp" diff --git a/librecomp/include/recomp.h b/librecomp/include/recomp.h index 87bd054..2f55a04 100644 --- a/librecomp/include/recomp.h +++ b/librecomp/include/recomp.h @@ -5,11 +5,6 @@ #include #include #include -#include - -#ifdef HAVE_MALLOC_H -#include -#endif typedef uint64_t gpr; diff --git a/librecomp/include/recomp_game.h b/librecomp/include/recomp_game.h index 788a7fd..a8d5b6c 100644 --- a/librecomp/include/recomp_game.h +++ b/librecomp/include/recomp_game.h @@ -16,7 +16,8 @@ namespace recomp { std::span cache_data; bool is_enabled; - void (*entrypoint)(); + gpr entrypoint_address; + void (*entrypoint)(uint8_t* rdram, recomp_context* context); std::u8string stored_filename() const; }; @@ -29,6 +30,7 @@ namespace recomp { IncorrectVersion, OtherError }; + void register_config_path(std::filesystem::path path); bool register_game(const recomp::GameEntry& entry); void register_patch(const char* patch, std::size_t size); void check_all_stored_roms(); @@ -41,12 +43,7 @@ namespace recomp { void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr); void start(ultramodern::WindowHandle window_handle, const recomp::rsp::callbacks_t& rsp_callbacks, const ultramodern::audio_callbacks_t& audio_callbacks, const ultramodern::input_callbacks_t& input_callbacks, const ultramodern::gfx_callbacks_t& gfx_callbacks, const ultramodern::events::callbacks_t& thread_callbacks, const ultramodern::error_handling::callbacks_t& error_handling_callbacks_); void start_game(const std::u8string& game_id); - std::filesystem::path get_app_folder_path(); std::u8string current_game_id(); - - // TODO: implement both - const std::u8string& get_program_id(); - void set_program_id(const std::u8string& program_id); } #endif diff --git a/librecomp/include/recomp_input.h b/librecomp/include/recomp_input.h deleted file mode 100644 index 85d9df7..0000000 --- a/librecomp/include/recomp_input.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef __RECOMP_INPUT_H__ -#define __RECOMP_INPUT_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include "json/json.hpp" - -namespace recomp { - // x-macros to build input enums and arrays. - // First parameter is the enum name, second parameter is the bit field for the input (or 0 if there is no associated one), third is the readable name. - // TODO refactor this to allow projects to rename these, or get rid of the readable name and leave that up to individual projects to map. - #define DEFINE_N64_BUTTON_INPUTS() \ - DEFINE_INPUT(A, 0x8000, "Action") \ - DEFINE_INPUT(B, 0x4000, "Attack/Cancel") \ - DEFINE_INPUT(Z, 0x2000, "Target") \ - DEFINE_INPUT(START, 0x1000, "Start") \ - DEFINE_INPUT(L, 0x0020, "Toggle map") \ - DEFINE_INPUT(R, 0x0010, "Shield") \ - DEFINE_INPUT(C_UP, 0x0008, "Look/Fairy") \ - DEFINE_INPUT(C_LEFT, 0x0002, "Item 1") \ - DEFINE_INPUT(C_DOWN, 0x0004, "Item 2") \ - DEFINE_INPUT(C_RIGHT, 0x0001, "Item 3") \ - DEFINE_INPUT(DPAD_UP, 0x0800, "Special Item 1") \ - DEFINE_INPUT(DPAD_RIGHT, 0x0100, "Special Item 2") \ - DEFINE_INPUT(DPAD_DOWN, 0x0400, "Special Item 3") \ - DEFINE_INPUT(DPAD_LEFT, 0x0200, "Special Item 4") - - #define DEFINE_N64_AXIS_INPUTS() \ - DEFINE_INPUT(Y_AXIS_POS, 0, "Up") \ - DEFINE_INPUT(Y_AXIS_NEG, 0, "Down") \ - DEFINE_INPUT(X_AXIS_NEG, 0, "Left") \ - DEFINE_INPUT(X_AXIS_POS, 0, "Right") \ - - #define DEFINE_ALL_INPUTS() \ - DEFINE_N64_BUTTON_INPUTS() \ - DEFINE_N64_AXIS_INPUTS() - - // Enum containing every recomp input. - #define DEFINE_INPUT(name, value, readable) name, - enum class GameInput { - DEFINE_ALL_INPUTS() - - COUNT, - N64_BUTTON_START = A, - N64_BUTTON_COUNT = C_RIGHT - N64_BUTTON_START + 1, - N64_AXIS_START = X_AXIS_NEG, - N64_AXIS_COUNT = Y_AXIS_POS - N64_AXIS_START + 1, - }; - #undef DEFINE_INPUT - - struct InputField { - uint32_t input_type; - int32_t input_id; - std::string to_string() const; - auto operator<=>(const InputField& rhs) const = default; - }; - - void poll_inputs(); - float get_input_analog(const InputField& field); - float get_input_analog(const std::span fields); - bool get_input_digital(const InputField& field); - bool get_input_digital(const std::span fields); - void get_gyro_deltas(float* x, float* y); - void get_mouse_deltas(float* x, float* y); - - enum class InputDevice { - Controller, - Keyboard, - COUNT - }; - - void start_scanning_input(InputDevice device); - void stop_scanning_input(); - void finish_scanning_input(InputField scanned_field); - void cancel_scanning_input(); - void config_menu_set_cont_or_kb(bool cont_interacted); - InputField get_scanned_input(); - - struct DefaultN64Mappings { - std::vector a; - std::vector b; - std::vector l; - std::vector r; - std::vector z; - std::vector start; - - std::vector c_left; - std::vector c_right; - std::vector c_up; - std::vector c_down; - - std::vector dpad_left; - std::vector dpad_right; - std::vector dpad_up; - std::vector dpad_down; - - std::vector analog_left; - std::vector analog_right; - std::vector analog_up; - std::vector analog_down; - }; - - extern const DefaultN64Mappings default_n64_keyboard_mappings; - extern const DefaultN64Mappings default_n64_controller_mappings; - - constexpr size_t bindings_per_input = 2; - - size_t get_num_inputs(); - const std::string& get_input_name(GameInput input); - const std::string& get_input_enum_name(GameInput input); - GameInput get_input_from_enum_name(const std::string_view name); - InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device); - void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value); - - void get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out); - void set_rumble(bool); - void update_rumble(); - void handle_events(); - - // Rumble strength ranges from 0 to 100. - int get_rumble_strength(); - void set_rumble_strength(int strength); - - // Gyro and mouse sensitivities range from 0 to 100. - int get_gyro_sensitivity(); - int get_mouse_sensitivity(); - void set_gyro_sensitivity(int strength); - void set_mouse_sensitivity(int strength); - - enum class TargetingMode { - Switch, - Hold, - OptionCount - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::TargetingMode, { - {recomp::TargetingMode::Switch, "Switch"}, - {recomp::TargetingMode::Hold, "Hold"} - }); - - TargetingMode get_targeting_mode(); - void set_targeting_mode(TargetingMode mode); - - enum class BackgroundInputMode { - On, - Off, - OptionCount - }; - - NLOHMANN_JSON_SERIALIZE_ENUM(recomp::BackgroundInputMode, { - {recomp::BackgroundInputMode::On, "On"}, - {recomp::BackgroundInputMode::Off, "Off"} - }); - - BackgroundInputMode get_background_input_mode(); - void set_background_input_mode(BackgroundInputMode mode); - - bool game_input_disabled(); - bool all_input_disabled(); - - // TODO move these somewhere else. - void quicksave_save(); - void quicksave_load(); - - void open_quit_game_prompt(); -} - -#endif diff --git a/librecomp/include/recomp_overlays.h b/librecomp/include/recomp_overlays.h index 1589e30..97c5942 100644 --- a/librecomp/include/recomp_overlays.h +++ b/librecomp/include/recomp_overlays.h @@ -16,8 +16,9 @@ namespace recomp { size_t len; }; - extern void register_overlays(const overlay_section_table_data_t& sections, const overlays_by_index_t& overlays); - extern void register_patch_section(SectionTableEntry* code_sections); + void register_overlays(const overlay_section_table_data_t& sections, const overlays_by_index_t& overlays); + void register_patch_section(SectionTableEntry* code_sections); + void load_patch_functions(); }; extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size); diff --git a/librecomp/include/rsp_vu.h b/librecomp/include/rsp_vu.h index 6e26983..2fbbd8a 100644 --- a/librecomp/include/rsp_vu.h +++ b/librecomp/include/rsp_vu.h @@ -110,8 +110,8 @@ struct RSP { bool divdp; } vpu; - static constexpr r128 zero{0}; - static constexpr r128 invert{(uint64_t)-1, (uint64_t)-1}; + static constexpr r128 zero{{0}}; + static constexpr r128 invert{{(uint64_t)-1, (uint64_t)-1}}; inline auto accumulatorGet(u32 index) const -> u64; inline auto accumulatorSet(u32 index, u64 value) -> void; diff --git a/librecomp/src/overlays.cpp b/librecomp/src/overlays.cpp index a091470..12764cf 100644 --- a/librecomp/src/overlays.cpp +++ b/librecomp/src/overlays.cpp @@ -142,8 +142,6 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) { } } -void load_patch_functions(); - void init_overlays() { section_addresses = (int32_t *)malloc(sections_info.total_num_sections * sizeof(int32_t)); @@ -158,7 +156,7 @@ void init_overlays() { } ); - load_patch_functions(); + recomp::load_patch_functions(); } extern "C" recomp_func_t * get_function(int32_t addr) { diff --git a/librecomp/src/patch_loading.cpp b/librecomp/src/patch_loading.cpp index 0e8effd..e456a51 100644 --- a/librecomp/src/patch_loading.cpp +++ b/librecomp/src/patch_loading.cpp @@ -9,10 +9,10 @@ static SectionTableEntry* code_sections = nullptr; void load_special_overlay(const SectionTableEntry& section, int32_t ram); -void register_patch_section(SectionTableEntry* sections) { +void recomp::register_patch_section(SectionTableEntry* sections) { code_sections = sections; } -void load_patch_functions() { +void recomp::load_patch_functions() { load_special_overlay(code_sections[0], code_sections[0].ram_addr); } diff --git a/librecomp/src/pi.cpp b/librecomp/src/pi.cpp index 405823d..2234f82 100644 --- a/librecomp/src/pi.cpp +++ b/librecomp/src/pi.cpp @@ -94,10 +94,11 @@ struct { } save_context; const std::u8string save_folder = u8"saves"; -const std::u8string save_filename = std::u8string{recomp::current_game_id()} + u8".bin"; + +extern std::filesystem::path config_path; std::filesystem::path get_save_file_path() { - return recomp::get_app_folder_path() / save_folder / save_filename; + return config_path / save_folder / (std::u8string{recomp::current_game_id()} + u8".bin"); } void update_save_file() { diff --git a/librecomp/src/recomp.cpp b/librecomp/src/recomp.cpp index 374859e..41ab597 100644 --- a/librecomp/src/recomp.cpp +++ b/librecomp/src/recomp.cpp @@ -11,12 +11,6 @@ #include #include -#ifdef _WIN32 -#include -#elif defined(__linux__) -#include -#endif - #include "recomp.h" #include "recomp_overlays.h" #include "recomp_game.h" @@ -46,6 +40,7 @@ std::mutex patch_data_mutex; std::mutex current_game_mutex; // Global variables +std::filesystem::path config_path; std::vector patch_data; std::unordered_map game_roms {}; @@ -53,6 +48,10 @@ std::u8string recomp::GameEntry::stored_filename() const { return game_id + u8".z64"; } +void recomp::register_config_path(std::filesystem::path path) { + config_path = path; +} + bool recomp::register_game(const recomp::GameEntry& entry) { std::lock_guard lock(game_roms_mutex); game_roms.insert({ entry.game_id, entry }); @@ -61,6 +60,7 @@ bool recomp::register_game(const recomp::GameEntry& entry) { void recomp::register_patch(const char* patch, std::size_t size) { std::lock_guard lock(patch_data_mutex); + patch_data.resize(size); std::memcpy(patch_data.data(), patch, size); } @@ -97,39 +97,12 @@ bool write_file(const std::filesystem::path& path, const std::vector& d return true; } -std::filesystem::path recomp::get_app_folder_path() { - std::filesystem::path recomp_dir{}; - -#if defined(_WIN32) - // Deduce local app data path. - PWSTR known_path = NULL; - HRESULT result = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &known_path); - if (result == S_OK) { - recomp_dir = std::filesystem::path{known_path} / recomp::get_program_id(); - } - - CoTaskMemFree(known_path); -#elif defined(__linux__) - const char *homedir; - - if ((homedir = getenv("HOME")) == nullptr) { - homedir = getpwuid(getuid())->pw_dir; - } - - if (homedir != nullptr) { - recomp_dir = std::filesystem::path{homedir} / (std::u8string{u8".config/"} + recomp::get_program_id()); - } -#endif - - return recomp_dir; -} - bool check_stored_rom(const recomp::GameEntry& game_entry) { - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / game_entry.stored_filename()); + std::vector stored_rom_data = read_file(config_path / game_entry.stored_filename()); if (!check_hash(stored_rom_data, game_entry.rom_hash)) { // Incorrect hash, remove the stored ROM file if it exists. - std::filesystem::remove(recomp::get_app_folder_path() / game_entry.stored_filename()); + std::filesystem::remove(config_path / game_entry.stored_filename()); return false; } @@ -157,11 +130,11 @@ bool recomp::load_stored_rom(std::u8string& game_id) { return false; } - std::vector stored_rom_data = read_file(recomp::get_app_folder_path() / find_it->second.stored_filename()); + std::vector stored_rom_data = read_file(config_path / find_it->second.stored_filename()); if (!check_hash(stored_rom_data, find_it->second.rom_hash)) { // The ROM no longer has the right hash, delete it. - std::filesystem::remove(recomp::get_app_folder_path() / find_it->second.stored_filename()); + std::filesystem::remove(config_path / find_it->second.stored_filename()); return false; } @@ -272,7 +245,7 @@ recomp::RomValidationError recomp::select_rom(const std::filesystem::path& rom_p } } - write_file(recomp::get_app_folder_path() / game_entry.stored_filename(), rom_data); + write_file(config_path / game_entry.stored_filename(), rom_data); return recomp::RomValidationError::Good; } @@ -346,24 +319,16 @@ void run_thread_function(uint8_t* rdram, uint64_t addr, uint64_t sp, uint64_t ar func(rdram, &ctx); } -// Recomp generation functions -extern "C" void recomp_entrypoint(uint8_t * rdram, recomp_context * ctx); -gpr get_entrypoint_address(); -const char* get_rom_name(); - void read_patch_data(uint8_t* rdram, gpr patch_data_address) { for (size_t i = 0; i < patch_data.size(); i++) { MEM_B(i, patch_data_address) = patch_data[i]; } } -void init(uint8_t* rdram, recomp_context* ctx) { +void init(uint8_t* rdram, recomp_context* ctx, gpr entrypoint) { // Initialize the overlays init_overlays(); - // Get entrypoint from recomp function - gpr entrypoint = get_entrypoint_address(); - // Load overlays in the first 1MB load_overlays(0x1000, (int32_t)entrypoint, 1024 * 1024); @@ -407,6 +372,7 @@ void recomp::start_game(const std::u8string& game_id) { std::lock_guard lock(current_game_mutex); current_game = game_id; game_status.store(GameStatus::Running); + game_status.notify_all(); } bool ultramodern::is_game_started() { @@ -479,13 +445,15 @@ void recomp::start(ultramodern::WindowHandle window_handle, const recomp::rsp::c ultramodern::error_handling::message_box("Error opening stored ROM! Please restart this program."); } + ultramodern::init_saving(rdram); + auto find_it = game_roms.find(current_game.value()); const recomp::GameEntry& game_entry = find_it->second; ultramodern::load_shader_cache(game_entry.cache_data); - init(rdram, &context); + init(rdram, &context, game_entry.entrypoint_address); try { - recomp_entrypoint(rdram, &context); + game_entry.entrypoint(rdram, &context); } catch (ultramodern::thread_terminated& terminated) { } diff --git a/librecomp/src/rsp.cpp b/librecomp/src/rsp.cpp index 9f27bbb..d8df936 100644 --- a/librecomp/src/rsp.cpp +++ b/librecomp/src/rsp.cpp @@ -53,7 +53,7 @@ bool recomp::rsp::run_task(uint8_t* rdram, const OSTask* task) { // Ensure that the ucode exited correctly if (exit_reason != RspExitReason::Broke) { - fprintf(stderr, "RSP ucode %" PRIu32 " exited unexpectedly. exit_reason: %i\n", task->t.type, exit_reason); + fprintf(stderr, "RSP ucode %" PRIu32 " exited unexpectedly. exit_reason: %i\n", task->t.type, static_cast(exit_reason)); assert(exit_reason == RspExitReason::Broke); return false; } diff --git a/ultramodern/include/ultramodern/ultramodern.hpp b/ultramodern/include/ultramodern/ultramodern.hpp index 1c6b8ed..547fba1 100644 --- a/ultramodern/include/ultramodern/ultramodern.hpp +++ b/ultramodern/include/ultramodern/ultramodern.hpp @@ -52,6 +52,12 @@ namespace ultramodern { Window window; auto operator<=>(const WindowHandle&) const = default; }; +#elif defined(__APPLE__) + struct WindowHandle { + void* window; + void* view; + auto operator<=>(const WindowHandle&) const = default; + }; #endif // We need a place in rdram to hold the PI handles, so pick an address in extended rdram diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index be1f3bd..7fba7b6 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -217,7 +217,11 @@ void task_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_r if (!ultramodern::rsp::run_task(PASS_RDRAM task)) { fprintf(stderr, "Failed to execute task type: %" PRIu32 "\n", task->t.type); assert(false); +# ifdef __APPLE__ + std::_Exit(EXIT_FAILURE); +# else std::quick_exit(EXIT_FAILURE); +# endif } // Tell the game that the RSP has completed @@ -508,6 +512,8 @@ std::string get_graphics_api_name(ultramodern::GraphicsApi api) { api = ultramodern::GraphicsApi::D3D12; #elif defined(__gnu_linux__) api = ultramodern::GraphicsApi::Vulkan; +#elif defined(__APPLE__) + api = ultramodern::GraphicsApi::Vulkan; #else static_assert(false && "Unimplemented") #endif diff --git a/ultramodern/src/rt64_layer.cpp b/ultramodern/src/rt64_layer.cpp index e82a07f..45290b1 100644 --- a/ultramodern/src/rt64_layer.cpp +++ b/ultramodern/src/rt64_layer.cpp @@ -129,6 +129,9 @@ ultramodern::RT64Context::RT64Context(uint8_t* rdram, ultramodern::WindowHandle #elif defined(__linux__) appCore.window.display = window_handle.display; appCore.window.window = window_handle.window; +#elif defined(__APPLE__) + appCore.window.window = window_handle.window; + appCore.window.view = window_handle.view; #endif appCore.checkInterrupts = dummy_check_interrupts; diff --git a/ultramodern/src/threads.cpp b/ultramodern/src/threads.cpp index 9b17a52..fd59338 100644 --- a/ultramodern/src/threads.cpp +++ b/ultramodern/src/threads.cpp @@ -111,6 +111,12 @@ void ultramodern::set_native_thread_priority(ThreadPriority pri) { // break; // } } +#elif defined(__APPLE__) +void ultramodern::set_native_thread_name(const std::string& name) { + pthread_setname_np(name.c_str()); +} + +void ultramodern::set_native_thread_priority(ThreadPriority pri) {} #endif std::atomic_int temporary_threads = 0; diff --git a/ultramodern/src/ultrainit.cpp b/ultramodern/src/ultrainit.cpp index 9dd19a2..4fca62a 100644 --- a/ultramodern/src/ultrainit.cpp +++ b/ultramodern/src/ultrainit.cpp @@ -22,7 +22,6 @@ void ultramodern::preinit(RDRAM_ARG ultramodern::WindowHandle window_handle) { ultramodern::init_events(PASS_RDRAM window_handle); ultramodern::init_timers(PASS_RDRAM1); ultramodern::init_audio(); - ultramodern::init_saving(PASS_RDRAM1); ultramodern::init_thread_cleanup(); }