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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1223 additions and 847 deletions

@ -1 +1 @@
Subproject commit 45c0e06bcb720113252dbf25f1a1c2cd1d95a8ee
Subproject commit a6a58d79491c2a5a9cf37cb6dd9bd750cfa2c8e4

View File

@ -110,6 +110,7 @@ set(LIBIMHEX_SOURCES
source/api/keybinding.cpp
source/api/plugin_manager.cpp
source/api/localization.cpp
source/api/project_file_manager.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
@ -123,7 +124,6 @@ set(LIBIMHEX_SOURCES
source/helpers/file.cpp
source/helpers/socket.cpp
source/helpers/patches.cpp
source/helpers/project_file_handler.cpp
source/helpers/encoding_file.cpp
source/helpers/logger.cpp
source/helpers/tar.cpp

View File

@ -224,7 +224,7 @@ namespace hex {
namespace impl {
using CreatorFunction = std::function<dp::Node *()>;
using CreatorFunction = std::function<std::unique_ptr<dp::Node>()>;
struct Entry {
std::string category;
@ -239,11 +239,15 @@ namespace hex {
template<std::derived_from<dp::Node> T, typename... Args>
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args &&...args) {
add(impl::Entry { unlocalizedCategory.c_str(), unlocalizedName.c_str(), [=] {
auto node = new T(std::forward<Args>(args)...);
add(impl::Entry {
unlocalizedCategory.c_str(),
unlocalizedName.c_str(),
[=] {
auto node = std::make_unique<T>(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
return node;
} });
}
});
}
void addSeparator();
@ -330,8 +334,10 @@ namespace hex {
}
template<std::derived_from<hex::prv::Provider> T>
void add(const std::string &unlocalizedName, bool addToList = true) {
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = unlocalizedName](const std::string &name, hex::prv::Provider **provider) {
void add(bool addToList = true) {
auto typeName = T().getTypeName();
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = typeName](const std::string &name, hex::prv::Provider **provider) {
if (name != expectedName) return;
auto newProvider = new T();
@ -343,7 +349,7 @@ namespace hex {
});
if (addToList)
impl::addProviderName(unlocalizedName);
impl::addProviderName(typeName);
}
std::vector<std::string> &getEntries();

View File

@ -107,13 +107,14 @@ namespace hex {
EVENT_DEF(EventHighlightingChanged);
EVENT_DEF(EventWindowClosing, GLFWwindow *);
EVENT_DEF(EventRegionSelected, Region);
EVENT_DEF(EventProjectFileStore);
EVENT_DEF(EventProjectFileLoad);
EVENT_DEF(EventSettingsChanged);
EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventOSThemeChanged);
EVENT_DEF(EventProviderCreated, prv::Provider *);
EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *);
EVENT_DEF(EventProviderOpened, prv::Provider *);
EVENT_DEF(EventProviderClosing, prv::Provider *, bool *);
EVENT_DEF(EventProviderClosed, prv::Provider *);
EVENT_DEF(EventProviderDeleted, prv::Provider *);
EVENT_DEF(EventFrameBegin);
EVENT_DEF(EventFrameEnd);

View File

@ -119,6 +119,13 @@ namespace hex {
namespace Provider {
namespace impl {
void resetClosingProvider();
prv::Provider* getClosingProvider();
}
prv::Provider *get();
const std::vector<prv::Provider *> &getProviders();
@ -126,6 +133,10 @@ namespace hex {
bool isValid();
void markDirty();
void resetDirty();
bool isDirty();
void add(prv::Provider *provider);
template<std::derived_from<prv::Provider> T>
@ -133,7 +144,7 @@ namespace hex {
add(new T(std::forward<decltype(args)>(args)...));
}
void remove(prv::Provider *provider);
void remove(prv::Provider *provider, bool noQuestions = false);
prv::Provider* createProvider(const std::string &unlocalizedName);

View File

@ -0,0 +1,58 @@
#pragma once
#include <list>
#include <string>
#include <string_view>
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/concepts.hpp>
#include <hex/helpers/tar.hpp>
namespace hex {
class ProjectFile {
public:
struct Handler {
using Function = std::function<bool(const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
Function load, store;
};
struct ProviderHandler {
using Function = std::function<bool(prv::Provider *provider, const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
Function load, store;
};
static bool load(const std::fs::path &filePath);
static bool store(std::optional<std::fs::path> filePath = std::nullopt);
static void registerHandler(const Handler &handler) {
getHandlers().push_back(handler);
}
static void registerPerProviderHandler(const ProviderHandler &handler) {
getProviderHandlers().push_back(handler);
}
static std::vector<Handler>& getHandlers() {
return s_handlers;
}
static std::vector<ProviderHandler>& getProviderHandlers() {
return s_providerHandlers;
}
private:
ProjectFile() = default;
static std::fs::path s_currProjectPath;
static std::vector<Handler> s_handlers;
static std::vector<ProviderHandler> s_providerHandlers;
};
}

View File

@ -42,7 +42,10 @@ namespace hex::dp {
virtual void store(nlohmann::json &j) { hex::unused(j); }
virtual void load(nlohmann::json &j) { hex::unused(j); }
using NodeError = std::pair<Node *, std::string>;
struct NodeError {
Node *node;
std::string message;
};
void resetOutputData() {
for (auto &attribute : this->m_attributes)

View File

@ -65,6 +65,12 @@ namespace hex::fs {
else return size;
}
static inline bool isSubPath(const std::fs::path& base, const std::fs::path& destination) {
const auto relative = std::fs::relative(destination, base).string();
return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
}
bool isPathWritable(const std::fs::path &path);
std::fs::path toShortPath(const std::fs::path &path);

View File

@ -1,105 +0,0 @@
#pragma once
#include <list>
#include <string>
#include <string_view>
#include "patches.hpp"
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/fs.hpp>
namespace hex {
class ProjectFile {
public:
ProjectFile() = delete;
static bool load(const std::fs::path &filePath);
static bool store(std::fs::path filePath = {});
[[nodiscard]] static bool hasUnsavedChanges() {
return ProjectFile::s_hasUnsavedChanged;
}
static void markDirty() {
bool setWindowTitle = !hasUnsavedChanges();
ProjectFile::s_hasUnsavedChanged = true;
if (setWindowTitle)
EventManager::post<RequestChangeWindowTitle>(std::fs::path(getFilePath()).filename().string());
}
[[nodiscard]] static const std::fs::path &getProjectFilePath() {
return ProjectFile::s_currProjectFilePath;
}
static void clearProjectFilePath() {
ProjectFile::s_currProjectFilePath.clear();
}
[[nodiscard]] static const std::fs::path &getFilePath() {
return ProjectFile::s_filePath;
}
static void setFilePath(const std::fs::path &filePath) {
ProjectFile::s_filePath = filePath;
EventManager::post<RequestChangeWindowTitle>(filePath.filename().string());
}
[[nodiscard]] static const std::string &getPattern() {
return ProjectFile::s_pattern;
}
static void setPattern(const std::string &pattern) {
markDirty();
ProjectFile::s_pattern = pattern;
}
[[nodiscard]] static const Patches &getPatches() {
return ProjectFile::s_patches;
}
static void setPatches(const Patches &patches) {
markDirty();
ProjectFile::s_patches = patches;
}
[[nodiscard]] static const std::list<ImHexApi::Bookmarks::Entry> &getBookmarks() {
return ProjectFile::s_bookmarks;
}
static void setBookmarks(const std::list<ImHexApi::Bookmarks::Entry> &bookmarks) {
markDirty();
ProjectFile::s_bookmarks = bookmarks;
}
[[nodiscard]] static const std::string &getDataProcessorContent() {
return ProjectFile::s_dataProcessorContent;
}
static void setDataProcessorContent(const std::string &json) {
markDirty();
ProjectFile::s_dataProcessorContent = json;
}
private:
static std::fs::path s_currProjectFilePath;
static bool s_hasUnsavedChanged;
static std::fs::path s_filePath;
static std::string s_pattern;
static Patches s_patches;
static std::list<ImHexApi::Bookmarks::Entry> s_bookmarks;
static std::string s_dataProcessorContent;
};
}

View File

@ -19,17 +19,31 @@ namespace hex {
Tar() = default;
Tar(const std::fs::path &path, Mode mode);
~Tar();
Tar(const Tar&) = delete;
Tar(Tar&&) noexcept;
Tar &operator=(Tar &&other) noexcept;
void close();
[[nodiscard]] std::vector<u8> read(const std::fs::path &path);
[[nodiscard]] std::string readString(const std::fs::path &path);
std::vector<u8> read(const std::fs::path &path);
void write(const std::fs::path &path, const std::vector<u8> &data);
void write(const std::fs::path &path, const std::string &data);
std::vector<std::fs::path> listEntries();
[[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = "/");
[[nodiscard]] bool contains(const std::fs::path &path);
void extract(const std::fs::path &path, const std::fs::path &outputPath);
void extractAll(const std::fs::path &outputPath);
[[nodiscard]] bool isValid() const { return this->m_valid; }
private:
mtar_t m_ctx = { };
bool m_valid = false;
};
}

View File

@ -8,9 +8,12 @@
#include <string>
#include <vector>
#include <hex/api/imhex_api.hpp>
#include <hex/providers/overlay.hpp>
#include <hex/helpers/fs.hpp>
#include <nlohmann/json.hpp>
namespace pl {
class PatternLanguage;
}
@ -67,8 +70,8 @@ namespace hex::prv {
[[nodiscard]] virtual std::string getName() const = 0;
[[nodiscard]] virtual std::vector<std::pair<std::string, std::string>> getDataInformation() const = 0;
[[nodiscard]] virtual bool open() = 0;
virtual void close() = 0;
[[nodiscard]] virtual bool open();
virtual void close();
void addPatch(u64 offset, const void *buffer, size_t size, bool createUndo = false);
void createUndoPoint();
@ -84,8 +87,17 @@ namespace hex::prv {
virtual void drawLoadInterface();
virtual void drawInterface();
pl::PatternLanguage &getPatternLanguageRuntime() { return *this->m_patternLanguageRuntime; }
std::string &getPatternLanguageSourceCode() { return this->m_patternLanguageSourceCode; }
[[nodiscard]] u32 getID() const {
return this->m_id;
}
[[nodiscard]] virtual nlohmann::json storeSettings(nlohmann::json settings = { }) const;
virtual void loadSettings(const nlohmann::json &settings);
[[nodiscard]] virtual std::string getTypeName() const = 0;
void markDirty(bool dirty = true) { this->m_dirty = dirty; }
[[nodiscard]] bool isDirty() const { return this->m_dirty; }
protected:
u32 m_currPage = 0;
@ -95,8 +107,12 @@ namespace hex::prv {
std::list<std::map<u64, u8>> m_patches;
std::list<Overlay *> m_overlays;
std::unique_ptr<pl::PatternLanguage> m_patternLanguageRuntime;
std::string m_patternLanguageSourceCode;
u32 m_id;
bool m_dirty = false;
private:
static u32 s_idCounter;
};
}

View File

@ -10,6 +10,8 @@
#include <nlohmann/json.hpp>
#include <hex/data_processor/node.hpp>
namespace hex {
namespace ContentRegistry::Settings {

View File

@ -214,6 +214,19 @@ namespace hex {
static u32 s_currentProvider;
static std::vector<prv::Provider *> s_providers;
namespace impl {
static prv::Provider *s_closingProvider = nullptr;
void resetClosingProvider() {
s_closingProvider = nullptr;
}
prv::Provider* getClosingProvider() {
return s_closingProvider;
}
}
prv::Provider *get() {
if (!ImHexApi::Provider::isValid())
return nullptr;
@ -240,6 +253,21 @@ namespace hex {
return !s_providers.empty() && s_currentProvider < s_providers.size();
}
void markDirty() {
get()->markDirty();
}
void resetDirty() {
for (auto &provider : s_providers)
provider->markDirty(false);
}
bool isDirty() {
return std::ranges::any_of(s_providers, [](const auto &provider) {
return provider->isDirty();
});
}
void add(prv::Provider *provider) {
if (Task::getRunningTaskCount() > 0)
return;
@ -250,13 +278,22 @@ namespace hex {
setCurrentProvider(s_providers.size() - 1);
}
void remove(prv::Provider *provider) {
void remove(prv::Provider *provider, bool noQuestions) {
if (provider == nullptr)
return;
if (Task::getRunningTaskCount() > 0)
return;
if (!noQuestions) {
impl::s_closingProvider = provider;
bool shouldClose = true;
EventManager::post<EventProviderClosing>(provider, &shouldClose);
if (!shouldClose)
return;
}
auto it = std::find(s_providers.begin(), s_providers.end(), provider);
if (it == s_providers.end())
return;

View File

@ -0,0 +1,107 @@
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/tar.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/providers/provider.hpp>
namespace hex {
constexpr static auto MetadataHeaderMagic = "HEX";
constexpr static auto MetadataPath = "IMHEX_METADATA";
std::vector<ProjectFile::Handler> ProjectFile::s_handlers;
std::vector<ProjectFile::ProviderHandler> ProjectFile::s_providerHandlers;
std::fs::path ProjectFile::s_currProjectPath;
bool ProjectFile::load(const std::fs::path &filePath) {
if (!fs::exists(filePath) || !fs::isRegularFile(filePath))
return false;
if (filePath.extension() != ".hexproj")
return false;
Tar tar(filePath, Tar::Mode::Read);
if (!tar.isValid())
return false;
if (!tar.contains(MetadataPath))
return false;
{
const auto metadataContent = tar.read(MetadataPath);
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic))
return false;
}
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.load(handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.load(provider, basePath / handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
}
ProjectFile::s_currProjectPath = filePath;
return result;
}
bool ProjectFile::store(std::optional<std::fs::path> filePath) {
if (!filePath.has_value())
filePath = ProjectFile::s_currProjectPath;
Tar tar(*filePath, Tar::Mode::Create);
if (!tar.isValid())
return false;
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.store(handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.store(provider, basePath / handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
}
{
const auto metadataContent = hex::format("{}\n{}", MetadataHeaderMagic, IMHEX_VERSION);
tar.write(MetadataPath, metadataContent);
}
return result;
}
}

View File

@ -1,115 +0,0 @@
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/imhex_api.hpp>
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace hex {
std::fs::path ProjectFile::s_currProjectFilePath;
bool ProjectFile::s_hasUnsavedChanged = false;
std::fs::path ProjectFile::s_filePath;
std::string ProjectFile::s_pattern;
Patches ProjectFile::s_patches;
std::list<ImHexApi::Bookmarks::Entry> ProjectFile::s_bookmarks;
std::string ProjectFile::s_dataProcessorContent;
void to_json(json &j, const ImHexApi::Bookmarks::Entry &b) {
j = json {
{"address", b.region.address},
{ "size", b.region.size },
{ "name", b.name.data() },
{ "comment", b.comment.data()},
{ "locked", b.locked },
{ "color", b.color }
};
}
void from_json(const json &j, ImHexApi::Bookmarks::Entry &b) {
std::string name, comment;
if (j.contains("address")) j.at("address").get_to(b.region.address);
if (j.contains("size")) j.at("size").get_to(b.region.size);
if (j.contains("name")) j.at("name").get_to(name);
if (j.contains("comment")) j.at("comment").get_to(comment);
if (j.contains("locked")) j.at("locked").get_to(b.locked);
if (j.contains("color")) j.at("color").get_to(b.color);
std::copy(name.begin(), name.end(), std::back_inserter(b.name));
b.name.push_back('\0');
std::copy(comment.begin(), comment.end(), std::back_inserter(b.comment));
b.comment.push_back('\0');
}
bool ProjectFile::load(const std::fs::path &filePath) {
ProjectFile::s_hasUnsavedChanged = false;
json projectFileData;
try {
std::ifstream projectFile(filePath);
projectFile >> projectFileData;
ProjectFile::s_filePath = std::fs::path(projectFileData["filePath"].get<std::u8string>());
ProjectFile::s_pattern = projectFileData["pattern"];
ProjectFile::s_patches = projectFileData["patches"].get<Patches>();
ProjectFile::s_dataProcessorContent = projectFileData["dataProcessor"];
ProjectFile::s_bookmarks.clear();
for (auto &element : projectFileData["bookmarks"].items()) {
ImHexApi::Bookmarks::Entry entry;
from_json(element.value(), entry);
ProjectFile::s_bookmarks.emplace_back(std::move(entry));
}
} catch (json::exception &e) {
return false;
} catch (std::ofstream::failure &e) {
return false;
}
ProjectFile::s_currProjectFilePath = filePath;
EventManager::post<EventProjectFileLoad>();
return true;
}
bool ProjectFile::store(std::fs::path filePath) {
EventManager::post<EventProjectFileStore>();
json projectFileData;
if (filePath.empty())
filePath = ProjectFile::s_currProjectFilePath;
try {
projectFileData["filePath"] = ProjectFile::s_filePath.u8string();
projectFileData["pattern"] = ProjectFile::s_pattern;
projectFileData["patches"] = ProjectFile::s_patches;
projectFileData["dataProcessor"] = ProjectFile::s_dataProcessorContent;
for (auto &bookmark : ProjectFile::s_bookmarks) {
to_json(projectFileData["bookmarks"].emplace_back(), bookmark);
}
std::ofstream projectFile(filePath.c_str(), std::fstream::trunc);
projectFile << projectFileData;
} catch (json::exception &e) {
return false;
} catch (std::ifstream::failure &e) {
return false;
}
ProjectFile::s_hasUnsavedChanged = false;
ProjectFile::s_currProjectFilePath = filePath;
return true;
}
}

View File

@ -7,26 +7,50 @@ namespace hex {
using namespace hex::literals;
Tar::Tar(const std::fs::path &path, Mode mode) {
int error = MTAR_ESUCCESS;
if (mode == Tar::Mode::Read)
mtar_open(&this->m_ctx, path.string().c_str(), "r");
error = mtar_open(&this->m_ctx, path.string().c_str(), "r");
else if (mode == Tar::Mode::Write)
mtar_open(&this->m_ctx, path.string().c_str(), "a");
error = mtar_open(&this->m_ctx, path.string().c_str(), "a");
else if (mode == Tar::Mode::Create)
mtar_open(&this->m_ctx, path.string().c_str(), "w");
error = mtar_open(&this->m_ctx, path.string().c_str(), "w");
else
error = MTAR_EFAILURE;
this->m_valid = (error == MTAR_ESUCCESS);
}
Tar::~Tar() {
mtar_finalize(&this->m_ctx);
mtar_close(&this->m_ctx);
this->close();
}
std::vector<std::fs::path> Tar::listEntries() {
Tar::Tar(hex::Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
this->m_valid = other.m_valid;
other.m_ctx = { };
other.m_valid = false;
}
Tar &Tar::operator=(Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
other.m_ctx = { };
this->m_valid = other.m_valid;
other.m_valid = false;
return *this;
}
std::vector<std::fs::path> Tar::listEntries(const std::fs::path &basePath) {
std::vector<std::fs::path> result;
const std::string PaxHeaderName = "@PaxHeader";
mtar_header_t header;
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
if (header.name != PaxHeaderName) {
std::fs::path path = header.name;
if (header.name != PaxHeaderName && fs::isSubPath(basePath, path)) {
result.emplace_back(header.name);
}
@ -36,9 +60,29 @@ namespace hex {
return result;
}
bool Tar::contains(const std::fs::path &path) {
mtar_header_t header;
return mtar_find(&this->m_ctx, path.string().c_str(), &header) == MTAR_ESUCCESS;
}
void Tar::close() {
if (this->m_valid) {
mtar_finalize(&this->m_ctx);
mtar_close(&this->m_ctx);
}
this->m_ctx = { };
this->m_valid = false;
}
std::vector<u8> Tar::read(const std::fs::path &path) {
mtar_header_t header;
mtar_find(&this->m_ctx, path.string().c_str(), &header);
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_find(&this->m_ctx, fixedPath.c_str(), &header);
std::vector<u8> result(header.size, 0x00);
mtar_read_data(&this->m_ctx, result.data(), result.size());
@ -46,19 +90,37 @@ namespace hex {
return result;
}
std::string Tar::readString(const std::fs::path &path) {
auto result = this->read(path);
return { result.begin(), result.end() };
}
void Tar::write(const std::fs::path &path, const std::vector<u8> &data) {
if (path.has_parent_path()) {
std::fs::path pathPart;
for (const auto &part : path.parent_path()) {
pathPart /= part;
mtar_write_dir_header(&this->m_ctx, pathPart.string().c_str());
auto fixedPath = pathPart.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_dir_header(&this->m_ctx, fixedPath.c_str());
}
}
mtar_write_file_header(&this->m_ctx, path.string().c_str(), data.size());
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_file_header(&this->m_ctx, fixedPath.c_str(), data.size());
mtar_write_data(&this->m_ctx, data.data(), data.size());
}
void Tar::write(const std::fs::path &path, const std::string &data) {
this->write(path, std::vector<u8>(data.begin(), data.end()));
}
static void writeFile(mtar_t *ctx, mtar_header_t *header, const std::fs::path &path) {
constexpr static u64 BufferSize = 1_MiB;

View File

@ -1,7 +1,7 @@
#include <hex/providers/provider.hpp>
#include <hex.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/api/event.hpp>
#include <cmath>
#include <cstring>
@ -10,9 +10,10 @@
namespace hex::prv {
Provider::Provider() {
u32 Provider::s_idCounter = 0;
Provider::Provider() : m_id(s_idCounter++) {
this->m_patches.emplace_back();
this->m_patternLanguageRuntime = ContentRegistry::PatternLanguage::createDefaultRuntime(this);
}
Provider::~Provider() {
@ -28,6 +29,7 @@ namespace hex::prv {
void Provider::write(u64 offset, const void *buffer, size_t size) {
this->writeRaw(offset - this->getBaseAddress(), buffer, size);
this->markDirty();
}
void Provider::save() { }
@ -36,7 +38,9 @@ namespace hex::prv {
}
void Provider::resize(size_t newSize) {
this->m_patternLanguageRuntime->setDataSize(newSize);
hex::unused(newSize);
this->markDirty();
}
void Provider::insert(u64 offset, size_t size) {
@ -53,6 +57,8 @@ namespace hex::prv {
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address + size, value });
this->markDirty();
}
void Provider::remove(u64 offset, size_t size) {
@ -69,6 +75,8 @@ namespace hex::prv {
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address - size, value });
this->markDirty();
}
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) {
@ -104,6 +112,7 @@ namespace hex::prv {
for (auto &[patchAddress, patch] : getPatches()) {
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
}
this->markDirty();
}
@ -137,7 +146,7 @@ namespace hex::prv {
void Provider::setBaseAddress(u64 address) {
this->m_baseAddress = address;
this->m_patternLanguageRuntime->setDataBaseAddress(address);
this->markDirty();
}
u64 Provider::getBaseAddress() const {
@ -161,6 +170,14 @@ namespace hex::prv {
return page;
}
bool Provider::open() {
EventManager::post<EventProviderOpened>(this);
return true;
}
void Provider::close() {
EventManager::post<EventProviderClosed>(this);
}
void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) {
if (this->m_patchTreeOffset > 0) {
auto iter = this->m_patches.end();
@ -184,6 +201,8 @@ namespace hex::prv {
else
getPatches()[offset + i] = patch;
}
this->markDirty();
}
void Provider::createUndoPoint() {
@ -223,4 +242,16 @@ namespace hex::prv {
void Provider::drawInterface() {
}
nlohmann::json Provider::storeSettings(nlohmann::json settings) const {
settings["baseAddress"] = this->m_baseAddress;
settings["currPage"] = this->m_currPage;
return settings;
}
void Provider::loadSettings(const nlohmann::json &settings) {
this->m_baseAddress = settings["baseAddress"];
this->m_currPage = settings["currPage"];
}
}

View File

@ -8,11 +8,12 @@
#include "init/splash_window.hpp"
#include "init/tasks.hpp"
#include <hex/api/project_file_manager.hpp>
int main(int argc, char **argv, char **envp) {
using namespace hex;
ImHexApi::System::impl::setProgramArguments(argc, argv, envp);
#if defined(OS_WINDOWS)
ImHexApi::System::impl::setBorderlessWindowMode(true);
#endif

View File

@ -32,7 +32,7 @@
#include <fonts/codicons_font.h>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <GLFW/glfw3.h>
@ -63,6 +63,20 @@ namespace hex {
buf->append("\n");
}
static void signalHandler(int signalNumber) {
log::fatal("Terminating with signal {}", signalNumber);
EventManager::post<EventAbnormalTermination>(signalNumber);
if (std::uncaught_exceptions() > 0) {
log::fatal("Uncaught exception thrown!");
}
// Let's not loop on this...
std::signal(signalNumber, nullptr);
std::raise(signalNumber);
};
Window::Window() {
{
for (const auto &[argument, value] : ImHexApi::System::getInitArguments()) {
@ -81,7 +95,7 @@ namespace hex {
this->setupNativeWindow();
EventManager::subscribe<RequestCloseImHex>(this, [this](bool noQuestions) {
glfwSetWindowShouldClose(this->m_window, true);
glfwSetWindowShouldClose(this->m_window, GLFW_TRUE);
if (!noQuestions)
EventManager::post<EventWindowClosing>(this->m_window);
@ -95,13 +109,14 @@ namespace hex {
std::string title = "ImHex";
if (ImHexApi::Provider::isValid()) {
auto provider = ImHexApi::Provider::get();
if (!windowTitle.empty())
title += " - " + windowTitle;
if (ProjectFile::hasUnsavedChanges())
if (provider->isDirty())
title += " (*)";
if (!ImHexApi::Provider::get()->isWritable())
if (!provider->isWritable())
title += " (Read Only)";
}
@ -114,11 +129,11 @@ namespace hex {
EventManager::subscribe<EventAbnormalTermination>(this, [this, CrashBackupFileName](int) {
ImGui::SaveIniSettingsToDisk(this->m_imguiSettingsPath.string().c_str());
if (!ProjectFile::hasUnsavedChanges())
if (!ImHexApi::Provider::isDirty())
return;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
if (ProjectFile::store(std::fs::path(path) / CrashBackupFileName))
if (ProjectFile::store((std::fs::path(path) / CrashBackupFileName).string()))
break;
}
});
@ -127,29 +142,9 @@ namespace hex {
this->m_popupsToOpen.push_back(name);
});
auto signalHandler = [](int signalNumber) {
EventManager::post<EventAbnormalTermination>(signalNumber);
if (std::uncaught_exceptions() > 0) {
log::fatal("Uncaught exception thrown!");
}
// Let's not loop on this...
std::signal(signalNumber, nullptr);
#if defined(DEBUG)
assert(false);
#else
std::raise(signalNumber);
#endif
};
std::signal(SIGTERM, signalHandler);
std::signal(SIGSEGV, signalHandler);
std::signal(SIGINT, signalHandler);
std::signal(SIGILL, signalHandler);
std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler);
for (u32 signal = 0; signal < NSIG; signal++)
std::signal(signal, signalHandler);
std::set_terminate([]{ signalHandler(SIGTERM); });
auto imhexLogo = romfs::get("logo.png");
this->m_logoTexture = ImGui::LoadImageFromMemory(reinterpret_cast<const ImU8 *>(imhexLogo.data()), imhexLogo.size());
@ -199,6 +194,13 @@ namespace hex {
this->frameBegin();
this->frame();
this->frameEnd();
const auto targetFps = ImHexApi::System::getTargetFPS();
if (targetFps <= 200)
std::this_thread::sleep_for(std::chrono::milliseconds(u64((this->m_lastFrameTime + 1 / targetFps - glfwGetTime()) * 1000)));
this->m_lastFrameTime = glfwGetTime();
this->m_hadEvent = false;
}
}
@ -493,12 +495,6 @@ namespace hex {
glfwMakeContextCurrent(backup_current_context);
glfwSwapBuffers(this->m_window);
const auto targetFps = ImHexApi::System::getTargetFPS();
if (targetFps <= 200)
std::this_thread::sleep_for(std::chrono::milliseconds(u64((this->m_lastFrameTime + 1 / targetFps - glfwGetTime()) * 1000)));
this->m_lastFrameTime = glfwGetTime();
}
void Window::initGLFW() {

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,12 +54,23 @@ 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
std::fs::path m_path;

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,9 +70,7 @@ 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"}
},
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} },
[](std::fs::path path) {
if (path.extension() != ".hexproj") {
path.replace_extension(".hexproj");
@ -79,8 +78,6 @@ namespace hex::plugin::builtin {
ProjectFile::store(path);
});
} else
ProjectFile::store();
}
});

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 {
@ -37,9 +41,13 @@ 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
}
@ -107,8 +115,8 @@ namespace hex::plugin::builtin::prv {
return false;
}
#else
const auto &path = this->m_path.native();
struct stat driveStat;
@ -133,27 +141,32 @@ namespace hex::plugin::builtin::prv {
#endif
Provider::resize(this->getActualSize());
return true;
return Provider::open();
}
void DiskProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
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;
u64 startOffset = offset;
@ -174,7 +187,9 @@ namespace hex::plugin::builtin::prv {
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,11 +208,13 @@ namespace hex::plugin::builtin::prv {
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#endif
}
void DiskProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
#if defined(OS_WINDOWS)
DWORD bytesWritten = 0;
u64 startOffset = offset;
@ -267,6 +284,7 @@ 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++) {
@ -299,11 +317,13 @@ namespace hex::plugin::builtin::prv {
::CloseHandle(handle);
}
#endif
}
void DiskProvider::drawLoadInterface() {
#if defined(OS_WINDOWS)
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
for (const auto &drive : this->m_availableDrives) {
@ -328,4 +348,17 @@ namespace hex::plugin::builtin::prv {
#endif
}
nlohmann::json DiskProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_path.string();
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 {
@ -260,8 +262,6 @@ namespace hex::plugin::builtin::prv {
}
mappingCleanup.release();
ProjectFile::setFilePath(this->m_path);
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
@ -272,6 +272,7 @@ namespace hex::plugin::builtin::prv {
fileCleanup.release();
#else
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
@ -298,25 +299,42 @@ namespace hex::plugin::builtin::prv {
return false;
}
#endif
Provider::resize(this->getActualSize());
return true;
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
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", "無法建立新檔案!" },

View File

@ -48,6 +48,8 @@ namespace hex::test {
return this->m_data->size();
}
[[nodiscard]] virtual std::string getTypeName() const { return "hex.test.provider.test"; }
bool open() override { return true; }
void close() override { }

View File

@ -53,9 +53,6 @@ TEST_SEQUENCE("TestProvider_write") {
std::fill(std::begin(buff), std::end(buff), 22);
provider2->write(1, data, 4);
provider2->applyPatches();
hex::log::error("asd {:#x}", buff[0]);
hex::log::error("asd {:#x}", buff[1]);
hex::log::error("asd {:#x}", buff[2]);
TEST_ASSERT(buff[0] == 22); // should be unchanged
TEST_ASSERT(buff[1] == 0xde);
TEST_ASSERT(buff[2] == 0xad);