mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-11 03:12:15 +00:00
Add dependencies and authors to manifest and update N64Recomp submodule
This commit is contained in:
parent
986881e4e1
commit
b9592c625d
5 changed files with 121 additions and 41 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit 747cd9f6acc09d20ea9a8148def8cea88728a5cb
|
||||
Subproject commit b8dcb21dec80048b2530b4258b57a87a37343008
|
||||
|
|
@ -34,7 +34,7 @@ namespace recomp {
|
|||
|
||||
static bool from_string(const std::string& str, Version& out);
|
||||
|
||||
auto operator<=>(const Version& rhs) {
|
||||
auto operator<=>(const Version& rhs) const {
|
||||
if (major != rhs.major) {
|
||||
return major <=> rhs.major;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ namespace recomp {
|
|||
IncorrectManifestFieldType,
|
||||
InvalidVersionString,
|
||||
InvalidMinimumRecompVersionString,
|
||||
InvalidDependencyString,
|
||||
MissingManifestField,
|
||||
DuplicateMod,
|
||||
WrongGame
|
||||
|
|
@ -65,6 +66,7 @@ namespace recomp {
|
|||
InvalidFunctionReplacement,
|
||||
FailedToFindReplacement,
|
||||
ReplacementConflict,
|
||||
MissingDependencyInManifest,
|
||||
MissingDependency,
|
||||
WrongDependencyVersion,
|
||||
ModConflict,
|
||||
|
|
@ -109,7 +111,7 @@ namespace recomp {
|
|||
std::vector<std::string> exports;
|
||||
};
|
||||
|
||||
struct DependencyDetails {
|
||||
struct Dependency {
|
||||
std::string mod_id;
|
||||
Version version;
|
||||
};
|
||||
|
|
@ -118,7 +120,7 @@ namespace recomp {
|
|||
std::string mod_id;
|
||||
Version version;
|
||||
std::vector<std::string> authors;
|
||||
std::vector<DependencyDetails> dependencies;
|
||||
std::vector<Dependency> dependencies;
|
||||
};
|
||||
|
||||
struct ModManifest {
|
||||
|
|
@ -126,6 +128,9 @@ namespace recomp {
|
|||
|
||||
std::vector<std::string> mod_game_ids;
|
||||
std::string mod_id;
|
||||
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;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "json/json.hpp"
|
||||
|
||||
#include "n64recomp.h"
|
||||
#include "librecomp/mods.hpp"
|
||||
|
||||
recomp::mods::ZipModFileHandle::~ZipModFileHandle() {
|
||||
|
|
@ -134,21 +135,27 @@ enum class ManifestField {
|
|||
GameModId,
|
||||
Id,
|
||||
Version,
|
||||
Authors,
|
||||
MinimumRecompVersion,
|
||||
Dependencies,
|
||||
NativeLibraries,
|
||||
};
|
||||
|
||||
const std::string game_mod_id_key = "game_id";
|
||||
const std::string mod_id_key = "id";
|
||||
const std::string version_key = "version";
|
||||
const std::string authors_key = "authors";
|
||||
const std::string minimum_recomp_version_key = "minimum_recomp_version";
|
||||
const std::string dependencies_key = "dependencies";
|
||||
const std::string native_libraries_key = "native_libraries";
|
||||
|
||||
std::unordered_map<std::string, ManifestField> field_map {
|
||||
{ game_mod_id_key, ManifestField::GameModId },
|
||||
{ mod_id_key, ManifestField::Id },
|
||||
{ version_key, ManifestField::Version },
|
||||
{ authors_key, ManifestField::Authors },
|
||||
{ minimum_recomp_version_key, ManifestField::MinimumRecompVersion },
|
||||
{ dependencies_key, ManifestField::Dependencies },
|
||||
{ native_libraries_key, ManifestField::NativeLibraries },
|
||||
};
|
||||
|
||||
|
|
@ -185,6 +192,38 @@ bool get_to_vec(const nlohmann::json& val, std::vector<T2>& out) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool parse_dependency(const std::string& val, recomp::mods::Dependency& out) {
|
||||
recomp::mods::Dependency ret;
|
||||
|
||||
bool validated_name;
|
||||
bool validated_version;
|
||||
|
||||
// Check if there's a version number specified.
|
||||
size_t colon_pos = val.find(':');
|
||||
if (colon_pos == std::string::npos) {
|
||||
// No version present, so just validate the dependency's id.
|
||||
validated_name = N64Recomp::validate_mod_id(std::string_view{val});
|
||||
ret.mod_id = val;
|
||||
validated_version = true;
|
||||
ret.version.minor = 0;
|
||||
ret.version.major = 0;
|
||||
ret.version.patch = 0;
|
||||
}
|
||||
else {
|
||||
// Version present, validate both the id and version.
|
||||
ret.mod_id = val.substr(0, colon_pos);
|
||||
validated_name = N64Recomp::validate_mod_id(ret.mod_id);
|
||||
validated_version = recomp::Version::from_string(val.substr(colon_pos + 1), ret.version);
|
||||
}
|
||||
|
||||
if (validated_name && validated_version) {
|
||||
out = std::move(ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
recomp::mods::ModOpenError parse_manifest(recomp::mods::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);
|
||||
|
|
@ -237,6 +276,12 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
|
|||
}
|
||||
}
|
||||
break;
|
||||
case ManifestField::Authors:
|
||||
if (!get_to_vec<std::string>(val, ret.authors)) {
|
||||
error_param = key;
|
||||
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
|
||||
}
|
||||
break;
|
||||
case ManifestField::MinimumRecompVersion:
|
||||
{
|
||||
const std::string* version_str = val.get_ptr<const std::string*>();
|
||||
|
|
@ -251,6 +296,27 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
|
|||
ret.minimum_recomp_version.suffix.clear();
|
||||
}
|
||||
break;
|
||||
case ManifestField::Dependencies:
|
||||
{
|
||||
std::vector<std::string> dep_strings{};
|
||||
if (!get_to_vec<std::string>(val, dep_strings)) {
|
||||
error_param = key;
|
||||
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
|
||||
}
|
||||
|
||||
for (const std::string& dep_string : dep_strings) {
|
||||
recomp::mods::Dependency cur_dep;
|
||||
if (!parse_dependency(dep_string, cur_dep)) {
|
||||
error_param = dep_string;
|
||||
return recomp::mods::ModOpenError::InvalidDependencyString;
|
||||
}
|
||||
|
||||
size_t dependency_index = ret.dependencies.size();
|
||||
ret.dependencies_by_id.emplace(cur_dep.mod_id, dependency_index);
|
||||
ret.dependencies.emplace_back(std::move(cur_dep));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ManifestField::NativeLibraries:
|
||||
{
|
||||
if (!val.is_object()) {
|
||||
|
|
@ -290,6 +356,10 @@ recomp::mods::ModOpenError validate_manifest(const recomp::mods::ModManifest& ma
|
|||
error_param = version_key;
|
||||
return ModOpenError::MissingManifestField;
|
||||
}
|
||||
if (manifest.authors.empty()) {
|
||||
error_param = authors_key;
|
||||
return ModOpenError::MissingManifestField;
|
||||
}
|
||||
if (manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1 || manifest.minimum_recomp_version.major == -1) {
|
||||
error_param = minimum_recomp_version_key;
|
||||
return ModOpenError::MissingManifestField;
|
||||
|
|
@ -445,6 +515,8 @@ std::string recomp::mods::error_to_string(ModLoadError error) {
|
|||
return "Failed to find replacement function";
|
||||
case ModLoadError::ReplacementConflict:
|
||||
return "Attempted to replace a function that cannot be replaced";
|
||||
case ModLoadError::MissingDependencyInManifest:
|
||||
return "Dependency is present in mod symbols but not in the manifest";
|
||||
case ModLoadError::MissingDependency:
|
||||
return "Missing dependency";
|
||||
case ModLoadError::WrongDependencyVersion:
|
||||
|
|
|
|||
|
|
@ -426,22 +426,13 @@ std::vector<recomp::mods::ModDetails> recomp::mods::ModContext::get_mod_details(
|
|||
|
||||
for (const ModHandle& mod : opened_mods) {
|
||||
if (all_games || mod.is_for_game(game_index)) {
|
||||
std::vector<DependencyDetails> cur_dependencies{};
|
||||
|
||||
// TODO the recompiler context isn't available at this point, since it's parsed on mod load.
|
||||
// Move that parsing to mod opening so it can be used here.
|
||||
// for (const auto& cur_dep : mod.recompiler_context->dependencies) {
|
||||
// cur_dependencies.emplace_back(DependencyDetails{
|
||||
// .mod_id = cur_dep.mod_id,
|
||||
// .version = Version{.major = cur_dep.major_version, .minor = cur_dep.minor_version, .patch = cur_dep.patch_version}
|
||||
// });
|
||||
// }
|
||||
std::vector<Dependency> cur_dependencies{};
|
||||
|
||||
ret.emplace_back(ModDetails{
|
||||
.mod_id = mod.manifest.mod_id,
|
||||
.version = mod.manifest.version,
|
||||
.authors = {}, // TODO add mod authors to the manifest and copy them here
|
||||
.dependencies = std::move(cur_dependencies)
|
||||
.authors = mod.manifest.authors,
|
||||
.dependencies = mod.manifest.dependencies
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -558,30 +549,34 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo
|
|||
|
||||
void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod, std::vector<std::pair<recomp::mods::ModLoadError, std::string>>& errors) {
|
||||
errors.clear();
|
||||
for (N64Recomp::Dependency& cur_dep : mod.recompiler_context->dependencies) {
|
||||
for (const auto& [cur_dep_id, cur_dep_index] : mod.recompiler_context->dependencies_by_name) {
|
||||
// Handle special dependency names.
|
||||
if (cur_dep.mod_id == N64Recomp::DependencyBaseRecomp || cur_dep.mod_id == N64Recomp::DependencySelf) {
|
||||
if (cur_dep_id == N64Recomp::DependencyBaseRecomp || cur_dep_id == N64Recomp::DependencySelf) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the dependency in the mod manifest to get its version.
|
||||
auto find_manifest_dep_it = mod.manifest.dependencies_by_id.find(cur_dep_id);
|
||||
if (find_manifest_dep_it == mod.manifest.dependencies_by_id.end()) {
|
||||
errors.emplace_back(ModLoadError::MissingDependencyInManifest, cur_dep_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& cur_dep = mod.manifest.dependencies[find_manifest_dep_it->second];
|
||||
|
||||
// Look for the dependency in the loaded mod mapping.
|
||||
auto find_it = loaded_mods_by_id.find(cur_dep.mod_id);
|
||||
if (find_it == loaded_mods_by_id.end()) {
|
||||
errors.emplace_back(ModLoadError::MissingDependency, cur_dep.mod_id);
|
||||
auto find_loaded_dep_it = loaded_mods_by_id.find(cur_dep_id);
|
||||
if (find_loaded_dep_it == loaded_mods_by_id.end()) {
|
||||
errors.emplace_back(ModLoadError::MissingDependency, cur_dep_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ModHandle& dep_mod = opened_mods[find_it->second];
|
||||
Version dep_version {
|
||||
.major = cur_dep.major_version,
|
||||
.minor = cur_dep.minor_version,
|
||||
.patch = cur_dep.patch_version
|
||||
};
|
||||
if (dep_version > dep_mod.manifest.version)
|
||||
const ModHandle& dep_mod = opened_mods[find_loaded_dep_it->second];
|
||||
if (cur_dep.version > dep_mod.manifest.version)
|
||||
{
|
||||
std::stringstream error_param_stream{};
|
||||
error_param_stream << "requires mod \"" << cur_dep.mod_id << "\" " <<
|
||||
(int)cur_dep.major_version << "." << (int)cur_dep.minor_version << "." << (int)cur_dep.patch_version << ", got " <<
|
||||
(int)cur_dep.version.major << "." << (int)cur_dep.version.minor << "." << (int)cur_dep.version.patch << ", got " <<
|
||||
(int)dep_mod.manifest.version.major << "." << (int)dep_mod.manifest.version.minor << "." << (int)dep_mod.manifest.version.patch << "";
|
||||
errors.emplace_back(ModLoadError::WrongDependencyVersion, error_param_stream.str());
|
||||
}
|
||||
|
|
@ -666,26 +661,34 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp
|
|||
mod.code_handle->set_reference_symbol_pointer(reference_sym_index, found_func);
|
||||
}
|
||||
|
||||
// Create a list of dependencies ordered by their index in the recompiler context.
|
||||
std::vector<std::string> dependencies_ordered{};
|
||||
dependencies_ordered.resize(mod.recompiler_context->dependencies_by_name.size());
|
||||
|
||||
for (const auto& [dependency, dependency_index] : mod.recompiler_context->dependencies_by_name) {
|
||||
dependencies_ordered[dependency_index] = dependency;
|
||||
}
|
||||
|
||||
// Imported symbols.
|
||||
for (size_t import_index = 0; import_index < mod.recompiler_context->import_symbols.size(); import_index++) {
|
||||
const N64Recomp::ImportSymbol& imported_func = mod.recompiler_context->import_symbols[import_index];
|
||||
const N64Recomp::Dependency& dependency = mod.recompiler_context->dependencies[imported_func.dependency_index];
|
||||
const std::string& dependency_id = dependencies_ordered[imported_func.dependency_index];
|
||||
|
||||
GenericFunction func_handle{};
|
||||
bool did_find_func = false;
|
||||
|
||||
if (dependency.mod_id == N64Recomp::DependencyBaseRecomp) {
|
||||
if (dependency_id == N64Recomp::DependencyBaseRecomp) {
|
||||
recomp_func_t* func_ptr = recomp::overlays::get_base_export(imported_func.base.name);
|
||||
did_find_func = func_ptr != nullptr;
|
||||
func_handle = func_ptr;
|
||||
}
|
||||
else if (dependency.mod_id == N64Recomp::DependencySelf) {
|
||||
else if (dependency_id == N64Recomp::DependencySelf) {
|
||||
did_find_func = mod.get_export_function(imported_func.base.name, func_handle);
|
||||
}
|
||||
else {
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency.mod_id);
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency_id);
|
||||
if (find_mod_it == loaded_mods_by_id.end()) {
|
||||
error_param = dependency.mod_id;
|
||||
error_param = dependency_id;
|
||||
return ModLoadError::MissingDependency;
|
||||
}
|
||||
const auto& dependency = opened_mods[find_mod_it->second];
|
||||
|
|
@ -693,7 +696,7 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp
|
|||
}
|
||||
|
||||
if (!did_find_func) {
|
||||
error_param = dependency.mod_id + ":" + imported_func.base.name;
|
||||
error_param = dependency_id + ":" + imported_func.base.name;
|
||||
return ModLoadError::InvalidImport;
|
||||
}
|
||||
|
||||
|
|
@ -703,24 +706,24 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp
|
|||
// Register callbacks.
|
||||
for (const N64Recomp::Callback& callback : mod.recompiler_context->callbacks) {
|
||||
const N64Recomp::DependencyEvent& dependency_event = mod.recompiler_context->dependency_events[callback.dependency_event_index];
|
||||
const N64Recomp::Dependency& dependency = mod.recompiler_context->dependencies[dependency_event.dependency_index];
|
||||
const std::string& dependency_id = dependencies_ordered[dependency_event.dependency_index];
|
||||
GenericFunction func = mod.code_handle->get_function_handle(callback.function_index);
|
||||
size_t event_index = 0;
|
||||
bool did_find_event = false;
|
||||
|
||||
if (dependency.mod_id == N64Recomp::DependencyBaseRecomp) {
|
||||
if (dependency_id == N64Recomp::DependencyBaseRecomp) {
|
||||
event_index = recomp::overlays::get_base_event_index(dependency_event.event_name);
|
||||
if (event_index != (size_t)-1) {
|
||||
did_find_event = true;
|
||||
}
|
||||
}
|
||||
else if (dependency.mod_id == N64Recomp::DependencySelf) {
|
||||
else if (dependency_id == N64Recomp::DependencySelf) {
|
||||
did_find_event = mod.get_global_event_index(dependency_event.event_name, event_index);
|
||||
}
|
||||
else {
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency.mod_id);
|
||||
auto find_mod_it = loaded_mods_by_id.find(dependency_id);
|
||||
if (find_mod_it == loaded_mods_by_id.end()) {
|
||||
error_param = dependency.mod_id;
|
||||
error_param = dependency_id;
|
||||
return ModLoadError::MissingDependency;
|
||||
}
|
||||
const auto& dependency_mod = opened_mods[find_mod_it->second];
|
||||
|
|
@ -728,7 +731,7 @@ recomp::mods::ModLoadError recomp::mods::ModContext::resolve_dependencies(recomp
|
|||
}
|
||||
|
||||
if (!did_find_event) {
|
||||
error_param = dependency.mod_id + ":" + dependency_event.event_name;
|
||||
error_param = dependency_id + ":" + dependency_event.event_name;
|
||||
return ModLoadError::InvalidCallbackEvent;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue