mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-07-14 02:11:26 +00:00
629 lines
30 KiB
C++
629 lines
30 KiB
C++
#ifndef __RECOMP_MODS_HPP__
|
|
#define __RECOMP_MODS_HPP__
|
|
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <tuple>
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <variant>
|
|
#include <mutex>
|
|
#include <optional>
|
|
|
|
#include "blockingconcurrentqueue.h"
|
|
|
|
#define MINIZ_NO_DEFLATE_APIS
|
|
#define MINIZ_NO_ARCHIVE_WRITING_APIS
|
|
#include "miniz.h"
|
|
#include "miniz_zip.h"
|
|
|
|
#include "recomp.h"
|
|
#include "librecomp/game.hpp"
|
|
#include "librecomp/sections.h"
|
|
#include "librecomp/overlays.hpp"
|
|
#include "librecomp/config.hpp"
|
|
|
|
namespace N64Recomp {
|
|
class Context;
|
|
struct LiveGeneratorOutput;
|
|
class ShimFunction;
|
|
};
|
|
|
|
namespace recomp {
|
|
namespace mods {
|
|
struct HookDefinition {
|
|
uint32_t section_rom;
|
|
uint32_t function_vram;
|
|
bool at_return;
|
|
bool operator==(const HookDefinition& rhs) const = default;
|
|
};
|
|
}
|
|
}
|
|
|
|
template <>
|
|
struct std::hash<recomp::mods::HookDefinition>
|
|
{
|
|
std::size_t operator()(const recomp::mods::HookDefinition& def) const {
|
|
// This hash packing only works if the resulting value is 64 bits.
|
|
static_assert(sizeof(std::size_t) == 8);
|
|
// Combine the three values into a single 64-bit value.
|
|
// The lower 2 bits of a function address will always be zero, so pack
|
|
// the value of at_return into the lowest bit.
|
|
return (size_t(def.section_rom) << 32) | size_t(def.function_vram) | size_t(def.at_return ? 1 : 0);
|
|
}
|
|
};
|
|
|
|
namespace recomp {
|
|
namespace mods {
|
|
static constexpr std::string_view mods_directory = "mods";
|
|
static constexpr std::string_view mod_config_directory = "mod_config";
|
|
|
|
enum class ModOpenError {
|
|
Good,
|
|
DoesNotExist,
|
|
NotAFileOrFolder,
|
|
FileError,
|
|
InvalidZip,
|
|
NoManifest,
|
|
FailedToParseManifest,
|
|
InvalidManifestSchema,
|
|
IncorrectManifestFieldType,
|
|
MissingConfigSchemaField,
|
|
IncorrectConfigSchemaType,
|
|
InvalidConfigSchemaDefault,
|
|
InvalidVersionString,
|
|
InvalidMinimumRecompVersionString,
|
|
InvalidDependencyString,
|
|
MissingManifestField,
|
|
DuplicateMod,
|
|
WrongGame,
|
|
InvalidDisableOptionDependency,
|
|
InvalidHiddenOptionDependency,
|
|
DuplicateEnumStrings,
|
|
};
|
|
|
|
std::string error_to_string(ModOpenError);
|
|
|
|
enum class ModLoadError {
|
|
Good,
|
|
InvalidGame,
|
|
MinimumRecompVersionNotMet,
|
|
MissingDependency,
|
|
WrongDependencyVersion,
|
|
FailedToLoadCode,
|
|
RomPatchConflict,
|
|
FailedToLoadPatch,
|
|
};
|
|
|
|
std::string error_to_string(ModLoadError);
|
|
void unmet_dependency_handler(uint8_t* rdram, recomp_context* ctx, uintptr_t arg);
|
|
|
|
enum class CodeModLoadError {
|
|
Good,
|
|
InternalError,
|
|
HasSymsButNoBinary,
|
|
HasBinaryButNoSyms,
|
|
FailedToParseSyms,
|
|
MissingDependencyInManifest,
|
|
FailedToLoadNativeCode,
|
|
FailedToLoadNativeLibrary,
|
|
FailedToFindNativeExport,
|
|
FailedToRecompile,
|
|
InvalidReferenceSymbol,
|
|
InvalidImport,
|
|
InvalidCallbackEvent,
|
|
InvalidFunctionReplacement,
|
|
HooksUnavailable,
|
|
InvalidHook,
|
|
CannotBeHooked,
|
|
FailedToFindReplacement,
|
|
BaseRecompConflict,
|
|
ModConflict,
|
|
DuplicateExport,
|
|
OfflineModHooked,
|
|
NoSpecifiedApiVersion,
|
|
UnsupportedApiVersion,
|
|
};
|
|
|
|
std::string error_to_string(CodeModLoadError);
|
|
|
|
enum class ConfigOptionType {
|
|
None,
|
|
Enum,
|
|
Number,
|
|
String
|
|
};
|
|
|
|
enum class DependencyStatus {
|
|
// Do not change these values as they're exposed in the mod API!
|
|
|
|
// The dependency was found and the version requirement was met.
|
|
Found = 0,
|
|
// The ID given is not a dependency of the mod in question.
|
|
InvalidDependency = 1,
|
|
// The dependency was not found.
|
|
NotFound = 2,
|
|
// The dependency was found, but the version requirement was not met.
|
|
WrongVersion = 3
|
|
};
|
|
|
|
struct ModFileHandle {
|
|
virtual ~ModFileHandle() = default;
|
|
virtual std::vector<char> read_file(const std::string& filepath, bool& exists) const = 0;
|
|
virtual bool file_exists(const std::string& filepath) const = 0;
|
|
};
|
|
|
|
struct ZipModFileHandle final : public ModFileHandle {
|
|
FILE* file_handle = nullptr;
|
|
std::unique_ptr<mz_zip_archive> archive;
|
|
|
|
ZipModFileHandle() = default;
|
|
ZipModFileHandle(const std::filesystem::path& mod_path, ModOpenError& error);
|
|
ZipModFileHandle(std::span<const uint8_t> mod_bytes, ModOpenError& error);
|
|
~ZipModFileHandle() final;
|
|
|
|
std::vector<char> read_file(const std::string& filepath, bool& exists) const final;
|
|
bool file_exists(const std::string& filepath) const final;
|
|
};
|
|
|
|
struct LooseModFileHandle final : public ModFileHandle {
|
|
std::filesystem::path root_path;
|
|
|
|
LooseModFileHandle() = default;
|
|
LooseModFileHandle(const std::filesystem::path& mod_path, ModOpenError& error);
|
|
~LooseModFileHandle() final;
|
|
|
|
std::vector<char> read_file(const std::string& filepath, bool& exists) const final;
|
|
bool file_exists(const std::string& filepath) const final;
|
|
};
|
|
|
|
struct NativeLibraryManifest {
|
|
std::string name;
|
|
std::vector<std::string> exports;
|
|
};
|
|
|
|
struct Dependency {
|
|
std::string mod_id;
|
|
Version version;
|
|
bool optional;
|
|
};
|
|
|
|
struct ModDetails {
|
|
std::string mod_id;
|
|
std::string display_name;
|
|
std::string description;
|
|
std::string short_description;
|
|
Version version;
|
|
std::vector<std::string> authors;
|
|
std::vector<Dependency> dependencies;
|
|
bool runtime_toggleable;
|
|
bool enabled_by_default;
|
|
bool custom_gamemode;
|
|
};
|
|
|
|
struct ModManifest {
|
|
std::filesystem::path mod_root_path;
|
|
|
|
std::vector<std::string> mod_game_ids;
|
|
std::string mod_id;
|
|
std::string display_name;
|
|
std::string description;
|
|
std::string short_description;
|
|
std::vector<std::string> authors;
|
|
std::vector<Dependency> dependencies;
|
|
std::unordered_map<std::string, size_t> dependencies_by_id;
|
|
Version minimum_recomp_version;
|
|
Version version;
|
|
bool runtime_toggleable;
|
|
bool enabled_by_default;
|
|
bool custom_gamemode;
|
|
|
|
std::vector<NativeLibraryManifest> native_libraries;
|
|
std::unique_ptr<ModFileHandle> file_handle;
|
|
};
|
|
|
|
struct ModOpenErrorDetails {
|
|
std::filesystem::path mod_path;
|
|
ModOpenError error;
|
|
std::string error_param;
|
|
ModOpenErrorDetails() = default;
|
|
ModOpenErrorDetails(const std::filesystem::path& mod_path_, ModOpenError error_, const std::string& error_param_) :
|
|
mod_path(mod_path_), error(error_), error_param(error_param_) {}
|
|
};
|
|
|
|
struct ModLoadErrorDetails {
|
|
std::string mod_id;
|
|
ModLoadError error;
|
|
std::string error_param;
|
|
ModLoadErrorDetails() = default;
|
|
ModLoadErrorDetails(const std::string& mod_id_, ModLoadError error_, const std::string& error_param_) :
|
|
mod_id(mod_id_), error(error_), error_param(error_param_) {}
|
|
};
|
|
|
|
void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index);
|
|
|
|
// Internal functions, TODO move to an internal header.
|
|
struct PatchData {
|
|
std::array<std::byte, 16> replaced_bytes;
|
|
std::string mod_id;
|
|
};
|
|
|
|
using GenericFunction = std::variant<recomp_func_t*>;
|
|
|
|
class ModContext;
|
|
class ModHandle;
|
|
using content_enabled_callback = void(ModContext&, const ModHandle&);
|
|
using content_disabled_callback = void(ModContext&, const ModHandle&);
|
|
using content_reordered_callback = void(ModContext&);
|
|
|
|
struct ModContentType {
|
|
// The file that's used to indicate that a mod contains this content type.
|
|
// If a mod contains this file, then it has this content type.
|
|
std::string content_filename;
|
|
// Whether or not this type of content can be toggled at runtime.
|
|
bool allow_runtime_toggle;
|
|
// Function to call when an instance of this content type is enabled.
|
|
content_enabled_callback* on_enabled;
|
|
// Function to call when an instance of this content type is disabled.
|
|
content_disabled_callback* on_disabled;
|
|
// Function to call when an instance of this content type has been reordered.
|
|
// No mod handle is provided as multiple instances may have been reordered at the same time.
|
|
// Will not be called if an instance of this content type was incidentally reordered due
|
|
// to the reordering of another mod, as the ordering of just instances of this content type will not have changed.
|
|
content_reordered_callback* on_reordered;
|
|
};
|
|
|
|
// Holds IDs for mod content types, which get assigned as they're registered.
|
|
// This is just a wrapper around a number for type safety purposes.
|
|
struct ModContentTypeId {
|
|
size_t value;
|
|
bool operator==(const ModContentTypeId& rhs) const = default;
|
|
};
|
|
|
|
struct ModContainerType {
|
|
// The types of content that this container is allowed to have.
|
|
// Leaving this empty will allow the container to have any type of content.
|
|
std::vector<ModContentTypeId> supported_content_types;
|
|
// Whether or not this container requires a manifest to be treated as a valid mod.
|
|
// If no manifest is present, a default one will be created.
|
|
bool requires_manifest;
|
|
};
|
|
|
|
struct ModConfigQueueSaveMod {
|
|
std::string mod_id;
|
|
};
|
|
|
|
struct ModConfigQueueSave {
|
|
uint32_t pad;
|
|
};
|
|
|
|
struct ModConfigQueueEnd {
|
|
uint32_t pad;
|
|
};
|
|
|
|
typedef std::variant<ModConfigQueueSaveMod, ModConfigQueueSave, ModConfigQueueEnd> ModConfigQueueVariant;
|
|
|
|
class LiveRecompilerCodeHandle;
|
|
class ModContext {
|
|
public:
|
|
ModContext();
|
|
~ModContext();
|
|
|
|
void register_game(const std::string& mod_game_id);
|
|
void register_embedded_mod(const std::string& mod_id, std::span<const uint8_t> mod_bytes);
|
|
std::vector<ModOpenErrorDetails> scan_mod_folder(const std::filesystem::path& mod_folder);
|
|
void close_mods();
|
|
void load_mods_config();
|
|
void enable_mod(const std::string& mod_id, bool enabled, bool trigger_save);
|
|
bool is_mod_enabled(const std::string& mod_id) const;
|
|
bool is_mod_auto_enabled(const std::string& mod_id) const;
|
|
size_t num_opened_mods();
|
|
std::vector<ModLoadErrorDetails> load_mods(const GameEntry& game_entry, const std::string& game_mode_id, uint8_t* rdram, int32_t load_address, uint32_t& ram_used);
|
|
void unload_mods();
|
|
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) const;
|
|
std::filesystem::path get_mod_filename(const std::string& mod_id) const;
|
|
size_t get_mod_order_index(const std::string& mod_id) const;
|
|
size_t get_mod_order_index(size_t mod_index) const;
|
|
std::optional<ModDetails> get_details_for_mod(const std::string& mod_id) const;
|
|
std::vector<ModDetails> get_all_mod_details(const std::string& mod_game_id);
|
|
size_t game_mode_count(const std::string& mod_game_id, bool include_disabled) const;
|
|
recomp::Version get_mod_version(size_t mod_index);
|
|
std::string get_mod_id(size_t mod_index);
|
|
void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index);
|
|
const config::ConfigSchema &get_mod_config_schema(const std::string &mod_id) const;
|
|
config::Config *get_mod_config(const std::string &mod_id);
|
|
const std::vector<char> &get_mod_thumbnail(const std::string &mod_id) const;
|
|
void set_mod_config_value(size_t mod_index, const std::string &option_id, const config::ConfigValueVariant &value);
|
|
void set_mod_config_value(const std::string &mod_id, const std::string &option_id, const config::ConfigValueVariant &value);
|
|
config::ConfigValueVariant get_mod_config_value(size_t mod_index, const std::string &option_id) const;
|
|
config::ConfigValueVariant get_mod_config_value(const std::string &mod_id, const std::string &option_id) const;
|
|
void set_mods_config_path(const std::filesystem::path &path);
|
|
void set_mod_config_directory(const std::filesystem::path &path);
|
|
ModContentTypeId register_content_type(const ModContentType& type);
|
|
bool register_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
|
|
ModContentTypeId get_code_content_type() const { return code_content_type_id; }
|
|
bool is_content_runtime_toggleable(ModContentTypeId content_type) const;
|
|
std::string get_mod_display_name(size_t mod_index) const;
|
|
std::filesystem::path get_mod_path(size_t mod_index) const;
|
|
std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index) const;
|
|
DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id) const;
|
|
private:
|
|
ModOpenError open_mod_from_manifest(ModManifest &manifest, std::string &error_param, const std::vector<ModContentTypeId> &supported_content_types, bool requires_manifest);
|
|
ModOpenError open_mod_from_path(const std::filesystem::path& mod_path, std::string& error_param, const std::vector<ModContentTypeId>& supported_content_types, bool requires_manifest);
|
|
ModOpenError open_mod_from_memory(std::span<const uint8_t> mod_bytes, std::string &error_param, const std::vector<ModContentTypeId> &supported_content_types, bool requires_manifest);
|
|
ModLoadError load_mod(ModHandle& mod, std::string& error_param);
|
|
void check_dependencies(ModHandle& mod, std::vector<std::pair<ModLoadError, std::string>>& errors);
|
|
CodeModLoadError init_mod_code(uint8_t* rdram, const std::unordered_map<uint32_t, uint16_t>& section_vrom_map, ModHandle& mod, int32_t load_address, bool hooks_available, uint32_t& ram_used, std::string& error_param);
|
|
CodeModLoadError load_mod_code(uint8_t* rdram, ModHandle& mod, uint32_t base_event_index, std::string& error_param);
|
|
CodeModLoadError resolve_code_dependencies(ModHandle& mod, size_t mod_index, const std::unordered_map<recomp_func_t*, recomp::overlays::BasePatchedFunction>& base_patched_funcs, std::string& error_param);
|
|
void add_opened_mod(ModManifest&& manifest, config::Config&& config, std::vector<size_t>&& game_indices, std::vector<ModContentTypeId>&& detected_content_types, std::vector<char>&& thumbnail);
|
|
std::vector<ModLoadErrorDetails> regenerate_with_hooks(
|
|
const std::vector<std::pair<HookDefinition, size_t>>& sorted_unprocessed_hooks,
|
|
const std::unordered_map<uint32_t, uint16_t>& section_vrom_map,
|
|
const std::unordered_map<recomp_func_t*, overlays::BasePatchedFunction>& base_patched_funcs,
|
|
std::span<const uint8_t> decompressed_rom);
|
|
void dirty_mod_configuration_thread_process();
|
|
void rebuild_mod_order_lookup();
|
|
|
|
static void on_code_mod_enabled(ModContext& context, const ModHandle& mod);
|
|
|
|
std::vector<ModContentType> content_types;
|
|
std::unordered_map<std::string, ModContainerType> container_types;
|
|
// Maps game mod ID to the mod's internal integer ID.
|
|
std::unordered_map<std::string, size_t> mod_game_ids;
|
|
std::unordered_map<std::string, std::span<const uint8_t>> embedded_mod_bytes;
|
|
std::vector<ModHandle> opened_mods;
|
|
std::unordered_map<std::string, size_t> opened_mods_by_id;
|
|
std::unordered_map<std::filesystem::path::string_type, size_t> opened_mods_by_filename;
|
|
std::vector<size_t> opened_mods_order; // order index -> mod index
|
|
std::vector<size_t> mod_order_lookup; // mod index -> order index
|
|
std::mutex opened_mods_mutex;
|
|
std::unordered_set<std::string> mod_ids;
|
|
std::unordered_set<std::string> enabled_mods;
|
|
std::unordered_set<std::string> auto_enabled_mods;
|
|
std::unordered_map<recomp_func_t*, PatchData> patched_funcs;
|
|
std::unordered_map<std::string, size_t> loaded_mods_by_id;
|
|
std::unique_ptr<std::thread> mod_configuration_thread;
|
|
moodycamel::BlockingConcurrentQueue<ModConfigQueueVariant> mod_configuration_thread_queue;
|
|
std::filesystem::path mods_config_path;
|
|
std::filesystem::path mod_config_directory;
|
|
mutable std::mutex mod_config_storage_mutex;
|
|
std::vector<size_t> loaded_code_mods;
|
|
// Code handle for vanilla code that was regenerated to add hooks.
|
|
std::unique_ptr<LiveRecompilerCodeHandle> regenerated_code_handle;
|
|
// Code handle for base patched code that was regenerated to add hooks.
|
|
std::unique_ptr<LiveRecompilerCodeHandle> base_patched_code_handle;
|
|
// Map of hook definition to the entry hook slot's index.
|
|
std::unordered_map<HookDefinition, size_t> hook_slots;
|
|
// Tracks which hook slots have already been processed. Used to regenerate vanilla functions as needed
|
|
// to add hooks to any functions that weren't already replaced by a mod.
|
|
std::vector<bool> processed_hook_slots;
|
|
// Generated shim functions to use for implementing shim exports.
|
|
std::vector<std::unique_ptr<N64Recomp::ShimFunction>> shim_functions;
|
|
config::ConfigSchema empty_schema;
|
|
std::vector<char> empty_bytes;
|
|
size_t num_events = 0;
|
|
ModContentTypeId code_content_type_id;
|
|
ModContentTypeId rom_patch_content_type_id;
|
|
size_t active_game = (size_t)-1;
|
|
};
|
|
|
|
class ModCodeHandle {
|
|
public:
|
|
virtual ~ModCodeHandle() {}
|
|
virtual bool good() = 0;
|
|
virtual uint32_t get_api_version() = 0;
|
|
virtual void set_imported_function(size_t import_index, GenericFunction func) = 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_local_section_address(size_t section_index, int32_t address) = 0;
|
|
virtual GenericFunction get_function_handle(size_t func_index) = 0;
|
|
};
|
|
|
|
class DynamicLibrary;
|
|
class ModHandle {
|
|
public:
|
|
// TODO make these private and expose methods for the functionality they're currently used in.
|
|
ModManifest manifest;
|
|
config::Config config;
|
|
std::unique_ptr<ModCodeHandle> code_handle;
|
|
std::unique_ptr<N64Recomp::Context> recompiler_context;
|
|
std::vector<uint32_t> section_load_addresses;
|
|
// Content types present in this mod.
|
|
std::vector<ModContentTypeId> content_types;
|
|
std::vector<char> thumbnail;
|
|
|
|
ModHandle(const ModContext& context, ModManifest&& manifest, config::Config&& config, std::vector<size_t>&& game_indices, std::vector<ModContentTypeId>&& content_types, std::vector<char>&& thumbnail);
|
|
ModHandle(const ModHandle& rhs) = delete;
|
|
ModHandle& operator=(const ModHandle& rhs) = delete;
|
|
ModHandle(ModHandle&& rhs);
|
|
ModHandle& operator=(ModHandle&& rhs);
|
|
~ModHandle();
|
|
|
|
size_t num_exports() const;
|
|
size_t num_events() const;
|
|
|
|
void populate_exports();
|
|
bool get_export_function(const std::string& export_name, GenericFunction& out) const;
|
|
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);
|
|
|
|
bool is_for_game(size_t game_index) const {
|
|
auto find_it = std::find(game_indices.begin(), game_indices.end(), game_index);
|
|
return find_it != game_indices.end();
|
|
}
|
|
|
|
bool is_runtime_toggleable() const {
|
|
return runtime_toggleable;
|
|
}
|
|
|
|
void disable_runtime_toggle() {
|
|
runtime_toggleable = false;
|
|
}
|
|
|
|
ModDetails get_details() const {
|
|
return ModDetails {
|
|
.mod_id = manifest.mod_id,
|
|
.display_name = manifest.display_name,
|
|
.description = manifest.description,
|
|
.short_description = manifest.short_description,
|
|
.version = manifest.version,
|
|
.authors = manifest.authors,
|
|
.dependencies = manifest.dependencies,
|
|
.runtime_toggleable = is_runtime_toggleable(),
|
|
.enabled_by_default = manifest.enabled_by_default,
|
|
.custom_gamemode = manifest.custom_gamemode
|
|
};
|
|
}
|
|
private:
|
|
// Mapping of export name to function index.
|
|
std::unordered_map<std::string, size_t> exports_by_name;
|
|
// Mapping of export name to native library function pointer.
|
|
std::unordered_map<std::string, recomp_func_t*> native_library_exports;
|
|
// Mapping of event name to local index.
|
|
std::unordered_map<std::string, size_t> events_by_name;
|
|
// Loaded dynamic libraries.
|
|
std::vector<std::unique_ptr<DynamicLibrary>> native_libraries; // Vector of pointers so that implementation can be elsewhere.
|
|
// Games that this mod supports.
|
|
std::vector<size_t> game_indices;
|
|
// Whether this mod can be toggled at runtime.
|
|
bool runtime_toggleable;
|
|
};
|
|
|
|
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:
|
|
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;
|
|
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 {
|
|
return GenericFunction{ functions[func_index] };
|
|
}
|
|
private:
|
|
void set_bad();
|
|
bool is_good = false;
|
|
std::unique_ptr<DynamicLibrary> dynamic_lib;
|
|
std::vector<recomp_func_t*> functions;
|
|
recomp_func_t** imported_funcs;
|
|
recomp_func_t** reference_symbol_funcs;
|
|
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;
|
|
int32_t* section_addresses;
|
|
};
|
|
|
|
class LiveRecompilerCodeHandle : public ModCodeHandle {
|
|
public:
|
|
LiveRecompilerCodeHandle(const N64Recomp::Context& context, const ModCodeHandleInputs& inputs,
|
|
std::unordered_map<size_t, size_t>&& entry_func_hooks, std::unordered_map<size_t, size_t>&& return_func_hooks, std::vector<size_t>&& original_section_indices, bool regenerated);
|
|
|
|
~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, size_t mod_index, GenericFunction callback);
|
|
void reset_events();
|
|
|
|
void setup_hooks(size_t num_hook_slots);
|
|
void set_hook_type(size_t hook_slot_index, bool is_return_hook);
|
|
void register_hook(size_t hook_slot_index, size_t mod_index, GenericFunction callback);
|
|
void finish_event_setup(const ModContext& context);
|
|
void finish_hook_setup(const ModContext& context);
|
|
void reset_hooks();
|
|
void register_hook_exports();
|
|
void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index);
|
|
|
|
ModOpenError parse_manifest(ModManifest &ret, const std::vector<char> &manifest_data, std::string &error_param, recomp::config::Config *config = nullptr);
|
|
CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param);
|
|
|
|
void initialize_mods();
|
|
void register_embedded_mod(const std::string &mod_id, std::span<const uint8_t> mod_bytes);
|
|
void scan_mods();
|
|
void close_mods();
|
|
std::filesystem::path get_mods_directory();
|
|
std::optional<ModDetails> get_details_for_mod(const std::string& mod_id);
|
|
std::vector<ModDetails> get_all_mod_details(const std::string& mod_game_id);
|
|
size_t game_mode_count(const std::string& mod_game_id, bool include_disabled);
|
|
recomp::Version get_mod_version(size_t mod_index);
|
|
std::string get_mod_id(size_t mod_index);
|
|
void enable_mod(const std::string& mod_id, bool enabled);
|
|
bool is_mod_enabled(const std::string& mod_id);
|
|
bool is_mod_auto_enabled(const std::string& mod_id);
|
|
const config::ConfigSchema &get_mod_config_schema(const std::string &mod_id);
|
|
config::Config *get_mod_config(const std::string &mod_id);
|
|
const std::vector<char> &get_mod_thumbnail(const std::string &mod_id);
|
|
void set_mod_config_value(size_t mod_index, const std::string &option_id, const config::ConfigValueVariant &value);
|
|
void set_mod_config_value(const std::string &mod_id, const std::string &option_id, const config::ConfigValueVariant &value);
|
|
config::ConfigValueVariant get_mod_config_value(size_t mod_index, const std::string &option_id);
|
|
config::ConfigValueVariant get_mod_config_value(const std::string &mod_id, const std::string &option_id);
|
|
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename);
|
|
std::filesystem::path get_mod_filename(const std::string& mod_id);
|
|
size_t get_mod_order_index(const std::string& mod_id);
|
|
size_t get_mod_order_index(size_t mod_index);
|
|
ModContentTypeId register_mod_content_type(const ModContentType& type);
|
|
bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
|
|
std::string get_mod_display_name(size_t mod_index);
|
|
std::filesystem::path get_mod_path(size_t mod_index);
|
|
std::pair<std::string, std::string> get_mod_import_info(size_t mod_index, size_t import_index);
|
|
DependencyStatus is_dependency_met(size_t mod_index, const std::string& dependency_id);
|
|
|
|
void register_config_exports();
|
|
}
|
|
};
|
|
|
|
extern "C" void recomp_trigger_event(uint8_t* rdram, recomp_context* ctx, uint32_t event_index);
|
|
|
|
#endif
|