1313#include < array>
1414#include < cstddef>
1515#include < variant>
16+ #include < mutex>
17+
18+ #include " blockingconcurrentqueue.h"
1619
1720#define MINIZ_NO_DEFLATE_APIS
1821#define MINIZ_NO_ARCHIVE_WRITING_APIS
2730namespace N64Recomp {
2831 class Context ;
2932 struct LiveGeneratorOutput ;
33+ class ShimFunction ;
3034};
3135
3236namespace recomp {
@@ -55,6 +59,9 @@ struct std::hash<recomp::mods::HookDefinition>
5559
5660namespace recomp {
5761 namespace mods {
62+ static constexpr std::string_view mods_directory = " mods" ;
63+ static constexpr std::string_view mod_config_directory = " mod_config" ;
64+
5865 enum class ModOpenError {
5966 Good,
6067 DoesNotExist,
@@ -65,6 +72,9 @@ namespace recomp {
6572 FailedToParseManifest,
6673 InvalidManifestSchema,
6774 IncorrectManifestFieldType,
75+ MissingConfigSchemaField,
76+ IncorrectConfigSchemaType,
77+ InvalidConfigSchemaDefault,
6878 InvalidVersionString,
6979 InvalidMinimumRecompVersionString,
7080 InvalidDependencyString,
@@ -115,6 +125,13 @@ namespace recomp {
115125
116126 std::string error_to_string (CodeModLoadError);
117127
128+ enum class ConfigOptionType {
129+ None,
130+ Enum,
131+ Number,
132+ String
133+ };
134+
118135 struct ModFileHandle {
119136 virtual ~ModFileHandle () = default ;
120137 virtual std::vector<char > read_file (const std::string& filepath, bool & exists) const = 0;
@@ -154,6 +171,45 @@ namespace recomp {
154171 Version version;
155172 };
156173
174+ struct ConfigOptionEnum {
175+ std::vector<std::string> options;
176+ uint32_t default_value = 0 ;
177+ };
178+
179+ struct ConfigOptionNumber {
180+ double min = 0.0 ;
181+ double max = 0.0 ;
182+ double step = 0.0 ;
183+ int precision = 0 ;
184+ bool percent = false ;
185+ double default_value = 0.0 ;
186+ };
187+
188+ struct ConfigOptionString {
189+ std::string default_value;
190+ };
191+
192+ typedef std::variant<ConfigOptionEnum, ConfigOptionNumber, ConfigOptionString> ConfigOptionVariant;
193+
194+ struct ConfigOption {
195+ std::string id;
196+ std::string name;
197+ std::string description;
198+ ConfigOptionType type;
199+ ConfigOptionVariant variant;
200+ };
201+
202+ struct ConfigSchema {
203+ std::vector<ConfigOption> options;
204+ std::unordered_map<std::string, size_t > options_by_id;
205+ };
206+
207+ typedef std::variant<std::monostate, uint32_t , double , std::string> ConfigValueVariant;
208+
209+ struct ConfigStorage {
210+ std::unordered_map<std::string, ConfigValueVariant> value_map;
211+ };
212+
157213 struct ModDetails {
158214 std::string mod_id;
159215 std::string display_name;
@@ -176,6 +232,7 @@ namespace recomp {
176232 std::vector<std::string> authors;
177233 std::vector<Dependency> dependencies;
178234 std::unordered_map<std::string, size_t > dependencies_by_id;
235+ ConfigSchema config_schema;
179236 Version minimum_recomp_version;
180237 Version version;
181238 bool runtime_toggleable;
@@ -203,6 +260,7 @@ namespace recomp {
203260 };
204261
205262 std::vector<ModDetails> get_mod_details (const std::string& mod_game_id);
263+ void set_mod_index (const std::string &mod_game_id, const std::string &mod_id, size_t index);
206264
207265 // Internal functions, TODO move to an internal header.
208266 struct PatchData {
@@ -244,6 +302,20 @@ namespace recomp {
244302 bool requires_manifest;
245303 };
246304
305+ struct ModConfigQueueSaveMod {
306+ std::string mod_id;
307+ };
308+
309+ struct ModConfigQueueSave {
310+ uint32_t pad;
311+ };
312+
313+ struct ModConfigQueueEnd {
314+ uint32_t pad;
315+ };
316+
317+ typedef std::variant<ModConfigQueueSaveMod, ModConfigQueueSave, ModConfigQueueEnd> ModConfigQueueVariant;
318+
247319 class LiveRecompilerCodeHandle ;
248320 class ModContext {
249321 public:
@@ -252,12 +324,23 @@ namespace recomp {
252324
253325 void register_game (const std::string& mod_game_id);
254326 std::vector<ModOpenErrorDetails> scan_mod_folder (const std::filesystem::path& mod_folder);
255- void enable_mod (const std::string& mod_id, bool enabled);
327+ void load_mods_config ();
328+ void enable_mod (const std::string& mod_id, bool enabled, bool trigger_save);
256329 bool is_mod_enabled (const std::string& mod_id);
330+ bool is_mod_auto_enabled (const std::string& mod_id);
257331 size_t num_opened_mods ();
258332 std::vector<ModLoadErrorDetails> load_mods (const GameEntry& game_entry, uint8_t * rdram, int32_t load_address, uint32_t & ram_used);
259333 void unload_mods ();
260334 std::vector<ModDetails> get_mod_details (const std::string& mod_game_id);
335+ void set_mod_index (const std::string &mod_game_id, const std::string &mod_id, size_t index);
336+ const ConfigSchema &get_mod_config_schema (const std::string &mod_id) const ;
337+ const std::vector<char > &get_mod_thumbnail (const std::string &mod_id) const ;
338+ void set_mod_config_value (size_t mod_index, const std::string &option_id, const ConfigValueVariant &value);
339+ void set_mod_config_value (const std::string &mod_id, const std::string &option_id, const ConfigValueVariant &value);
340+ ConfigValueVariant get_mod_config_value (size_t mod_index, const std::string &option_id);
341+ ConfigValueVariant get_mod_config_value (const std::string &mod_id, const std::string &option_id);
342+ void set_mods_config_path (const std::filesystem::path &path);
343+ void set_mod_config_directory (const std::filesystem::path &path);
261344 ModContentTypeId register_content_type (const ModContentType& type);
262345 bool register_container_type (const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
263346 ModContentTypeId get_code_content_type () const { return code_content_type_id; }
@@ -268,14 +351,15 @@ namespace recomp {
268351 void check_dependencies (ModHandle& mod, std::vector<std::pair<ModLoadError, std::string>>& errors);
269352 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);
270353 CodeModLoadError load_mod_code (uint8_t * rdram, ModHandle& mod, uint32_t base_event_index, std::string& error_param);
271- CodeModLoadError resolve_code_dependencies (ModHandle& mod, const std::unordered_map<recomp_func_t *, recomp::overlays::BasePatchedFunction>& base_patched_funcs, std::string& error_param);
272- void add_opened_mod (ModManifest&& manifest, std::vector<size_t >&& game_indices, std::vector<ModContentTypeId>&& detected_content_types);
354+ 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);
355+ void add_opened_mod (ModManifest&& manifest, ConfigStorage&& config_storage, std::vector<size_t >&& game_indices, std::vector<ModContentTypeId>&& detected_content_types, std::vector< char >&& thumbnail );
273356 void close_mods ();
274357 std::vector<ModLoadErrorDetails> regenerate_with_hooks (
275358 const std::vector<std::pair<HookDefinition, size_t >>& sorted_unprocessed_hooks,
276359 const std::unordered_map<uint32_t , uint16_t >& section_vrom_map,
277360 const std::unordered_map<recomp_func_t *, overlays::BasePatchedFunction>& base_patched_funcs,
278361 std::span<const uint8_t > decompressed_rom);
362+ void dirty_mod_configuration_thread_process ();
279363
280364 static void on_code_mod_enabled (ModContext& context, const ModHandle& mod);
281365
@@ -285,10 +369,18 @@ namespace recomp {
285369 std::unordered_map<std::string, size_t > mod_game_ids;
286370 std::vector<ModHandle> opened_mods;
287371 std::unordered_map<std::string, size_t > opened_mods_by_id;
372+ std::vector<size_t > opened_mods_order;
373+ std::mutex opened_mods_mutex;
288374 std::unordered_set<std::string> mod_ids;
289375 std::unordered_set<std::string> enabled_mods;
376+ std::unordered_set<std::string> auto_enabled_mods;
290377 std::unordered_map<recomp_func_t *, PatchData> patched_funcs;
291378 std::unordered_map<std::string, size_t > loaded_mods_by_id;
379+ std::unique_ptr<std::thread> mod_configuration_thread;
380+ moodycamel::BlockingConcurrentQueue<ModConfigQueueVariant> mod_configuration_thread_queue;
381+ std::filesystem::path mods_config_path;
382+ std::filesystem::path mod_config_directory;
383+ std::mutex mod_config_storage_mutex;
292384 std::vector<size_t > loaded_code_mods;
293385 // Code handle for vanilla code that was regenerated to add hooks.
294386 std::unique_ptr<LiveRecompilerCodeHandle> regenerated_code_handle;
@@ -299,6 +391,10 @@ namespace recomp {
299391 // Tracks which hook slots have already been processed. Used to regenerate vanilla functions as needed
300392 // to add hooks to any functions that weren't already replaced by a mod.
301393 std::vector<bool > processed_hook_slots;
394+ // Generated shim functions to use for implementing shim exports.
395+ std::vector<std::unique_ptr<N64Recomp::ShimFunction>> shim_functions;
396+ ConfigSchema empty_schema;
397+ std::vector<char > empty_bytes;
302398 size_t num_events = 0 ;
303399 ModContentTypeId code_content_type_id;
304400 size_t active_game = (size_t )-1 ;
@@ -321,13 +417,15 @@ namespace recomp {
321417 public:
322418 // TODO make these private and expose methods for the functionality they're currently used in.
323419 ModManifest manifest;
420+ ConfigStorage config_storage;
324421 std::unique_ptr<ModCodeHandle> code_handle;
325422 std::unique_ptr<N64Recomp::Context> recompiler_context;
326423 std::vector<uint32_t > section_load_addresses;
327424 // Content types present in this mod.
328425 std::vector<ModContentTypeId> content_types;
426+ std::vector<char > thumbnail;
329427
330- ModHandle (const ModContext& context, ModManifest&& manifest, std::vector<size_t >&& game_indices, std::vector<ModContentTypeId>&& content_types);
428+ ModHandle (const ModContext& context, ModManifest&& manifest, ConfigStorage&& config_storage, std::vector<size_t >&& game_indices, std::vector<ModContentTypeId>&& content_types, std::vector< char >&& thumbnail );
331429 ModHandle (const ModHandle& rhs) = delete ;
332430 ModHandle& operator =(const ModHandle& rhs) = delete ;
333431 ModHandle (ModHandle&& rhs);
@@ -457,12 +555,23 @@ namespace recomp {
457555
458556 CodeModLoadError validate_api_version (uint32_t api_version, std::string& error_param);
459557
460- void initialize_mod_recompiler ();
558+ void initialize_mods ();
461559 void scan_mods ();
560+ std::filesystem::path get_mods_directory ();
462561 void enable_mod (const std::string& mod_id, bool enabled);
463562 bool is_mod_enabled (const std::string& mod_id);
563+ bool is_mod_auto_enabled (const std::string& mod_id);
564+ const ConfigSchema &get_mod_config_schema (const std::string &mod_id);
565+ const std::vector<char > &get_mod_thumbnail (const std::string &mod_id);
566+ void set_mod_config_value (size_t mod_index, const std::string &option_id, const ConfigValueVariant &value);
567+ void set_mod_config_value (const std::string &mod_id, const std::string &option_id, const ConfigValueVariant &value);
568+ ConfigValueVariant get_mod_config_value (size_t mod_index, const std::string &option_id);
569+ ConfigValueVariant get_mod_config_value (const std::string &mod_id, const std::string &option_id);
464570 ModContentTypeId register_mod_content_type (const ModContentType& type);
465571 bool register_mod_container_type (const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
572+
573+
574+ void register_config_exports ();
466575 }
467576};
468577
0 commit comments