Added ability to load mods from directories instead of zip files

This commit is contained in:
Mr-Wiseguy 2024-07-05 12:35:54 -04:00
parent c7737c9ca6
commit 374244fa71
2 changed files with 96 additions and 38 deletions

View file

@ -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);
}
};

View 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 {};