mirror of
				https://github.com/N64Recomp/N64ModernRuntime.git
				synced 2025-10-30 08:02:29 +00:00 
			
		
		
		
	Integrate the live recompiler for mod loading (#76)
This commit is contained in:
		
							parent
							
								
									1361c48f59
								
							
						
					
					
						commit
						d17a3f34cb
					
				
					 12 changed files with 244 additions and 408 deletions
				
			
		|  | @ -1 +1 @@ | |||
| Subproject commit d5ab74220da3d8468d6873ed29afbcad03b890f5 | ||||
| Subproject commit fc696046da3e703450559154d9370ca74c197f8b | ||||
|  | @ -56,5 +56,5 @@ endif() | |||
| add_subdirectory(${PROJECT_SOURCE_DIR}/../thirdparty/miniz ${CMAKE_CURRENT_BINARY_DIR}/miniz) | ||||
| add_subdirectory(${PROJECT_SOURCE_DIR}/../N64Recomp ${CMAKE_CURRENT_BINARY_DIR}/N64Recomp EXCLUDE_FROM_ALL) | ||||
| 
 | ||||
| target_link_libraries(librecomp PRIVATE ultramodern N64Recomp) | ||||
| target_link_libraries(librecomp PRIVATE ultramodern N64Recomp LiveRecomp) | ||||
| target_link_libraries(librecomp PUBLIC miniz) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| 
 | ||||
| #include <cstdint> | ||||
| #include "ultramodern/ultra64.h" | ||||
| #include "librecomp/recomp.h" | ||||
| #include "recomp.h" | ||||
| 
 | ||||
| namespace recomp { | ||||
|     // 512GB (kseg0 size)
 | ||||
|  |  | |||
|  | @ -19,12 +19,13 @@ | |||
| #include "miniz.h" | ||||
| #include "miniz_zip.h" | ||||
| 
 | ||||
| #include "recomp.h" | ||||
| #include "librecomp/game.hpp" | ||||
| #include "librecomp/recomp.h" | ||||
| #include "librecomp/sections.h" | ||||
| 
 | ||||
| namespace N64Recomp { | ||||
|     class Context; | ||||
|     struct LiveGeneratorOutput; | ||||
| }; | ||||
| 
 | ||||
| namespace recomp { | ||||
|  | @ -71,6 +72,7 @@ namespace recomp { | |||
|             FailedToLoadNativeCode, | ||||
|             FailedToLoadNativeLibrary, | ||||
|             FailedToFindNativeExport, | ||||
|             FailedToRecompile, | ||||
|             InvalidReferenceSymbol, | ||||
|             InvalidImport, | ||||
|             InvalidCallbackEvent, | ||||
|  | @ -258,16 +260,8 @@ namespace recomp { | |||
|             virtual bool good() = 0; | ||||
|             virtual uint32_t get_api_version() = 0; | ||||
|             virtual void set_imported_function(size_t import_index, GenericFunction func) = 0; | ||||
|             virtual void set_reference_symbol_pointer(size_t symbol_index, recomp_func_t* ptr) = 0; | ||||
|             virtual void set_base_event_index(uint32_t global_event_index) = 0; | ||||
|             virtual CodeModLoadError populate_reference_symbols(const N64Recomp::Context& recompiler_context, std::string& error_param) = 0; | ||||
|             virtual uint32_t get_base_event_index() = 0; | ||||
|             virtual void set_recomp_trigger_event_pointer(void (*ptr)(uint8_t* rdram, recomp_context* ctx, uint32_t index)) = 0; | ||||
|             virtual void set_get_function_pointer(recomp_func_t* (*ptr)(int32_t)) = 0; | ||||
|             virtual void set_cop0_status_write_pointer(void (*ptr)(recomp_context* ctx, gpr value)) = 0; | ||||
|             virtual void set_cop0_status_read_pointer(gpr (*ptr)(recomp_context* ctx)) = 0; | ||||
|             virtual void set_switch_error_pointer(void (*ptr)(const char* func, uint32_t vram, uint32_t jtbl)) = 0; | ||||
|             virtual void set_do_break_pointer(void (*ptr)(uint32_t vram)) = 0; | ||||
|             virtual void set_reference_section_addresses_pointer(int32_t* ptr) = 0; | ||||
|             virtual void set_local_section_address(size_t section_index, int32_t address) = 0; | ||||
|             virtual GenericFunction get_function_handle(size_t func_index) = 0; | ||||
|         }; | ||||
|  | @ -293,9 +287,9 @@ namespace recomp { | |||
|             size_t num_exports() const; | ||||
|             size_t num_events() const; | ||||
| 
 | ||||
|             CodeModLoadError populate_exports(std::string& error_param); | ||||
|             void populate_exports(); | ||||
|             bool get_export_function(const std::string& export_name, GenericFunction& out) const; | ||||
|             CodeModLoadError populate_events(size_t base_event_index, std::string& error_param); | ||||
|             void populate_events(); | ||||
|             bool get_global_event_index(const std::string& event_name, size_t& event_index_out) const; | ||||
|             CodeModLoadError load_native_library(const NativeLibraryManifest& lib_manifest, std::string& error_param); | ||||
| 
 | ||||
|  | @ -326,43 +320,28 @@ namespace recomp { | |||
|             bool runtime_toggleable; | ||||
|         }; | ||||
|          | ||||
|         class NativeCodeHandle : public ModCodeHandle { | ||||
|         struct ModCodeHandleInputs { | ||||
|             uint32_t base_event_index; | ||||
|             void (*recomp_trigger_event)(uint8_t* rdram, recomp_context* ctx, uint32_t index); | ||||
|             recomp_func_t* (*get_function)(int32_t vram); | ||||
|             void (*cop0_status_write)(recomp_context* ctx, gpr value); | ||||
|             gpr (*cop0_status_read)(recomp_context* ctx); | ||||
|             void (*switch_error)(const char* func, uint32_t vram, uint32_t jtbl); | ||||
|             void (*do_break)(uint32_t vram); | ||||
|             int32_t* reference_section_addresses; | ||||
|         }; | ||||
| 
 | ||||
|         class DynamicLibraryCodeHandle : public ModCodeHandle { | ||||
|         public: | ||||
|             NativeCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context); | ||||
|             ~NativeCodeHandle() = default; | ||||
|             DynamicLibraryCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context, const ModCodeHandleInputs& inputs); | ||||
|             ~DynamicLibraryCodeHandle() = default; | ||||
|             bool good() final; | ||||
|             uint32_t get_api_version() final; | ||||
|             void set_imported_function(size_t import_index, GenericFunction func) final; | ||||
|             void set_reference_symbol_pointer(size_t symbol_index, recomp_func_t* ptr) final { | ||||
|                 reference_symbol_funcs[symbol_index] = ptr; | ||||
|             }; | ||||
|             void set_base_event_index(uint32_t global_event_index) final { | ||||
|                 *base_event_index = global_event_index; | ||||
|             }; | ||||
|             CodeModLoadError populate_reference_symbols(const N64Recomp::Context& context, std::string& error_param) final; | ||||
|             uint32_t get_base_event_index() final { | ||||
|                 return *base_event_index; | ||||
|             } | ||||
|             void set_recomp_trigger_event_pointer(void (*ptr)(uint8_t* rdram, recomp_context* ctx, uint32_t index)) final { | ||||
|                 *recomp_trigger_event = ptr; | ||||
|             }; | ||||
|             void set_get_function_pointer(recomp_func_t* (*ptr)(int32_t)) final { | ||||
|                 *get_function = ptr; | ||||
|             }; | ||||
|             void set_cop0_status_write_pointer(void (*ptr)(recomp_context* ctx, gpr value)) final { | ||||
|                 *cop0_status_write = ptr; | ||||
|             } | ||||
|             void set_cop0_status_read_pointer(gpr (*ptr)(recomp_context* ctx)) final { | ||||
|                 *cop0_status_read = ptr; | ||||
|             } | ||||
|             void set_switch_error_pointer(void (*ptr)(const char* func, uint32_t vram, uint32_t jtbl)) final { | ||||
|                 *switch_error = ptr; | ||||
|             } | ||||
|             void set_do_break_pointer(void (*ptr)(uint32_t vram)) final { | ||||
|                 *do_break = ptr; | ||||
|             } | ||||
|             void set_reference_section_addresses_pointer(int32_t* ptr) final { | ||||
|                 *reference_section_addresses = ptr; | ||||
|             }; | ||||
|             void set_local_section_address(size_t section_index, int32_t address) final { | ||||
|                 section_addresses[section_index] = address; | ||||
|             }; | ||||
|  | @ -387,12 +366,41 @@ namespace recomp { | |||
|             int32_t* section_addresses; | ||||
|         }; | ||||
| 
 | ||||
|         class LiveRecompilerCodeHandle : public ModCodeHandle { | ||||
|         public: | ||||
|             LiveRecompilerCodeHandle(const N64Recomp::Context& context, const ModCodeHandleInputs& inputs); | ||||
| 
 | ||||
|             ~LiveRecompilerCodeHandle() = default; | ||||
|              | ||||
|             // Disable copying.
 | ||||
|             LiveRecompilerCodeHandle(const LiveRecompilerCodeHandle& rhs) = delete; | ||||
|             LiveRecompilerCodeHandle& operator=(const LiveRecompilerCodeHandle& rhs) = delete; | ||||
| 
 | ||||
|             bool good() final { return is_good; } | ||||
|             uint32_t get_api_version() final { return 1; } | ||||
|             void set_imported_function(size_t import_index, GenericFunction func) final; | ||||
|             CodeModLoadError populate_reference_symbols(const N64Recomp::Context& context, std::string& error_param) final; | ||||
|             uint32_t get_base_event_index() final {  | ||||
|                 return base_event_index; | ||||
|             } | ||||
|             void set_local_section_address(size_t section_index, int32_t address) final { | ||||
|                 section_addresses[section_index] = address; | ||||
|             } | ||||
|             GenericFunction get_function_handle(size_t func_index) final; | ||||
|         private: | ||||
|             uint32_t base_event_index; | ||||
|             std::unique_ptr<N64Recomp::LiveGeneratorOutput> recompiler_output; | ||||
|             void set_bad(); | ||||
|             bool is_good = false; | ||||
|             std::unique_ptr<int32_t[]> section_addresses; | ||||
|         }; | ||||
| 
 | ||||
|         void setup_events(size_t num_events); | ||||
|         void register_event_callback(size_t event_index, GenericFunction callback); | ||||
|         void reset_events(); | ||||
|         CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param); | ||||
| 
 | ||||
| 
 | ||||
|         void initialize_mod_recompiler(); | ||||
|         void scan_mods(); | ||||
|         void enable_mod(const std::string& mod_id, bool enabled); | ||||
|         bool is_mod_enabled(const std::string& mod_id); | ||||
|  |  | |||
|  | @ -1,293 +0,0 @@ | |||
| #ifndef __RECOMP_H__ | ||||
| #define __RECOMP_H__ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <stdint.h> | ||||
| #include <math.h> | ||||
| #include <assert.h> | ||||
| 
 | ||||
| // Compiler definition to disable inter-procedural optimization, allowing multiple functions to be in a single file without breaking interposition.
 | ||||
| #if defined(_MSC_VER) && !defined(__clang__) | ||||
|     // MSVC's __declspec(noinline) seems to disable inter-procedural optimization entirely, so it's all that's needed.
 | ||||
|     #define RECOMP_FUNC __declspec(noinline) | ||||
| #elif defined(__clang__) | ||||
|     // Clang has no dedicated IPO attribute, so we use a combination of other attributes to give the desired behavior.
 | ||||
|     // The inline keyword allows multiple definitions during linking, and extern forces clang to emit an externally visible definition.
 | ||||
|     // Weak forces Clang to not perform any IPO as the symbol can be interposed, which prevents actual inlining due to the inline keyword.
 | ||||
|     // Add noinline on for good measure, which doesn't conflict with the inline keyword as they have different meanings.
 | ||||
|     #define RECOMP_FUNC extern inline __attribute__((weak,noinline)) | ||||
| #elif defined(__GNUC__) | ||||
|     // Use GCC's attribute for disabling inter-procedural optimizations.
 | ||||
|     #define RECOMP_FUNC __attribute__((noipa)) | ||||
| #else | ||||
|     #error "No RECOMP_FUNC definition for this compiler" | ||||
| #endif | ||||
| 
 | ||||
| typedef uint64_t gpr; | ||||
| 
 | ||||
| #define SIGNED(val) \ | ||||
|     ((int64_t)(val)) | ||||
| 
 | ||||
| #define ADD32(a, b) \ | ||||
|     ((gpr)(int32_t)((a) + (b))) | ||||
| 
 | ||||
| #define SUB32(a, b) \ | ||||
|     ((gpr)(int32_t)((a) - (b))) | ||||
| 
 | ||||
| #define MEM_W(offset, reg) \ | ||||
|     (*(int32_t*)(rdram + ((((reg) + (offset))) - 0xFFFFFFFF80000000))) | ||||
| 
 | ||||
| #define MEM_H(offset, reg) \ | ||||
|     (*(int16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) | ||||
| 
 | ||||
| #define MEM_B(offset, reg) \ | ||||
|     (*(int8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) | ||||
| 
 | ||||
| #define MEM_HU(offset, reg) \ | ||||
|     (*(uint16_t*)(rdram + ((((reg) + (offset)) ^ 2) - 0xFFFFFFFF80000000))) | ||||
| 
 | ||||
| #define MEM_BU(offset, reg) \ | ||||
|     (*(uint8_t*)(rdram + ((((reg) + (offset)) ^ 3) - 0xFFFFFFFF80000000))) | ||||
| 
 | ||||
| #define SD(val, offset, reg) { \ | ||||
|     *(uint32_t*)(rdram + ((((reg) + (offset) + 4)) - 0xFFFFFFFF80000000)) = (uint32_t)((gpr)(val) >> 0); \ | ||||
|     *(uint32_t*)(rdram + ((((reg) + (offset) + 0)) - 0xFFFFFFFF80000000)) = (uint32_t)((gpr)(val) >> 32); \ | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t load_doubleword(uint8_t* rdram, gpr reg, gpr offset) { | ||||
|     uint64_t ret = 0; | ||||
|     uint64_t lo = (uint64_t)(uint32_t)MEM_W(reg, offset + 4); | ||||
|     uint64_t hi = (uint64_t)(uint32_t)MEM_W(reg, offset + 0); | ||||
|     ret = (lo << 0) | (hi << 32); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| #define LD(offset, reg) \ | ||||
|     load_doubleword(rdram, offset, reg) | ||||
| 
 | ||||
| static inline gpr do_lwl(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { | ||||
|     // Calculate the overall address
 | ||||
|     gpr address = (offset + reg); | ||||
| 
 | ||||
|     // Load the aligned word
 | ||||
|     gpr word_address = address & ~0x3; | ||||
|     uint32_t loaded_value = MEM_W(0, word_address); | ||||
| 
 | ||||
|     // Mask the existing value and shift the loaded value appropriately
 | ||||
|     gpr misalignment = address & 0x3; | ||||
|     gpr masked_value = initial_value & ~(0xFFFFFFFFu << (misalignment * 8)); | ||||
|     loaded_value <<= (misalignment * 8); | ||||
| 
 | ||||
|     // Cast to int32_t to sign extend first
 | ||||
|     return (gpr)(int32_t)(masked_value | loaded_value); | ||||
| } | ||||
| 
 | ||||
| static inline gpr do_lwr(uint8_t* rdram, gpr initial_value, gpr offset, gpr reg) { | ||||
|     // Calculate the overall address
 | ||||
|     gpr address = (offset + reg); | ||||
|      | ||||
|     // Load the aligned word
 | ||||
|     gpr word_address = address & ~0x3; | ||||
|     uint32_t loaded_value = MEM_W(0, word_address); | ||||
| 
 | ||||
|     // Mask the existing value and shift the loaded value appropriately
 | ||||
|     gpr misalignment = address & 0x3; | ||||
|     gpr masked_value = initial_value & ~(0xFFFFFFFFu >> (24 - misalignment * 8)); | ||||
|     loaded_value >>= (24 - misalignment * 8); | ||||
| 
 | ||||
|     // Cast to int32_t to sign extend first
 | ||||
|     return (gpr)(int32_t)(masked_value | loaded_value); | ||||
| } | ||||
| 
 | ||||
| static inline void do_swl(uint8_t* rdram, gpr offset, gpr reg, gpr val) { | ||||
|     // Calculate the overall address
 | ||||
|     gpr address = (offset + reg); | ||||
| 
 | ||||
|     // Get the initial value of the aligned word
 | ||||
|     gpr word_address = address & ~0x3; | ||||
|     uint32_t initial_value = MEM_W(0, word_address); | ||||
| 
 | ||||
|     // Mask the initial value and shift the input value appropriately
 | ||||
|     gpr misalignment = address & 0x3; | ||||
|     uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu >> (misalignment * 8)); | ||||
|     uint32_t shifted_input_value = ((uint32_t)val) >> (misalignment * 8); | ||||
|     MEM_W(0, word_address) = masked_initial_value | shifted_input_value; | ||||
| } | ||||
| 
 | ||||
| static inline void do_swr(uint8_t* rdram, gpr offset, gpr reg, gpr val) { | ||||
|     // Calculate the overall address
 | ||||
|     gpr address = (offset + reg); | ||||
| 
 | ||||
|     // Get the initial value of the aligned word
 | ||||
|     gpr word_address = address & ~0x3; | ||||
|     uint32_t initial_value = MEM_W(0, word_address); | ||||
| 
 | ||||
|     // Mask the initial value and shift the input value appropriately
 | ||||
|     gpr misalignment = address & 0x3; | ||||
|     uint32_t masked_initial_value = initial_value & ~(0xFFFFFFFFu << (24 - misalignment * 8)); | ||||
|     uint32_t shifted_input_value = ((uint32_t)val) << (24 - misalignment * 8); | ||||
|     MEM_W(0, word_address) = masked_initial_value | shifted_input_value; | ||||
| } | ||||
| 
 | ||||
| #define S32(val) \ | ||||
|     ((int32_t)(val)) | ||||
|      | ||||
| #define U32(val) \ | ||||
|     ((uint32_t)(val)) | ||||
| 
 | ||||
| #define S64(val) \ | ||||
|     ((int64_t)(val)) | ||||
| 
 | ||||
| #define U64(val) \ | ||||
|     ((uint64_t)(val)) | ||||
| 
 | ||||
| #define MUL_S(val1, val2) \ | ||||
|     ((val1) * (val2)) | ||||
| 
 | ||||
| #define MUL_D(val1, val2) \ | ||||
|     ((val1) * (val2)) | ||||
| 
 | ||||
| #define DIV_S(val1, val2) \ | ||||
|     ((val1) / (val2)) | ||||
| 
 | ||||
| #define DIV_D(val1, val2) \ | ||||
|     ((val1) / (val2)) | ||||
| 
 | ||||
| #define CVT_S_W(val) \ | ||||
|     ((float)((int32_t)(val))) | ||||
| 
 | ||||
| #define CVT_D_W(val) \ | ||||
|     ((double)((int32_t)(val))) | ||||
| 
 | ||||
| #define CVT_D_S(val) \ | ||||
|     ((double)(val)) | ||||
| 
 | ||||
| #define CVT_S_D(val) \ | ||||
|     ((float)(val)) | ||||
| 
 | ||||
| #define TRUNC_W_S(val) \ | ||||
|     ((int32_t)(val)) | ||||
| 
 | ||||
| #define TRUNC_W_D(val) \ | ||||
|     ((int32_t)(val)) | ||||
| 
 | ||||
| #define TRUNC_L_S(val) \ | ||||
|     ((int64_t)(val)) | ||||
| 
 | ||||
| #define TRUNC_L_D(val) \ | ||||
|     ((int64_t)(val)) | ||||
| 
 | ||||
| #define DEFAULT_ROUNDING_MODE 0 | ||||
| 
 | ||||
| static inline int32_t do_cvt_w_s(float val, unsigned int rounding_mode) { | ||||
|     switch (rounding_mode) { | ||||
|         case 0: // round to nearest value
 | ||||
|             return (int32_t)lroundf(val); | ||||
|         case 1: // round to zero (truncate)
 | ||||
|             return (int32_t)val; | ||||
|         case 2: // round to positive infinity (ceil)
 | ||||
|             return (int32_t)ceilf(val); | ||||
|         case 3: // round to negative infinity (floor)
 | ||||
|             return (int32_t)floorf(val); | ||||
|     } | ||||
|     assert(0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #define CVT_W_S(val) \ | ||||
|     do_cvt_w_s(val, rounding_mode) | ||||
| 
 | ||||
| static inline int32_t do_cvt_w_d(double val, unsigned int rounding_mode) { | ||||
|     switch (rounding_mode) { | ||||
|         case 0: // round to nearest value
 | ||||
|             return (int32_t)lround(val); | ||||
|         case 1: // round to zero (truncate)
 | ||||
|             return (int32_t)val; | ||||
|         case 2: // round to positive infinity (ceil)
 | ||||
|             return (int32_t)ceil(val); | ||||
|         case 3: // round to negative infinity (floor)
 | ||||
|             return (int32_t)floor(val); | ||||
|     } | ||||
|     assert(0); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #define CVT_W_D(val) \ | ||||
|     do_cvt_w_d(val, rounding_mode) | ||||
| 
 | ||||
| #define NAN_CHECK(val) \ | ||||
|     assert(val == val) | ||||
| 
 | ||||
| //#define NAN_CHECK(val)
 | ||||
| 
 | ||||
| typedef union { | ||||
|     double d; | ||||
|     struct { | ||||
|         float fl; | ||||
|         float fh; | ||||
|     }; | ||||
|     struct { | ||||
|         uint32_t u32l; | ||||
|         uint32_t u32h; | ||||
|     }; | ||||
|     uint64_t u64; | ||||
| } fpr; | ||||
| 
 | ||||
| typedef struct { | ||||
|     gpr r0,  r1,  r2,  r3,  r4,  r5,  r6,  r7, | ||||
|         r8,  r9,  r10, r11, r12, r13, r14, r15, | ||||
|         r16, r17, r18, r19, r20, r21, r22, r23, | ||||
|         r24, r25, r26, r27, r28, r29, r30, r31; | ||||
|     fpr f0,  f1,  f2,  f3,  f4,  f5,  f6,  f7, | ||||
|         f8,  f9,  f10, f11, f12, f13, f14, f15, | ||||
|         f16, f17, f18, f19, f20, f21, f22, f23, | ||||
|         f24, f25, f26, f27, f28, f29, f30, f31; | ||||
|     uint64_t hi, lo; | ||||
|     uint32_t* f_odd; | ||||
|     uint32_t status_reg; | ||||
|     uint8_t mips3_float_mode; | ||||
| } recomp_context; | ||||
| 
 | ||||
| // Checks if the target is an even float register or that mips3 float mode is enabled
 | ||||
| #define CHECK_FR(ctx, idx) \ | ||||
|     assert(((idx) & 1) == 0 || (ctx)->mips3_float_mode) | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| void cop0_status_write(recomp_context* ctx, gpr value); | ||||
| gpr cop0_status_read(recomp_context* ctx); | ||||
| void switch_error(const char* func, uint32_t vram, uint32_t jtbl); | ||||
| void do_break(uint32_t vram); | ||||
| 
 | ||||
| typedef void (recomp_func_t)(uint8_t* rdram, recomp_context* ctx); | ||||
| 
 | ||||
| recomp_func_t* get_function(int32_t vram); | ||||
| 
 | ||||
| #define LOOKUP_FUNC(val) \ | ||||
|     get_function((int32_t)(val)) | ||||
| 
 | ||||
| extern int32_t* section_addresses; | ||||
| 
 | ||||
| #define LO16(x) \ | ||||
|     ((x) & 0xFFFF) | ||||
| 
 | ||||
| #define HI16(x) \ | ||||
|     (((x) >> 16) + (((x) >> 15) & 1)) | ||||
| 
 | ||||
| #define RELOC_HI16(section_index, offset) \ | ||||
|     HI16(section_addresses[section_index] + (offset)) | ||||
| 
 | ||||
| #define RELOC_LO16(section_index, offset) \ | ||||
|     LO16(section_addresses[section_index] + (offset)) | ||||
| 
 | ||||
| void recomp_syscall_handler(uint8_t* rdram, recomp_context* ctx, int32_t instruction_vram); | ||||
| 
 | ||||
| void pause_self(uint8_t *rdram); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  | @ -1,4 +1,4 @@ | |||
| #include "librecomp/recomp.h" | ||||
| #include "recomp.h" | ||||
| #include "librecomp/game.hpp" | ||||
| 
 | ||||
| #include "ultramodern/ultra64.h" | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| #include <cassert> | ||||
| #include <ultramodern/ultra64.h> | ||||
| #include <ultramodern/ultramodern.hpp> | ||||
| #include "librecomp/recomp.h" | ||||
| #include "recomp.h" | ||||
| #include "librecomp/addresses.hpp" | ||||
| #include "librecomp/game.hpp" | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| 
 | ||||
| #include "json/json.hpp" | ||||
| 
 | ||||
| #include "n64recomp.h" | ||||
| #include "recompiler/context.h" | ||||
| #include "librecomp/mods.hpp" | ||||
| 
 | ||||
| recomp::mods::ZipModFileHandle::~ZipModFileHandle() { | ||||
|  | @ -571,6 +571,8 @@ std::string recomp::mods::error_to_string(CodeModLoadError error) { | |||
|             return "Failed to load mod library"; | ||||
|         case CodeModLoadError::FailedToFindNativeExport: | ||||
|             return "Failed to find native export"; | ||||
|         case CodeModLoadError::FailedToRecompile: | ||||
|             return "Failed to recompile mod"; | ||||
|         case CodeModLoadError::InvalidReferenceSymbol: | ||||
|             return "Reference symbol does not exist"; | ||||
|         case CodeModLoadError::InvalidImport: | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ | |||
| #include "librecomp/mods.hpp" | ||||
| #include "librecomp/overlays.hpp" | ||||
| #include "librecomp/game.hpp" | ||||
| #include "n64recomp.h" | ||||
| #include "recompiler/context.h" | ||||
| #include "recompiler/live_recompiler.h" | ||||
| 
 | ||||
| // Architecture detection.
 | ||||
| 
 | ||||
|  | @ -240,13 +241,11 @@ size_t recomp::mods::ModHandle::num_events() const { | |||
|     return recompiler_context->event_symbols.size(); | ||||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::ModHandle::populate_exports(std::string& error_param) { | ||||
| void recomp::mods::ModHandle::populate_exports() { | ||||
|     for (size_t func_index : recompiler_context->exported_funcs) { | ||||
|         const auto& func_handle = recompiler_context->functions[func_index]; | ||||
|         exports_by_name.emplace(func_handle.name, func_index); | ||||
|     } | ||||
| 
 | ||||
|     return CodeModLoadError::Good; | ||||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::ModHandle::load_native_library(const recomp::mods::NativeLibraryManifest& lib_manifest, std::string& error_param) { | ||||
|  | @ -309,14 +308,11 @@ bool recomp::mods::ModHandle::get_export_function(const std::string& export_name | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::ModHandle::populate_events(size_t base_event_index, std::string& error_param) { | ||||
| void recomp::mods::ModHandle::populate_events() { | ||||
|     for (size_t event_index = 0; event_index < recompiler_context->event_symbols.size(); event_index++) { | ||||
|         const N64Recomp::EventSymbol& event = recompiler_context->event_symbols[event_index]; | ||||
|         events_by_name.emplace(event.base.name, event_index); | ||||
|     } | ||||
| 
 | ||||
|     code_handle->set_base_event_index(base_event_index); | ||||
|     return CodeModLoadError::Good; | ||||
| } | ||||
| 
 | ||||
| bool recomp::mods::ModHandle::get_global_event_index(const std::string& event_name, size_t& event_index_out) const { | ||||
|  | @ -329,7 +325,7 @@ bool recomp::mods::ModHandle::get_global_event_index(const std::string& event_na | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| recomp::mods::NativeCodeHandle::NativeCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context) { | ||||
| recomp::mods::DynamicLibraryCodeHandle::DynamicLibraryCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context, const ModCodeHandleInputs& inputs) { | ||||
|     is_good = true; | ||||
|     // Load the DLL.
 | ||||
|     dynamic_lib = std::make_unique<DynamicLibrary>(dll_path); | ||||
|  | @ -366,22 +362,33 @@ recomp::mods::NativeCodeHandle::NativeCodeHandle(const std::filesystem::path& dl | |||
|     is_good &= dynamic_lib->get_dll_symbol(do_break, "do_break"); | ||||
|     is_good &= dynamic_lib->get_dll_symbol(reference_section_addresses, "reference_section_addresses"); | ||||
|     is_good &= dynamic_lib->get_dll_symbol(section_addresses, "section_addresses"); | ||||
| 
 | ||||
|     if (is_good) { | ||||
|         *base_event_index = inputs.base_event_index; | ||||
|         *recomp_trigger_event = inputs.recomp_trigger_event; | ||||
|         *get_function = inputs.get_function; | ||||
|         *cop0_status_write = inputs.cop0_status_write; | ||||
|         *cop0_status_read = inputs.cop0_status_read; | ||||
|         *switch_error = inputs.switch_error; | ||||
|         *do_break = inputs.do_break; | ||||
|         *reference_section_addresses = inputs.reference_section_addresses; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool recomp::mods::NativeCodeHandle::good()  { | ||||
| bool recomp::mods::DynamicLibraryCodeHandle::good()  { | ||||
|     return dynamic_lib->good() && is_good; | ||||
| } | ||||
| 
 | ||||
| uint32_t recomp::mods::NativeCodeHandle::get_api_version() { | ||||
| uint32_t recomp::mods::DynamicLibraryCodeHandle::get_api_version() { | ||||
|     return dynamic_lib->get_api_version(); | ||||
| } | ||||
| 
 | ||||
| void recomp::mods::NativeCodeHandle::set_bad() { | ||||
| void recomp::mods::DynamicLibraryCodeHandle::set_bad() { | ||||
|     dynamic_lib.reset(); | ||||
|     is_good = false; | ||||
| } | ||||
| 
 | ||||
| void recomp::mods::NativeCodeHandle::set_imported_function(size_t import_index, GenericFunction func) { | ||||
| void recomp::mods::DynamicLibraryCodeHandle::set_imported_function(size_t import_index, GenericFunction func) { | ||||
|     std::visit(overloaded { | ||||
|         [this, import_index](recomp_func_t* native_func) { | ||||
|             imported_funcs[import_index] = native_func; | ||||
|  | @ -389,6 +396,95 @@ void recomp::mods::NativeCodeHandle::set_imported_function(size_t import_index, | |||
|     }, func); | ||||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::DynamicLibraryCodeHandle::populate_reference_symbols(const N64Recomp::Context& context, std::string& error_param) { | ||||
|     size_t reference_symbol_index = 0; | ||||
|     for (const auto& section : context.sections) { | ||||
|         for (const auto& reloc : section.relocs) { | ||||
|             if (reloc.type == N64Recomp::RelocType::R_MIPS_26 && reloc.reference_symbol && context.is_regular_reference_section(reloc.target_section)) { | ||||
|                 recomp_func_t* cur_func = recomp::overlays::get_func_by_section_index_function_offset(reloc.target_section, reloc.target_section_offset); | ||||
|                 if (cur_func == nullptr) { | ||||
|                     std::stringstream error_param_stream{}; | ||||
|                     error_param_stream << std::hex << | ||||
|                         "section: " << reloc.target_section << | ||||
|                         " func offset: 0x" << reloc.target_section_offset; | ||||
|                     error_param = error_param_stream.str(); | ||||
|                     return CodeModLoadError::InvalidReferenceSymbol; | ||||
|                 } | ||||
|                 reference_symbol_funcs[reference_symbol_index] = cur_func; | ||||
|                 reference_symbol_index++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return CodeModLoadError::Good; | ||||
| } | ||||
| 
 | ||||
| recomp::mods::LiveRecompilerCodeHandle::LiveRecompilerCodeHandle(const N64Recomp::Context& context, const ModCodeHandleInputs& inputs) { | ||||
|     section_addresses = std::make_unique<int32_t[]>(context.sections.size()); | ||||
|     base_event_index = inputs.base_event_index; | ||||
| 
 | ||||
|     N64Recomp::LiveGeneratorInputs recompiler_inputs{ | ||||
|         .base_event_index = inputs.base_event_index, | ||||
|         .cop0_status_write = inputs.cop0_status_write, | ||||
|         .cop0_status_read = inputs.cop0_status_read, | ||||
|         .switch_error = inputs.switch_error, | ||||
|         .do_break = inputs.do_break, | ||||
|         .get_function = inputs.get_function, | ||||
|         .syscall_handler = nullptr, // TODO hook this up
 | ||||
|         .pause_self = pause_self, | ||||
|         .trigger_event = inputs.recomp_trigger_event, | ||||
|         .reference_section_addresses = inputs.reference_section_addresses, | ||||
|         .local_section_addresses = section_addresses.get(), | ||||
|     }; | ||||
| 
 | ||||
|     N64Recomp::LiveGenerator generator{ context.functions.size(), recompiler_inputs }; | ||||
|     std::vector<std::vector<uint32_t>> dummy_static_funcs{}; | ||||
| 
 | ||||
|     for (size_t func_index = 0; func_index < context.functions.size(); func_index++) { | ||||
|         std::ostringstream dummy_ostream{}; | ||||
| 
 | ||||
|         if (!N64Recomp::recompile_function_live(generator, context, func_index, dummy_ostream, dummy_static_funcs, true)) { | ||||
|             is_good = false; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Generate the code.
 | ||||
|     recompiler_output = std::make_unique<N64Recomp::LiveGeneratorOutput>(generator.finish()); | ||||
|     is_good = recompiler_output->good; | ||||
| } | ||||
| 
 | ||||
| void recomp::mods::LiveRecompilerCodeHandle::set_imported_function(size_t import_index, GenericFunction func) { | ||||
|     std::visit(overloaded { | ||||
|         [this, import_index](recomp_func_t* native_func) { | ||||
|             recompiler_output->populate_import_symbol_jumps(import_index, native_func); | ||||
|         } | ||||
|     }, func); | ||||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::LiveRecompilerCodeHandle::populate_reference_symbols(const N64Recomp::Context& context, std::string& error_param) { | ||||
|     size_t num_reference_jumps = recompiler_output->num_reference_symbol_jumps(); | ||||
|     for (size_t jump_index = 0; jump_index < num_reference_jumps; jump_index++) { | ||||
|         N64Recomp::ReferenceJumpDetails jump_details = recompiler_output->get_reference_symbol_jump_details(jump_index); | ||||
| 
 | ||||
|         recomp_func_t* cur_func = recomp::overlays::get_func_by_section_index_function_offset(jump_details.section, jump_details.section_offset); | ||||
|         if (cur_func == nullptr) { | ||||
|             std::stringstream error_param_stream{}; | ||||
|             error_param_stream << std::hex << | ||||
|                 "section: " << jump_details.section << | ||||
|                 " func offset: 0x" << jump_details.section_offset; | ||||
|             error_param = error_param_stream.str(); | ||||
|             return CodeModLoadError::InvalidReferenceSymbol; | ||||
|         } | ||||
| 
 | ||||
|         recompiler_output->set_reference_symbol_jump(jump_index, cur_func); | ||||
|     } | ||||
|     return CodeModLoadError::Good; | ||||
| } | ||||
| 
 | ||||
| recomp::mods::GenericFunction recomp::mods::LiveRecompilerCodeHandle::get_function_handle(size_t func_index) { | ||||
|     return GenericFunction{ recompiler_output->functions[func_index] }; | ||||
| } | ||||
| 
 | ||||
| void patch_func(recomp_func_t* target_func, recomp::mods::GenericFunction replacement_func) { | ||||
|     uint8_t* target_func_u8 = reinterpret_cast<uint8_t*>(target_func); | ||||
|     size_t offset = 0; | ||||
|  | @ -655,6 +751,7 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo | |||
|     std::vector<recomp::mods::ModLoadErrorDetails> ret{}; | ||||
|     ram_used = 0; | ||||
|     num_events = recomp::overlays::num_base_events(); | ||||
|     loaded_code_mods.clear(); | ||||
| 
 | ||||
|     auto find_index_it = mod_game_ids.find(mod_game_id); | ||||
|     if (find_index_it == mod_game_ids.end()) { | ||||
|  | @ -722,8 +819,13 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo | |||
|         std::string cur_error_param; | ||||
|         CodeModLoadError cur_error = load_mod_code(rdram, section_vrom_map, mod, load_address, cur_ram_used, cur_error_param); | ||||
|         if (cur_error != CodeModLoadError::Good) { | ||||
|             if (cur_error_param.empty()) { | ||||
|                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error)); | ||||
|             } | ||||
|             else { | ||||
|                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error) + ":" + cur_error_param); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             load_address += cur_ram_used; | ||||
|             ram_used += cur_ram_used; | ||||
|  | @ -745,9 +847,14 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo | |||
|         std::string cur_error_param; | ||||
|         CodeModLoadError cur_error = resolve_code_dependencies(mod, cur_error_param); | ||||
|         if (cur_error != CodeModLoadError::Good) { | ||||
|             if (cur_error_param.empty()) { | ||||
|                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error)); | ||||
|             } | ||||
|             else { | ||||
|                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error) + ":" + cur_error_param); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Exit early if errors were found.
 | ||||
|     if (!ret.empty()) { | ||||
|  | @ -814,6 +921,19 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | |||
|         return CodeModLoadError::FailedToParseSyms; | ||||
|     } | ||||
| 
 | ||||
|     // Set all reference sections as relocatable, since the only relocations present in a mod's context
 | ||||
|     // are ones that target relocatable sections.
 | ||||
|     mod.recompiler_context->set_all_reference_sections_relocatable(); | ||||
|     // Disable validation of reference symbols (so we can skip populating them). Validation will still happen
 | ||||
|     // later on in the live recompilation process.
 | ||||
|     mod.recompiler_context->skip_validating_reference_symbols = true; | ||||
| 
 | ||||
|     // Populate the mod's export map.
 | ||||
|     mod.populate_exports(); | ||||
| 
 | ||||
|     // Populate the mod's event map and set its base event index.
 | ||||
|     mod.populate_events(); | ||||
| 
 | ||||
|     // Validate that the dependencies present in the symbol file are all present in the mod's manifest as well.
 | ||||
|     for (const auto& [cur_dep_id, cur_dep_index] : mod.recompiler_context->dependencies_by_name) { | ||||
|         // Handle special dependency names.
 | ||||
|  | @ -875,14 +995,33 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | |||
| 
 | ||||
|     ram_used = cur_section_addr - load_address; | ||||
| 
 | ||||
|     // TODO implement LuaJIT recompilation and allow it instead of native code loading via a mod manifest flag.
 | ||||
|      | ||||
|     std::string cur_error_param; | ||||
|     CodeModLoadError cur_error; | ||||
|     if (1) { | ||||
|     ModCodeHandleInputs handle_inputs{ | ||||
|         .base_event_index = static_cast<uint32_t>(num_events), | ||||
|         .recomp_trigger_event = recomp_trigger_event, | ||||
|         .get_function = get_function, | ||||
|         .cop0_status_write = cop0_status_write, | ||||
|         .cop0_status_read = cop0_status_read, | ||||
|         .switch_error = switch_error, | ||||
|         .do_break = do_break, | ||||
|         .reference_section_addresses = section_addresses, | ||||
|     }; | ||||
| 
 | ||||
|     // Allocate the event indices used by the mod.
 | ||||
|     num_events += mod.num_events(); | ||||
| 
 | ||||
|     // Copy the mod's binary into the recompiler context so it can be analyzed during code loading.
 | ||||
|     // TODO move it instead, right now the move can't be done because of a signedness difference in the types.
 | ||||
|     mod.recompiler_context->rom.assign(binary_span.begin(), binary_span.end()); | ||||
| 
 | ||||
|     // Use a dynamic library code handle. This feature isn't meant to be used by end users, but provides a more debuggable
 | ||||
|     // experience than the live recompiler for mod developers.
 | ||||
|     // Enabled if the mod's filename ends with ".offline.dll".
 | ||||
|     if (mod.manifest.mod_root_path.filename().string().ends_with(".offline.nrm")) { | ||||
|         std::filesystem::path dll_path = mod.manifest.mod_root_path; | ||||
|         dll_path.replace_extension(DynamicLibrary::PlatformExtension); | ||||
|         mod.code_handle = std::make_unique<NativeCodeHandle>(dll_path, *mod.recompiler_context); | ||||
|         mod.code_handle = std::make_unique<DynamicLibraryCodeHandle>(dll_path, *mod.recompiler_context, handle_inputs); | ||||
|         if (!mod.code_handle->good()) { | ||||
|             mod.code_handle.reset(); | ||||
|             error_param = dll_path.string(); | ||||
|  | @ -901,13 +1040,15 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | |||
|             return cur_error; | ||||
|         } | ||||
|     } | ||||
|     // Live recompiler code handle.
 | ||||
|     else { | ||||
|         mod.code_handle = std::make_unique<LiveRecompilerCodeHandle>(*mod.recompiler_context, handle_inputs); | ||||
|          | ||||
|     // Populate the mod's export map.
 | ||||
|     cur_error = mod.populate_exports(cur_error_param); | ||||
| 
 | ||||
|     if (cur_error != CodeModLoadError::Good) { | ||||
|         error_param = std::move(cur_error_param); | ||||
|         return cur_error; | ||||
|         if (!mod.code_handle->good()) { | ||||
|             mod.code_handle.reset(); | ||||
|             error_param = {}; | ||||
|             return CodeModLoadError::FailedToRecompile; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Load any native libraries specified by the mod and validate/register the expors.
 | ||||
|  | @ -920,17 +1061,6 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Populate the mod's event map and set its base event index.
 | ||||
|     cur_error = mod.populate_events(num_events, cur_error_param); | ||||
| 
 | ||||
|     if (cur_error != CodeModLoadError::Good) { | ||||
|         error_param = std::move(cur_error_param); | ||||
|         return cur_error; | ||||
|     } | ||||
| 
 | ||||
|     // Allocate the event indices used by the mod.
 | ||||
|     num_events += mod.num_events(); | ||||
| 
 | ||||
|     // Add each function from the mod into the function lookup table.
 | ||||
|     for (size_t func_index = 0; func_index < mod.recompiler_context->functions.size(); func_index++) { | ||||
|         const auto& func = mod.recompiler_context->functions[func_index]; | ||||
|  | @ -953,25 +1083,13 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | |||
| } | ||||
| 
 | ||||
| recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependencies(recomp::mods::ModHandle& mod, std::string& error_param) { | ||||
|     // Reference symbols from the base recomp.1:1 with relocs for offline mods.
 | ||||
|     // TODO this won't be needed for LuaJIT recompilation, so move this logic into the code handle.
 | ||||
|     size_t reference_symbol_index = 0; | ||||
|     for (const auto& section : mod.recompiler_context->sections) { | ||||
|         for (const auto& reloc : section.relocs) { | ||||
|             if (reloc.type == N64Recomp::RelocType::R_MIPS_26 && reloc.reference_symbol && mod.recompiler_context->is_regular_reference_section(reloc.target_section)) { | ||||
|                 recomp_func_t* cur_func = recomp::overlays::get_func_by_section_index_function_offset(reloc.target_section, reloc.target_section_offset); | ||||
|                 if (cur_func == nullptr) { | ||||
|                     std::stringstream error_param_stream{}; | ||||
|                     error_param_stream << std::hex << | ||||
|                         "section: " << reloc.target_section << | ||||
|                         " func offset: 0x" << reloc.target_section_offset; | ||||
|                     error_param = error_param_stream.str(); | ||||
|                     return CodeModLoadError::InvalidReferenceSymbol; | ||||
|                 } | ||||
|                 mod.code_handle->set_reference_symbol_pointer(reference_symbol_index, cur_func); | ||||
|                 reference_symbol_index++; | ||||
|             } | ||||
|         } | ||||
|     // Reference symbols.
 | ||||
|     std::string reference_syms_error_param{}; | ||||
|     CodeModLoadError reference_syms_error = mod.code_handle->populate_reference_symbols(*mod.recompiler_context, reference_syms_error_param); | ||||
| 
 | ||||
|     if (reference_syms_error != CodeModLoadError::Good) { | ||||
|         error_param = std::move(reference_syms_error_param); | ||||
|         return reference_syms_error; | ||||
|     } | ||||
| 
 | ||||
|     // Create a list of dependencies ordered by their index in the recompiler context.
 | ||||
|  | @ -1055,14 +1173,7 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::resolve_code_dependenci | |||
|         recomp::mods::register_event_callback(event_index, func); | ||||
|     } | ||||
| 
 | ||||
|     // Populate the mod's state fields.
 | ||||
|     mod.code_handle->set_recomp_trigger_event_pointer(recomp_trigger_event); | ||||
|     mod.code_handle->set_get_function_pointer(get_function); | ||||
|     mod.code_handle->set_cop0_status_write_pointer(cop0_status_write); | ||||
|     mod.code_handle->set_cop0_status_read_pointer(cop0_status_read); | ||||
|     mod.code_handle->set_switch_error_pointer(switch_error); | ||||
|     mod.code_handle->set_do_break_pointer(do_break); | ||||
|     mod.code_handle->set_reference_section_addresses_pointer(section_addresses); | ||||
|     // Populate the relocated section addresses for the mod.
 | ||||
|     for (size_t section_index = 0; section_index < mod.section_load_addresses.size(); section_index++) { | ||||
|         mod.code_handle->set_local_section_address(section_index, mod.section_load_addresses[section_index]); | ||||
|     } | ||||
|  | @ -1109,3 +1220,7 @@ void recomp::mods::ModContext::unload_mods() { | |||
|     num_events = recomp::overlays::num_base_events(); | ||||
|     active_game = (size_t)-1; | ||||
| } | ||||
| 
 | ||||
| void recomp::mods::initialize_mod_recompiler() { | ||||
|     N64Recomp::live_recompiler_init(); | ||||
| } | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <cstring> | ||||
| #include <string> | ||||
| #include <mutex> | ||||
| #include "librecomp/recomp.h" | ||||
| #include "recomp.h" | ||||
| #include "librecomp/addresses.hpp" | ||||
| #include "librecomp/game.hpp" | ||||
| #include "librecomp/files.hpp" | ||||
|  |  | |||
|  | @ -15,7 +15,7 @@ | |||
| #include <cuchar> | ||||
| #include <charconv> | ||||
| 
 | ||||
| #include "librecomp/recomp.h" | ||||
| #include "recomp.h" | ||||
| #include "librecomp/overlays.hpp" | ||||
| #include "librecomp/game.hpp" | ||||
| #include "xxHash/xxh3.h" | ||||
|  | @ -632,6 +632,8 @@ void recomp::start( | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     recomp::mods::initialize_mod_recompiler(); | ||||
| 
 | ||||
|     // Allocate rdram without comitting it. Use a platform-specific virtual allocation function
 | ||||
|     // that initializes to zero. Protect the region above the memory size to catch accesses to invalid addresses.
 | ||||
|     uint8_t* rdram; | ||||
|  |  | |||
|  | @ -18,6 +18,8 @@ | |||
| #   undef LockMask | ||||
| #   undef Always | ||||
| #   undef Success | ||||
| #   undef False | ||||
| #   undef True | ||||
| #endif | ||||
| 
 | ||||
| #include "ultra64.h" | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Wiseguy
						Wiseguy