Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions librecomp/include/librecomp/mods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <cstddef>
#include <variant>
#include <mutex>
#include <optional>

#include "blockingconcurrentqueue.h"

Expand Down Expand Up @@ -259,7 +260,9 @@ namespace recomp {
mod_id(mod_id_), error(error_), error_param(error_param_) {}
};

std::vector<ModDetails> get_mod_details(const std::string& mod_game_id);
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename);
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);
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.
Expand Down Expand Up @@ -324,14 +327,17 @@ namespace recomp {

void register_game(const std::string& mod_game_id);
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);
bool is_mod_auto_enabled(const std::string& mod_id);
size_t num_opened_mods();
std::vector<ModLoadErrorDetails> load_mods(const GameEntry& game_entry, uint8_t* rdram, int32_t load_address, uint32_t& ram_used);
void unload_mods();
std::vector<ModDetails> get_mod_details(const std::string& mod_game_id);
std::string get_mod_id_from_filename(const std::filesystem::path& mod_filename) 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);
void set_mod_index(const std::string &mod_game_id, const std::string &mod_id, size_t index);
const ConfigSchema &get_mod_config_schema(const std::string &mod_id) const;
const std::vector<char> &get_mod_thumbnail(const std::string &mod_id) const;
Expand All @@ -353,7 +359,6 @@ namespace recomp {
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, ConfigStorage&& config_storage, std::vector<size_t>&& game_indices, std::vector<ModContentTypeId>&& detected_content_types, std::vector<char>&& thumbnail);
void close_mods();
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,
Expand All @@ -369,6 +374,7 @@ namespace recomp {
std::unordered_map<std::string, size_t> mod_game_ids;
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;
std::mutex opened_mods_mutex;
std::unordered_set<std::string> mod_ids;
Expand Down Expand Up @@ -453,6 +459,19 @@ namespace recomp {
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()
};
}
private:
// Mapping of export name to function index.
std::unordered_map<std::string, size_t> exports_by_name;
Expand Down Expand Up @@ -553,10 +572,12 @@ namespace recomp {
void reset_hooks();
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);
CodeModLoadError validate_api_version(uint32_t api_version, std::string& error_param);

void initialize_mods();
void scan_mods();
void close_mods();
std::filesystem::path get_mods_directory();
void enable_mod(const std::string& mod_id, bool enabled);
bool is_mod_enabled(const std::string& mod_id);
Expand All @@ -570,7 +591,6 @@ namespace recomp {
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);


void register_config_exports();
}
};
Expand Down
52 changes: 26 additions & 26 deletions librecomp/src/mod_manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,81 +509,81 @@ recomp::mods::ModOpenError parse_manifest_config_schema_option(const nlohmann::j
return recomp::mods::ModOpenError::Good;
}

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

if (manifest_json.is_discarded()) {
return recomp::mods::ModOpenError::FailedToParseManifest;
return ModOpenError::FailedToParseManifest;
}

if (!manifest_json.is_object()) {
return recomp::mods::ModOpenError::InvalidManifestSchema;
return ModOpenError::InvalidManifestSchema;
}

recomp::mods::ModOpenError current_error = recomp::mods::ModOpenError::Good;
ModOpenError current_error = ModOpenError::Good;

// Mod Game ID
std::string mod_game_id{};
current_error = try_get<json::string_t>(mod_game_id, manifest_json, game_mod_id_key, true, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}
ret.mod_game_ids.emplace_back(std::move(mod_game_id));

// Mod ID
current_error = try_get<json::string_t>(ret.mod_id, manifest_json, mod_id_key, true, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}

// Display name
current_error = try_get<json::string_t>(ret.display_name, manifest_json, display_name_key, true, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}

// Description (optional)
current_error = try_get<json::string_t>(ret.description, manifest_json, description_key, false, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}

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

