mirror of
https://github.com/N64Recomp/N64ModernRuntime.git
synced 2026-05-17 22:31:17 +00:00
Added ability to load mods from directories instead of zip files
This commit is contained in:
parent
c7737c9ca6
commit
374244fa71
2 changed files with 96 additions and 38 deletions
|
|
@ -18,7 +18,7 @@ namespace recomp {
|
|||
enum class ModLoadError {
|
||||
Good,
|
||||
DoesNotExist,
|
||||
NotAFile,
|
||||
NotAFileOrFolder,
|
||||
FileError,
|
||||
InvalidZip,
|
||||
NoManifest,
|
||||
|
|
@ -28,19 +28,30 @@ namespace recomp {
|
|||
IncorrectManifestFieldType,
|
||||
};
|
||||
|
||||
struct ZipModHandle {
|
||||
struct ModHandle {
|
||||
virtual ~ModHandle() = default;
|
||||
virtual std::vector<char> read_file(const std::string& filepath, bool& exists) = 0;
|
||||
};
|
||||
|
||||
struct ZipModHandle : public ModHandle {
|
||||
FILE* file_handle = nullptr;
|
||||
std::unique_ptr<mz_zip_archive> archive;
|
||||
|
||||
ZipModHandle() = default;
|
||||
ZipModHandle(const std::filesystem::path& mod_path, ModLoadError& error);
|
||||
ZipModHandle(const ZipModHandle& rhs) = delete;
|
||||
ZipModHandle& operator=(const ZipModHandle& rhs) = delete;
|
||||
ZipModHandle(ZipModHandle&& rhs);
|
||||
ZipModHandle& operator=(ZipModHandle&& rhs);
|
||||
~ZipModHandle();
|
||||
~ZipModHandle() final;
|
||||
|
||||
std::vector<char> read_file(const std::string& filename, bool& exists);
|
||||
std::vector<char> read_file(const std::string& filepath, bool& exists) final;
|
||||
};
|
||||
|
||||
struct LooseModHandle : public ModHandle {
|
||||
std::filesystem::path root_path;
|
||||
|
||||
LooseModHandle() = default;
|
||||
LooseModHandle(const std::filesystem::path& mod_path, ModLoadError& error);
|
||||
~LooseModHandle() final;
|
||||
|
||||
std::vector<char> read_file(const std::string& filepath, bool& exists) final;
|
||||
};
|
||||
|
||||
struct ModManifest {
|
||||
|
|
@ -58,10 +69,11 @@ namespace recomp {
|
|||
std::string rom_patch_path;
|
||||
std::string rom_patch_syms_path;
|
||||
|
||||
ZipModHandle mod_handle;
|
||||
std::unique_ptr<ModHandle> mod_handle;
|
||||
};
|
||||
|
||||
ModManifest load_mod(const std::filesystem::path& mod_path, ModLoadError& error, std::string& error_string);
|
||||
bool load_mod_(uint8_t* rdram, int32_t target_vram, const std::filesystem::path& symbol_file, const std::filesystem::path& binary_file);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,23 +16,6 @@ recomp::mods::ZipModHandle::~ZipModHandle() {
|
|||
archive = {};
|
||||
}
|
||||
|
||||
recomp::mods::ZipModHandle::ZipModHandle(ZipModHandle&& rhs) {
|
||||
*this = std::move(rhs);
|
||||
}
|
||||
|
||||
recomp::mods::ZipModHandle& recomp::mods::ZipModHandle::operator=(ZipModHandle&& rhs) {
|
||||
if (file_handle) {
|
||||
fclose(file_handle);
|
||||
}
|
||||
file_handle = rhs.file_handle;
|
||||
rhs.file_handle = nullptr;
|
||||
|
||||
mz_zip_reader_end(archive.get());
|
||||
archive = std::move(rhs.archive);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
recomp::mods::ZipModHandle::ZipModHandle(const std::filesystem::path& mod_path, ModLoadError& error) {
|
||||
#ifdef _WIN32
|
||||
if (_wfopen_s(&file_handle, mod_path.c_str(), L"rb") != 0) {
|
||||
|
|
@ -55,11 +38,11 @@ recomp::mods::ZipModHandle::ZipModHandle(const std::filesystem::path& mod_path,
|
|||
error = ModLoadError::Good;
|
||||
}
|
||||
|
||||
std::vector<char> recomp::mods::ZipModHandle::read_file(const std::string& filename, bool& exists) {
|
||||
std::vector<char> recomp::mods::ZipModHandle::read_file(const std::string& filepath, bool& exists) {
|
||||
std::vector<char> ret{};
|
||||
|
||||
|
||||
mz_uint32 file_index;
|
||||
if (!mz_zip_reader_locate_file_v2(archive.get(), filename.c_str(), nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE, &file_index)) {
|
||||
if (!mz_zip_reader_locate_file_v2(archive.get(), filepath.c_str(), nullptr, MZ_ZIP_FLAG_CASE_SENSITIVE, &file_index)) {
|
||||
exists = false;
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -80,6 +63,53 @@ std::vector<char> recomp::mods::ZipModHandle::read_file(const std::string& filen
|
|||
return ret;
|
||||
}
|
||||
|
||||
recomp::mods::LooseModHandle::~LooseModHandle() {
|
||||
// Nothing to do here, members will be destroyed automatically.
|
||||
}
|
||||
|
||||
recomp::mods::LooseModHandle::LooseModHandle(const std::filesystem::path& mod_path, ModLoadError& error) {
|
||||
root_path = mod_path;
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::is_directory(root_path, ec)) {
|
||||
error = ModLoadError::NotAFileOrFolder;
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
error = ModLoadError::FileError;
|
||||
}
|
||||
|
||||
error = ModLoadError::Good;
|
||||
}
|
||||
|
||||
std::vector<char> recomp::mods::LooseModHandle::read_file(const std::string& filepath, bool& exists) {
|
||||
std::vector<char> ret{};
|
||||
std::filesystem::path full_path = root_path / filepath;
|
||||
|
||||
std::error_code ec;
|
||||
if (!std::filesystem::is_regular_file(full_path, ec) || ec) {
|
||||
exists = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::ifstream file{ full_path, std::ios::binary };
|
||||
|
||||
if (!file.good()) {
|
||||
exists = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
size_t file_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
ret.resize(file_size);
|
||||
file.read(ret.data(), ret.size());
|
||||
|
||||
exists = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
enum class ManifestField {
|
||||
Id,
|
||||
MajorVersion,
|
||||
|
|
@ -116,7 +146,7 @@ bool get_to(const nlohmann::json& val, T2& out) {
|
|||
|
||||
bool parse_manifest(recomp::mods::ModManifest& ret, const std::vector<char>& manifest_data, recomp::mods::ModLoadError& error, std::string& error_param) {
|
||||
using json = nlohmann::json;
|
||||
json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), false);
|
||||
json manifest_json = json::parse(manifest_data.begin(), manifest_data.end(), nullptr, false);
|
||||
|
||||
if (manifest_json.is_discarded()) {
|
||||
error = recomp::mods::ModLoadError::FailedToParseManifest;
|
||||
|
|
@ -212,23 +242,39 @@ recomp::mods::ModManifest recomp::mods::load_mod(const std::filesystem::path& mo
|
|||
}
|
||||
|
||||
// TODO support symlinks?
|
||||
if (!std::filesystem::is_regular_file(mod_path, ec) || ec) {
|
||||
error = ModLoadError::NotAFile;
|
||||
bool is_file = std::filesystem::is_regular_file(mod_path, ec);
|
||||
if (ec) {
|
||||
error = ModLoadError::FileError;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Load the zip file.
|
||||
ModLoadError zip_error;
|
||||
ret.mod_handle = recomp::mods::ZipModHandle(mod_path, zip_error);
|
||||
bool is_directory = std::filesystem::is_directory(mod_path, ec);
|
||||
if (ec) {
|
||||
error = ModLoadError::FileError;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (zip_error != ModLoadError::Good) {
|
||||
error = zip_error;
|
||||
// Load the directory or zip file.
|
||||
ModLoadError handle_error;
|
||||
if (is_file) {
|
||||
ret.mod_handle = std::make_unique<recomp::mods::ZipModHandle>(mod_path, handle_error);
|
||||
}
|
||||
else if (is_directory) {
|
||||
ret.mod_handle = std::make_unique<recomp::mods::LooseModHandle>(mod_path, handle_error);
|
||||
}
|
||||
else {
|
||||
error = ModLoadError::NotAFileOrFolder;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (handle_error != ModLoadError::Good) {
|
||||
error = handle_error;
|
||||
return {};
|
||||
}
|
||||
|
||||
{
|
||||
bool exists;
|
||||
std::vector<char> manifest_data = ret.mod_handle.read_file("manifest.json", exists);
|
||||
std::vector<char> manifest_data = ret.mod_handle->read_file("manifest.json", exists);
|
||||
if (!exists) {
|
||||
error = ModLoadError::NoManifest;
|
||||
return {};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue