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}/../thirdparty/miniz ${CMAKE_CURRENT_BINARY_DIR}/miniz) | ||||||
| add_subdirectory(${PROJECT_SOURCE_DIR}/../N64Recomp ${CMAKE_CURRENT_BINARY_DIR}/N64Recomp EXCLUDE_FROM_ALL) | 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) | target_link_libraries(librecomp PUBLIC miniz) | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
| #include "ultramodern/ultra64.h" | #include "ultramodern/ultra64.h" | ||||||
| #include "librecomp/recomp.h" | #include "recomp.h" | ||||||
| 
 | 
 | ||||||
| namespace recomp { | namespace recomp { | ||||||
|     // 512GB (kseg0 size)
 |     // 512GB (kseg0 size)
 | ||||||
|  |  | ||||||
|  | @ -19,12 +19,13 @@ | ||||||
| #include "miniz.h" | #include "miniz.h" | ||||||
| #include "miniz_zip.h" | #include "miniz_zip.h" | ||||||
| 
 | 
 | ||||||
|  | #include "recomp.h" | ||||||
| #include "librecomp/game.hpp" | #include "librecomp/game.hpp" | ||||||
| #include "librecomp/recomp.h" |  | ||||||
| #include "librecomp/sections.h" | #include "librecomp/sections.h" | ||||||
| 
 | 
 | ||||||
| namespace N64Recomp { | namespace N64Recomp { | ||||||
|     class Context; |     class Context; | ||||||
|  |     struct LiveGeneratorOutput; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| namespace recomp { | namespace recomp { | ||||||
|  | @ -71,6 +72,7 @@ namespace recomp { | ||||||
|             FailedToLoadNativeCode, |             FailedToLoadNativeCode, | ||||||
|             FailedToLoadNativeLibrary, |             FailedToLoadNativeLibrary, | ||||||
|             FailedToFindNativeExport, |             FailedToFindNativeExport, | ||||||
|  |             FailedToRecompile, | ||||||
|             InvalidReferenceSymbol, |             InvalidReferenceSymbol, | ||||||
|             InvalidImport, |             InvalidImport, | ||||||
|             InvalidCallbackEvent, |             InvalidCallbackEvent, | ||||||
|  | @ -258,16 +260,8 @@ namespace recomp { | ||||||
|             virtual bool good() = 0; |             virtual bool good() = 0; | ||||||
|             virtual uint32_t get_api_version() = 0; |             virtual uint32_t get_api_version() = 0; | ||||||
|             virtual void set_imported_function(size_t import_index, GenericFunction func) = 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 CodeModLoadError populate_reference_symbols(const N64Recomp::Context& recompiler_context, std::string& error_param) = 0; | ||||||
|             virtual void set_base_event_index(uint32_t global_event_index) = 0; |  | ||||||
|             virtual uint32_t get_base_event_index() = 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 void set_local_section_address(size_t section_index, int32_t address) = 0; | ||||||
|             virtual GenericFunction get_function_handle(size_t func_index) = 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_exports() const; | ||||||
|             size_t num_events() 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; |             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; |             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); |             CodeModLoadError load_native_library(const NativeLibraryManifest& lib_manifest, std::string& error_param); | ||||||
| 
 | 
 | ||||||
|  | @ -326,43 +320,28 @@ namespace recomp { | ||||||
|             bool runtime_toggleable; |             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: |         public: | ||||||
|             NativeCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context); |             DynamicLibraryCodeHandle(const std::filesystem::path& dll_path, const N64Recomp::Context& context, const ModCodeHandleInputs& inputs); | ||||||
|             ~NativeCodeHandle() = default; |             ~DynamicLibraryCodeHandle() = default; | ||||||
|             bool good() final; |             bool good() final; | ||||||
|             uint32_t get_api_version() final; |             uint32_t get_api_version() final; | ||||||
|             void set_imported_function(size_t import_index, GenericFunction func) 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 { |             CodeModLoadError populate_reference_symbols(const N64Recomp::Context& context, std::string& error_param) final; | ||||||
|                 reference_symbol_funcs[symbol_index] = ptr; |  | ||||||
|             }; |  | ||||||
|             void set_base_event_index(uint32_t global_event_index) final { |  | ||||||
|                 *base_event_index = global_event_index; |  | ||||||
|             }; |  | ||||||
|             uint32_t get_base_event_index() final { |             uint32_t get_base_event_index() final { | ||||||
|                 return *base_event_index; |                 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 { |             void set_local_section_address(size_t section_index, int32_t address) final { | ||||||
|                 section_addresses[section_index] = address; |                 section_addresses[section_index] = address; | ||||||
|             }; |             }; | ||||||
|  | @ -387,12 +366,41 @@ namespace recomp { | ||||||
|             int32_t* section_addresses; |             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 setup_events(size_t num_events); | ||||||
|         void register_event_callback(size_t event_index, GenericFunction callback); |         void register_event_callback(size_t event_index, GenericFunction callback); | ||||||
|         void reset_events(); |         void reset_events(); | ||||||
|         CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param); |         CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param); | ||||||
| 
 | 
 | ||||||
| 
 |         void initialize_mod_recompiler(); | ||||||
|         void scan_mods(); |         void scan_mods(); | ||||||
|         void enable_mod(const std::string& mod_id, bool enabled); |         void enable_mod(const std::string& mod_id, bool enabled); | ||||||
|         bool is_mod_enabled(const std::string& mod_id); |         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 "librecomp/game.hpp" | ||||||
| 
 | 
 | ||||||
| #include "ultramodern/ultra64.h" | #include "ultramodern/ultra64.h" | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| #include <cassert> | #include <cassert> | ||||||
| #include <ultramodern/ultra64.h> | #include <ultramodern/ultra64.h> | ||||||
| #include <ultramodern/ultramodern.hpp> | #include <ultramodern/ultramodern.hpp> | ||||||
| #include "librecomp/recomp.h" | #include "recomp.h" | ||||||
| #include "librecomp/addresses.hpp" | #include "librecomp/addresses.hpp" | ||||||
| #include "librecomp/game.hpp" | #include "librecomp/game.hpp" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "json/json.hpp" | #include "json/json.hpp" | ||||||
| 
 | 
 | ||||||
| #include "n64recomp.h" | #include "recompiler/context.h" | ||||||
| #include "librecomp/mods.hpp" | #include "librecomp/mods.hpp" | ||||||
| 
 | 
 | ||||||
| recomp::mods::ZipModFileHandle::~ZipModFileHandle() { | recomp::mods::ZipModFileHandle::~ZipModFileHandle() { | ||||||
|  | @ -571,6 +571,8 @@ std::string recomp::mods::error_to_string(CodeModLoadError error) { | ||||||
|             return "Failed to load mod library"; |             return "Failed to load mod library"; | ||||||
|         case CodeModLoadError::FailedToFindNativeExport: |         case CodeModLoadError::FailedToFindNativeExport: | ||||||
|             return "Failed to find native export"; |             return "Failed to find native export"; | ||||||
|  |         case CodeModLoadError::FailedToRecompile: | ||||||
|  |             return "Failed to recompile mod"; | ||||||
|         case CodeModLoadError::InvalidReferenceSymbol: |         case CodeModLoadError::InvalidReferenceSymbol: | ||||||
|             return "Reference symbol does not exist"; |             return "Reference symbol does not exist"; | ||||||
|         case CodeModLoadError::InvalidImport: |         case CodeModLoadError::InvalidImport: | ||||||
|  |  | ||||||
|  | @ -6,7 +6,8 @@ | ||||||
| #include "librecomp/mods.hpp" | #include "librecomp/mods.hpp" | ||||||
| #include "librecomp/overlays.hpp" | #include "librecomp/overlays.hpp" | ||||||
| #include "librecomp/game.hpp" | #include "librecomp/game.hpp" | ||||||
| #include "n64recomp.h" | #include "recompiler/context.h" | ||||||
|  | #include "recompiler/live_recompiler.h" | ||||||
| 
 | 
 | ||||||
| // Architecture detection.
 | // Architecture detection.
 | ||||||
| 
 | 
 | ||||||
|  | @ -240,13 +241,11 @@ size_t recomp::mods::ModHandle::num_events() const { | ||||||
|     return recompiler_context->event_symbols.size(); |     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) { |     for (size_t func_index : recompiler_context->exported_funcs) { | ||||||
|         const auto& func_handle = recompiler_context->functions[func_index]; |         const auto& func_handle = recompiler_context->functions[func_index]; | ||||||
|         exports_by_name.emplace(func_handle.name, 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) { | 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; |     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++) { |     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]; |         const N64Recomp::EventSymbol& event = recompiler_context->event_symbols[event_index]; | ||||||
|         events_by_name.emplace(event.base.name, 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 { | 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; |     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; |     is_good = true; | ||||||
|     // Load the DLL.
 |     // Load the DLL.
 | ||||||
|     dynamic_lib = std::make_unique<DynamicLibrary>(dll_path); |     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(do_break, "do_break"); | ||||||
|     is_good &= dynamic_lib->get_dll_symbol(reference_section_addresses, "reference_section_addresses"); |     is_good &= dynamic_lib->get_dll_symbol(reference_section_addresses, "reference_section_addresses"); | ||||||
|     is_good &= dynamic_lib->get_dll_symbol(section_addresses, "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; |     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(); |     return dynamic_lib->get_api_version(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void recomp::mods::NativeCodeHandle::set_bad() { | void recomp::mods::DynamicLibraryCodeHandle::set_bad() { | ||||||
|     dynamic_lib.reset(); |     dynamic_lib.reset(); | ||||||
|     is_good = false; |     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 { |     std::visit(overloaded { | ||||||
|         [this, import_index](recomp_func_t* native_func) { |         [this, import_index](recomp_func_t* native_func) { | ||||||
|             imported_funcs[import_index] = native_func; |             imported_funcs[import_index] = native_func; | ||||||
|  | @ -389,6 +396,95 @@ void recomp::mods::NativeCodeHandle::set_imported_function(size_t import_index, | ||||||
|     }, func); |     }, 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) { | void patch_func(recomp_func_t* target_func, recomp::mods::GenericFunction replacement_func) { | ||||||
|     uint8_t* target_func_u8 = reinterpret_cast<uint8_t*>(target_func); |     uint8_t* target_func_u8 = reinterpret_cast<uint8_t*>(target_func); | ||||||
|     size_t offset = 0; |     size_t offset = 0; | ||||||
|  | @ -655,6 +751,7 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo | ||||||
|     std::vector<recomp::mods::ModLoadErrorDetails> ret{}; |     std::vector<recomp::mods::ModLoadErrorDetails> ret{}; | ||||||
|     ram_used = 0; |     ram_used = 0; | ||||||
|     num_events = recomp::overlays::num_base_events(); |     num_events = recomp::overlays::num_base_events(); | ||||||
|  |     loaded_code_mods.clear(); | ||||||
| 
 | 
 | ||||||
|     auto find_index_it = mod_game_ids.find(mod_game_id); |     auto find_index_it = mod_game_ids.find(mod_game_id); | ||||||
|     if (find_index_it == mod_game_ids.end()) { |     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; |         std::string cur_error_param; | ||||||
|         CodeModLoadError cur_error = load_mod_code(rdram, section_vrom_map, mod, load_address, cur_ram_used, 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 != 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); |                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error) + ":" + cur_error_param); | ||||||
|             } |             } | ||||||
|  |         } | ||||||
|         else { |         else { | ||||||
|             load_address += cur_ram_used; |             load_address += cur_ram_used; | ||||||
|             ram_used += 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; |         std::string cur_error_param; | ||||||
|         CodeModLoadError cur_error = resolve_code_dependencies(mod, cur_error_param); |         CodeModLoadError cur_error = resolve_code_dependencies(mod, cur_error_param); | ||||||
|         if (cur_error != CodeModLoadError::Good) { |         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); |                 ret.emplace_back(mod.manifest.mod_id, ModLoadError::FailedToLoadCode, error_to_string(cur_error) + ":" + cur_error_param); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // Exit early if errors were found.
 |     // Exit early if errors were found.
 | ||||||
|     if (!ret.empty()) { |     if (!ret.empty()) { | ||||||
|  | @ -814,6 +921,19 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | ||||||
|         return CodeModLoadError::FailedToParseSyms; |         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.
 |     // 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) { |     for (const auto& [cur_dep_id, cur_dep_index] : mod.recompiler_context->dependencies_by_name) { | ||||||
|         // Handle special dependency names.
 |         // 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; |     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; |     std::string cur_error_param; | ||||||
|     CodeModLoadError cur_error; |     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; |         std::filesystem::path dll_path = mod.manifest.mod_root_path; | ||||||
|         dll_path.replace_extension(DynamicLibrary::PlatformExtension); |         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()) { |         if (!mod.code_handle->good()) { | ||||||
|             mod.code_handle.reset(); |             mod.code_handle.reset(); | ||||||
|             error_param = dll_path.string(); |             error_param = dll_path.string(); | ||||||
|  | @ -901,13 +1040,15 @@ recomp::mods::CodeModLoadError recomp::mods::ModContext::load_mod_code(uint8_t* | ||||||
|             return cur_error; |             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.
 |         if (!mod.code_handle->good()) { | ||||||
|     cur_error = mod.populate_exports(cur_error_param); |             mod.code_handle.reset(); | ||||||
| 
 |             error_param = {}; | ||||||
|     if (cur_error != CodeModLoadError::Good) { |             return CodeModLoadError::FailedToRecompile; | ||||||
|         error_param = std::move(cur_error_param); |         } | ||||||
|         return cur_error; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Load any native libraries specified by the mod and validate/register the expors.
 |     // 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.
 |     // 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++) { |     for (size_t func_index = 0; func_index < mod.recompiler_context->functions.size(); func_index++) { | ||||||
|         const auto& func = mod.recompiler_context->functions[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) { | 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.
 |     // Reference symbols.
 | ||||||
|     // TODO this won't be needed for LuaJIT recompilation, so move this logic into the code handle.
 |     std::string reference_syms_error_param{}; | ||||||
|     size_t reference_symbol_index = 0; |     CodeModLoadError reference_syms_error = mod.code_handle->populate_reference_symbols(*mod.recompiler_context, reference_syms_error_param); | ||||||
|     for (const auto& section : mod.recompiler_context->sections) { | 
 | ||||||
|         for (const auto& reloc : section.relocs) { |     if (reference_syms_error != CodeModLoadError::Good) { | ||||||
|             if (reloc.type == N64Recomp::RelocType::R_MIPS_26 && reloc.reference_symbol && mod.recompiler_context->is_regular_reference_section(reloc.target_section)) { |         error_param = std::move(reference_syms_error_param); | ||||||
|                 recomp_func_t* cur_func = recomp::overlays::get_func_by_section_index_function_offset(reloc.target_section, reloc.target_section_offset); |         return reference_syms_error; | ||||||
|                 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++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Create a list of dependencies ordered by their index in the recompiler context.
 |     // 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); |         recomp::mods::register_event_callback(event_index, func); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Populate the mod's state fields.
 |     // Populate the relocated section addresses for the mod.
 | ||||||
|     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); |  | ||||||
|     for (size_t section_index = 0; section_index < mod.section_load_addresses.size(); section_index++) { |     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]); |         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(); |     num_events = recomp::overlays::num_base_events(); | ||||||
|     active_game = (size_t)-1; |     active_game = (size_t)-1; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void recomp::mods::initialize_mod_recompiler() { | ||||||
|  |     N64Recomp::live_recompiler_init(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| #include <cstring> | #include <cstring> | ||||||
| #include <string> | #include <string> | ||||||
| #include <mutex> | #include <mutex> | ||||||
| #include "librecomp/recomp.h" | #include "recomp.h" | ||||||
| #include "librecomp/addresses.hpp" | #include "librecomp/addresses.hpp" | ||||||
| #include "librecomp/game.hpp" | #include "librecomp/game.hpp" | ||||||
| #include "librecomp/files.hpp" | #include "librecomp/files.hpp" | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ | ||||||
| #include <cuchar> | #include <cuchar> | ||||||
| #include <charconv> | #include <charconv> | ||||||
| 
 | 
 | ||||||
| #include "librecomp/recomp.h" | #include "recomp.h" | ||||||
| #include "librecomp/overlays.hpp" | #include "librecomp/overlays.hpp" | ||||||
| #include "librecomp/game.hpp" | #include "librecomp/game.hpp" | ||||||
| #include "xxHash/xxh3.h" | #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
 |     // 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.
 |     // that initializes to zero. Protect the region above the memory size to catch accesses to invalid addresses.
 | ||||||
|     uint8_t* rdram; |     uint8_t* rdram; | ||||||
|  |  | ||||||
|  | @ -18,6 +18,8 @@ | ||||||
| #   undef LockMask | #   undef LockMask | ||||||
| #   undef Always | #   undef Always | ||||||
| #   undef Success | #   undef Success | ||||||
|  | #   undef False | ||||||
|  | #   undef True | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| #include "ultra64.h" | #include "ultra64.h" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Wiseguy
						Wiseguy