mirror of
				https://github.com/Zelda64Recomp/Zelda64Recomp.git
				synced 2025-10-30 08:03:03 +00: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
					
				
					 21 changed files with 264 additions and 60 deletions
				
			
		
							
								
								
									
										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(); | ||||
|  | @ -751,4 +755,9 @@ 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…
	
	Add table
		
		Reference in a new issue
	
	 Mr-Wiseguy
						Mr-Wiseguy