mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-09-06 04:13:15 +02:00
Removed use of std::exit and changed recomp runtime to exit normally, added helpers for getting recompiled args and returning values, added example of patch code calling native code
This commit is contained in:
parent
398988a961
commit
8188aee2c1
46
include/recomp_helpers.h
Normal file
46
include/recomp_helpers.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef __RECOMP_HELPERS__
|
||||
#define __RECOMP_HELPERS__
|
||||
|
||||
#include "recomp.h"
|
||||
|
||||
template<int index, typename T>
|
||||
T _arg(uint8_t* rdram, recomp_context* ctx) {
|
||||
static_assert(index < 4, "Only args 0 through 3 supported");
|
||||
gpr raw_arg = (&ctx->r4)[index];
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
if constexpr (index < 2) {
|
||||
static_assert(index != 1, "Floats in arg 1 not supported");
|
||||
return ctx->f12.fl;
|
||||
}
|
||||
else {
|
||||
return std::bit_cast<T>(raw_arg);
|
||||
}
|
||||
}
|
||||
else if constexpr (std::is_pointer_v<T>) {
|
||||
static_assert (!std::is_pointer_v<std::remove_pointer_t<T>>, "Double pointers not supported");
|
||||
return TO_PTR(std::remove_pointer_t<T>, raw_arg);
|
||||
}
|
||||
else if constexpr (std::is_integral_v<T>) {
|
||||
static_assert(sizeof(T) <= 4, "64-bit args not supported");
|
||||
return static_cast<T>(raw_arg);
|
||||
}
|
||||
else {
|
||||
// static_assert in else workaround
|
||||
[] <bool flag = false>() {
|
||||
static_assert(flag, "Unsupported type");
|
||||
}();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void _return(recomp_context* ctx, T val) {
|
||||
static_assert(sizeof(T) <= 4 && "Only 32-bit value returns supported currently");
|
||||
if (std::is_same_v<T, float>) {
|
||||
ctx->f0.fl = val;
|
||||
}
|
||||
else if (std::is_integral_v<T> && sizeof(T) <= 4) {
|
||||
ctx->r2 = int32_t(val);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -21,5 +21,6 @@ enum class Menu {
|
||||
};
|
||||
|
||||
void set_current_menu(Menu menu);
|
||||
void destroy_ui();
|
||||
|
||||
#endif
|
||||
|
@ -76,6 +76,7 @@ DLLIMPORT void ProcessRDPList(void);
|
||||
DLLIMPORT void ProcessDList(void);
|
||||
DLLIMPORT void UpdateScreen(void);
|
||||
DLLIMPORT void ChangeWindow(void);
|
||||
DLLIMPORT void PluginShutdown(void);
|
||||
|
||||
void set_rt64_hooks();
|
||||
|
||||
|
@ -5,7 +5,7 @@ LD := ld.lld
|
||||
OBJCOPY := llvm-objcopy
|
||||
|
||||
CFLAGS := -target mips -mips2 -mabi=32 -O2 -mno-odd-spreg -fomit-frame-pointer -G0 -Wall -Wextra -Wno-incompatible-library-redeclaration -Wno-unused-parameter -Wno-unknown-pragmas -Wno-unused-variable
|
||||
CPPFLAGS := -nostdinc -D_LANGUAGE_C -I ../../mm/include -I ../../mm/src -I ../../mm/build -I ../../mm/assets
|
||||
CPPFLAGS := -nostdinc -D_LANGUAGE_C -DMIPS -I ../../mm/include -I ../../mm/src -I ../../mm/build -I ../../mm/assets
|
||||
LDFLAGS := -nostdlib -T patches.ld -T syms.ld
|
||||
BINFLAGS := -O binary
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#define Audio_PlaySfx play_sound
|
||||
#include "global.h"
|
||||
#include "patches.h"
|
||||
|
||||
// Infinite magic
|
||||
s32 Magic_Consume(PlayState* play, s16 magicToConsume, s16 type) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "global.h"
|
||||
#include "patches.h"
|
||||
|
||||
// Disable frustum culling for actors, but leave distance culling intact
|
||||
s32 func_800BA2FC(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW) {
|
||||
|
24
patches/input.c
Normal file
24
patches/input.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include "patches.h"
|
||||
#include "input.h"
|
||||
|
||||
u32 sPlayerItemButtons[] = {
|
||||
BTN_B,
|
||||
BTN_CLEFT,
|
||||
BTN_CDOWN,
|
||||
BTN_CRIGHT,
|
||||
};
|
||||
|
||||
// Return currently-pressed button, in order of priority B, CLEFT, CDOWN, CRIGHT.
|
||||
EquipSlot func_8082FDC4(void) {
|
||||
EquipSlot i;
|
||||
RecompInputs cur_inputs;
|
||||
recomp_get_item_inputs(&cur_inputs);
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(sPlayerItemButtons); i++) {
|
||||
if (CHECK_BTN_ALL(cur_inputs.buttons, sPlayerItemButtons[i])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
35
patches/input.h
Normal file
35
patches/input.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __INPUT_H__
|
||||
#define __INPUT_H__
|
||||
|
||||
#ifdef MIPS
|
||||
#include "ultra64.h"
|
||||
#else
|
||||
#include "recomp.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct RecompInputs {
|
||||
u32 buttons;
|
||||
float x;
|
||||
float y;
|
||||
} RecompInputs;
|
||||
|
||||
|
||||
#ifdef MIPS
|
||||
# define DECLARE_FUNC(type, name, ...) \
|
||||
type name(__VA_ARGS__);
|
||||
#else
|
||||
# define DECLARE_FUNC(type, name, ...) \
|
||||
void name(uint8_t* rdram, recomp_context* ctx);
|
||||
#endif
|
||||
|
||||
DECLARE_FUNC(void, recomp_get_item_inputs, RecompInputs* inputs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
6
patches/patches.h
Normal file
6
patches/patches.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef __PATCHES_H__
|
||||
#define __PATCHES_H__
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#endif
|
@ -1,7 +1,13 @@
|
||||
__start = 0x80000000;
|
||||
|
||||
/* Dummy addresses that get recompiled into function calls */
|
||||
recomp_get_item_inputs = 0x81000000;
|
||||
|
||||
/* TODO pull these symbols from the elf file directly */
|
||||
Player_PostLimbDrawGameplay = 0x80128BD0;
|
||||
Player_DrawImpl = 0x801246F4;
|
||||
gRegEditor = 0x801f3f60;
|
||||
play_sound = 0x8019f0c8;
|
||||
Audio_PlaySfx = 0x8019f0c8;
|
||||
gSaveContext = 0x801ef670;
|
||||
Interface_SetHudVisibility = 0x8010ef68;
|
||||
Player_GetItemOnButton = 0x8012364C;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "ultra64.h"
|
||||
#include "multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
#include "recomp_ui.h"
|
||||
#include "rsp.h"
|
||||
|
||||
struct SpTaskAction {
|
||||
@ -42,17 +43,14 @@ static struct {
|
||||
OSMesg msg = (OSMesg)0;
|
||||
} sp;
|
||||
struct {
|
||||
std::thread thread;
|
||||
PTR(OSMesgQueue) mq = NULLPTR;
|
||||
OSMesg msg = (OSMesg)0;
|
||||
} dp;
|
||||
struct {
|
||||
std::thread thread;
|
||||
PTR(OSMesgQueue) mq = NULLPTR;
|
||||
OSMesg msg = (OSMesg)0;
|
||||
} ai;
|
||||
struct {
|
||||
std::thread thread;
|
||||
PTR(OSMesgQueue) mq = NULLPTR;
|
||||
OSMesg msg = (OSMesg)0;
|
||||
} si;
|
||||
@ -95,6 +93,9 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
||||
|
||||
uint64_t total_vis = 0;
|
||||
|
||||
|
||||
extern std::atomic_bool exited;
|
||||
|
||||
void set_dummy_vi();
|
||||
|
||||
void vi_thread_func() {
|
||||
@ -106,7 +107,7 @@ void vi_thread_func() {
|
||||
|
||||
int remaining_retraces = events_context.vi.retrace_count;
|
||||
|
||||
while (true) {
|
||||
while (!exited) {
|
||||
// Determine the next VI time (more accurate than adding 16ms each VI interrupt)
|
||||
auto next = Multilibultra::get_start() + (total_vis * 1000000us) / (60 * Multilibultra::get_speed_multiplier());
|
||||
//if (next > std::chrono::system_clock::now()) {
|
||||
@ -177,6 +178,7 @@ void RT64Init(uint8_t* rom, uint8_t* rdram, Multilibultra::WindowHandle window_h
|
||||
void RT64SendDL(uint8_t* rdram, const OSTask* task);
|
||||
void RT64UpdateScreen(uint32_t vi_origin);
|
||||
void RT64ChangeWindow();
|
||||
void RT64Shutdown();
|
||||
|
||||
uint8_t dmem[0x1000];
|
||||
uint16_t rspReciprocals[512];
|
||||
@ -225,10 +227,14 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
|
||||
thread_ready->test_and_set();
|
||||
thread_ready->notify_all();
|
||||
|
||||
while (1) {
|
||||
while (true) {
|
||||
// Wait until an RSP task has been sent
|
||||
events_context.sp_task.wait(nullptr);
|
||||
|
||||
if (exited) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the task pointer and clear the pending RSP task
|
||||
OSTask* task = events_context.sp_task;
|
||||
events_context.sp_task.store(nullptr);
|
||||
@ -265,7 +271,7 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
||||
thread_ready->test_and_set();
|
||||
thread_ready->notify_all();
|
||||
|
||||
while (true) {
|
||||
while (!exited) {
|
||||
// Try to pull an action from the queue
|
||||
Action action;
|
||||
if (events_context.action_queue.wait_dequeue_timed(action, 1ms)) {
|
||||
@ -284,6 +290,9 @@ void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_read
|
||||
}
|
||||
}
|
||||
}
|
||||
destroy_ui();
|
||||
// TODO restore this call once the RT64 shutdown issue is fixed.
|
||||
// RT64Shutdown();
|
||||
}
|
||||
|
||||
extern unsigned int VI_STATUS_REG;
|
||||
@ -469,3 +478,21 @@ void Multilibultra::init_events(uint8_t* rdram, uint8_t* rom, Multilibultra::Win
|
||||
|
||||
events_context.vi.thread = std::thread{ vi_thread_func };
|
||||
}
|
||||
|
||||
void Multilibultra::join_event_threads() {
|
||||
events_context.sp.gfx_thread.join();
|
||||
events_context.vi.thread.join();
|
||||
|
||||
// Send a dummy RSP task so that the task thread is able to exit it's atomic wait and terminate.
|
||||
OSTask dummy_task{};
|
||||
OSTask* expected = nullptr;
|
||||
|
||||
// Attempt to exchange the task with the dummy task one until it was nullptr, as that indicates the
|
||||
// task thread was ready for a new task.
|
||||
do {
|
||||
expected = nullptr;
|
||||
} while (!events_context.sp_task.compare_exchange_weak(expected, &dummy_task));
|
||||
events_context.sp_task.notify_all();
|
||||
|
||||
events_context.sp.task_thread.join();
|
||||
}
|
||||
|
@ -125,6 +125,8 @@ struct gfx_callbacks_t {
|
||||
void start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks);
|
||||
void start_game(int game);
|
||||
bool is_game_started();
|
||||
void quit();
|
||||
void join_event_threads();
|
||||
|
||||
} // namespace Multilibultra
|
||||
|
||||
|
@ -144,6 +144,8 @@ void swap_running_thread(thread_queue_t& running_thread_queue, OSThread*& cur_ru
|
||||
}
|
||||
}
|
||||
|
||||
extern std::atomic_bool exited;
|
||||
|
||||
void scheduler_func() {
|
||||
thread_queue_t running_thread_queue{};
|
||||
OSThread* cur_running_thread = nullptr;
|
||||
@ -169,8 +171,13 @@ void scheduler_func() {
|
||||
// Handle threads that have changed priority
|
||||
handle_thread_reprioritization(running_thread_queue);
|
||||
|
||||
// Determine which thread to run, stopping the current running thread if necessary
|
||||
swap_running_thread(running_thread_queue, cur_running_thread);
|
||||
if (!exited) {
|
||||
// Determine which thread to run, stopping the current running thread if necessary
|
||||
swap_running_thread(running_thread_queue, cur_running_thread);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
std::this_thread::yield();
|
||||
if (old_running_thread != cur_running_thread && old_running_thread && cur_running_thread) {
|
||||
|
@ -161,12 +161,6 @@ extern "C" void osStartThread(RDRAM_ARG PTR(OSThread) t_) {
|
||||
} else {
|
||||
Multilibultra::schedule_running_thread(t);
|
||||
}
|
||||
|
||||
// The main thread "becomes" the first thread started, so join on it and exit after it completes.
|
||||
if (is_main_thread) {
|
||||
t->context->host_thread.join();
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void osCreateThread(RDRAM_ARG PTR(OSThread) t_, OSId id, PTR(thread_func_t) entrypoint, PTR(void) arg, PTR(void) sp, OSPri pri) {
|
||||
|
@ -121,7 +121,8 @@ void timer_thread(RDRAM_ARG1) {
|
||||
active_timers.insert(cur_timer_);
|
||||
// Process the new action
|
||||
process_timer_action(cur_action);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Waiting for the timer completed, so send the timer's message to its message queue
|
||||
osSendMesg(PASS_RDRAM cur_timer->mq, cur_timer->msg, OS_MESG_NOBLOCK);
|
||||
// If the timer has a specified interval then reload it with that value
|
||||
@ -135,6 +136,7 @@ void timer_thread(RDRAM_ARG1) {
|
||||
|
||||
void Multilibultra::init_timers(RDRAM_ARG1) {
|
||||
timer_context.thread = std::thread{ timer_thread, PASS_RDRAM1 };
|
||||
timer_context.thread.detach();
|
||||
}
|
||||
|
||||
uint32_t Multilibultra::get_speed_multiplier() {
|
||||
|
36
src/cont.cpp
36
src/cont.cpp
@ -1,5 +1,5 @@
|
||||
#include "../portultra/multilibultra.hpp"
|
||||
#include "recomp.h"
|
||||
#include "recomp_helpers.h"
|
||||
|
||||
static Multilibultra::input_callbacks_t input_callbacks;
|
||||
|
||||
@ -10,8 +10,8 @@ void set_input_callbacks(const Multilibultra::input_callbacks_t& callbacks) {
|
||||
static int max_controllers = 0;
|
||||
|
||||
extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
gpr bitpattern = ctx->r5;
|
||||
gpr status = ctx->r6;
|
||||
PTR(void) bitpattern = _arg<1, PTR(void)>(rdram, ctx);
|
||||
PTR(void) status = _arg<2, PTR(void)>(rdram, ctx);
|
||||
|
||||
// Set bit 0 to indicate that controller 0 is present
|
||||
MEM_B(0, bitpattern) = 0x01;
|
||||
@ -29,22 +29,15 @@ extern "C" void osContInit_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
MEM_B(4 * controller + 3, status) = 0x80 >> 4; // errno: CONT_NO_RESPONSE_ERROR >> 4
|
||||
}
|
||||
|
||||
ctx->r2 = 0;
|
||||
_return<s32>(ctx, 0);
|
||||
}
|
||||
|
||||
extern "C" void osContStartReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
Multilibultra::send_si_message();
|
||||
}
|
||||
|
||||
struct OSContPad {
|
||||
u16 button;
|
||||
s8 stick_x; /* -80 <= stick_x <= 80 */
|
||||
s8 stick_y; /* -80 <= stick_y <= 80 */
|
||||
u8 errno_;
|
||||
};
|
||||
|
||||
extern "C" void osContGetReadData_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
int32_t pad = (int32_t)ctx->r4;
|
||||
PTR(void) pad = _arg<0, PTR(void)>(rdram, ctx);
|
||||
|
||||
uint16_t buttons = 0;
|
||||
float x = 0.0f;
|
||||
@ -74,7 +67,7 @@ extern "C" void osContStartQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
}
|
||||
|
||||
extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
gpr status = ctx->r4;
|
||||
PTR(void) status = _arg<0, PTR(void)>(rdram, ctx);
|
||||
|
||||
// Mark controller 0 as present
|
||||
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
|
||||
@ -89,8 +82,8 @@ extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
|
||||
}
|
||||
|
||||
extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
max_controllers = std::min((unsigned int)ctx->r4, 4u);
|
||||
ctx->r2 = 0;
|
||||
max_controllers = std::min(_arg<0, u8>(rdram, ctx), u8(4));
|
||||
_return<s32>(ctx, 0);
|
||||
}
|
||||
|
||||
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
@ -108,3 +101,16 @@ extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
|
||||
;
|
||||
}
|
||||
|
||||
#include "../patches/input.h"
|
||||
|
||||
extern "C" void recomp_get_item_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
RecompInputs* inputs = _arg<0, RecompInputs*>(rdram, ctx);
|
||||
|
||||
if (input_callbacks.get_input) {
|
||||
u16 buttons;
|
||||
input_callbacks.get_input(&buttons, &inputs->x, &inputs->y);
|
||||
// TODO remap the inputs for items here
|
||||
inputs->buttons = buttons;
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ std::vector<GameControllerButtonMapping> controller_button_map{
|
||||
|
||||
std::vector<SDL_JoystickID> controllers{};
|
||||
|
||||
int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
switch (event->type) {
|
||||
//case SDL_EventType::SDL_KEYUP:
|
||||
//case SDL_EventType::SDL_KEYDOWN:
|
||||
@ -138,13 +138,13 @@ int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_QUIT:
|
||||
std::quick_exit(EXIT_SUCCESS);
|
||||
break;
|
||||
Multilibultra::quit();
|
||||
return true;
|
||||
default:
|
||||
queue_event(*event);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
return false;
|
||||
}
|
||||
|
||||
Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
@ -185,8 +185,9 @@ void update_gfx(void*) {
|
||||
constexpr int max_events_per_frame = 16;
|
||||
SDL_Event cur_event;
|
||||
int i = 0;
|
||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event)) {
|
||||
sdl_event_filter(nullptr, &cur_event);
|
||||
static bool exited = false;
|
||||
while (i++ < max_events_per_frame && SDL_PollEvent(&cur_event) && !exited) {
|
||||
exited = sdl_event_filter(nullptr, &cur_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,28 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size);
|
||||
std::unique_ptr<uint8_t[]> rdram_buffer;
|
||||
recomp_context context{};
|
||||
|
||||
void read_patch_data(uint8_t* rdram, gpr patch_data_address) {
|
||||
const char patches_data_file_path[] = "patches/patches.bin";
|
||||
std::ifstream patches_data_file{ patches_data_file_path, std::ios::binary };
|
||||
|
||||
if (!patches_data_file) {
|
||||
fprintf(stderr, "Failed to open patches data file: %s\n", patches_data_file_path);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
patches_data_file.seekg(0, std::ios::end);
|
||||
size_t patches_data_size = patches_data_file.tellg();
|
||||
patches_data_file.seekg(0, std::ios::beg);
|
||||
|
||||
std::unique_ptr<uint8_t[]> patches_data = std::make_unique<uint8_t[]>(patches_data_size);
|
||||
|
||||
patches_data_file.read(reinterpret_cast<char*>(patches_data.get()), patches_data_size);
|
||||
|
||||
for (size_t i = 0; i < patches_data_size; i++) {
|
||||
MEM_B(i, patch_data_address) = patches_data[i];
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT extern "C" void init() {
|
||||
{
|
||||
std::ifstream rom_file{ get_rom_name(), std::ios::binary };
|
||||
@ -160,6 +182,9 @@ EXPORT extern "C" void init() {
|
||||
// Initial 1MB DMA (rom address 0x1000 = physical address 0x10001000)
|
||||
do_rom_read(rdram_buffer.get(), entrypoint, 0x10001000, 0x100000);
|
||||
|
||||
// Read in any extra data from patches
|
||||
read_patch_data(rdram_buffer.get(), (gpr)(s32)0x80800100);
|
||||
|
||||
// Set up stack pointer
|
||||
context.r29 = 0xFFFFFFFF803FFFF0u;
|
||||
|
||||
@ -197,6 +222,15 @@ bool Multilibultra::is_game_started() {
|
||||
void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks);
|
||||
void set_input_callbacks(const Multilibultra::input_callbacks_t& callback);
|
||||
|
||||
std::atomic_bool exited = false;
|
||||
|
||||
void Multilibultra::quit() {
|
||||
exited.store(true);
|
||||
int desired = -1;
|
||||
game_started.compare_exchange_strong(desired, -2);
|
||||
game_started.notify_all();
|
||||
}
|
||||
|
||||
void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks_) {
|
||||
set_audio_callbacks(audio_callbacks);
|
||||
set_input_callbacks(input_callbacks);
|
||||
@ -231,16 +265,20 @@ void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& a
|
||||
case 0:
|
||||
recomp_entrypoint(rdram_buffer.get(), &context);
|
||||
break;
|
||||
case -2:
|
||||
break;
|
||||
}
|
||||
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
}, window_handle};
|
||||
|
||||
while (true) {
|
||||
while (!exited) {
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
if (gfx_callbacks.update_gfx != nullptr) {
|
||||
gfx_callbacks.update_gfx(gfx_data);
|
||||
}
|
||||
}
|
||||
game_thread.join();
|
||||
Multilibultra::join_event_threads();
|
||||
}
|
||||
|
@ -130,3 +130,7 @@ void RT64UpdateScreen(uint32_t vi_origin) {
|
||||
void RT64ChangeWindow() {
|
||||
ChangeWindow();
|
||||
}
|
||||
|
||||
void RT64Shutdown() {
|
||||
PluginShutdown();
|
||||
}
|
||||
|
@ -587,6 +587,10 @@ struct {
|
||||
Rml::Context* context;
|
||||
std::unique_ptr<Rml::EventListenerInstancer> event_listener_instancer;
|
||||
|
||||
void unload() {
|
||||
render_interface.reset();
|
||||
}
|
||||
|
||||
void swap_document(Menu menu) {
|
||||
if (current_document != nullptr) {
|
||||
current_document->Hide();
|
||||
@ -752,3 +756,8 @@ void set_rt64_hooks() {
|
||||
void set_current_menu(Menu menu) {
|
||||
open_menu.store(menu);
|
||||
}
|
||||
|
||||
void destroy_ui() {
|
||||
Rml::Shutdown();
|
||||
UIContext.rml.unload();
|
||||
}
|
||||
|
30
us.rev1.toml
30
us.rev1.toml
@ -10,10 +10,15 @@ relocatable_sections_path = "overlays.us.rev1.txt"
|
||||
[patches]
|
||||
stubs = [
|
||||
# Stub out unused functions that directly manipulate RCP status.
|
||||
"func_80084940",
|
||||
"func_80084968",
|
||||
"RcpUtils_PrintRegisterStatus",
|
||||
"RcpUtils_Reset",
|
||||
# Stub out an unnecessary function that accesses kseg1 addresses.
|
||||
"func_800818F4"
|
||||
"CIC6105_Init"
|
||||
]
|
||||
|
||||
ignored = [
|
||||
# Not actually a function
|
||||
"D_80186028"
|
||||
]
|
||||
|
||||
# Hooks
|
||||
@ -25,23 +30,14 @@ args = ["u32", "u32", "u32"]
|
||||
|
||||
# Function hooks for overlay loading.
|
||||
[[patches.hook]]
|
||||
func = "Idle_InitCodeAndMemory"
|
||||
calls = "load_overlays"
|
||||
args = ["a2", "a1", "a3"]
|
||||
func = "Main_Init"
|
||||
text = " load_overlays((uint32_t)ctx->r6, (uint32_t)ctx->r5, (uint32_t)ctx->r7);"
|
||||
after_vram = 0x800802A4
|
||||
|
||||
[[patches.hook]]
|
||||
func = "Load2_LoadOverlay"
|
||||
calls = "load_overlays"
|
||||
# args = [
|
||||
# "a0", # $a0 contains rom start
|
||||
# {operation = "load", type = "u32", base = "sp", offset = 0x10}, # sp + 10 contains the ram address
|
||||
# {operation = "subtract", arguments = ["a1", "a0"]} # Calculate size from rom end - rom start
|
||||
# ]
|
||||
args = ["a1", "a0", "a2"]
|
||||
# This vram address is an instruction in a delay slot. In that case, the recompiler will emit the
|
||||
# hook call after this instruction is run and before the function is called.
|
||||
after_vram = 0x80085048
|
||||
func = "Overlay_Load"
|
||||
text = " load_overlays((uint32_t)ctx->r4, MEM_W(0x10, ctx->r29), (uint32_t)(ctx->r5 - ctx->r4));"
|
||||
# No after_vram means this will be placed at the start of the function
|
||||
|
||||
# Single-instruction patches
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user