Skip to content

Commit db1b1a1

Browse files
Expose functionality needed for runtime mod installation (#97)
* DnD prototype. * Remaining changes needed for runtime mod installation * Change path unordered map to use strings as keys instead to fix MacOS compilation --------- Co-authored-by: Dario <dariosamo@gmail.com>
1 parent 6f8393f commit db1b1a1

File tree

4 files changed

+93
-43
lines changed

4 files changed

+93
-43
lines changed

librecomp/include/librecomp/mods.hpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <cstddef>
1515
#include <variant>
1616
#include <mutex>
17+
#include <optional>
1718

1819
#include "blockingconcurrentqueue.h"
1920

@@ -259,7 +260,9 @@ namespace recomp {
259260
mod_id(mod_id_), error(error_), error_param(error_param_) {}
260261
};
261262

262-
std::vector<ModDetails> get_mod_details(const std::string& mod_game_id);
263+
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename);
264+
std::optional<ModDetails> get_details_for_mod(const std::string& mod_id);
265+
std::vector<ModDetails> get_all_mod_details(const std::string& mod_game_id);
263266
void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index);
264267

265268
// Internal functions, TODO move to an internal header.
@@ -324,14 +327,17 @@ namespace recomp {
324327

325328
void register_game(const std::string& mod_game_id);
326329
std::vector<ModOpenErrorDetails> scan_mod_folder(const std::filesystem::path& mod_folder);
330+
void close_mods();
327331
void load_mods_config();
328332
void enable_mod(const std::string& mod_id, bool enabled, bool trigger_save);
329333
bool is_mod_enabled(const std::string& mod_id);
330334
bool is_mod_auto_enabled(const std::string& mod_id);
331335
size_t num_opened_mods();
332336
std::vector<ModLoadErrorDetails> load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used);
333337
void unload_mods();
334-
std::vector<ModDetails> get_mod_details(const std::string& mod_game_id);
338+
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) const;
339+
std::optional<ModDetails> get_details_for_mod(const std::string& mod_id) const;
340+
std::vector<ModDetails> get_all_mod_details(const std::string& mod_game_id);
335341
void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index);
336342
const ConfigSchema &get_mod_config_schema(const std::string &mod_id) const;
337343
const std::vector<char> &get_mod_thumbnail(const std::string &mod_id) const;
@@ -353,7 +359,6 @@ namespace recomp {
353359
CodeModLoadError load_mod_code(uint8_t* rdram, ModHandle& mod, uint32_t base_event_index, std::string& error_param);
354360
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);
355361
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);
356-
void close_mods();
357362
std::vector<ModLoadErrorDetails> regenerate_with_hooks(
358363
const std::vector<std::pair<HookDefinition, size_t>>& sorted_unprocessed_hooks,
359364
const std::unordered_map<uint32_t, uint16_t>& section_vrom_map,
@@ -369,6 +374,7 @@ namespace recomp {
369374
std::unordered_map<std::string, size_t> mod_game_ids;
370375
std::vector<ModHandle> opened_mods;
371376
std::unordered_map<std::string, size_t> opened_mods_by_id;
377+
std::unordered_map<std::filesystem::path::string_type, size_t> opened_mods_by_filename;
372378
std::vector<size_t> opened_mods_order;
373379
std::mutex opened_mods_mutex;
374380
std::unordered_set<std::string> mod_ids;
@@ -453,6 +459,19 @@ namespace recomp {
453459
void disable_runtime_toggle() {
454460
runtime_toggleable = false;
455461
}
462+
463+
ModDetails get_details() const {
464+
return ModDetails {
465+
.mod_id = manifest.mod_id,
466+
.display_name = manifest.display_name,
467+
.description = manifest.description,
468+
.short_description = manifest.short_description,
469+
.version = manifest.version,
470+
.authors = manifest.authors,
471+
.dependencies = manifest.dependencies,
472+
.runtime_toggleable = is_runtime_toggleable()
473+
};
474+
}
456475
private:
457476
// Mapping of export name to function index.
458477
std::unordered_map<std::string, size_t> exports_by_name;
@@ -553,10 +572,12 @@ namespace recomp {
553572
void reset_hooks();
554573
void run_hook(uint8_t* rdram, recomp_context* ctx, size_t hook_slot_index);
555574

575+
ModOpenError parse_manifest(ModManifest &ret, const std::vector<char> &manifest_data, std::string &error_param);
556576
CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param);
557577

558578
void initialize_mods();
559579
void scan_mods();
580+
void close_mods();
560581
std::filesystem::path get_mods_directory();
561582
void enable_mod(const std::string& mod_id, bool enabled);
562583
bool is_mod_enabled(const std::string& mod_id);
@@ -570,7 +591,6 @@ namespace recomp {
570591
ModContentTypeId register_mod_content_type(const ModContentType& type);
571592
bool register_mod_container_type(const std::string& extension, const std::vector<ModContentTypeId>& content_types, bool requires_manifest);
572593

573-
574594
void register_config_exports();
575595
}
576596
};

librecomp/src/mod_manifest.cpp

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -509,81 +509,81 @@ recomp::mods::ModOpenError parse_manifest_config_schema_option(const nlohmann::j
509509
return recomp::mods::ModOpenError::Good;
510510
}
511511

512-
recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const std::vector<char>& manifest_data, std::string& error_param) {
512+
recomp::mods::ModOpenError recomp::mods::parse_manifest(ModManifest& ret, const std::vector<char>& manifest_data, std::string& error_param) {
513513
using json = nlohmann::json;
514514
json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), nullptr, false);
515515

516516
if (manifest_json.is_discarded()) {
517-
return recomp::mods::ModOpenError::FailedToParseManifest;
517+
return ModOpenError::FailedToParseManifest;
518518
}
519519

520520
if (!manifest_json.is_object()) {
521-
return recomp::mods::ModOpenError::InvalidManifestSchema;
521+
return ModOpenError::InvalidManifestSchema;
522522
}
523523

524-
recomp::mods::ModOpenError current_error = recomp::mods::ModOpenError::Good;
524+
ModOpenError current_error = ModOpenError::Good;
525525

526526
// Mod Game ID
527527
std::string mod_game_id{};
528528
current_error = try_get<json::string_t>(mod_game_id, manifest_json, game_mod_id_key, true, error_param);
529-
if (current_error != recomp::mods::ModOpenError::Good) {
529+
if (current_error != ModOpenError::Good) {
530530
return current_error;
531531
}
532532
ret.mod_game_ids.emplace_back(std::move(mod_game_id));
533533

534534
// Mod ID
535535
current_error = try_get<json::string_t>(ret.mod_id, manifest_json, mod_id_key, true, error_param);
536-
if (current_error != recomp::mods::ModOpenError::Good) {
536+
if (current_error != ModOpenError::Good) {
537537
return current_error;
538538
}
539539

540540
// Display name
541541
current_error = try_get<json::string_t>(ret.display_name, manifest_json, display_name_key, true, error_param);
542-
if (current_error != recomp::mods::ModOpenError::Good) {
542+
if (current_error != ModOpenError::Good) {
543543
return current_error;
544544
}
545545

546546
// Description (optional)
547547
current_error = try_get<json::string_t>(ret.description, manifest_json, description_key, false, error_param);
548-
if (current_error != recomp::mods::ModOpenError::Good) {
548+
if (current_error != ModOpenError::Good) {
549549
return current_error;
550550
}
551551

552552
// Short Description (optional)
553553
current_error = try_get<json::string_t>(ret.short_description, manifest_json, short_description_key, false, error_param);
554-
if (current_error != recomp::mods::ModOpenError::Good) {
554+
if (current_error != ModOpenError::Good) {
555555
return current_error;
556556
}
557557

558558
// Version
559-
current_error = try_get_version(ret.version, manifest_json, version_key, error_param, recomp::mods::ModOpenError::InvalidVersionString);
560-
if (current_error != recomp::mods::ModOpenError::Good) {
559+
current_error = try_get_version(ret.version, manifest_json, version_key, error_param, ModOpenError::InvalidVersionString);
560+
if (current_error != ModOpenError::Good) {
561561
return current_error;
562562
}
563563

564564
// Authors
565565
current_error = try_get_vec<json::string_t>(ret.authors, manifest_json, authors_key, true, error_param);
566-
if (current_error != recomp::mods::ModOpenError::Good) {
566+
if (current_error != ModOpenError::Good) {
567567
return current_error;
568568
}
569569

570570
// Minimum recomp version
571-
current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, recomp::mods::ModOpenError::InvalidMinimumRecompVersionString);
572-
if (current_error != recomp::mods::ModOpenError::Good) {
571+
current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, ModOpenError::InvalidMinimumRecompVersionString);
572+
if (current_error != ModOpenError::Good) {
573573
return current_error;
574574
}
575575

576576
// Dependencies (optional)
577577
std::vector<std::string> dep_strings{};
578578
current_error = try_get_vec<json::string_t>(dep_strings, manifest_json, dependencies_key, false, error_param);
579-
if (current_error != recomp::mods::ModOpenError::Good) {
579+
if (current_error != ModOpenError::Good) {
580580
return current_error;
581581
}
582582
for (const std::string& dep_string : dep_strings) {
583-
recomp::mods::Dependency cur_dep;
583+
Dependency cur_dep;
584584
if (!parse_dependency(dep_string, cur_dep)) {
585585
error_param = dep_string;
586-
return recomp::mods::ModOpenError::InvalidDependencyString;
586+
return ModOpenError::InvalidDependencyString;
587587
}
588588

589589
size_t dependency_index = ret.dependencies.size();
@@ -597,15 +597,15 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
597597
auto& val = *find_libs_it;
598598
if (!val.is_object()) {
599599
error_param = native_libraries_key;
600-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
600+
return ModOpenError::IncorrectManifestFieldType;
601601
}
602602
for (const auto& [lib_name, lib_exports] : val.items()) {
603-
recomp::mods::NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back();
603+
NativeLibraryManifest& cur_lib = ret.native_libraries.emplace_back();
604604

605605
cur_lib.name = lib_name;
606606
if (!get_to_vec<std::string>(lib_exports, cur_lib.exports)) {
607607
error_param = native_libraries_key;
608-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
608+
return ModOpenError::IncorrectManifestFieldType;
609609
}
610610
}
611611
}
@@ -616,30 +616,30 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
616616
auto& val = *find_config_schema_it;
617617
if (!val.is_object()) {
618618
error_param = config_schema_key;
619-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
619+
return ModOpenError::IncorrectManifestFieldType;
620620
}
621621

622622
auto options = val.find(config_schema_options_key);
623623
if (options != val.end()) {
624624
if (!options->is_array()) {
625625
error_param = config_schema_options_key;
626-
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
626+
return ModOpenError::IncorrectManifestFieldType;
627627
}
628628

629629
for (const json &option : *options) {
630-
recomp::mods::ModOpenError open_error = parse_manifest_config_schema_option(option, ret, error_param);
631-
if (open_error != recomp::mods::ModOpenError::Good) {
630+
ModOpenError open_error = parse_manifest_config_schema_option(option, ret, error_param);
631+
if (open_error != ModOpenError::Good) {
632632
return open_error;
633633
}
634634
}
635635
}
636636
else {
637637
error_param = config_schema_options_key;
638-
return recomp::mods::ModOpenError::MissingConfigSchemaField;
638+
return ModOpenError::MissingConfigSchemaField;
639639
}
640640
}
641641

642-
return recomp::mods::ModOpenError::Good;
642+
return ModOpenError::Good;
643643
}
644644

645645
bool parse_mod_config_storage(const std::filesystem::path &path, const std::string &expected_mod_id, recomp::mods::ConfigStorage &config_storage, const recomp::mods::ConfigSchema &config_schema) {

librecomp/src/mods.cpp

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ void recomp::mods::ModContext::add_opened_mod(ModManifest&& manifest, ConfigStor
598598
std::unique_lock lock(opened_mods_mutex);
599599
size_t mod_index = opened_mods.size();
600600
opened_mods_by_id.emplace(manifest.mod_id, mod_index);
601+
opened_mods_by_filename.emplace(manifest.mod_root_path.filename().native(), mod_index);
601602
opened_mods.emplace_back(*this, std::move(manifest), std::move(config_storage), std::move(game_indices), std::move(detected_content_types), std::move(thumbnail));
602603
opened_mods_order.emplace_back(mod_index);
603604
}
@@ -626,6 +627,7 @@ void recomp::mods::ModContext::register_game(const std::string& mod_game_id) {
626627
void recomp::mods::ModContext::close_mods() {
627628
std::unique_lock lock(opened_mods_mutex);
628629
opened_mods_by_id.clear();
630+
opened_mods_by_filename.clear();
629631
opened_mods.clear();
630632
opened_mods_order.clear();
631633
mod_ids.clear();
@@ -1066,7 +1068,27 @@ size_t recomp::mods::ModContext::num_opened_mods() {
10661068
return opened_mods.size();
10671069
}
10681070

1069-
std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(const std::string &mod_game_id) {
1071+
std::string recomp::mods::ModContext::get_mod_id_from_filename(const std::filesystem::path& filename) const {
1072+
auto find_it = opened_mods_by_filename.find(filename.native());
1073+
if (find_it == opened_mods_by_filename.end()) {
1074+
return {};
1075+
}
1076+
1077+
return opened_mods[find_it->second].manifest.mod_id;
1078+
}
1079+
1080+
std::optional<recomp::mods::ModDetails> recomp::mods::ModContext::get_details_for_mod(const std::string& mod_id) const {
1081+
auto find_it = opened_mods_by_id.find(mod_id);
1082+
if (find_it == opened_mods_by_id.end()) {
1083+
return {};
1084+
}
1085+
1086+
size_t mod_index = find_it->second;
1087+
const ModHandle &mod = opened_mods[mod_index];
1088+
return mod.get_details();
1089+
}
1090+
1091+
std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_all_mod_details(const std::string &mod_game_id) {
10701092
std::vector<ModDetails> ret{};
10711093
bool all_games = mod_game_id.empty();
10721094
size_t game_index = (size_t)-1;
@@ -1081,16 +1103,7 @@ std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(
10811103
if (all_games || mod.is_for_game(game_index)) {
10821104
std::vector<Dependency> cur_dependencies{};
10831105

1084-
ret.emplace_back(ModDetails{
1085-
.mod_id = mod.manifest.mod_id,
1086-
.display_name = mod.manifest.display_name,
1087-
.description = mod.manifest.description,
1088-
.short_description = mod.manifest.short_description,
1089-
.version = mod.manifest.version,
1090-
.authors = mod.manifest.authors,
1091-
.dependencies = mod.manifest.dependencies,
1092-
.runtime_toggleable = mod.is_runtime_toggleable()
1093-
});
1106+
ret.emplace_back(mod.get_details());
10941107
}
10951108
}
10961109

librecomp/src/recomp.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ void recomp::mods::scan_mods() {
103103
mod_context->load_mods_config();
104104
}
105105

106+
void recomp::mods::close_mods() {
107+
{
108+
std::lock_guard mod_lock{ mod_context_mutex };
109+
mod_context->close_mods();
110+
}
111+
}
112+
106113
std::filesystem::path recomp::mods::get_mods_directory() {
107114
return config_path / mods_directory;
108115
}
@@ -550,9 +557,19 @@ recomp::mods::ConfigValueVariant recomp::mods::get_mod_config_value(const std::s
550557
return mod_context->get_mod_config_value(mod_id, option_id);
551558
}
552559

553-
std::vector<recomp::mods::ModDetails> recomp::mods::get_mod_details(const std::string& mod_game_id) {
560+
std::string recomp::mods::get_mod_id_from_filename(const std::filesystem::path& mod_filename) {
561+
std::lock_guard lock { mod_context_mutex };
562+
return mod_context->get_mod_id_from_filename(mod_filename);
563+
}
564+
565+
std::optional<recomp::mods::ModDetails> recomp::mods::get_details_for_mod(const std::string& mod_id) {
566+
std::lock_guard lock { mod_context_mutex };
567+
return mod_context->get_details_for_mod(mod_id);
568+
}
569+
570+
std::vector<recomp::mods::ModDetails> recomp::mods::get_all_mod_details(const std::string& mod_game_id) {
554571
std::lock_guard lock { mod_context_mutex };
555-
return mod_context->get_mod_details(mod_game_id);
572+
return mod_context->get_all_mod_details(mod_game_id);
556573
}
557574

558575
void recomp::mods::set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index) {

0 commit comments

Comments
 (0)