impr: Added better crash backup and restore mechanism (#1094)
- Add a new file 'crash.json' to store metadata about the crash, like the log file or project opened - show the log file of the session that caused the crash to the user - Correctly restore the project path
This commit is contained in:
parent
91635346be
commit
7ce0613977
@ -9,9 +9,12 @@
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
|
||||
namespace hex::log {
|
||||
|
||||
FILE *getDestination();
|
||||
wolv::io::File& getFile();
|
||||
bool isRedirected();
|
||||
|
||||
namespace {
|
||||
|
@ -15,6 +15,10 @@ namespace hex::log {
|
||||
return stdout;
|
||||
}
|
||||
|
||||
wolv::io::File& getFile() {
|
||||
return g_loggerFile;
|
||||
}
|
||||
|
||||
bool isRedirected() {
|
||||
return g_loggerFile.isValid();
|
||||
}
|
||||
|
@ -45,11 +45,34 @@ namespace hex {
|
||||
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
static void saveCrashFile(){
|
||||
nlohmann::json crashData{
|
||||
{"logFile", hex::log::getFile().getPath()},
|
||||
{"project", ProjectFile::getPath()},
|
||||
};
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
wolv::io::File file(path / "crash.json", wolv::io::File::Mode::Write);
|
||||
if (file.isValid()) {
|
||||
file.writeString(crashData.dump(4));
|
||||
file.close();
|
||||
log::info("Wrote crash.json file to {}", wolv::util::toUTF8String(file.getPath()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
log::warn("Could not write crash.json file !");
|
||||
}
|
||||
|
||||
// Custom signal handler to print various information and a stacktrace when the application crashes
|
||||
static void signalHandler(int signalNumber, const std::string &signalName) {
|
||||
log::fatal("Terminating with signal '{}' ({})", signalName, signalNumber);
|
||||
|
||||
// save crash.json file
|
||||
saveCrashFile();
|
||||
|
||||
// Trigger an event so that plugins can handle crashes
|
||||
// It may affect things (like the project path),
|
||||
// so we do this after saving the crash file
|
||||
EventManager::post<EventAbnormalTermination>(signalNumber);
|
||||
|
||||
// Detect if the crash was due to an uncaught exception
|
||||
@ -175,12 +198,12 @@ namespace hex {
|
||||
constexpr static auto CrashBackupFileName = "crash_backup.hexproj";
|
||||
|
||||
// Save a backup project when the application crashes
|
||||
// We need to save the project no mater if it is dirty,
|
||||
// because this save is responsible for telling us which files
|
||||
// were opened in case there wasn't a project
|
||||
EventManager::subscribe<EventAbnormalTermination>(this, [this](int) {
|
||||
ImGui::SaveIniSettingsToDisk(wolv::util::toUTF8String(this->m_imguiSettingsPath).c_str());
|
||||
|
||||
if (!ImHexApi::Provider::isDirty())
|
||||
return;
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
if (ProjectFile::store(path / CrashBackupFileName))
|
||||
break;
|
||||
@ -219,6 +242,12 @@ namespace hex {
|
||||
ex.what()
|
||||
);
|
||||
}
|
||||
|
||||
// save crash.json file
|
||||
saveCrashFile();
|
||||
|
||||
// send the event. It may affect things (like the project path),
|
||||
// so we do this after saving the crash file
|
||||
EventManager::post<EventAbnormalTermination>(0);
|
||||
});
|
||||
}
|
||||
|
@ -842,6 +842,7 @@
|
||||
"hex.builtin.welcome.plugins.plugin",
|
||||
"hex.builtin.popup.safety_backup.delete",
|
||||
"hex.builtin.popup.safety_backup.desc",
|
||||
"hex.builtin.popup.safety_backup.log_file",
|
||||
"hex.builtin.popup.safety_backup.restore",
|
||||
"hex.builtin.popup.safety_backup.title",
|
||||
"hex.builtin.welcome.start.create_file",
|
||||
|
@ -921,6 +921,7 @@
|
||||
"hex.builtin.welcome.plugins.plugin": "Plugin",
|
||||
"hex.builtin.popup.safety_backup.delete": "No, Delete",
|
||||
"hex.builtin.popup.safety_backup.desc": "Oh no, ImHex crashed last time.\nDo you want to restore your past work?",
|
||||
"hex.builtin.popup.safety_backup.log_file": "Log file: ",
|
||||
"hex.builtin.popup.safety_backup.restore": "Yes, Restore",
|
||||
"hex.builtin.popup.safety_backup.title": "Restore lost data",
|
||||
"hex.builtin.welcome.start.create_file": "Create New File",
|
||||
|
@ -68,30 +68,39 @@ namespace hex::plugin::builtin {
|
||||
};
|
||||
|
||||
class PopupRestoreBackup : public Popup<PopupRestoreBackup> {
|
||||
private:
|
||||
std::fs::path m_logFilePath;
|
||||
std::function<void()> m_restoreCallback;
|
||||
std::function<void()> m_deleteCallback;
|
||||
public:
|
||||
PopupRestoreBackup() : Popup("hex.builtin.popup.safety_backup.title") { }
|
||||
PopupRestoreBackup(std::fs::path logFilePath, std::function<void()> restoreCallback, std::function<void()> deleteCallback)
|
||||
: Popup("hex.builtin.popup.safety_backup.title"),
|
||||
m_logFilePath(logFilePath),
|
||||
m_restoreCallback(restoreCallback),
|
||||
m_deleteCallback(deleteCallback) { }
|
||||
|
||||
void drawContent() override {
|
||||
ImGui::TextUnformatted("hex.builtin.popup.safety_backup.desc"_lang);
|
||||
ImGui::NewLine();
|
||||
|
||||
if (!this->m_logFilePath.empty()) {
|
||||
ImGui::TextUnformatted("hex.builtin.popup.safety_backup.log_file"_lang);
|
||||
if (ImGui::Hyperlink(this->m_logFilePath.filename().string().c_str())) {
|
||||
fs::openFolderWithSelectionExternal(this->m_logFilePath);
|
||||
}
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
auto width = ImGui::GetWindowWidth();
|
||||
ImGui::SetCursorPosX(width / 9);
|
||||
if (ImGui::Button("hex.builtin.popup.safety_backup.restore"_lang, ImVec2(width / 3, 0))) {
|
||||
ProjectFile::load(s_safetyBackupPath);
|
||||
ProjectFile::clearPath();
|
||||
|
||||
for (const auto &provider : ImHexApi::Provider::getProviders())
|
||||
provider->markDirty();
|
||||
|
||||
wolv::io::fs::remove(s_safetyBackupPath);
|
||||
this->m_restoreCallback();
|
||||
this->m_deleteCallback();
|
||||
|
||||
this->close();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width / 9 * 5);
|
||||
if (ImGui::Button("hex.builtin.popup.safety_backup.delete"_lang, ImVec2(width / 3, 0))) {
|
||||
wolv::io::fs::remove(s_safetyBackupPath);
|
||||
this->m_deleteCallback();
|
||||
|
||||
this->close();
|
||||
}
|
||||
@ -608,17 +617,54 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
|
||||
// Check for crash backup
|
||||
constexpr static auto CrashBackupFileName = "crash_backup.hexproj";
|
||||
constexpr static auto CrashFileName = "crash.json";
|
||||
constexpr static auto BackupFileName = "crash_backup.hexproj";
|
||||
bool hasCrashed = false;
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
if (auto filePath = std::fs::path(path) / CrashBackupFileName; wolv::io::fs::exists(filePath)) {
|
||||
s_safetyBackupPath = filePath;
|
||||
PopupRestoreBackup::open();
|
||||
if (auto crashFilePath = std::fs::path(path) / CrashFileName; wolv::io::fs::exists(crashFilePath)) {
|
||||
hasCrashed = true;
|
||||
|
||||
log::info("Found crash.json file at {}", wolv::util::toUTF8String(crashFilePath));
|
||||
wolv::io::File crashFile(crashFilePath, wolv::io::File::Mode::Read);
|
||||
auto crashFileData = nlohmann::json::parse(crashFile.readString());
|
||||
crashFile.close();
|
||||
bool hasProject = !crashFileData.value("project", "").empty();
|
||||
|
||||
auto backupFilePath = path / BackupFileName;
|
||||
bool hasBackupFile = wolv::io::fs::exists(backupFilePath);
|
||||
|
||||
PopupRestoreBackup::open(
|
||||
// path of log file
|
||||
crashFileData.value("logFile", ""),
|
||||
// restore callback
|
||||
[=]{
|
||||
if (hasBackupFile) {
|
||||
ProjectFile::load(backupFilePath);
|
||||
if (hasProject) {
|
||||
ProjectFile::setPath(crashFileData["project"].get<std::string>());
|
||||
} else {
|
||||
ProjectFile::setPath("");
|
||||
}
|
||||
EventManager::post<RequestUpdateWindowTitle>();
|
||||
}else{
|
||||
if (hasProject) {
|
||||
ProjectFile::setPath(crashFileData["project"].get<std::string>());
|
||||
}
|
||||
}
|
||||
},
|
||||
// delete callback (also executed after restore)
|
||||
[crashFilePath, backupFilePath]{
|
||||
wolv::io::fs::remove(crashFilePath);
|
||||
wolv::io::fs::remove(backupFilePath);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Tip of the day
|
||||
auto tipsData = romfs::get("tips.json");
|
||||
if(s_safetyBackupPath.empty() && tipsData.valid()){
|
||||
if (!hasCrashed && tipsData.valid()) {
|
||||
auto tipsCategories = nlohmann::json::parse(tipsData.string());
|
||||
|
||||
auto now = std::chrono::system_clock::now();
|
||||
|
Loading…
x
Reference in New Issue
Block a user