// Version
current_error = try_get_version(ret.version, manifest_json, version_key, error_param, recomp::mods::ModOpenError::InvalidVersionString);
if (current_error != recomp::mods::ModOpenError::Good) {
current_error = try_get_version(ret.version, manifest_json, version_key, error_param, ModOpenError::InvalidVersionString);
if (current_error != ModOpenError::Good) {
return current_error;
}

// Authors
current_error = try_get_vec<json::string_t>(ret.authors, manifest_json, authors_key, true, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}

// Minimum recomp version
current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, recomp::mods::ModOpenError::InvalidMinimumRecompVersionString);
if (current_error != recomp::mods::ModOpenError::Good) {
current_error = try_get_version(ret.minimum_recomp_version, manifest_json, minimum_recomp_version_key, error_param, ModOpenError::InvalidMinimumRecompVersionString);
if (current_error != ModOpenError::Good) {
return current_error;
}

// Dependencies (optional)
std::vector<std::string> dep_strings{};
current_error = try_get_vec<json::string_t>(dep_strings, manifest_json, dependencies_key, false, error_param);
if (current_error != recomp::mods::ModOpenError::Good) {
if (current_error != ModOpenError::Good) {
return current_error;
}
for (const std::string& dep_string : dep_strings) {
recomp::mods::Dependency cur_dep;
Dependency cur_dep;
if (!parse_dependency(dep_string, cur_dep)) {
error_param = dep_string;
return recomp::mods::ModOpenError::InvalidDependencyString;
return ModOpenError::InvalidDependencyString;
}

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

cur_lib.name = lib_name;
if (!get_to_vec<std::string>(lib_exports, cur_lib.exports)) {
error_param = native_libraries_key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
return ModOpenError::IncorrectManifestFieldType;
}
}
}
Expand All @@ -616,30 +616,30 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
auto& val = *find_config_schema_it;
if (!val.is_object()) {
error_param = config_schema_key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
return ModOpenError::IncorrectManifestFieldType;
}

auto options = val.find(config_schema_options_key);
if (options != val.end()) {
if (!options->is_array()) {
error_param = config_schema_options_key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
return ModOpenError::IncorrectManifestFieldType;
}

for (const json &option : *options) {
recomp::mods::ModOpenError open_error = parse_manifest_config_schema_option(option, ret, error_param);
if (open_error != recomp::mods::ModOpenError::Good) {
ModOpenError open_error = parse_manifest_config_schema_option(option, ret, error_param);
if (open_error != ModOpenError::Good) {
return open_error;
}
}
}
else {
error_param = config_schema_options_key;
return recomp::mods::ModOpenError::MissingConfigSchemaField;
return ModOpenError::MissingConfigSchemaField;
}
}

return recomp::mods::ModOpenError::Good;
return ModOpenError::Good;
}

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) {
Expand Down
35 changes: 24 additions & 11 deletions librecomp/src/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ void recomp::mods::ModContext::add_opened_mod(ModManifest&& manifest, ConfigStor
std::unique_lock lock(opened_mods_mutex);
size_t mod_index = opened_mods.size();
opened_mods_by_id.emplace(manifest.mod_id, mod_index);
opened_mods_by_filename.emplace(manifest.mod_root_path.filename().native(), mod_index);
opened_mods.emplace_back(*this, std::move(manifest), std::move(config_storage), std::move(game_indices), std::move(detected_content_types), std::move(thumbnail));
opened_mods_order.emplace_back(mod_index);
}
Expand Down Expand Up @@ -626,6 +627,7 @@ void recomp::mods::ModContext::register_game(const std::string& mod_game_id) {
void recomp::mods::ModContext::close_mods() {
std::unique_lock lock(opened_mods_mutex);
opened_mods_by_id.clear();
opened_mods_by_filename.clear();
opened_mods.clear();
opened_mods_order.clear();
mod_ids.clear();
Expand Down Expand Up @@ -1066,7 +1068,27 @@ size_t recomp::mods::ModContext::num_opened_mods() {
return opened_mods.size();
}

std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(const std::string &mod_game_id) {
std::string recomp::mods::ModContext::get_mod_id_from_filename(const std::filesystem::path& filename) const {
auto find_it = opened_mods_by_filename.find(filename.native());
if (find_it == opened_mods_by_filename.end()) {
return {};
}

return opened_mods[find_it->second].manifest.mod_id;
}

std::optional<recomp::mods::ModDetails> recomp::mods::ModContext::get_details_for_mod(const std::string& mod_id) const {
auto find_it = opened_mods_by_id.find(mod_id);
if (find_it == opened_mods_by_id.end()) {
return {};
}

size_t mod_index = find_it->second;
const ModHandle &mod = opened_mods[mod_index];
return mod.get_details();
}

std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_all_mod_details(const std::string &mod_game_id) {
std::vector<ModDetails> ret{};
bool all_games = mod_game_id.empty();
size_t game_index = (size_t)-1;
Expand All @@ -1081,16 +1103,7 @@ std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(
if (all_games || mod.is_for_game(game_index)) {
std::vector<Dependency> cur_dependencies{};

ret.emplace_back(ModDetails{
.mod_id = mod.manifest.mod_id,
.display_name = mod.manifest.display_name,
.description = mod.manifest.description,
.short_description = mod.manifest.short_description,
.version = mod.manifest.version,
.authors = mod.manifest.authors,
.dependencies = mod.manifest.dependencies,
.runtime_toggleable = mod.is_runtime_toggleable()
});
ret.emplace_back(mod.get_details());
}
}

Expand Down
21 changes: 19 additions & 2 deletions librecomp/src/recomp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ void recomp::mods::scan_mods() {
mod_context->load_mods_config();
}

void recomp::mods::close_mods() {
{
std::lock_guard mod_lock{ mod_context_mutex };
mod_context->close_mods();
}
}

std::filesystem::path recomp::mods::get_mods_directory() {
return config_path / mods_directory;
}
Expand Down Expand Up @@ -550,9 +557,19 @@ recomp::mods::ConfigValueVariant recomp::mods::get_mod_config_value(const std::s
return mod_context->get_mod_config_value(mod_id, option_id);
}

std::vector<recomp::mods::ModDetails> recomp::mods::get_mod_details(const std::string& mod_game_id) {
std::string recomp::mods::get_mod_id_from_filename(const std::filesystem::path& mod_filename) {
std::lock_guard lock { mod_context_mutex };
return mod_context->get_mod_id_from_filename(mod_filename);
}

std::optional<recomp::mods::ModDetails> recomp::mods::get_details_for_mod(const std::string& mod_id) {
std::lock_guard lock { mod_context_mutex };
return mod_context->get_details_for_mod(mod_id);
}

std::vector<recomp::mods::ModDetails> recomp::mods::get_all_mod_details(const std::string& mod_game_id) {
std::lock_guard lock { mod_context_mutex };
return mod_context->get_mod_details(mod_game_id);
return mod_context->get_all_mod_details(mod_game_id);
}

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