Skip to content

Commit

Permalink
Added version parsing with version number as argument to recomp initi…
Browse files Browse the repository at this point in the history
…alization and minimum recomp versions for mods
  • Loading branch information
Mr-Wiseguy committed Sep 3, 2024
1 parent 5699906 commit 09f5759
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 69 deletions.
24 changes: 24 additions & 0 deletions librecomp/include/librecomp/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,28 @@ namespace recomp {

std::u8string stored_filename() const;
};
struct Version {
int major = -1;
int minor = -1;
int patch = -1;
std::string suffix;

std::string to_string() const {
return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch) + suffix;
}

static bool from_string(const std::string& str, Version& out);

auto operator<=>(const Version& rhs) {
if (major != rhs.major) {
return major <=> rhs.major;
}
if (minor != rhs.minor) {
return minor <=> rhs.minor;
}
return patch <=> rhs.patch;
}
};
enum class RomValidationError {
Good,
FailedToOpen,
Expand All @@ -41,6 +63,7 @@ namespace recomp {
void set_rom_contents(std::vector<uint8_t>&& new_rom);
void do_rom_read(uint8_t* rdram, gpr ram_address, uint32_t physical_addr, size_t num_bytes);
void do_rom_pio(uint8_t* rdram, gpr ram_address, uint32_t physical_addr);
const Version& get_project_version();

/**
* The following arguments contain mandatory callbacks that need to be registered (i.e., can't be `nullptr`):
Expand All @@ -51,6 +74,7 @@ namespace recomp {
*/
void start(
uint32_t rdram_size,
const Version& project_version,
ultramodern::renderer::WindowHandle window_handle,
const recomp::rsp::callbacks_t& rsp_callbacks,
const ultramodern::renderer::callbacks_t& renderer_callbacks,
Expand Down
22 changes: 18 additions & 4 deletions librecomp/include/librecomp/mods.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "miniz.h"
#include "miniz_zip.h"

#include "librecomp/game.hpp"
#include "librecomp/recomp.h"
#include "librecomp/sections.h"

Expand All @@ -39,6 +40,8 @@ namespace recomp {
InvalidManifestSchema,
UnrecognizedManifestField,
IncorrectManifestFieldType,
InvalidVersionString,
InvalidMinimumRecompVersionString,
MissingManifestField,
InnerFileDoesNotExist,
DuplicateMod,
Expand All @@ -50,6 +53,7 @@ namespace recomp {
enum class ModLoadError {
Good,
InvalidGame,
MinimumRecompVersionNotMet,
FailedToLoadSyms,
FailedToLoadBinary,
FailedToLoadNativeCode,
Expand Down Expand Up @@ -105,15 +109,25 @@ namespace recomp {
std::vector<std::string> exports;
};

struct DependencyDetails {
std::string mod_id;
Version version;
};

struct ModDetails {
std::string mod_id;
Version version;
std::vector<std::string> authors;
std::vector<DependencyDetails> dependencies;
};

struct ModManifest {
std::filesystem::path mod_root_path;

std::vector<std::string> mod_game_ids;
std::string mod_id;

int major_version = -1;
int minor_version = -1;
int patch_version = -1;
Version minimum_recomp_version;
Version version;

// These are all relative to the base path for loose mods or inside the zip for zipped mods.
std::string binary_path;
Expand Down
84 changes: 46 additions & 38 deletions librecomp/src/mod_manifest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,8 @@ bool recomp::mods::LooseModFileHandle::file_exists(const std::string& filepath)
enum class ManifestField {
GameModId,
Id,
MajorVersion,
MinorVersion,
PatchVersion,
Version,
MinimumRecompVersion,
BinaryPath,
BinarySymsPath,
RomPatchPath,
Expand All @@ -145,26 +144,24 @@ enum class ManifestField {

const std::string game_mod_id_key = "game_id";
const std::string mod_id_key = "id";
const std::string major_version_key = "major_version";
const std::string minor_version_key = "minor_version";
const std::string patch_version_key = "patch_version";
const std::string version_key = "version";
const std::string minimum_recomp_version_key = "minimum_recomp_version";
const std::string binary_path_key = "binary";
const std::string binary_syms_path_key = "binary_syms";
const std::string rom_patch_path_key = "rom_patch";
const std::string rom_patch_syms_path_key = "rom_patch_syms";
const std::string native_library_paths_key = "native_libraries";

std::unordered_map<std::string, ManifestField> field_map {
{ game_mod_id_key, ManifestField::GameModId },
{ mod_id_key, ManifestField::Id },
{ major_version_key, ManifestField::MajorVersion },
{ minor_version_key, ManifestField::MinorVersion },
{ patch_version_key, ManifestField::PatchVersion },
{ binary_path_key, ManifestField::BinaryPath },
{ binary_syms_path_key, ManifestField::BinarySymsPath },
{ rom_patch_path_key, ManifestField::RomPatchPath },
{ rom_patch_syms_path_key, ManifestField::RomPatchSymsPath },
{ native_library_paths_key, ManifestField::NativeLibraryPaths },
{ game_mod_id_key, ManifestField::GameModId },
{ mod_id_key, ManifestField::Id },
{ version_key, ManifestField::Version },
{ minimum_recomp_version_key, ManifestField::MinimumRecompVersion },
{ binary_path_key, ManifestField::BinaryPath },
{ binary_syms_path_key, ManifestField::BinarySymsPath },
{ rom_patch_path_key, ManifestField::RomPatchPath },
{ rom_patch_syms_path_key, ManifestField::RomPatchSymsPath },
{ native_library_paths_key, ManifestField::NativeLibraryPaths },
};

template <typename T1, typename T2>
Expand Down Expand Up @@ -239,22 +236,31 @@ recomp::mods::ModOpenError parse_manifest(recomp::mods::ModManifest& ret, const
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
}
break;
case ManifestField::MajorVersion:
if (!get_to<json::number_unsigned_t>(val, ret.major_version)) {
error_param = key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
}
break;
case ManifestField::MinorVersion:
if (!get_to<json::number_unsigned_t>(val, ret.minor_version)) {
error_param = key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
case ManifestField::Version:
{
const std::string* version_str = val.get_ptr<const std::string*>();
if (version_str == nullptr) {
error_param = key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
}
if (!recomp::Version::from_string(*version_str, ret.version)) {
error_param = *version_str;
return recomp::mods::ModOpenError::InvalidVersionString;
}
}
break;
case ManifestField::PatchVersion:
if (!get_to<json::number_unsigned_t>(val, ret.patch_version)) {
error_param = key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
case ManifestField::MinimumRecompVersion:
{
const std::string* version_str = val.get_ptr<const std::string*>();
if (version_str == nullptr) {
error_param = key;
return recomp::mods::ModOpenError::IncorrectManifestFieldType;
}
if (!recomp::Version::from_string(*version_str, ret.minimum_recomp_version)) {
error_param = *version_str;
return recomp::mods::ModOpenError::InvalidMinimumRecompVersionString;
}
ret.minimum_recomp_version.suffix.clear();
}
break;
case ManifestField::BinaryPath:
Expand Down Expand Up @@ -328,16 +334,12 @@ recomp::mods::ModOpenError validate_manifest(const recomp::mods::ModManifest& ma
error_param = mod_id_key;
return ModOpenError::MissingManifestField;
}
if (manifest.major_version == -1) {
error_param = major_version_key;
return ModOpenError::MissingManifestField;
}
if (manifest.minor_version == -1) {
error_param = minor_version_key;
if (manifest.version.major == -1 || manifest.version.major == -1 || manifest.version.major == -1) {
error_param = version_key;
return ModOpenError::MissingManifestField;
}
if (manifest.patch_version == -1) {
error_param = patch_version_key;
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;
}

Expand Down Expand Up @@ -477,6 +479,10 @@ std::string recomp::mods::error_to_string(ModOpenError error) {
return "Unrecognized field in manifest.json";
case ModOpenError::IncorrectManifestFieldType:
return "Incorrect type for field in manifest.json";
case ModOpenError::InvalidVersionString:
return "Invalid version string in manifest.json";
case ModOpenError::InvalidMinimumRecompVersionString:
return "Invalid minimum recomp version string in manifest.json";
case ModOpenError::MissingManifestField:
return "Missing required field in manifest";
case ModOpenError::InnerFileDoesNotExist:
Expand All @@ -495,6 +501,8 @@ std::string recomp::mods::error_to_string(ModLoadError error) {
return "Good";
case ModLoadError::InvalidGame:
return "Invalid game";
case ModLoadError::MinimumRecompVersionNotMet:
return "Mod requires a newer version of this project";
case ModLoadError::FailedToLoadSyms:
return "Failed to load mod symbol file";
case ModLoadError::FailedToLoadBinary:
Expand Down
42 changes: 15 additions & 27 deletions librecomp/src/mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,12 @@ void recomp::mods::ModContext::add_opened_mod(ModManifest&& manifest, std::vecto
recomp::mods::ModLoadError recomp::mods::ModContext::load_mod(uint8_t* rdram, const std::unordered_map<uint32_t, uint16_t>& section_vrom_map, recomp::mods::ModHandle& handle, int32_t load_address, uint32_t& ram_used, std::string& error_param) {
using namespace recomp::mods;
handle.section_load_addresses.clear();

// Check that the mod's minimum recomp version is met.
if (get_project_version() < handle.manifest.minimum_recomp_version) {
error_param = handle.manifest.minimum_recomp_version.to_string();
return recomp::mods::ModLoadError::MinimumRecompVersionNotMet;
}

// Load the mod symbol data from the file provided in the manifest.
bool binary_syms_exists = false;
Expand Down Expand Up @@ -365,7 +371,7 @@ std::vector<recomp::mods::ModOpenErrorDetails> recomp::mods::ModContext::scan_mo
std::vector<recomp::mods::ModOpenErrorDetails> ret{};
std::error_code ec;
for (const auto& mod_path : std::filesystem::directory_iterator{mod_folder, std::filesystem::directory_options::skip_permission_denied, ec}) {
if ((mod_path.is_regular_file() && mod_path.path().extension() == ".zip") || mod_path.is_directory()) {
if ((mod_path.is_regular_file() && mod_path.path().extension() == ".nrm") || mod_path.is_directory()) {
printf("Opening mod " PATHFMT "\n", mod_path.path().stem().c_str());
std::string open_error_param;
ModOpenError open_error = open_mod(mod_path, open_error_param);
Expand Down Expand Up @@ -510,27 +516,6 @@ std::vector<recomp::mods::ModLoadErrorDetails> recomp::mods::ModContext::load_mo
return ret;
}

bool dependency_version_met(uint8_t major, uint8_t minor, uint8_t patch, uint8_t major_target, uint8_t minor_target, uint8_t patch_target) {
if (major > major_target) {
return true;
}
else if (major < major_target) {
return false;
}

if (minor > minor_target) {
return true;
}
else if (minor < minor_target) {
return false;
}

if (patch >= patch_target) {
return true;
}
return false;
}

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) {
Expand All @@ -546,15 +531,18 @@ void recomp::mods::ModContext::check_dependencies(recomp::mods::ModHandle& mod,
continue;
}

const auto& mod = opened_mods[find_it->second];
if (!dependency_version_met(
mod.manifest.major_version, mod.manifest.minor_version, mod.manifest.patch_version,
cur_dep.major_version, cur_dep.minor_version, cur_dep.patch_version))
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)
{
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)mod.manifest.major_version << "." << (int)mod.manifest.minor_version << "." << (int)mod.manifest.patch_version << "";
(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());
}
}
Expand Down
Loading

0 comments on commit 09f5759

Please sign in to comment.