mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-03 05:57:40 -05:00
feat: Display detailed error message when loading of project fails (#1135)
In order to do this I add to make some other additions : - Add a warning popup (TODO, maybe add some icons to differentiate error/warning popups in a future PR ?) - create showError() and showWarning() functions, as helpers to show a message both to the logs and as a popup
This commit is contained in:
16
plugins/builtin/source/content/helpers/notification.cpp
Normal file
16
plugins/builtin/source/content/helpers/notification.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <content/popups/popup_notification.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
void showError(const std::string& message){
|
||||
PopupError::open(message);
|
||||
log::error(message);
|
||||
}
|
||||
|
||||
void showWarning(const std::string& message){
|
||||
PopupWarning::open(message);
|
||||
log::warn(message);
|
||||
}
|
||||
}
|
||||
166
plugins/builtin/source/content/project.cpp
Normal file
166
plugins/builtin/source/content/project.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/localization.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <content/helpers/notification.hpp>
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
constexpr static auto MetadataHeaderMagic = "HEX";
|
||||
constexpr static auto MetadataPath = "IMHEX_METADATA";
|
||||
|
||||
bool load(const std::fs::path &filePath) {
|
||||
auto originalPath = ProjectFile::getPath();
|
||||
|
||||
ProjectFile::setPath(filePath);
|
||||
auto resetPath = SCOPE_GUARD {
|
||||
ProjectFile::setPath(originalPath);
|
||||
};
|
||||
|
||||
if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath)) {
|
||||
showError(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()) {
|
||||
showError(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)) {
|
||||
showError(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)) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
|
||||
));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto providers = auto(ImHexApi::Provider::getProviders());
|
||||
for (const auto &provider : providers) {
|
||||
ImHexApi::Provider::remove(provider);
|
||||
}
|
||||
|
||||
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();
|
||||
EventManager::post<EventProjectOpened>();
|
||||
EventManager::post<RequestUpdateWindowTitle>();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store(std::optional<std::fs::path> filePath = std::nullopt) {
|
||||
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, IMHEX_VERSION);
|
||||
tar.writeString(MetadataPath, metadataContent);
|
||||
}
|
||||
|
||||
ImHexApi::Provider::resetDirty();
|
||||
resetPath.release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void registerProjectHandlers() {
|
||||
hex::ProjectFile::setProjectFunctions(load, store);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "content/providers/motorola_srec_provider.hpp"
|
||||
#include "content/providers/memory_file_provider.hpp"
|
||||
#include "content/providers/view_provider.hpp"
|
||||
#include "content/popups/popup_notification.hpp"
|
||||
#include "content/helpers/notification.hpp"
|
||||
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
#include <hex/api/task.hpp>
|
||||
@@ -31,63 +33,93 @@ namespace hex::plugin::builtin {
|
||||
ContentRegistry::Provider::add<ViewProvider>(false);
|
||||
|
||||
ProjectFile::registerHandler({
|
||||
.basePath = "providers",
|
||||
.required = true,
|
||||
.load = [](const std::fs::path &basePath, Tar &tar) {
|
||||
auto json = nlohmann::json::parse(tar.readString(basePath / "providers.json"));
|
||||
auto providerIds = json.at("providers").get<std::vector<int>>();
|
||||
.basePath = "providers",
|
||||
.required = true,
|
||||
.load = [](const std::fs::path &basePath, Tar &tar) {
|
||||
auto json = nlohmann::json::parse(tar.readString(basePath / "providers.json"));
|
||||
auto providerIds = json.at("providers").get<std::vector<int>>();
|
||||
|
||||
bool success = true;
|
||||
for (const auto &id : providerIds) {
|
||||
auto providerSettings = nlohmann::json::parse(tar.readString(basePath / hex::format("{}.json", id)));
|
||||
bool success = true;
|
||||
std::map<hex::prv::Provider*, std::string> providerWarnings;
|
||||
for (const auto &id : providerIds) {
|
||||
auto providerSettings = nlohmann::json::parse(tar.readString(basePath / hex::format("{}.json", id)));
|
||||
|
||||
auto provider = ImHexApi::Provider::createProvider(providerSettings.at("type").get<std::string>(), true, false);
|
||||
ON_SCOPE_EXIT {
|
||||
if (!success) {
|
||||
for (auto &task : TaskManager::getRunningTasks())
|
||||
task->interrupt();
|
||||
auto providerType = providerSettings.at("type").get<std::string>();
|
||||
auto provider = ImHexApi::Provider::createProvider(providerType, true, false);
|
||||
ON_SCOPE_EXIT {
|
||||
if (!success) {
|
||||
for (auto &task : TaskManager::getRunningTasks())
|
||||
task->interrupt();
|
||||
|
||||
TaskManager::runWhenTasksFinished([]{
|
||||
for (auto provider : ImHexApi::Provider::getProviders())
|
||||
TaskManager::runWhenTasksFinished([]{
|
||||
for (auto provider : ImHexApi::Provider::getProviders())
|
||||
ImHexApi::Provider::remove(provider, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (provider == nullptr) {
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
if (provider == nullptr) {
|
||||
// if a provider is not created, it will be overwritten when saving the project,
|
||||
// so we should prevent the project from loading at all
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType)
|
||||
));
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
provider->setID(id);
|
||||
provider->loadSettings(providerSettings.at("settings"));
|
||||
if (!provider->open() || !provider->isAvailable() || !provider->isReadable())
|
||||
success = false;
|
||||
else
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
}
|
||||
provider->setID(id);
|
||||
provider->loadSettings(providerSettings.at("settings"));
|
||||
if (!provider->open() || !provider->isAvailable() || !provider->isReadable()) {
|
||||
providerWarnings[provider] = provider->getErrorMessage();
|
||||
} else
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
}
|
||||
|
||||
return success;
|
||||
},
|
||||
.store = [](const std::fs::path &basePath, Tar &tar) {
|
||||
std::vector<int> providerIds;
|
||||
for (const auto &provider : ImHexApi::Provider::getProviders()) {
|
||||
auto id = provider->getID();
|
||||
providerIds.push_back(id);
|
||||
std::string warningMsg;
|
||||
for(const auto &warning : providerWarnings){
|
||||
ImHexApi::Provider::remove(warning.first);
|
||||
warningMsg.append(
|
||||
hex::format("\n - {} : {}", warning.first->getName(), warning.second));
|
||||
}
|
||||
|
||||
nlohmann::json json;
|
||||
json["type"] = provider->getTypeName();
|
||||
json["settings"] = provider->storeSettings();
|
||||
// if no providers were opened, display an error with
|
||||
// the warnings that happened when opening them
|
||||
if (ImHexApi::Provider::getProviders().size() == 0) {
|
||||
showError(hex::format("hex.builtin.popup.error.project.load"_lang,
|
||||
hex::format("hex.builtin.popup.error.project.load.no_providers"_lang)) + warningMsg);
|
||||
|
||||
tar.writeString(basePath / hex::format("{}.json", id), json.dump(4));
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
|
||||
tar.writeString(basePath / "providers.json",
|
||||
// else, if are warnings, still display them
|
||||
if (warningMsg.empty()) return true;
|
||||
else {
|
||||
showWarning(
|
||||
hex::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMsg));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
},
|
||||
.store = [](const std::fs::path &basePath, Tar &tar) {
|
||||
std::vector<int> providerIds;
|
||||
for (const auto &provider : ImHexApi::Provider::getProviders()) {
|
||||
auto id = provider->getID();
|
||||
providerIds.push_back(id);
|
||||
|
||||
nlohmann::json json;
|
||||
json["type"] = provider->getTypeName();
|
||||
json["settings"] = provider->storeSettings();
|
||||
|
||||
tar.writeString(basePath / hex::format("{}.json", id), json.dump(4));
|
||||
}
|
||||
|
||||
tar.writeString(basePath / "providers.json",
|
||||
nlohmann::json({ { "providers", providerIds } }).dump(4)
|
||||
);
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -205,6 +205,11 @@ namespace hex::plugin::builtin {
|
||||
this->m_readable = true;
|
||||
this->m_writable = true;
|
||||
|
||||
if (!std::fs::exists(this->m_path)) {
|
||||
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, this->m_path.string(), ::strerror(ENOENT)));
|
||||
return false;
|
||||
}
|
||||
|
||||
wolv::io::File file(this->m_path, wolv::io::File::Mode::Write);
|
||||
if (!file.isValid()) {
|
||||
this->m_writable = false;
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace hex::plugin::builtin {
|
||||
void registerBackgroundServices();
|
||||
void registerNetworkEndpoints();
|
||||
void registerFileHandlers();
|
||||
void registerProjectHandlers();
|
||||
|
||||
void addFooterItems();
|
||||
void addTitleBarButtons();
|
||||
@@ -71,6 +72,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {
|
||||
registerBackgroundServices();
|
||||
registerNetworkEndpoints();
|
||||
registerFileHandlers();
|
||||
registerProjectHandlers();
|
||||
|
||||
addFooterItems();
|
||||
addTitleBarButtons();
|
||||
|
||||
Reference in New Issue
Block a user