mirror of https://github.com/WerWolv/ImHex
feat: Add option to create auto backups of files before they're modified
This commit is contained in:
parent
c2e07bf7b2
commit
855e4c4913
|
|
@ -239,6 +239,14 @@ EXPORT_MODULE namespace hex {
|
||||||
nlohmann::json store() override { return {}; }
|
nlohmann::json store() override { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Spacer : public Widget {
|
||||||
|
public:
|
||||||
|
bool draw(const std::string &name) override;
|
||||||
|
|
||||||
|
void load(const nlohmann::json &) override {}
|
||||||
|
nlohmann::json store() override { return {}; }
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,21 @@ namespace hex::prv {
|
||||||
[[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;
|
[[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IProviderDataBackupable {
|
||||||
|
public:
|
||||||
|
explicit IProviderDataBackupable(Provider *provider);
|
||||||
|
virtual ~IProviderDataBackupable() = default;
|
||||||
|
|
||||||
|
void createBackupIfNeeded(const std::fs::path &inputFilePath);
|
||||||
|
private:
|
||||||
|
Provider *m_provider = nullptr;
|
||||||
|
bool m_backupCreated = false;
|
||||||
|
|
||||||
|
bool m_shouldCreateBackups = true;
|
||||||
|
u64 m_maxSize;
|
||||||
|
std::string m_backupExtension;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Represent the data source for a tab in the UI
|
* @brief Represent the data source for a tab in the UI
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -594,6 +594,12 @@ namespace hex {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Spacer::draw(const std::string& name) {
|
||||||
|
std::ignore = name;
|
||||||
|
ImGui::NewLine();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <hex/api/content_registry/settings.hpp>
|
||||||
|
|
||||||
#include <hex/helpers/magic.hpp>
|
#include <hex/helpers/magic.hpp>
|
||||||
#include <wolv/io/file.hpp>
|
#include <wolv/io/file.hpp>
|
||||||
#include <wolv/literals.hpp>
|
#include <wolv/literals.hpp>
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <wolv/utils/string.hpp>
|
||||||
|
|
||||||
namespace hex::prv {
|
namespace hex::prv {
|
||||||
|
|
||||||
|
|
@ -24,6 +26,28 @@ namespace hex::prv {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) {
|
||||||
|
m_shouldCreateBackups = ContentRegistry::Settings::read<bool>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.enable", true);
|
||||||
|
m_maxSize = ContentRegistry::Settings::read<u32>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.max_size", 1_MiB);
|
||||||
|
m_backupExtension = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.extension", ".bak");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) {
|
||||||
|
if (!m_shouldCreateBackups || m_backupCreated)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_provider->getActualSize() > m_maxSize)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension;
|
||||||
|
if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) {
|
||||||
|
if (wolv::io::fs::exists(backupFilePath)) {
|
||||||
|
m_backupCreated = true;
|
||||||
|
log::info("Created backup of provider data at '{}'", backupFilePath.string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ namespace hex::plugin::builtin {
|
||||||
class FileProvider : public prv::Provider,
|
class FileProvider : public prv::Provider,
|
||||||
public prv::IProviderDataDescription,
|
public prv::IProviderDataDescription,
|
||||||
public prv::IProviderFilePicker,
|
public prv::IProviderFilePicker,
|
||||||
public prv::IProviderMenuItems {
|
public prv::IProviderMenuItems,
|
||||||
|
public prv::IProviderDataBackupable {
|
||||||
public:
|
public:
|
||||||
FileProvider() = default;
|
FileProvider() : IProviderDataBackupable(this) {}
|
||||||
~FileProvider() override = default;
|
~FileProvider() override = default;
|
||||||
|
|
||||||
[[nodiscard]] bool isAvailable() const override;
|
[[nodiscard]] bool isAvailable() const override;
|
||||||
|
|
|
||||||
|
|
@ -493,11 +493,15 @@
|
||||||
"hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more",
|
"hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more",
|
||||||
"hex.builtin.setting.folders.remove_folder": "Remove currently selected folder from list",
|
"hex.builtin.setting.folders.remove_folder": "Remove currently selected folder from list",
|
||||||
"hex.builtin.setting.general": "General",
|
"hex.builtin.setting.general": "General",
|
||||||
|
"hex.builtin.setting.general.backups": "Backups",
|
||||||
|
"hex.builtin.setting.general.backups.auto_backup_time": "Periodically backup project",
|
||||||
|
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Every {0}s",
|
||||||
|
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Every {0}m {1}s",
|
||||||
|
"hex.builtin.setting.general.backups.file_backup.enable": "Backup data sources before modification if possible",
|
||||||
|
"hex.builtin.setting.general.backups.file_backup.max_size": "Max file size for file backups",
|
||||||
|
"hex.builtin.setting.general.backups.file_backup.extension": "Backup file extension",
|
||||||
"hex.builtin.setting.general.patterns": "Patterns",
|
"hex.builtin.setting.general.patterns": "Patterns",
|
||||||
"hex.builtin.setting.general.network": "Network",
|
"hex.builtin.setting.general.network": "Network",
|
||||||
"hex.builtin.setting.general.auto_backup_time": "Periodically backup project",
|
|
||||||
"hex.builtin.setting.general.auto_backup_time.format.simple": "Every {0}s",
|
|
||||||
"hex.builtin.setting.general.auto_backup_time.format.extended": "Every {0}m {1}s",
|
|
||||||
"hex.builtin.setting.general.auto_apply_patterns": "Auto-load supported pattern",
|
"hex.builtin.setting.general.auto_apply_patterns": "Auto-load supported pattern",
|
||||||
"hex.builtin.setting.general.suggest_patterns": "Suggest patterns based on loaded data",
|
"hex.builtin.setting.general.suggest_patterns": "Suggest patterns based on loaded data",
|
||||||
"hex.builtin.setting.general.server_contact": "Enable update checks and usage statistics",
|
"hex.builtin.setting.general.server_contact": "Enable update checks and usage statistics",
|
||||||
|
|
|
||||||
|
|
@ -70,13 +70,16 @@ namespace hex::plugin::builtin {
|
||||||
|
|
||||||
if (m_loadedIntoMemory)
|
if (m_loadedIntoMemory)
|
||||||
std::memcpy(m_data.data() + offset, buffer, size);
|
std::memcpy(m_data.data() + offset, buffer, size);
|
||||||
else
|
else {
|
||||||
|
this->createBackupIfNeeded(m_file.getPath());
|
||||||
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
|
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileProvider::save() {
|
void FileProvider::save() {
|
||||||
if (m_loadedIntoMemory) {
|
if (m_loadedIntoMemory) {
|
||||||
m_ignoreNextChangeEvent = true;
|
m_ignoreNextChangeEvent = true;
|
||||||
|
this->createBackupIfNeeded(m_file.getPath());
|
||||||
m_file.open();
|
m_file.open();
|
||||||
m_file.writeVectorAtomic(0x00, m_data);
|
m_file.writeVectorAtomic(0x00, m_data);
|
||||||
m_file.setSize(m_data.size());
|
m_file.setSize(m_data.size());
|
||||||
|
|
@ -113,8 +116,10 @@ namespace hex::plugin::builtin {
|
||||||
void FileProvider::resizeRaw(u64 newSize) {
|
void FileProvider::resizeRaw(u64 newSize) {
|
||||||
if (m_loadedIntoMemory)
|
if (m_loadedIntoMemory)
|
||||||
m_data.resize(newSize);
|
m_data.resize(newSize);
|
||||||
else
|
else {
|
||||||
|
this->createBackupIfNeeded(m_file.getPath());
|
||||||
m_file.setSize(newSize);
|
m_file.setSize(newSize);
|
||||||
|
}
|
||||||
|
|
||||||
m_fileSize = newSize;
|
m_fileSize = newSize;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,9 +227,9 @@ namespace hex::plugin::builtin {
|
||||||
if (value == 0)
|
if (value == 0)
|
||||||
return "hex.ui.common.off"_lang;
|
return "hex.ui.common.off"_lang;
|
||||||
else if (value < 60)
|
else if (value < 60)
|
||||||
return fmt::format("hex.builtin.setting.general.auto_backup_time.format.simple"_lang, value);
|
return fmt::format("hex.builtin.setting.general.backups.auto_backup_time.format.simple"_lang, value);
|
||||||
else
|
else
|
||||||
return fmt::format("hex.builtin.setting.general.auto_backup_time.format.extended"_lang, value / 60, value % 60);
|
return fmt::format("hex.builtin.setting.general.backups.auto_backup_time.format.extended"_lang, value / 60, value % 60);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (ImGui::SliderInt(name.data(), &m_value, 0, (30 * 60) / 30, format.c_str(), ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) {
|
if (ImGui::SliderInt(name.data(), &m_value, 0, (30 * 60) / 30, format.c_str(), ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) {
|
||||||
|
|
@ -756,7 +756,6 @@ namespace hex::plugin::builtin {
|
||||||
|
|
||||||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.show_tips", false);
|
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.show_tips", false);
|
||||||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.save_recent_providers", true);
|
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.save_recent_providers", true);
|
||||||
ContentRegistry::Settings::add<AutoBackupWidget>("hex.builtin.setting.general", "", "hex.builtin.setting.general.auto_backup_time");
|
|
||||||
ContentRegistry::Settings::add<Widgets::SliderDataSize>("hex.builtin.setting.general", "", "hex.builtin.setting.general.max_mem_file_size", 512_MiB, 0_bytes, 32_GiB, 1_MiB)
|
ContentRegistry::Settings::add<Widgets::SliderDataSize>("hex.builtin.setting.general", "", "hex.builtin.setting.general.max_mem_file_size", 512_MiB, 0_bytes, 32_GiB, 1_MiB)
|
||||||
.setTooltip("hex.builtin.setting.general.max_mem_file_size.desc");
|
.setTooltip("hex.builtin.setting.general.max_mem_file_size.desc");
|
||||||
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.pattern_data_max_filter_items", 128, 32, 1024);
|
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.pattern_data_max_filter_items", 128, 32, 1024);
|
||||||
|
|
@ -773,6 +772,20 @@ namespace hex::plugin::builtin {
|
||||||
ContentRegistry::Settings::add<ServerContactWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.server_contact");
|
ContentRegistry::Settings::add<ServerContactWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.server_contact");
|
||||||
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.upload_crash_logs", true);
|
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.upload_crash_logs", true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
ContentRegistry::Settings::add<AutoBackupWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.auto_backup_time");
|
||||||
|
ContentRegistry::Settings::add<Widgets::Spacer>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.spacer");
|
||||||
|
|
||||||
|
auto fileBackupEnabledWidget = ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.enable", true);
|
||||||
|
ContentRegistry::Settings::add<Widgets::SliderDataSize>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.max_size", 512_MiB, 0_bytes, 32_GiB, 1_MiB)
|
||||||
|
.setEnabledCallback([=] {
|
||||||
|
return static_cast<Widgets::Checkbox&>(fileBackupEnabledWidget.getWidget()).isChecked();
|
||||||
|
});
|
||||||
|
ContentRegistry::Settings::add<Widgets::TextBox>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.extension", ".bak")
|
||||||
|
.setEnabledCallback([=] {
|
||||||
|
return static_cast<Widgets::Checkbox&>(fileBackupEnabledWidget.getWidget()).isChecked();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Interface */
|
/* Interface */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue