sys: Replace existing bad project system with a much better one (#663)

* sys: Initial effort to replace existing project files with a better system

* sys: Added back marking provider as dirty

* sys: Remove git commit information from project files

* sys: Format data processor save file nicely

* fix: Automatic pattern loading not working correctly

* ui: Added warning popup when closing a provider with modifications

Closes #604

* sys: Fixed build issues

* tests: Removed useless debug logs

* patterns: Updated pattern language

* sys: Added log message when crashing with a signal

* sys: Make sure abnormal termination handlers are being called more reliably
This commit is contained in:
WerWolv
2022-08-08 21:23:52 +02:00
committed by GitHub
parent f0756bceb8
commit 966f3b8597
50 changed files with 1223 additions and 847 deletions

View File

@@ -38,6 +38,13 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] bool hasLoadInterface() const override { return true; }
void drawLoadInterface() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings = { }) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.disk";
}
protected:
void reloadDrives();

View File

@@ -7,11 +7,15 @@
#include <sys/stat.h>
#if defined(OS_WINDOWS)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#include <sys/fcntl.h>
#endif
namespace hex::plugin::builtin::prv {
@@ -50,13 +54,24 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] bool open() override;
void close() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.file";
}
protected:
#if defined(OS_WINDOWS)
HANDLE m_file = INVALID_HANDLE_VALUE;
HANDLE m_mapping = INVALID_HANDLE_VALUE;
#else
int m_file = -1;
#endif
#if defined(OS_WINDOWS)
HANDLE m_file = INVALID_HANDLE_VALUE;
HANDLE m_mapping = INVALID_HANDLE_VALUE;
#else
int m_file = -1;
#endif
std::fs::path m_path;
void *m_mappedFile = nullptr;

View File

@@ -12,7 +12,7 @@ namespace hex::plugin::builtin::prv {
class GDBProvider : public hex::prv::Provider {
public:
explicit GDBProvider();
GDBProvider();
~GDBProvider() override;
[[nodiscard]] bool isAvailable() const override;
@@ -42,6 +42,13 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] bool hasLoadInterface() const override { return true; }
void drawLoadInterface() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.gdb";
}
protected:
hex::Socket m_socket;

View File

@@ -15,7 +15,6 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::list<ImHexApi::Bookmarks::Entry> m_bookmarks;
std::string m_currFilter;
};

View File

@@ -20,25 +20,17 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::list<dp::Node *> m_endNodes;
std::list<dp::Node *> m_nodes;
std::list<dp::Link> m_links;
std::vector<hex::prv::Overlay *> m_dataOverlays;
int m_rightClickedId = -1;
ImVec2 m_rightClickedCoords;
std::optional<dp::Node::NodeError> m_currNodeError;
bool m_continuousEvaluation = false;
void eraseLink(u32 id);
void eraseNodes(const std::vector<int> &ids);
void processNodes();
std::string saveNodes();
void loadNodes(const std::string &data);
std::string saveNodes(prv::Provider *provider);
void loadNodes(prv::Provider *provider, const std::string &data);
};
}

View File

@@ -0,0 +1,56 @@
#pragma once
#include <map>
#include <hex/providers/provider.hpp>
#include <pl/pattern_language.hpp>
#include <hex/data_processor/attribute.hpp>
#include <hex/data_processor/node.hpp>
#include <hex/data_processor/link.hpp>
namespace hex::plugin::builtin {
class ProviderExtraData {
public:
struct Data {
bool dataDirty = false;
struct {
std::string sourceCode;
std::unique_ptr<pl::PatternLanguage> runtime;
} patternLanguage;
std::list<ImHexApi::Bookmarks::Entry> bookmarks;
struct {
std::list<dp::Node*> endNodes;
std::list<std::unique_ptr<dp::Node>> nodes;
std::list<dp::Link> links;
std::vector<hex::prv::Overlay *> dataOverlays;
std::optional<dp::Node::NodeError> currNodeError;
} dataProcessor;
};
static Data& getCurrent() {
return get(ImHexApi::Provider::get());
}
static Data& get(hex::prv::Provider *provider) {
return s_data[provider];
}
static void erase(hex::prv::Provider *provider) {
s_data.erase(provider);
}
static bool markDirty() {
return getCurrent().dataDirty = true;
}
private:
ProviderExtraData() = default;
static inline std::map<hex::prv::Provider*, Data> s_data = {};
};
}

View File

@@ -196,11 +196,11 @@ namespace hex::plugin::builtin {
class NodeComment : public dp::Node {
public:
NodeComment() : Node("hex.builtin.nodes.constants.comment.header", {}) {
this->m_comment.resize(0xFFF, 0x00);
}
void drawNode() override {
ImGui::InputTextMultiline("##string", reinterpret_cast<char *>(this->m_comment.data()), this->m_comment.size() - 1, ImVec2(150, 100));
ImGui::InputTextMultiline("##string", this->m_comment, scaled(ImVec2(150, 100)));
}
void process() override {

View File

@@ -3,14 +3,16 @@
#include <hex/providers/provider.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imgui.h>
#include <nlohmann/json.hpp>
#include "content/providers/file_provider.hpp"
#include "provider_extra_data.hpp"
namespace hex::plugin::builtin {
@@ -38,25 +40,27 @@ namespace hex::plugin::builtin {
return;
}
ProjectFile::setFilePath(path);
EventManager::post<EventFileLoaded>(path);
EventManager::post<EventDataChanged>();
EventManager::post<EventHighlightingChanged>();
}
void registerEventHandlers() {
EventManager::subscribe<EventProjectFileLoad>([]() {
EventManager::post<RequestOpenFile>(ProjectFile::getFilePath());
});
EventManager::subscribe<EventWindowClosing>([](GLFWwindow *window) {
if (ProjectFile::hasUnsavedChanges()) {
if (ImHexApi::Provider::isDirty()) {
glfwSetWindowShouldClose(window, GLFW_FALSE);
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.exit_application.title"_lang); });
}
});
EventManager::subscribe<EventProviderClosing>([](hex::prv::Provider *provider, bool *shouldClose) {
if (provider->isDirty()) {
*shouldClose = false;
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.close_provider.title"_lang); });
}
});
EventManager::subscribe<RequestOpenFile>(openFile);
EventManager::subscribe<RequestOpenWindow>([](const std::string &name) {
@@ -93,6 +97,11 @@ namespace hex::plugin::builtin {
if (provider->hasLoadInterface())
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
});
EventManager::subscribe<EventProviderDeleted>([](hex::prv::Provider *provider) {
ProviderExtraData::erase(provider);
});
}
}

View File

@@ -4,9 +4,10 @@
#include <implot.h>
#include <hex/ui/view.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/patches.hpp>
#include <thread>
@@ -69,18 +70,14 @@ namespace hex::plugin::builtin {
}
if (ImGui::MenuItem("hex.builtin.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) {
if (ProjectFile::getProjectFilePath() == "") {
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"}
},
[](std::fs::path path) {
if (path.extension() != ".hexproj") {
path.replace_extension(".hexproj");
}
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} },
[](std::fs::path path) {
if (path.extension() != ".hexproj") {
path.replace_extension(".hexproj");
}
ProjectFile::store(path);
});
} else
ProjectFile::store();
ProjectFile::store(path);
});
}
});

View File

@@ -6,59 +6,6 @@
namespace hex::plugin::builtin {
void registerPatternLanguagePragmas() {
ContentRegistry::PatternLanguage::addPragma("endian", [](pl::PatternLanguage &runtime, const std::string &value) {
if (value == "big") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::big);
return true;
} else if (value == "little") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::little);
return true;
} else if (value == "native") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::native);
return true;
} else
return false;
});
ContentRegistry::PatternLanguage::addPragma("eval_depth", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setEvaluationDepth(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("array_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setArrayLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("pattern_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setPatternLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("loop_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setLoopLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("base_address", [](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
@@ -69,18 +16,6 @@ namespace hex::plugin::builtin {
return true;
});
ContentRegistry::PatternLanguage::addPragma("bitfield_order", [](pl::PatternLanguage &runtime, const std::string &value) {
if (value == "left_to_right") {
runtime.getInternals().evaluator->setBitfieldOrder(pl::core::BitfieldOrder::LeftToRight);
return true;
} else if (value == "right_to_left") {
runtime.getInternals().evaluator->setBitfieldOrder(pl::core::BitfieldOrder::RightToLeft);
return true;
} else {
return false;
}
});
ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
}

View File

@@ -4,13 +4,63 @@
#include "content/providers/file_provider.hpp"
#include "content/providers/disk_provider.hpp"
#include <hex/api/project_file_manager.hpp>
#include <nlohmann/json.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::plugin::builtin {
void registerProviders() {
ContentRegistry::Provider::add<prv::FileProvider>("hex.builtin.provider.file", false);
ContentRegistry::Provider::add<prv::GDBProvider>("hex.builtin.provider.gdb");
ContentRegistry::Provider::add<prv::DiskProvider>("hex.builtin.provider.disk");
ContentRegistry::Provider::add<prv::FileProvider>(false);
ContentRegistry::Provider::add<prv::GDBProvider>();
ContentRegistry::Provider::add<prv::DiskProvider>();
ProjectFile::registerHandler({
.basePath = "providers",
.load = [](const std::fs::path &basePath, Tar &tar) {
auto json = nlohmann::json::parse(tar.readString(basePath / "providers.json"));
auto providerIds = json["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)));
auto provider = ImHexApi::Provider::createProvider(providerSettings["type"].get<std::string>());
if (provider == nullptr) {
success = false;
continue;
}
provider->loadSettings(providerSettings["settings"]);
if (!provider->open())
success = false;
}
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.write(basePath / hex::format("{}.json", id), json.dump(4));
}
tar.write(basePath / "providers.json",
nlohmann::json({
{"providers", providerIds}
}).dump(4)
);
return true;
}
});
}
}

View File

@@ -12,17 +12,21 @@
#include <imgui.h>
#if defined(OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define lseek lseek64
#elif defined(OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace hex::plugin::builtin::prv {
@@ -36,11 +40,15 @@ namespace hex::plugin::builtin::prv {
}
bool DiskProvider::isAvailable() const {
#if defined(OS_WINDOWS)
return this->m_diskHandle != INVALID_HANDLE_VALUE;
#else
return this->m_diskHandle != -1;
#endif
#if defined(OS_WINDOWS)
return this->m_diskHandle != INVALID_HANDLE_VALUE;
#else
return this->m_diskHandle != -1;
#endif
}
bool DiskProvider::isReadable() const {
@@ -68,113 +76,120 @@ namespace hex::plugin::builtin::prv {
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
const auto &path = this->m_path.native();
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
if (this->m_diskHandle == INVALID_HANDLE_VALUE)
return false;
}
{
DISK_GEOMETRY_EX diskGeometry = { };
DWORD bytesRead = 0;
if (DeviceIoControl(
this->m_diskHandle,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
nullptr,
0,
&diskGeometry,
sizeof(DISK_GEOMETRY_EX),
&bytesRead,
nullptr)) {
this->m_diskSize = diskGeometry.DiskSize.QuadPart;
this->m_sectorSize = diskGeometry.Geometry.BytesPerSector;
this->m_sectorBuffer.resize(this->m_sectorSize);
if (this->m_diskHandle == INVALID_HANDLE_VALUE)
return false;
}
}
if (this->m_diskHandle == nullptr || this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_readable = false;
this->m_diskHandle = nullptr;
CloseHandle(this->m_diskHandle);
{
DISK_GEOMETRY_EX diskGeometry = { };
DWORD bytesRead = 0;
if (DeviceIoControl(
this->m_diskHandle,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
nullptr,
0,
&diskGeometry,
sizeof(DISK_GEOMETRY_EX),
&bytesRead,
nullptr)) {
this->m_diskSize = diskGeometry.DiskSize.QuadPart;
this->m_sectorSize = diskGeometry.Geometry.BytesPerSector;
this->m_sectorBuffer.resize(this->m_sectorSize);
}
}
return false;
}
if (this->m_diskHandle == nullptr || this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_readable = false;
this->m_diskHandle = nullptr;
CloseHandle(this->m_diskHandle);
return false;
}
#else
const auto &path = this->m_path.native();
#else
struct stat driveStat;
const auto &path = this->m_path.native();
if (::stat(path.c_str(), &driveStat) == 0)
this->m_diskSize = driveStat.st_size;
else
this->m_diskSize = 0;
struct stat driveStat;
this->m_sectorSize = 0;
if (::stat(path.c_str(), &driveStat) == 0)
this->m_diskSize = driveStat.st_size;
else
this->m_diskSize = 0;
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
this->m_sectorSize = 0;
if (this->m_diskHandle == -1) {
this->m_readable = false;
return false;
}
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
#endif
if (this->m_diskHandle == -1) {
this->m_readable = false;
return false;
}
Provider::resize(this->getActualSize());
#endif
return true;
return Provider::open();
}
void DiskProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
#if defined(OS_WINDOWS)
this->m_diskHandle = INVALID_HANDLE_VALUE;
#else
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
this->m_diskHandle = -1;
#endif
this->m_diskHandle = INVALID_HANDLE_VALUE;
#else
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
this->m_diskHandle = -1;
#endif
Provider::close();
}
void DiskProvider::readRaw(u64 offset, void *buffer, size_t size) {
#if defined(OS_WINDOWS)
DWORD bytesRead = 0;
#if defined(OS_WINDOWS)
u64 startOffset = offset;
DWORD bytesRead = 0;
while (size > 0) {
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
u64 startOffset = offset;
if (this->m_sectorBufferAddress != static_cast<u64>(seekPosition.QuadPart)) {
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::ReadFile(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size(), &bytesRead, nullptr);
this->m_sectorBufferAddress = seekPosition.QuadPart;
while (size > 0) {
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
if (this->m_sectorBufferAddress != static_cast<u64>(seekPosition.QuadPart)) {
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::ReadFile(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size(), &bytesRead, nullptr);
this->m_sectorBufferAddress = seekPosition.QuadPart;
}
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size));
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size));
#else
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#else
u64 startOffset = offset;
while (size > 0) {
@@ -193,59 +208,61 @@ namespace hex::plugin::builtin::prv {
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#endif
#endif
}
void DiskProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
#if defined(OS_WINDOWS)
DWORD bytesWritten = 0;
#if defined(OS_WINDOWS)
u64 startOffset = offset;
DWORD bytesWritten = 0;
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
u64 startOffset = offset;
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::WriteFile(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size(), &bytesWritten, nullptr);
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
offset += currSize;
size -= currSize;
}
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::WriteFile(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size(), &bytesWritten, nullptr);
#else
offset += currSize;
size -= currSize;
}
u64 startOffset = offset;
#else
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
u64 startOffset = offset;
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
offset += currSize;
size -= currSize;
}
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
#endif
offset += currSize;
size -= currSize;
}
#endif
}
size_t DiskProvider::getActualSize() const {
@@ -266,66 +283,82 @@ namespace hex::plugin::builtin::prv {
void DiskProvider::reloadDrives() {
#if defined(OS_WINDOWS)
this->m_availableDrives.clear();
std::bitset<32> drives = ::GetLogicalDrives();
for (char i = 0; i < 26; i++) {
if (drives[i])
this->m_availableDrives.insert(hex::format(R"(\\.\{:c}:)", 'A' + i));
}
#if defined(OS_WINDOWS)
auto logicalDrives = this->m_availableDrives;
for (const auto &drive : logicalDrives) {
auto handle = reinterpret_cast<HANDLE>(::CreateFile(drive.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
if (handle == INVALID_HANDLE_VALUE) continue;
VOLUME_DISK_EXTENTS diskExtents = { };
DWORD bytesRead = 0;
auto result = ::DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr,
0,
&diskExtents,
sizeof(VOLUME_DISK_EXTENTS),
&bytesRead,
nullptr);
if (result) {
auto diskPath = hex::format(R"(\\.\PhysicalDrive{})", diskExtents.Extents[0].DiskNumber);
this->m_availableDrives.insert(diskPath);
this->m_availableDrives.clear();
std::bitset<32> drives = ::GetLogicalDrives();
for (char i = 0; i < 26; i++) {
if (drives[i])
this->m_availableDrives.insert(hex::format(R"(\\.\{:c}:)", 'A' + i));
}
::CloseHandle(handle);
}
#endif
auto logicalDrives = this->m_availableDrives;
for (const auto &drive : logicalDrives) {
auto handle = reinterpret_cast<HANDLE>(::CreateFile(drive.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
if (handle == INVALID_HANDLE_VALUE) continue;
VOLUME_DISK_EXTENTS diskExtents = { };
DWORD bytesRead = 0;
auto result = ::DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr,
0,
&diskExtents,
sizeof(VOLUME_DISK_EXTENTS),
&bytesRead,
nullptr);
if (result) {
auto diskPath = hex::format(R"(\\.\PhysicalDrive{})", diskExtents.Extents[0].DiskNumber);
this->m_availableDrives.insert(diskPath);
}
::CloseHandle(handle);
}
#endif
}
void DiskProvider::drawLoadInterface() {
#if defined(OS_WINDOWS)
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
#if defined(OS_WINDOWS)
for (const auto &drive : this->m_availableDrives) {
if (ImGui::Selectable(drive.c_str(), this->m_path == drive))
this->m_path = drive;
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
for (const auto &drive : this->m_availableDrives) {
if (ImGui::Selectable(drive.c_str(), this->m_path == drive))
this->m_path = drive;
}
ImGui::EndListBox();
}
ImGui::EndListBox();
ImGui::SameLine();
if (ImGui::Button("hex.builtin.provider.disk.reload"_lang)) {
this->reloadDrives();
}
ImGui::SameLine();
#else
if (ImGui::Button("hex.builtin.provider.disk.reload"_lang)) {
this->reloadDrives();
}
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
#else
#endif
}
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
nlohmann::json DiskProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_path.string();
#endif
return Provider::storeSettings(settings);
}
void DiskProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->setPath(settings["path"].get<std::string>());
this->reloadDrives();
}
}

View File

@@ -2,11 +2,13 @@
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
@@ -19,11 +21,11 @@ namespace hex::plugin::builtin::prv {
bool FileProvider::isAvailable() const {
#if defined(OS_WINDOWS)
return this->m_file != INVALID_HANDLE_VALUE && this->m_mapping != INVALID_HANDLE_VALUE && this->m_mappedFile != nullptr;
#else
return this->m_file != -1 && this->m_mappedFile != nullptr;
#endif
#if defined(OS_WINDOWS)
return this->m_file != INVALID_HANDLE_VALUE && this->m_mapping != INVALID_HANDLE_VALUE && this->m_mappedFile != nullptr;
#else
return this->m_file != -1 && this->m_mappedFile != nullptr;
#endif
}
bool FileProvider::isReadable() const {
@@ -202,121 +204,137 @@ namespace hex::plugin::builtin::prv {
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
this->m_fileStatsValid = wstat(path.c_str(), &this->m_fileStats) == 0;
this->m_fileStatsValid = wstat(path.c_str(), &this->m_fileStats) == 0;
LARGE_INTEGER fileSize = {};
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
LARGE_INTEGER fileSize = {};
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
GetFileSizeEx(this->m_file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
CloseHandle(this->m_file);
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
}
auto fileCleanup = SCOPE_GUARD {
GetFileSizeEx(this->m_file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
CloseHandle(this->m_file);
this->m_readable = false;
this->m_file = nullptr;
};
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return false;
}
if (this->m_fileSize > 0) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE)
return false;
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
}
auto mappingCleanup = SCOPE_GUARD {
CloseHandle(this->m_mapping);
auto fileCleanup = SCOPE_GUARD {
CloseHandle(this->m_file);
this->m_mapping = nullptr;
this->m_readable = false;
this->m_file = nullptr;
};
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_READ, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
return false;
}
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return false;
}
mappingCleanup.release();
if (this->m_fileSize > 0) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE) {
ProjectFile::setFilePath(this->m_path);
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
} else {
return false;
}
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READONLY, 0, 0, nullptr);
fileCleanup.release();
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE)
return false;
}
#else
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
auto mappingCleanup = SCOPE_GUARD {
CloseHandle(this->m_mapping);
int mmapprot = PROT_READ | PROT_WRITE;
this->m_mapping = nullptr;
this->m_readable = false;
};
this->m_file = ::open(path.c_str(), O_RDWR);
if (this->m_file == -1) {
this->m_file = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
mmapprot &= ~(PROT_WRITE);
}
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
if (this->m_file == -1) {
this->m_readable = false;
return false;
}
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_READ, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
this->m_fileSize = this->m_fileStats.st_size;
return false;
}
}
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, mmapprot, MAP_SHARED, this->m_file, 0);
if (this->m_mappedFile == MAP_FAILED) {
::close(this->m_file);
this->m_file = -1;
mappingCleanup.release();
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
} else {
return false;
}
return false;
}
#endif
fileCleanup.release();
Provider::resize(this->getActualSize());
#else
return true;
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
int mmapprot = PROT_READ | PROT_WRITE;
this->m_file = ::open(path.c_str(), O_RDWR);
if (this->m_file == -1) {
this->m_file = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
mmapprot &= ~(PROT_WRITE);
}
if (this->m_file == -1) {
this->m_readable = false;
return false;
}
this->m_fileSize = this->m_fileStats.st_size;
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, mmapprot, MAP_SHARED, this->m_file, 0);
if (this->m_mappedFile == MAP_FAILED) {
::close(this->m_file);
this->m_file = -1;
return false;
}
#endif
return Provider::open();
}
void FileProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
::UnmapViewOfFile(this->m_mappedFile);
if (this->m_mapping != nullptr)
::CloseHandle(this->m_mapping);
if (this->m_file != nullptr)
::CloseHandle(this->m_file);
#else
::munmap(this->m_mappedFile, this->m_fileSize);
::close(this->m_file);
#endif
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
::UnmapViewOfFile(this->m_mappedFile);
if (this->m_mapping != nullptr)
::CloseHandle(this->m_mapping);
if (this->m_file != nullptr)
::CloseHandle(this->m_file);
#else
::munmap(this->m_mappedFile, this->m_fileSize);
::close(this->m_file);
#endif
Provider::close();
}
void FileProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->setPath(settings["path"].get<std::string>());
}
nlohmann::json FileProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_path.string();
return Provider::storeSettings(settings);
}
}

View File

@@ -11,6 +11,8 @@
#include <hex/helpers/crypto.hpp>
#include <hex/api/localization.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
using namespace std::chrono_literals;
@@ -276,9 +278,7 @@ namespace hex::plugin::builtin::prv {
}
});
Provider::resize(this->getActualSize());
return true;
return Provider::open();
} else {
return false;
}
@@ -290,6 +290,8 @@ namespace hex::plugin::builtin::prv {
if (this->m_cacheUpdateThread.joinable()) {
this->m_cacheUpdateThread.join();
}
Provider::close();
}
bool GDBProvider::isConnected() const {
@@ -311,4 +313,20 @@ namespace hex::plugin::builtin::prv {
this->m_port = 0xFFFF;
}
void GDBProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->m_ipAddress = settings["ip"].get<std::string>();
this->m_port = settings["port"].get<int>();
this->m_size = settings["size"].get<size_t>();
}
nlohmann::json GDBProvider::storeSettings(nlohmann::json settings) const {
settings["ip"] = this->m_ipAddress;
settings["port"] = this->m_port;
settings["size"] = this->m_size;
return Provider::storeSettings(settings);
}
}

View File

@@ -28,8 +28,27 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.popup.exit_application.desc"_lang);
ImGui::NewLine();
View::confirmButtons(
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Common::closeImHex(true); }, [] { ImGui::CloseCurrentPopup(); });
View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
[] { ImHexApi::Common::closeImHex(true); },
[] { ImGui::CloseCurrentPopup(); }
);
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
// "Are you sure you want to close provider?" Popup
if (ImGui::BeginPopupModal("hex.builtin.popup.close_provider.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::NewLine();
ImGui::TextUnformatted("hex.builtin.popup.close_provider.desc"_lang);
ImGui::NewLine();
View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
[] { ImHexApi::Provider::remove(ImHexApi::Provider::impl::getClosingProvider(), true); ImGui::CloseCurrentPopup(); },
[] { ImGui::CloseCurrentPopup(); }
);
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
@@ -313,7 +332,7 @@ namespace hex::plugin::builtin {
bool open = true;
ImGui::PushID(tabProvider);
if (ImGui::BeginTabItem(tabProvider->getName().c_str(), &open)) {
if (ImGui::BeginTabItem(tabProvider->getName().c_str(), &open, provider->isDirty() ? ImGuiTabItemFlags_UnsavedDocument : ImGuiTabItemFlags_None)) {
ImHexApi::Provider::setCurrentProvider(i);
ImGui::EndTabItem();
}

View File

@@ -1,16 +1,18 @@
#include "content/views/view_bookmarks.hpp"
#include <hex/api/project_file_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <nlohmann/json.hpp>
#include <cstring>
#include <provider_extra_data.hpp>
namespace hex::plugin::builtin {
ViewBookmarks::ViewBookmarks() : View("hex.builtin.view.bookmarks.name") {
EventManager::subscribe<RequestAddBookmark>(this, [this](Region region, std::string name, std::string comment, color_t color) {
EventManager::subscribe<RequestAddBookmark>(this, [](Region region, std::string name, std::string comment, color_t color) {
if (name.empty()) {
name = hex::format("hex.builtin.view.bookmarks.default_title"_lang, region.address, region.address + region.size - 1);
}
@@ -18,34 +20,21 @@ namespace hex::plugin::builtin {
if (color == 0x00)
color = ImGui::GetColorU32(ImGuiCol_Header);
this->m_bookmarks.push_back({ region,
ProviderExtraData::getCurrent().bookmarks.push_back({
region,
name,
std::move(comment),
color,
false
});
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
});
EventManager::subscribe<EventProjectFileLoad>(this, [this] {
this->m_bookmarks = ProjectFile::getBookmarks();
});
EventManager::subscribe<EventProjectFileStore>(this, [this] {
ProjectFile::setBookmarks(this->m_bookmarks);
});
EventManager::subscribe<EventProviderDeleted>(this, [this](const auto*) {
this->m_bookmarks.clear();
});
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
ImHexApi::HexEditor::addBackgroundHighlightingProvider([](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
hex::unused(data);
for (const auto &bookmark : this->m_bookmarks) {
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
if (Region { address, size }.isWithin(bookmark.region))
return bookmark.color;
}
@@ -53,9 +42,9 @@ namespace hex::plugin::builtin {
return std::nullopt;
});
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
ImHexApi::HexEditor::addTooltipProvider([](u64 address, const u8 *data, size_t size) {
hex::unused(data);
for (const auto &bookmark : this->m_bookmarks) {
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
if (!Region { address, size }.isWithin(bookmark.region))
continue;
@@ -109,15 +98,69 @@ namespace hex::plugin::builtin {
ImGui::EndTooltip();
}
});
ProjectFile::registerPerProviderHandler({
.basePath = "bookmarks.json",
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
auto fileContent = tar.read(basePath);
if (fileContent.empty())
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
if (!data.contains("bookmarks"))
return false;
auto &bookmarks = ProviderExtraData::get(provider).bookmarks;
bookmarks.clear();
for (const auto &bookmark : data["bookmarks"]) {
if (!bookmark.contains("name") || !bookmark.contains("comment") || !bookmark.contains("color") || !bookmark.contains("region") || !bookmark.contains("locked"))
continue;
const auto &region = bookmark["region"];
if (!region.contains("address") || !region.contains("size"))
continue;
bookmarks.push_back({
.region = { region["address"], region["size"] },
.name = bookmark["name"],
.comment = bookmark["comment"],
.color = bookmark["color"],
.locked = bookmark["locked"]
});
}
return true;
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
nlohmann::json data;
data["bookmarks"] = nlohmann::json::array();
size_t index = 0;
for (const auto &bookmark : ProviderExtraData::get(provider).bookmarks) {
data["bookmarks"][index] = {
{ "name", bookmark.name },
{ "comment", bookmark.comment },
{ "color", bookmark.color },
{ "region", {
{ "address", bookmark.region.address },
{ "size", bookmark.region.size }
}
},
{ "locked", bookmark.locked }
};
index++;
}
tar.write(basePath, data.dump(4));
return true;
}
});
}
ViewBookmarks::~ViewBookmarks() {
EventManager::unsubscribe<RequestAddBookmark>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProviderDeleted>(this);
this->m_bookmarks.clear();
}
void ViewBookmarks::drawContent() {
@@ -130,14 +173,14 @@ namespace hex::plugin::builtin {
ImGui::NewLine();
if (ImGui::BeginChild("##bookmarks")) {
if (this->m_bookmarks.empty()) {
auto &bookmarks = ProviderExtraData::getCurrent().bookmarks;
if (bookmarks.empty()) {
ImGui::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang);
}
u32 id = 1;
auto bookmarkToRemove = this->m_bookmarks.end();
for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) {
auto bookmarkToRemove = bookmarks.end();
for (auto iter = bookmarks.begin(); iter != bookmarks.end(); iter++) {
auto &[region, name, comment, color, locked] = *iter;
if (!this->m_currFilter.empty()) {
@@ -248,9 +291,8 @@ namespace hex::plugin::builtin {
id++;
}
if (bookmarkToRemove != this->m_bookmarks.end()) {
this->m_bookmarks.erase(bookmarkToRemove);
ProjectFile::markDirty();
if (bookmarkToRemove != bookmarks.end()) {
bookmarks.erase(bookmarkToRemove);
}
}
ImGui::EndChild();

View File

@@ -5,11 +5,13 @@
#include <hex/helpers/file.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imnodes.h>
#include <nlohmann/json.hpp>
#include "provider_extra_data.hpp"
namespace hex::plugin::builtin {
ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") {
@@ -30,24 +32,28 @@ namespace hex::plugin::builtin {
ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines;
});
EventManager::subscribe<EventProjectFileStore>(this, [this] {
ProjectFile::setDataProcessorContent(this->saveNodes());
});
ProjectFile::registerPerProviderHandler({
.basePath = "data_processor.json",
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto save = tar.readString(basePath);
EventManager::subscribe<EventProjectFileLoad>(this, [this] {
try {
this->loadNodes(ProjectFile::getDataProcessorContent());
} catch (nlohmann::json::exception &e) {
this->loadNodes(provider, save);
return true;
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
tar.write(basePath, this->saveNodes(provider));
return true;
}
});
EventManager::subscribe<EventFileLoaded>(this, [this](const std::fs::path &path) {
hex::unused(path);
for (auto &node : this->m_nodes) {
EventManager::subscribe<EventProviderChanged>(this, [](const auto &, const auto &) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
for (auto &node : data.nodes) {
node->setCurrentOverlay(nullptr);
}
this->m_dataOverlays.clear();
data.dataOverlays.clear();
});
EventManager::subscribe<EventDataChanged>(this, [this] {
@@ -56,22 +62,25 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] {
bool providerValid = ImHexApi::Provider::isValid();
auto provider = ImHexApi::Provider::get();
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang, nullptr, false, providerValid)) {
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
[this](const std::fs::path &path) {
[&, this](const std::fs::path &path) {
fs::File file(path, fs::File::Mode::Read);
if (file.isValid())
this->loadNodes(file.readString());
this->loadNodes(provider, file.readString());
});
}
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !this->m_nodes.empty() && providerValid)) {
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !data.nodes.empty() && providerValid)) {
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
[this](const std::fs::path &path) {
[&, this](const std::fs::path &path) {
fs::File file(path, fs::File::Mode::Create);
if (file.isValid())
file.write(this->saveNodes());
file.write(this->saveNodes(provider));
});
}
});
@@ -80,44 +89,45 @@ namespace hex::plugin::builtin {
fs::File file(path, fs::File::Mode::Read);
if (!file.isValid()) return false;
this->loadNodes(file.readString());
this->loadNodes(ImHexApi::Provider::get(), file.readString());
return true;
});
}
ViewDataProcessor::~ViewDataProcessor() {
for (auto &node : this->m_nodes)
delete node;
EventManager::unsubscribe<RequestChangeTheme>(this);
EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventDataChanged>(this);
}
void ViewDataProcessor::eraseLink(u32 id) {
auto link = std::find_if(this->m_links.begin(), this->m_links.end(), [&id](auto link) { return link.getId() == id; });
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (link == this->m_links.end())
auto link = std::find_if(data.links.begin(), data.links.end(), [&id](auto link) { return link.getId() == id; });
if (link == data.links.end())
return;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
attribute.removeConnectedAttribute(id);
}
}
this->m_links.erase(link);
data.links.erase(link);
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::eraseNodes(const std::vector<int> &ids) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
for (u32 id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node) { return node->getId() == id; });
auto node = std::find_if(data.nodes.begin(), data.nodes.end(),
[&id](const auto &node) {
return node->getId() == id;
});
for (auto &attr : (*node)->getAttributes()) {
std::vector<u32> linksToRemove;
@@ -130,52 +140,51 @@ namespace hex::plugin::builtin {
}
for (u32 id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node) { return node->getId() == id; });
auto node = std::find_if(data.nodes.begin(), data.nodes.end(), [&id](const auto &node) { return node->getId() == id; });
std::erase_if(this->m_endNodes, [&id](auto node) { return node->getId() == id; });
std::erase_if(data.endNodes, [&id](const auto &node) { return node->getId() == id; });
delete *node;
this->m_nodes.erase(node);
data.nodes.erase(node);
}
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::processNodes() {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (this->m_dataOverlays.size() != this->m_endNodes.size()) {
for (auto overlay : this->m_dataOverlays)
if (data.dataOverlays.size() != data.endNodes.size()) {
for (auto overlay : data.dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay);
this->m_dataOverlays.clear();
data.dataOverlays.clear();
for (u32 i = 0; i < this->m_endNodes.size(); i++)
this->m_dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
for (u32 i = 0; i < data.endNodes.size(); i++)
data.dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
u32 overlayIndex = 0;
for (auto endNode : this->m_endNodes) {
endNode->setCurrentOverlay(this->m_dataOverlays[overlayIndex]);
for (auto endNode : data.endNodes) {
endNode->setCurrentOverlay(data.dataOverlays[overlayIndex]);
overlayIndex++;
}
}
this->m_currNodeError.reset();
data.currNodeError.reset();
try {
for (auto &endNode : this->m_endNodes) {
for (auto &endNode : data.endNodes) {
endNode->resetOutputData();
for (auto &node : this->m_nodes)
for (auto &node : data.nodes)
node->resetProcessedInputs();
endNode->process();
}
} catch (dp::Node::NodeError &e) {
this->m_currNodeError = e;
data.currNodeError = e;
for (auto overlay : this->m_dataOverlays)
for (auto overlay : data.dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay);
this->m_dataOverlays.clear();
data.dataOverlays.clear();
} catch (std::runtime_error &e) {
printf("Node implementation bug! %s\n", e.what());
@@ -183,6 +192,8 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::drawContent() {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
@@ -200,7 +211,7 @@ namespace hex::plugin::builtin {
}
if (ImGui::BeginPopup("Context Menu")) {
dp::Node *node = nullptr;
std::unique_ptr<dp::Node> node;
if (ImNodes::NumSelectedNodes() > 0 || ImNodes::NumSelectedLinks() > 0) {
if (ImGui::MenuItem("hex.builtin.view.data_processor.name"_lang)) {
@@ -226,13 +237,11 @@ namespace hex::plugin::builtin {
} else if (unlocalizedCategory.empty()) {
if (ImGui::MenuItem(LangEntry(unlocalizedName))) {
node = function();
ProjectFile::markDirty();
}
} else {
if (ImGui::BeginMenu(LangEntry(unlocalizedCategory))) {
if (ImGui::MenuItem(LangEntry(unlocalizedName))) {
node = function();
ProjectFile::markDirty();
}
ImGui::EndMenu();
}
@@ -240,8 +249,6 @@ namespace hex::plugin::builtin {
}
if (node != nullptr) {
this->m_nodes.push_back(node);
bool hasOutput = false;
bool hasInput = false;
for (auto &attr : node->getAttributes()) {
@@ -253,9 +260,11 @@ namespace hex::plugin::builtin {
}
if (hasInput && !hasOutput)
this->m_endNodes.push_back(node);
data.endNodes.push_back(node.get());
ImNodes::SetNodeScreenSpacePos(node->getId(), this->m_rightClickedCoords);
data.nodes.push_back(std::move(node));
ImHexApi::Provider::markDirty();
}
ImGui::EndPopup();
@@ -277,11 +286,11 @@ namespace hex::plugin::builtin {
{
int nodeId;
if (ImNodes::IsNodeHovered(&nodeId) && this->m_currNodeError.has_value() && this->m_currNodeError->first->getId() == static_cast<u32>(nodeId)) {
if (ImNodes::IsNodeHovered(&nodeId) && data.currNodeError.has_value() && data.currNodeError->node->getId() == static_cast<u32>(nodeId)) {
ImGui::BeginTooltip();
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
ImGui::Separator();
ImGui::TextUnformatted(this->m_currNodeError->second.c_str());
ImGui::TextUnformatted(data.currNodeError->message.c_str());
ImGui::EndTooltip();
}
}
@@ -289,8 +298,8 @@ namespace hex::plugin::builtin {
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3))) {
ImNodes::BeginNodeEditor();
for (auto &node : this->m_nodes) {
const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node;
for (auto &node : data.nodes) {
const bool hasError = data.currNodeError.has_value() && data.currNodeError->node == node.get();
if (hasError)
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
@@ -336,12 +345,12 @@ namespace hex::plugin::builtin {
ImNodes::PopColorStyle();
}
for (const auto &link : this->m_links)
for (const auto &link : data.links)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
if (this->m_nodes.empty())
if (data.nodes.empty())
ImGui::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
ImNodes::EndNodeEditor();
@@ -367,7 +376,7 @@ namespace hex::plugin::builtin {
do {
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
if (attribute.getId() == static_cast<u32>(from))
fromAttr = &attribute;
@@ -388,7 +397,7 @@ namespace hex::plugin::builtin {
if (!toAttr->getConnectedAttributes().empty())
break;
auto newLink = this->m_links.emplace_back(from, to);
auto newLink = data.links.emplace_back(from, to);
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
@@ -423,12 +432,14 @@ namespace hex::plugin::builtin {
ImGui::End();
}
std::string ViewDataProcessor::saveNodes() {
std::string ViewDataProcessor::saveNodes(prv::Provider *provider) {
auto &data = ProviderExtraData::get(provider).dataProcessor;
using json = nlohmann::json;
json output;
output["nodes"] = json::object();
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
auto id = node->getId();
auto &currNodeOutput = output["nodes"][std::to_string(id)];
auto pos = ImNodes::GetNodeGridSpacePos(id);
@@ -453,7 +464,7 @@ namespace hex::plugin::builtin {
}
output["links"] = json::object();
for (auto &link : this->m_links) {
for (auto &link : data.links) {
auto id = link.getId();
auto &currOutput = output["links"][std::to_string(id)];
@@ -462,30 +473,29 @@ namespace hex::plugin::builtin {
currOutput["to"] = link.getToId();
}
return output.dump();
return output.dump(4);
}
void ViewDataProcessor::loadNodes(const std::string &data) {
void ViewDataProcessor::loadNodes(prv::Provider *provider, const std::string &jsonData) {
if (!ImHexApi::Provider::isValid()) return;
auto &data = ProviderExtraData::get(provider).dataProcessor;
using json = nlohmann::json;
json input = json::parse(data);
json input = json::parse(jsonData);
u32 maxNodeId = 0;
u32 maxAttrId = 0;
u32 maxLinkId = 0;
for (auto &node : this->m_nodes)
delete node;
this->m_nodes.clear();
this->m_endNodes.clear();
this->m_links.clear();
data.nodes.clear();
data.endNodes.clear();
data.links.clear();
auto &nodeEntries = ContentRegistry::DataProcessorNode::getEntries();
for (auto &node : input["nodes"]) {
dp::Node *newNode = nullptr;
std::unique_ptr<dp::Node> newNode;
for (auto &entry : nodeEntries) {
if (entry.name == node["type"])
newNode = entry.creatorFunction();
@@ -520,9 +530,9 @@ namespace hex::plugin::builtin {
newNode->load(node["data"]);
if (hasInput && !hasOutput)
this->m_endNodes.push_back(newNode);
data.endNodes.push_back(newNode.get());
this->m_nodes.push_back(newNode);
data.nodes.push_back(std::move(newNode));
ImNodes::SetNodeGridSpacePos(nodeId, ImVec2(node["pos"]["x"], node["pos"]["y"]));
}
@@ -533,10 +543,10 @@ namespace hex::plugin::builtin {
maxLinkId = std::max(linkId, maxLinkId);
newLink.setID(linkId);
this->m_links.push_back(newLink);
data.links.push_back(newLink);
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
if (attribute.getId() == newLink.getFromId())
fromAttr = &attribute;

View File

@@ -1,7 +1,6 @@
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <hex/helpers/crypto.hpp>
@@ -9,6 +8,7 @@
#include "math_evaluator.hpp"
#include <imgui_internal.h>
#include <nlohmann/json.hpp>
#include <thread>

View File

@@ -2,7 +2,8 @@
#include <hex/providers/provider.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <nlohmann/json.hpp>
#include <string>
@@ -11,22 +12,26 @@ using namespace std::literals::string_literals;
namespace hex::plugin::builtin {
ViewPatches::ViewPatches() : View("hex.builtin.view.patches.name") {
EventManager::subscribe<EventProjectFileStore>(this, [] {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid())
ProjectFile::setPatches(provider->getPatches());
});
EventManager::subscribe<EventProjectFileLoad>(this, [] {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid())
provider->getPatches() = ProjectFile::getPatches();
ProjectFile::registerPerProviderHandler({
.basePath = "patches.json",
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto json = nlohmann::json::parse(tar.read(basePath));
provider->getPatches() = json["patches"].get<std::map<u64, u8>>();
return true;
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
nlohmann::json json;
json["patches"] = provider->getPatches();
tar.write(basePath, json.dump(4));
return true;
}
});
}
ViewPatches::~ViewPatches() {
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
}
void ViewPatches::drawContent() {
@@ -86,7 +91,6 @@ namespace hex::plugin::builtin {
if (ImGui::BeginPopup("PatchContextMenu")) {
if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) {
patches.erase(this->m_selectedPatch);
ProjectFile::markDirty();
}
ImGui::EndPopup();
}

View File

@@ -5,6 +5,8 @@
#include <pl/pattern_language.hpp>
#include <pl/patterns/pattern.hpp>
#include <provider_extra_data.hpp>
namespace hex::plugin::builtin {
ViewPatternData::ViewPatternData() : View("hex.builtin.view.pattern_data.name") {
@@ -109,7 +111,7 @@ namespace hex::plugin::builtin {
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternTable(provider, provider->getPatternLanguageRuntime().getPatterns(), sortedPatterns)) {
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getPatterns(), sortedPatterns)) {
ImGui::TableHeadersRow();
if (!sortedPatterns.empty()) {

View File

@@ -11,9 +11,11 @@
#include <hex/helpers/fs.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/magic.hpp>
#include <provider_extra_data.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin {
@@ -86,15 +88,6 @@ namespace hex::plugin::builtin {
this->m_envVarEntries.push_back({ 0, "", 0, EnvVarType::Integer });
this->m_envVarIdCounter = 1;
EventManager::subscribe<EventProjectFileStore>(this, [this]() {
ProjectFile::setPattern(this->m_textEditor.GetText());
});
EventManager::subscribe<EventProjectFileLoad>(this, [this]() {
this->m_textEditor.SetText(ProjectFile::getPattern());
this->evaluatePattern(this->m_textEditor.GetText());
});
EventManager::subscribe<RequestSetPatternLanguageCode>(this, [this](const std::string &code) {
this->m_textEditor.SetText(code);
});
@@ -115,21 +108,25 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderCreated>(this, [this](prv::Provider *provider) {
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
if (!this->m_autoLoadPatterns)
return;
auto &patternLanguageData = ProviderExtraData::get(provider).patternLanguage;
patternLanguageData.runtime = ContentRegistry::PatternLanguage::createDefaultRuntime(provider);
// Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) {
provider->getPatternLanguageSourceCode() = this->m_textEditor.GetText();
patternLanguageData.sourceCode = this->m_textEditor.GetText();
}
auto &runtime = provider->getPatternLanguageRuntime();
auto &runtime = patternLanguageData.runtime;
auto mimeType = magic::getMIMEType(ImHexApi::Provider::get());
auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false;
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
if (value == mimeType) {
@@ -152,16 +149,16 @@ namespace hex::plugin::builtin {
if (!file.isValid())
continue;
runtime.getInternals().preprocessor->preprocess(runtime, file.readString());
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime.reset();
runtime->reset();
}
}
runtime.addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
@@ -170,14 +167,14 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderDeleted>(this, [](auto *provider) {
provider->getPatternLanguageRuntime().abort();
});
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
if (!this->m_syncPatternSourceCode) {
if (oldProvider != nullptr) oldProvider->getPatternLanguageSourceCode() = this->m_textEditor.GetText();
if (newProvider != nullptr) this->m_textEditor.SetText(newProvider->getPatternLanguageSourceCode());
if (oldProvider != nullptr) ProviderExtraData::get(oldProvider).patternLanguage.sourceCode = this->m_textEditor.GetText();
if (newProvider != nullptr)
this->m_textEditor.SetText(ProviderExtraData::get(newProvider).patternLanguage.sourceCode);
else
this->m_textEditor.SetText("");
auto lines = this->m_textEditor.GetTextLines();
lines.pop_back();
@@ -257,7 +254,7 @@ namespace hex::plugin::builtin {
return std::nullopt;
std::optional<ImColor> color;
for (const auto &pattern : ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(address)) {
for (const auto &pattern : ProviderExtraData::getCurrent().patternLanguage.runtime->getPatterns(address)) {
if (pattern->isHidden())
continue;
@@ -273,7 +270,7 @@ namespace hex::plugin::builtin {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
hex::unused(data, size);
auto patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(address);
auto patterns = ProviderExtraData::getCurrent().patternLanguage.runtime->getPatterns(address);
if (!patterns.empty() && !std::all_of(patterns.begin(), patterns.end(), [](const auto &pattern) { return pattern->isHidden(); })) {
ImGui::BeginTooltip();
for (const auto &pattern : patterns) {
@@ -298,11 +295,37 @@ namespace hex::plugin::builtin {
ImGui::EndTooltip();
}
});
ProjectFile::registerPerProviderHandler({
.basePath = "pattern_source_code.hexpat",
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
std::string sourceCode = tar.readString(basePath);
if (!this->m_syncPatternSourceCode)
ProviderExtraData::get(provider).patternLanguage.sourceCode = sourceCode;
this->m_textEditor.SetText(sourceCode);
return true;
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
std::string sourceCode;
if (provider == ImHexApi::Provider::get())
ProviderExtraData::get(provider).patternLanguage.sourceCode = this->m_textEditor.GetText();
if (this->m_syncPatternSourceCode)
sourceCode = this->m_textEditor.GetText();
else
sourceCode = ProviderExtraData::get(provider).patternLanguage.sourceCode;
tar.write(basePath, sourceCode);
return true;
}
});
}
ViewPatternEditor::~ViewPatternEditor() {
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<RequestSetPatternLanguageCode>(this);
EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProviderDeleted>(this);
@@ -342,10 +365,10 @@ namespace hex::plugin::builtin {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
auto &runtime = provider->getPatternLanguageRuntime();
if (runtime.isRunning()) {
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
if (runtime->isRunning()) {
if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
runtime.abort();
runtime->abort();
} else {
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
this->evaluatePattern(this->m_textEditor.GetText());
@@ -369,13 +392,13 @@ namespace hex::plugin::builtin {
ImGui::SameLine();
ImGui::TextFormatted("{} / {}",
provider->getPatternLanguageRuntime().getCreatedPatternCount(),
provider->getPatternLanguageRuntime().getMaximumPatternCount());
runtime->getCreatedPatternCount(),
runtime->getMaximumPatternCount());
}
if (this->m_textEditor.IsTextChanged()) {
ProjectFile::markDirty();
this->m_hasUnevaluatedChanges = true;
ImHexApi::Provider::markDirty();
}
if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) {
@@ -785,14 +808,16 @@ namespace hex::plugin::builtin {
this->m_textEditor.SetErrorMarkers({});
this->m_console.clear();
ImHexApi::Provider::get()->getPatternLanguageRuntime().reset();
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
runtime->reset();
EventManager::post<EventHighlightingChanged>();
auto provider = ImHexApi::Provider::get();
std::thread([this, code, provider] {
std::thread([this, code, &runtime] {
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.pattern_editor.evaluating", 1);
std::map<std::string, pl::core::Token::Literal> envVars;
for (const auto &[id, name, value, type] : this->m_envVarEntries)
envVars.insert({ name, value });
@@ -805,9 +830,7 @@ namespace hex::plugin::builtin {
inVariables[name] = variable.value;
}
auto &runtime = provider->getPatternLanguageRuntime();
runtime.setDangerousFunctionCallHandler([this]{
runtime->setDangerousFunctionCallHandler([this]{
this->m_dangerousFunctionCalled = true;
while (this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Ask) {
@@ -817,15 +840,15 @@ namespace hex::plugin::builtin {
return this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Allow;
});
this->m_lastEvaluationResult = runtime.executeString(code, envVars, inVariables);
this->m_lastEvaluationResult = runtime->executeString(code, envVars, inVariables);
if (!this->m_lastEvaluationResult) {
this->m_lastEvaluationError = runtime.getError();
this->m_lastEvaluationError = runtime->getError();
}
runtime.flattenPatterns();
runtime->flattenPatterns();
this->m_lastEvaluationLog = runtime.getConsoleLog();
this->m_lastEvaluationOutVars = runtime.getOutVariables();
this->m_lastEvaluationLog = runtime->getConsoleLog();
this->m_lastEvaluationOutVars = runtime->getOutVariables();
this->m_runningEvaluators--;
this->m_lastEvaluationProcessed = false;

View File

@@ -7,8 +7,7 @@
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imgui.h>
#include <implot.h>
@@ -90,9 +89,6 @@ namespace hex::plugin::builtin {
ImGui::SetCursorPosX(width / 9);
if (ImGui::Button("hex.builtin.welcome.safety_backup.restore"_lang, ImVec2(width / 3, 0))) {
ProjectFile::load(s_safetyBackupPath);
ProjectFile::markDirty();
ProjectFile::clearProjectFilePath();
fs::remove(s_safetyBackupPath);
ImGui::CloseCurrentPopup();

View File

@@ -94,7 +94,9 @@ namespace hex::plugin::builtin {
{ "hex.builtin.common.encoding.utf8", "UTF-8" },
{ "hex.builtin.popup.exit_application.title", "Applikation verlassen?" },
{ "hex.builtin.popup.exit_application.desc", "Es wurden ungespeicherte Änderungen an diesem Projekt vorgenommen\nBist du sicher, dass du ImHex schliessen willst?" },
{ "hex.builtin.popup.exit_application.desc", "Es wurden ungespeicherte Änderungen an diesem Projekt vorgenommen.\nBist du sicher, dass du ImHex schliessen willst?" },
{ "hex.builtin.popup.close_provider.title", "Provider schliessen?" },
{ "hex.builtin.popup.close_provider.desc", "Es wurden ungespeicherte Änderungen an diesem Provider vorgenommen.\nBist du sicher, dass du ihn schliessen willst?" },
{ "hex.builtin.popup.error.read_only", "Schreibzugriff konnte nicht erlangt werden. Datei wurde im Lesemodus geöffnet." },
{ "hex.builtin.popup.error.open", "Öffnen der Datei fehlgeschlagen!" },
{ "hex.builtin.popup.error.create", "Erstellen der neuen Datei fehlgeschlagen!" },

View File

@@ -97,6 +97,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "Exit Application?" },
{ "hex.builtin.popup.exit_application.desc", "You have unsaved changes made to your Project.\nAre you sure you want to exit?" },
{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "Couldn't get write access. File opened in read-only mode." },
{ "hex.builtin.popup.error.open", "Failed to open file!" },
{ "hex.builtin.popup.error.create", "Failed to create new file!" },

View File

@@ -95,6 +95,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "Uscire dall'applicazione?" },
{ "hex.builtin.popup.exit_application.desc", "Hai delle modifiche non salvate nel tuo progetto.\nSei sicuro di voler uscire?" },
//{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
//{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "Impossibile scrivere sul File. File aperto solo in modalità lettura" },
{ "hex.builtin.popup.error.open", "Impossibile aprire il File!" },
{ "hex.builtin.popup.error.create", "Impossibile creare il nuovo File!" },

View File

@@ -95,6 +95,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "アプリケーションを終了しますか?" },
{ "hex.builtin.popup.exit_application.desc", "プロジェクトに保存されていない変更があります。\n終了してもよろしいですか?" },
//{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
//{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "書き込み権限を取得できませんでした。ファイルが読み取り専用で開かれました。" },
{ "hex.builtin.popup.error.open", "ファイルを開けませんでした。" },
{ "hex.builtin.popup.error.create", "新しいファイルを作成できませんでした。" },

View File

@@ -95,6 +95,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "Sair da aplicação?" },
{ "hex.builtin.popup.exit_application.desc", "Você tem alterações não salvas feitas em seu projeto.\nVocê tem certeza que quer sair?" },
//{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
//{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "Não foi possível obter acesso de gravação. Arquivo aberto no modo somente leitura." },
{ "hex.builtin.popup.error.open", "Falha ao abrir o arquivo!" },
{ "hex.builtin.popup.error.create", "Falha ao criar um novo arquivo!" },

View File

@@ -95,6 +95,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "退出?" },
{ "hex.builtin.popup.exit_application.desc", "工程还有未保存的更改。\n确定要退出吗?" },
//{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
//{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "无法获得写权限,文件以只读方式打开。" },
{ "hex.builtin.popup.error.open", "打开文件失败!" },
{ "hex.builtin.popup.error.create", "创建新文件失败!" },

View File

@@ -95,6 +95,8 @@ namespace hex::plugin::builtin {
{ "hex.builtin.popup.exit_application.title", "離開應用程式?" },
{ "hex.builtin.popup.exit_application.desc", "您的專案有未儲存的更動。\n您確定要離開嗎?" },
//{ "hex.builtin.popup.close_provider.title", "Close Provider?" },
//{ "hex.builtin.popup.close_provider.desc", "You have unsaved changes made to this Provider.\nAre you sure you want to close it?" },
{ "hex.builtin.popup.error.read_only", "無法取得寫入權限。檔案已以唯讀模式開啟。" },
{ "hex.builtin.popup.error.open", "無法開啟檔案!" },
{ "hex.builtin.popup.error.create", "無法建立新檔案!" },