mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-05-07 17:31:48 +00:00
Removed dependency version from mod symbols (moved to manifest)
This commit is contained in:
parent
4f61ef4be9
commit
b8dcb21dec
3 changed files with 124 additions and 84 deletions
|
|
@ -3,6 +3,7 @@
|
|||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <cctype>
|
||||
#include "fmt/format.h"
|
||||
#include "n64recomp.h"
|
||||
#include <toml++/toml.hpp>
|
||||
|
|
@ -13,7 +14,8 @@ struct ModConfig {
|
|||
std::filesystem::path elf_path;
|
||||
std::filesystem::path func_reference_syms_file_path;
|
||||
std::vector<std::filesystem::path> data_reference_syms_file_paths;
|
||||
std::vector<N64Recomp::Dependency> dependencies;
|
||||
std::vector<std::string> dependencies;
|
||||
std::vector<std::string> full_dependency_strings;
|
||||
};
|
||||
|
||||
static std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) {
|
||||
|
|
@ -23,10 +25,14 @@ static std::filesystem::path concat_if_not_empty(const std::filesystem::path& pa
|
|||
return child;
|
||||
}
|
||||
|
||||
static bool parse_version_string(std::string_view str, uint8_t& major, uint8_t& minor, uint8_t& patch) {
|
||||
static bool validate_version_string(std::string_view str) {
|
||||
std::array<size_t, 2> period_indices;
|
||||
size_t num_periods = 0;
|
||||
size_t cur_pos = 0;
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t patch;
|
||||
std::string suffix;
|
||||
|
||||
// Find the 2 required periods.
|
||||
cur_pos = str.find('.', cur_pos);
|
||||
|
|
@ -47,46 +53,75 @@ static bool parse_version_string(std::string_view str, uint8_t& major, uint8_t&
|
|||
parse_results[1] = std::from_chars(str.data() + parse_starts[1], str.data() + parse_ends[1], minor);
|
||||
parse_results[2] = std::from_chars(str.data() + parse_starts[2], str.data() + parse_ends[2], patch);
|
||||
|
||||
// Check that all 3 parsed correctly.
|
||||
// Check that the first two parsed correctly.
|
||||
auto did_parse = [&](size_t i) {
|
||||
return parse_results[i].ec == std::errc{} && parse_results[i].ptr == str.data() + parse_ends[i];
|
||||
};
|
||||
|
||||
|
||||
if (!did_parse(0) || !did_parse(1) || !did_parse(2)) {
|
||||
if (!did_parse(0) || !did_parse(1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the third had a successful parse, but not necessarily read all the characters.
|
||||
if (parse_results[2].ec != std::errc{}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow a plus or minus directly after the third number.
|
||||
if (parse_results[2].ptr != str.data() + parse_ends[2]) {
|
||||
if (*parse_results[2].ptr == '+' || *parse_results[2].ptr == '-') {
|
||||
suffix = str.substr(std::distance(str.data(), parse_results[2].ptr));
|
||||
}
|
||||
// Failed to parse, as nothing is allowed directly after the last number besides a plus or minus.
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_dependency_string(const std::string& val, N64Recomp::Dependency& dep) {
|
||||
N64Recomp::Dependency ret;
|
||||
size_t id_pos = 0;
|
||||
size_t id_length = 0;
|
||||
static bool validate_dependency_string(const std::string& val, size_t& name_length) {
|
||||
std::string ret;
|
||||
size_t name_length_temp;
|
||||
|
||||
// Don't allow an empty dependency name.
|
||||
if (val.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
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) {
|
||||
id_length = val.size();
|
||||
ret.major_version = 0;
|
||||
ret.minor_version = 0;
|
||||
ret.patch_version = 0;
|
||||
// No version present, so just validate the dependency's id.
|
||||
|
||||
validated_name = N64Recomp::validate_mod_id(std::string_view{val});
|
||||
name_length_temp = val.size();
|
||||
validated_version = true;
|
||||
}
|
||||
else {
|
||||
id_length = colon_pos;
|
||||
uint8_t major, minor, patch;
|
||||
if (!parse_version_string(std::string_view{val.begin() + colon_pos + 1, val.end()}, major, minor, patch)) {
|
||||
// Version present, validate it.
|
||||
|
||||
// Don't allow an empty dependency name after accounting for the colon.
|
||||
if (colon_pos == 0) {
|
||||
return false;
|
||||
}
|
||||
ret.major_version = major;
|
||||
ret.minor_version = minor;
|
||||
ret.patch_version = patch;
|
||||
|
||||
name_length_temp = colon_pos;
|
||||
|
||||
// Validate the dependency's id and version.
|
||||
validated_name = N64Recomp::validate_mod_id(std::string_view{val.begin(), val.begin() + colon_pos});
|
||||
validated_version = validate_version_string(std::string_view{val.begin() + colon_pos + 1, val.end()});
|
||||
}
|
||||
|
||||
ret.mod_id = val.substr(id_pos, id_length);
|
||||
if (validated_name && validated_version) {
|
||||
name_length = name_length_temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
dep = std::move(ret);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<std::filesystem::path> get_toml_path_array(const toml::array* toml_array, const std::filesystem::path& basedir) {
|
||||
|
|
@ -177,11 +212,13 @@ ModConfig parse_mod_config(const std::filesystem::path& config_path, bool& good)
|
|||
ret.dependencies.reserve(dependency_array->size());
|
||||
dependency_array->for_each([&ret](auto&& el) {
|
||||
if constexpr (toml::is_string<decltype(el)>) {
|
||||
N64Recomp::Dependency dep;
|
||||
if (!parse_dependency_string(el.ref<std::string>(), dep)) {
|
||||
size_t dependency_id_length;
|
||||
if (!validate_dependency_string(el.ref<std::string>(), dependency_id_length)) {
|
||||
throw toml::parse_error("Invalid dependency entry", el.source());
|
||||
}
|
||||
ret.dependencies.emplace_back(std::move(dep));
|
||||
std::string dependency_id = el.ref<std::string>().substr(0, dependency_id_length);
|
||||
ret.dependencies.emplace_back(dependency_id);
|
||||
ret.full_dependency_strings.emplace_back(el.ref<std::string>());
|
||||
}
|
||||
else {
|
||||
throw toml::parse_error("Invalid toml type for dependency", el.source());
|
||||
|
|
@ -215,7 +252,7 @@ bool parse_callback_name(std::string_view data, std::string& dependency_name, st
|
|||
std::string_view dependency_name_view = std::string_view{data}.substr(0, period_pos);
|
||||
std::string_view event_name_view = std::string_view{data}.substr(period_pos + 1);
|
||||
|
||||
if (!N64Recomp::validate_mod_name(dependency_name_view)) {
|
||||
if (!N64Recomp::validate_mod_id(dependency_name_view)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -253,7 +290,6 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
|||
ret.rom = input_context.rom;
|
||||
|
||||
// Copy the dependency data from the input context.
|
||||
ret.dependencies = input_context.dependencies;
|
||||
ret.dependencies_by_name = input_context.dependencies_by_name;
|
||||
ret.import_symbols = input_context.import_symbols;
|
||||
ret.dependency_events = input_context.dependency_events;
|
||||
|
|
@ -345,17 +381,17 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bo
|
|||
else if (import_section) {
|
||||
for (const auto& input_func_index : cur_section_funcs) {
|
||||
const auto& cur_func = input_context.functions[input_func_index];
|
||||
std::string dependency_name = cur_section.name.substr(N64Recomp::ImportSectionPrefix.size());
|
||||
if (!N64Recomp::validate_mod_name(dependency_name)) {
|
||||
fmt::print("Failed to import function {} as {} is an invalid mod name.\n",
|
||||
cur_func.name, dependency_name);
|
||||
std::string dependency_id = cur_section.name.substr(N64Recomp::ImportSectionPrefix.size());
|
||||
if (!N64Recomp::validate_mod_id(dependency_id)) {
|
||||
fmt::print("Failed to import function {} as {} is an invalid mod id.\n",
|
||||
cur_func.name, dependency_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t dependency_index;
|
||||
if (!ret.find_dependency(dependency_name, dependency_index)) {
|
||||
if (!ret.find_dependency(dependency_id, dependency_index)) {
|
||||
fmt::print("Failed to import function {} from mod {} as the mod is not a registered dependency.\n",
|
||||
cur_func.name, dependency_name);
|
||||
cur_func.name, dependency_id);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,13 +128,6 @@ namespace N64Recomp {
|
|||
extern const std::unordered_set<std::string> ignored_funcs;
|
||||
extern const std::unordered_set<std::string> renamed_funcs;
|
||||
|
||||
struct Dependency {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint8_t patch_version;
|
||||
std::string mod_id;
|
||||
};
|
||||
|
||||
struct ImportSymbol {
|
||||
ReferenceSymbol base;
|
||||
size_t dependency_index;
|
||||
|
|
@ -202,8 +195,6 @@ namespace N64Recomp {
|
|||
//// Mod dependencies and their symbols
|
||||
|
||||
//// Imported values
|
||||
// List of dependencies.
|
||||
std::vector<Dependency> dependencies;
|
||||
// Mapping of dependency name to dependency index.
|
||||
std::unordered_map<std::string, size_t> dependencies_by_name;
|
||||
// List of symbols imported from dependencies.
|
||||
|
|
@ -235,45 +226,37 @@ namespace N64Recomp {
|
|||
|
||||
Context() = default;
|
||||
|
||||
bool add_dependency(const std::string& id, uint8_t major_version, uint8_t minor_version, uint8_t patch_version) {
|
||||
bool add_dependency(const std::string& id) {
|
||||
if (dependencies_by_name.contains(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t dependency_index = dependencies.size();
|
||||
dependencies.emplace_back(N64Recomp::Dependency {
|
||||
.major_version = major_version,
|
||||
.minor_version = minor_version,
|
||||
.patch_version = patch_version,
|
||||
.mod_id = id
|
||||
});
|
||||
size_t dependency_index = dependencies_by_name.size();
|
||||
|
||||
dependencies_by_name.emplace(id, dependency_index);
|
||||
dependency_events_by_name.resize(dependencies.size());
|
||||
dependency_imports_by_name.resize(dependencies.size());
|
||||
dependency_events_by_name.resize(dependencies_by_name.size());
|
||||
dependency_imports_by_name.resize(dependencies_by_name.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_dependencies(const std::vector<Dependency>& new_dependencies) {
|
||||
dependencies.reserve(dependencies.size() + new_dependencies.size());
|
||||
bool add_dependencies(const std::vector<std::string>& new_dependencies) {
|
||||
dependencies_by_name.reserve(dependencies_by_name.size() + new_dependencies.size());
|
||||
|
||||
// Check if any of the dependencies already exist and fail if so.
|
||||
for (const Dependency& dep : new_dependencies) {
|
||||
if (dependencies_by_name.contains(dep.mod_id)) {
|
||||
for (const std::string& dep : new_dependencies) {
|
||||
if (dependencies_by_name.contains(dep)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (const Dependency& dep : new_dependencies) {
|
||||
size_t dependency_index = dependencies.size();
|
||||
dependencies.emplace_back(dep);
|
||||
dependencies_by_name.emplace(dep.mod_id, dependency_index);
|
||||
for (const std::string& dep : new_dependencies) {
|
||||
size_t dependency_index = dependencies_by_name.size();
|
||||
dependencies_by_name.emplace(dep, dependency_index);
|
||||
}
|
||||
|
||||
dependency_events_by_name.resize(dependencies.size());
|
||||
dependency_imports_by_name.resize(dependencies.size());
|
||||
dependency_events_by_name.resize(dependencies_by_name.size());
|
||||
dependency_imports_by_name.resize(dependencies_by_name.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +268,7 @@ namespace N64Recomp {
|
|||
else {
|
||||
// Handle special dependency names.
|
||||
if (mod_id == DependencySelf || mod_id == DependencyBaseRecomp) {
|
||||
add_dependency(mod_id, 0, 0, 0);
|
||||
add_dependency(mod_id);
|
||||
dependency_index = dependencies_by_name[mod_id];
|
||||
}
|
||||
else {
|
||||
|
|
@ -428,7 +411,7 @@ namespace N64Recomp {
|
|||
}
|
||||
|
||||
bool find_import_symbol(const std::string& symbol_name, size_t dependency_index, SymbolReference& ref_out) const {
|
||||
if (dependency_index >= dependencies.size()) {
|
||||
if (dependency_index >= dependencies_by_name.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +459,7 @@ namespace N64Recomp {
|
|||
}
|
||||
|
||||
bool add_dependency_event(const std::string& event_name, size_t dependency_index, size_t& dependency_event_index) {
|
||||
if (dependency_index >= dependencies.size()) {
|
||||
if (dependency_index >= dependencies_by_name.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -547,18 +530,37 @@ namespace N64Recomp {
|
|||
ModSymbolsError parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, Context& context_out);
|
||||
std::vector<uint8_t> symbols_to_bin_v1(const Context& mod_context);
|
||||
|
||||
inline bool validate_mod_name(std::string_view str) {
|
||||
// Disallow mod names with a colon in them, since you can't specify that in a dependency string orin callbacks.
|
||||
for (char c : str) {
|
||||
if (c == ':') {
|
||||
inline bool validate_mod_id(std::string_view str) {
|
||||
// Disallow empty ids.
|
||||
if (str.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow special dependency ids.
|
||||
if (str == N64Recomp::DependencySelf || str == N64Recomp::DependencyBaseRecomp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// These following rules basically describe C identifiers. There's no specific reason to enforce them besides colon (currently),
|
||||
// so this is just to prevent "weird" mod ids.
|
||||
|
||||
// Check the first character, which must be alphabetical or an underscore.
|
||||
if (!isalpha(str[0]) && str[0] != '_') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the remaining characters, which can be alphanumeric or underscore.
|
||||
for (char c : str.substr(1)) {
|
||||
if (!isalnum(c) && c != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool validate_mod_name(const std::string& str) {
|
||||
return validate_mod_name(std::string_view{str});
|
||||
inline bool validate_mod_id(const std::string& str) {
|
||||
return validate_mod_id(std::string_view{str});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,9 +49,6 @@ struct RelocV1 {
|
|||
};
|
||||
|
||||
struct DependencyV1 {
|
||||
uint8_t major_version;
|
||||
uint8_t minor_version;
|
||||
uint8_t patch_version;
|
||||
uint8_t reserved;
|
||||
uint32_t mod_id_start;
|
||||
uint32_t mod_id_size;
|
||||
|
|
@ -143,7 +140,6 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
|
||||
// TODO add proper creation methods for the remaining vectors and change these to reserves instead.
|
||||
mod_context.sections.resize(num_sections); // Add method
|
||||
mod_context.dependencies.reserve(num_dependencies);
|
||||
mod_context.dependencies_by_name.reserve(num_dependencies);
|
||||
mod_context.import_symbols.reserve(num_imports);
|
||||
mod_context.dependency_events.reserve(num_dependency_events);
|
||||
|
|
@ -274,7 +270,7 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
}
|
||||
|
||||
std::string_view mod_id{ string_data + mod_id_start, string_data + mod_id_start + mod_id_size };
|
||||
mod_context.add_dependency(std::string{mod_id}, dependency_in.major_version, dependency_in.minor_version, dependency_in.patch_version);
|
||||
mod_context.add_dependency(std::string{mod_id});
|
||||
}
|
||||
|
||||
const ImportV1* imports = reinterpret_data<ImportV1>(data, offset, num_imports);
|
||||
|
|
@ -506,7 +502,7 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
|
||||
vec_put(ret, &header);
|
||||
|
||||
size_t num_dependencies = context.dependencies.size();
|
||||
size_t num_dependencies = context.dependencies_by_name.size();
|
||||
size_t num_imported_funcs = context.import_symbols.size();
|
||||
size_t num_dependency_events = context.dependency_events.size();
|
||||
|
||||
|
|
@ -533,15 +529,24 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
|
||||
// Build the string data from the exports and imports.
|
||||
size_t strings_start = ret.size();
|
||||
|
||||
// Order the dependencies by their index. This isn't necessary, but it makes the dependency name order
|
||||
// in the symbol file match the indices of the dependencies makes debugging easier.
|
||||
std::vector<std::string> dependencies_ordered{};
|
||||
dependencies_ordered.resize(context.dependencies_by_name.size());
|
||||
|
||||
for (const auto& [dependency, dependency_index] : context.dependencies_by_name) {
|
||||
dependencies_ordered[dependency_index] = dependency;
|
||||
}
|
||||
|
||||
// Track the start of every dependency's name in the string data.
|
||||
std::vector<uint32_t> dependency_name_positions{};
|
||||
dependency_name_positions.resize(num_dependencies);
|
||||
for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) {
|
||||
const Dependency& dependency = context.dependencies[dependency_index];
|
||||
const std::string& dependency = dependencies_ordered[dependency_index];
|
||||
|
||||
dependency_name_positions[dependency_index] = static_cast<uint32_t>(ret.size() - strings_start);
|
||||
vec_put(ret, dependency.mod_id);
|
||||
vec_put(ret, dependency);
|
||||
}
|
||||
|
||||
// Track the start of every imported function's name in the string data.
|
||||
|
|
@ -658,14 +663,11 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
|
|||
|
||||
// Write the dependencies.
|
||||
for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) {
|
||||
const Dependency& dependency = context.dependencies[dependency_index];
|
||||
const std::string& dependency = dependencies_ordered[dependency_index];
|
||||
|
||||
DependencyV1 dependency_out {
|
||||
.major_version = dependency.major_version,
|
||||
.minor_version = dependency.minor_version,
|
||||
.patch_version = dependency.patch_version,
|
||||
.mod_id_start = dependency_name_positions[dependency_index],
|
||||
.mod_id_size = static_cast<uint32_t>(dependency.mod_id.size())
|
||||
.mod_id_size = static_cast<uint32_t>(dependency.size())
|
||||
};
|
||||
|
||||
vec_put(ret, &dependency_out);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue