mirror of https://github.com/WerWolv/ImHex
impr: Calculate hashes in a background thread
This commit is contained in:
parent
ab54acb176
commit
f2e8d402dd
|
|
@ -38,24 +38,14 @@ EXPORT_MODULE namespace hex {
|
|||
[[nodiscard]] const Hash *getType() const { return m_type; }
|
||||
[[nodiscard]] const std::string& getName() const { return m_name; }
|
||||
|
||||
const std::vector<u8>& get(const Region& region, prv::Provider *provider) {
|
||||
if (m_cache.empty()) {
|
||||
m_cache = m_callback(region, provider);
|
||||
}
|
||||
|
||||
return m_cache;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_cache.clear();
|
||||
std::vector<u8> get(const Region& region, prv::Provider *provider) const {
|
||||
return m_callback(region, provider);
|
||||
}
|
||||
|
||||
private:
|
||||
Hash *m_type;
|
||||
std::string m_name;
|
||||
Callback m_callback;
|
||||
|
||||
std::vector<u8> m_cache;
|
||||
};
|
||||
|
||||
virtual void draw() { }
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ EXPORT_MODULE namespace hex {
|
|||
[[nodiscard]] u64 getValue() const;
|
||||
[[nodiscard]] u64 getMaxValue() const;
|
||||
|
||||
void wait() const;
|
||||
|
||||
private:
|
||||
void finish();
|
||||
void interruption();
|
||||
|
|
@ -87,9 +89,9 @@ EXPORT_MODULE namespace hex {
|
|||
std::atomic<bool> m_background = true;
|
||||
std::atomic<bool> m_blocking = false;
|
||||
|
||||
std::atomic<bool> m_interrupted = false;
|
||||
std::atomic<bool> m_finished = false;
|
||||
std::atomic<bool> m_hadException = false;
|
||||
std::atomic_flag m_interrupted = false;
|
||||
std::atomic_flag m_finished = false;
|
||||
std::atomic_flag m_hadException = false;
|
||||
std::string m_exceptionMessage;
|
||||
|
||||
struct TaskInterruptor { virtual ~TaskInterruptor() = default; };
|
||||
|
|
@ -114,6 +116,7 @@ EXPORT_MODULE namespace hex {
|
|||
[[nodiscard]] u32 getProgress() const;
|
||||
|
||||
void interrupt() const;
|
||||
void wait() const;
|
||||
private:
|
||||
std::weak_ptr<Task> m_task;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -82,9 +82,17 @@ namespace hex {
|
|||
m_maxValue = u64(other.m_maxValue);
|
||||
m_currValue = u64(other.m_currValue);
|
||||
|
||||
m_finished = bool(other.m_finished);
|
||||
m_hadException = bool(other.m_hadException);
|
||||
m_interrupted = bool(other.m_interrupted);
|
||||
if (other.m_finished.test())
|
||||
m_finished.test_and_set();
|
||||
if (other.m_hadException.test())
|
||||
m_hadException.test_and_set();
|
||||
if (other.m_interrupted.test())
|
||||
m_interrupted.test_and_set();
|
||||
|
||||
m_finished.notify_all();
|
||||
m_hadException.notify_all();
|
||||
m_interrupted.notify_all();
|
||||
|
||||
m_shouldInterrupt = bool(other.m_shouldInterrupt);
|
||||
}
|
||||
|
||||
|
|
@ -143,11 +151,11 @@ namespace hex {
|
|||
|
||||
|
||||
bool Task::isFinished() const {
|
||||
return m_finished;
|
||||
return m_finished.test();
|
||||
}
|
||||
|
||||
bool Task::hadException() const {
|
||||
return m_hadException;
|
||||
return m_hadException.test();
|
||||
}
|
||||
|
||||
bool Task::shouldInterrupt() const {
|
||||
|
|
@ -155,11 +163,11 @@ namespace hex {
|
|||
}
|
||||
|
||||
bool Task::wasInterrupted() const {
|
||||
return m_interrupted;
|
||||
return m_interrupted.test();
|
||||
}
|
||||
|
||||
void Task::clearException() {
|
||||
m_hadException = false;
|
||||
m_hadException.clear();
|
||||
}
|
||||
|
||||
std::string Task::getExceptionMessage() const {
|
||||
|
|
@ -180,12 +188,18 @@ namespace hex {
|
|||
return m_maxValue;
|
||||
}
|
||||
|
||||
void Task::wait() const {
|
||||
m_finished.wait(false);
|
||||
}
|
||||
|
||||
void Task::finish() {
|
||||
m_finished = true;
|
||||
m_finished.test_and_set();
|
||||
m_finished.notify_all();
|
||||
}
|
||||
|
||||
void Task::interruption() {
|
||||
m_interrupted = true;
|
||||
m_interrupted.test_and_set();
|
||||
m_interrupted.notify_all();
|
||||
}
|
||||
|
||||
void Task::exception(const char *message) {
|
||||
|
|
@ -193,7 +207,8 @@ namespace hex {
|
|||
|
||||
// Store information about the caught exception
|
||||
m_exceptionMessage = message;
|
||||
m_hadException = true;
|
||||
m_hadException.test_and_set();
|
||||
m_hadException.notify_all();
|
||||
|
||||
// Call the interrupt callback on the current thread if one is set
|
||||
if (m_interruptCallback)
|
||||
|
|
@ -241,6 +256,14 @@ namespace hex {
|
|||
task->interrupt();
|
||||
}
|
||||
|
||||
void TaskHolder::wait() const {
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
task->wait();
|
||||
}
|
||||
|
||||
u32 TaskHolder::getProgress() const {
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/api/content_registry/hashes.hpp>
|
||||
#include <hex/api/imhex_api/hex_editor.hpp>
|
||||
#include <hex/providers/memory_provider.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
|
|
@ -14,6 +17,58 @@ namespace hex::plugin::hashes {
|
|||
void drawContent() override;
|
||||
void drawHelpText() override;
|
||||
|
||||
class Function {
|
||||
public:
|
||||
explicit Function(ContentRegistry::Hashes::Hash::Function hashFunction) : m_hashFunction(std::move(hashFunction)) { }
|
||||
|
||||
void update(const Region ®ion, prv::Provider *provider) {
|
||||
m_region = { region, provider };
|
||||
}
|
||||
|
||||
void update(std::vector<u8> data) {
|
||||
m_data = std::move(data);
|
||||
}
|
||||
|
||||
std::vector<u8> get() {
|
||||
if (!m_task.isRunning()) {
|
||||
if (m_region.has_value()) {
|
||||
m_lastResult.clear();
|
||||
m_task = TaskManager::createBackgroundTask("Updating hash", [this, region = m_region]() {
|
||||
m_lastResult = m_hashFunction.get(region->getRegion(), region->getProvider());
|
||||
});
|
||||
|
||||
m_region.reset();
|
||||
} else if (!m_data.empty()) {
|
||||
m_lastResult.clear();
|
||||
m_task = TaskManager::createBackgroundTask("Updating hash", [this, data = std::move(m_data)]() {
|
||||
prv::MemoryProvider provider({ data.begin(), data.end() });
|
||||
m_lastResult = m_hashFunction.get(Region { 0x00, provider.getActualSize() }, &provider);
|
||||
});
|
||||
|
||||
m_region.reset();
|
||||
m_data = {};
|
||||
}
|
||||
}
|
||||
|
||||
return m_lastResult;
|
||||
}
|
||||
|
||||
bool isCalculating() const {
|
||||
return m_task.isRunning();
|
||||
}
|
||||
|
||||
const ContentRegistry::Hashes::Hash::Function& getFunction() const {
|
||||
return m_hashFunction;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> m_data;
|
||||
std::optional<ImHexApi::HexEditor::ProviderRegion> m_region;
|
||||
ContentRegistry::Hashes::Hash::Function m_hashFunction;
|
||||
std::vector<u8> m_lastResult;
|
||||
TaskHolder m_task;
|
||||
};
|
||||
|
||||
private:
|
||||
bool importHashes(prv::Provider *provider, const nlohmann::json &json);
|
||||
bool exportHashes(prv::Provider *provider, nlohmann::json &json);
|
||||
|
|
@ -24,7 +79,7 @@ namespace hex::plugin::hashes {
|
|||
ContentRegistry::Hashes::Hash *m_selectedHash = nullptr;
|
||||
std::string m_newHashName;
|
||||
|
||||
PerProvider<std::vector<ContentRegistry::Hashes::Hash::Function>> m_hashFunctions;
|
||||
PerProvider<std::list<Function>> m_hashFunctions;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ namespace hex::plugin::hashes {
|
|||
|
||||
class PopupTextHash : public Popup<PopupTextHash> {
|
||||
public:
|
||||
explicit PopupTextHash(const ContentRegistry::Hashes::Hash::Function &hash)
|
||||
: hex::Popup<PopupTextHash>(hash.getName(), true, false),
|
||||
explicit PopupTextHash(const ViewHashes::Function &hash)
|
||||
: hex::Popup<PopupTextHash>(hash.getFunction().getName(), true, false),
|
||||
m_hash(hash) { }
|
||||
|
||||
void drawContent() override {
|
||||
|
|
@ -29,16 +29,25 @@ namespace hex::plugin::hashes {
|
|||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGui::InputTextMultiline("##input", m_input)) {
|
||||
prv::MemoryProvider provider({ m_input.begin(), m_input.end() });
|
||||
|
||||
m_hash.reset();
|
||||
auto bytes = m_hash.get(Region { 0x00, provider.getActualSize() }, &provider);
|
||||
|
||||
m_result = crypt::encode16(bytes);
|
||||
m_hash.update({ m_input.begin(), m_input.end() });
|
||||
m_result.reset();
|
||||
}
|
||||
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::InputText("##result", m_result, ImGuiInputTextFlags_ReadOnly);
|
||||
if (m_hash.isCalculating()) {
|
||||
ImGuiExt::TextSpinner("");
|
||||
} else {
|
||||
if (!m_result.has_value()) {
|
||||
const auto data = m_hash.get();
|
||||
if (!data.empty())
|
||||
m_result = crypt::encode16(data);
|
||||
}
|
||||
|
||||
auto result = m_result.value_or("???");
|
||||
ImGui::InputText("##result", result, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
|
||||
|
|
@ -50,22 +59,22 @@ namespace hex::plugin::hashes {
|
|||
}
|
||||
|
||||
ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 200 });
|
||||
return scaled({ 400, 230 });
|
||||
}
|
||||
|
||||
ImVec2 getMaxSize() const override { return this->getMinSize(); }
|
||||
|
||||
private:
|
||||
std::string m_input;
|
||||
std::string m_result;
|
||||
ContentRegistry::Hashes::Hash::Function m_hash;
|
||||
std::optional<std::string> m_result;
|
||||
ViewHashes::Function m_hash;
|
||||
};
|
||||
|
||||
ViewHashes::ViewHashes() : View::Window("hex.hashes.view.hashes.name", ICON_VS_KEY) {
|
||||
EventRegionSelected::subscribe(this, [this](const auto &providerRegion) {
|
||||
if (providerRegion.getProvider() != nullptr)
|
||||
for (auto &function : m_hashFunctions.get(providerRegion.getProvider()))
|
||||
function.reset();
|
||||
function.update(providerRegion.getRegion(), providerRegion.getProvider());
|
||||
});
|
||||
|
||||
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
|
||||
|
|
@ -92,22 +101,15 @@ namespace hex::plugin::hashes {
|
|||
if (provider == nullptr)
|
||||
continue;
|
||||
|
||||
std::vector<u8> bytes;
|
||||
try {
|
||||
bytes = function.get(*selection, provider);
|
||||
} catch (const std::exception &) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", function.getName());
|
||||
ImGuiExt::TextFormatted("{}", function.getFunction().getName());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted(" ");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", crypt::encode16(bytes));
|
||||
ImGuiExt::TextFormatted("{}", crypt::encode16(function.get()));
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
|
@ -198,7 +200,7 @@ namespace hex::plugin::hashes {
|
|||
ImGui::BeginDisabled(m_newHashName.empty() || m_selectedHash == nullptr);
|
||||
if (ImGuiExt::DimmedButton("hex.hashes.view.hashes.add"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
if (m_selectedHash != nullptr) {
|
||||
m_hashFunctions->push_back(m_selectedHash->create(m_newHashName));
|
||||
m_hashFunctions->emplace_back(m_selectedHash->create(m_newHashName));
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.misc", "hex.hashes.achievement.misc.create_hash.name");
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
@ -222,10 +224,10 @@ namespace hex::plugin::hashes {
|
|||
auto provider = ImHexApi::Provider::get();
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
std::optional<u32> indexToRemove;
|
||||
for (u32 i = 0; i < m_hashFunctions->size(); i++) {
|
||||
auto &function = (*m_hashFunctions)[i];
|
||||
|
||||
u32 i = 0;
|
||||
auto itToRemove = m_hashFunctions->end();
|
||||
for (auto it = m_hashFunctions->begin(); it != m_hashFunctions->end(); ++it) {
|
||||
auto &function = *it;
|
||||
ImGui::PushID(i + 1);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -234,17 +236,17 @@ namespace hex::plugin::hashes {
|
|||
ImGui::PushStyleColor(ImGuiCol_Header, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0x00);
|
||||
ImGui::Selectable(function.getName().c_str(), false);
|
||||
ImGui::Selectable(function.getFunction().getName().c_str(), false);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", Lang(function.getType()->getUnlocalizedName()));
|
||||
ImGuiExt::TextFormatted("{}", Lang(function.getFunction().getType()->getUnlocalizedName()));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
std::string result;
|
||||
if (provider != nullptr && selection.has_value()) {
|
||||
try {
|
||||
result = crypt::encode16(function.get(*selection, provider));
|
||||
result = crypt::encode16(function.get());
|
||||
} catch (const std::exception &e) {
|
||||
result = e.what();
|
||||
}
|
||||
|
|
@ -252,7 +254,10 @@ namespace hex::plugin::hashes {
|
|||
result = "???";
|
||||
}
|
||||
|
||||
ImGuiExt::TextFormattedSelectable("{}", result);
|
||||
if (!function.isCalculating())
|
||||
ImGuiExt::TextFormattedSelectable("{}", result);
|
||||
else
|
||||
ImGuiExt::TextSpinner("");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
|
|
@ -262,15 +267,17 @@ namespace hex::plugin::hashes {
|
|||
}
|
||||
ImGui::SameLine(0, 3_scaled);
|
||||
if (ImGuiExt::DimmedIconButton(ICON_VS_CHROME_CLOSE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
indexToRemove = i;
|
||||
itToRemove = it;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (indexToRemove.has_value()) {
|
||||
m_hashFunctions->erase(m_hashFunctions->begin() + indexToRemove.value());
|
||||
if (itToRemove != m_hashFunctions->end()) {
|
||||
m_hashFunctions->erase(itToRemove);
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
|
@ -318,7 +325,7 @@ namespace hex::plugin::hashes {
|
|||
auto newFunction = newHash->create(hash["name"]);
|
||||
newFunction.getType()->load(hash["settings"]);
|
||||
|
||||
m_hashFunctions.get(provider).push_back(std::move(newFunction));
|
||||
m_hashFunctions.get(provider).emplace_back(std::move(newFunction));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -331,10 +338,11 @@ namespace hex::plugin::hashes {
|
|||
json["hashes"] = nlohmann::json::array();
|
||||
size_t index = 0;
|
||||
for (const auto &hashFunction : m_hashFunctions.get(provider)) {
|
||||
const auto &function = hashFunction.getFunction();
|
||||
json["hashes"][index] = {
|
||||
{ "name", hashFunction.getName() },
|
||||
{ "type", hashFunction.getType()->getUnlocalizedName() },
|
||||
{ "settings", hashFunction.getType()->store() }
|
||||
{ "name", function.getName() },
|
||||
{ "type", function.getType()->getUnlocalizedName() },
|
||||
{ "settings", function.getType()->store() }
|
||||
};
|
||||
index++;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue