mirror of https://github.com/WerWolv/ImHex
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:
parent
f0756bceb8
commit
966f3b8597
|
|
@ -1 +1 @@
|
|||
Subproject commit 45c0e06bcb720113252dbf25f1a1c2cd1d95a8ee
|
||||
Subproject commit a6a58d79491c2a5a9cf37cb6dd9bd750cfa2c8e4
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <hex/data_processor/node.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace ContentRegistry::Settings {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ namespace hex::plugin::builtin {
|
|||
void drawContent() override;
|
||||
|
||||
private:
|
||||
std::list<ImHexApi::Bookmarks::Entry> m_bookmarks;
|
||||
std::string m_currFilter;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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 = {};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(); });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ®ion = 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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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!" },
|
||||
|
|
|
|||
|
|
@ -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!" },
|
||||
|
|
|
|||
|
|
@ -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!" },
|
||||
|
|
|
|||
|
|
@ -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", "新しいファイルを作成できませんでした。" },
|
||||
|
|
|
|||
|
|
@ -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!" },
|
||||
|
|
|
|||
|
|
@ -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", "创建新文件失败!" },
|
||||
|
|
|
|||
|
|
@ -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", "無法建立新檔案!" },
|
||||
|
|
|
|||
|
|
@ -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 { }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue