1
0
mirror of synced 2024-12-12 07:51:05 +01:00
ImHex/plugins/builtin/source/content/project.cpp
iTrooz 47362559ef
tests: Add infrastructure for testing plugins (#1538)
This PR adds a test architecture to be able to test plugins

Main infrastructure done by @WerWolv

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
2024-02-26 20:51:08 +01:00

183 lines
6.3 KiB
C++

#include <filesystem>
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <hex/api/event_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
constexpr static auto MetadataHeaderMagic = "HEX";
constexpr static auto MetadataPath = "IMHEX_METADATA";
bool load(const std::fs::path &filePath) {
if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.file_not_found"_lang,
wolv::util::toUTF8String(filePath)
)));
return false;
}
Tar tar(filePath, Tar::Mode::Read);
if (!tar.isValid()) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_tar"_lang,
tar.getOpenErrorString()
)));
return false;
}
if (!tar.contains(MetadataPath)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
));
return false;
}
{
const auto metadataContent = tar.readVector(MetadataPath);
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
));
return false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
ImHexApi::Provider::remove(provider);
}
auto originalPath = ProjectFile::getPath();
ProjectFile::setPath(filePath);
auto resetPath = SCOPE_GUARD {
ProjectFile::setPath(originalPath);
};
for (const auto &handler : ProjectFile::getHandlers()) {
bool result = true;
// Handlers are supposed to show the error/warning popup to the user themselves, so we don't show one here
try {
if (!handler.load(handler.basePath, tar)) {
log::warn("Project file handler for {} failed to load {}", filePath.string(), handler.basePath.string());
result = false;
}
} catch (std::exception &e) {
log::warn("Project file handler for {} failed to load {}: {}", filePath.string(), handler.basePath.string(), e.what());
result = false;
}
if (!result && handler.required) {
return false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
bool result = true;
// Handlers are supposed to show the error/warning popup to the user themselves, so we don't show one here
try {
if (!handler.load(provider, basePath / handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
if (!result && handler.required) {
return false;
}
}
}
resetPath.release();
EventProjectOpened::post();
RequestUpdateWindowTitle::post();
return true;
}
bool store(std::optional<std::fs::path> filePath = std::nullopt, bool updateLocation = true) {
auto originalPath = ProjectFile::getPath();
if (!filePath.has_value())
filePath = originalPath;
ProjectFile::setPath(filePath.value());
auto resetPath = SCOPE_GUARD {
ProjectFile::setPath(originalPath);
};
Tar tar(*filePath, Tar::Mode::Create);
if (!tar.isValid())
return false;
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.store(handler.basePath, tar) && handler.required)
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
if (handler.required)
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.store(provider, basePath / handler.basePath, tar) && handler.required)
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
if (handler.required)
result = false;
}
}
}
{
const auto metadataContent = hex::format("{}\n{}", MetadataHeaderMagic, ImHexApi::System::getImHexVersion());
tar.writeString(MetadataPath, metadataContent);
}
ImHexApi::Provider::resetDirty();
// If saveLocation is false, reset the project path (do not release the lock)
if (updateLocation) {
resetPath.release();
// Request, as this puts us into a project state
RequestUpdateWindowTitle::post();
}
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.save_project.name");
return result;
}
void registerProjectHandlers() {
hex::ProjectFile::setProjectFunctions(load, store);
}
}