refactor: Streamline entire view system
This commit is contained in:
parent
fc23efdb25
commit
c89a870fe9
@ -11,6 +11,7 @@
|
||||
|
||||
using ImGuiID = unsigned int;
|
||||
struct ImVec2;
|
||||
struct ImFontAtlas;
|
||||
|
||||
namespace hex {
|
||||
|
||||
@ -340,12 +341,14 @@ namespace hex {
|
||||
|
||||
void setCustomFontPath(const std::fs::path &path);
|
||||
void setFontSize(float size);
|
||||
void setFontAtlas(ImFontAtlas *fontAtlas);
|
||||
|
||||
void setGPUVendor(const std::string &vendor);
|
||||
|
||||
void setPortableVersion(bool enabled);
|
||||
|
||||
void addInitArgument(const std::string &key, const std::string &value = { });
|
||||
|
||||
}
|
||||
|
||||
struct ProgramArguments {
|
||||
@ -457,6 +460,12 @@ namespace hex {
|
||||
*/
|
||||
float getFontSize();
|
||||
|
||||
/**
|
||||
* @brief Gets the current font atlas
|
||||
* @return Current font atlas
|
||||
*/
|
||||
ImFontAtlas* getFontAtlas();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Sets if ImHex should follow the system theme
|
||||
|
@ -274,6 +274,8 @@ namespace ImGuiExt {
|
||||
void BeginSubWindow(const char *label, ImVec2 size = ImVec2(0, 0), ImGuiChildFlags flags = ImGuiChildFlags_None);
|
||||
void EndSubWindow();
|
||||
|
||||
void ConfirmButtons(const char *textLeft, const char *textRight, const std::function<void()> &leftButtonCallback, const std::function<void()> &rightButtonCallback);
|
||||
|
||||
template<typename T>
|
||||
constexpr ImGuiDataType getImGuiDataType() {
|
||||
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;
|
||||
|
@ -18,25 +18,72 @@
|
||||
|
||||
#include <hex/api/localization.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View {
|
||||
public:
|
||||
explicit View(std::string unlocalizedName);
|
||||
public:
|
||||
virtual ~View() = default;
|
||||
|
||||
virtual void drawContent() = 0;
|
||||
virtual void drawAlwaysVisible() { }
|
||||
[[nodiscard]] virtual bool isAvailable() const;
|
||||
[[nodiscard]] virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); }
|
||||
/**
|
||||
* @brief Draws the view
|
||||
* @note Do not override this method. Override drawContent() instead
|
||||
*/
|
||||
virtual void draw() = 0;
|
||||
|
||||
/**
|
||||
* @brief Draws the content of the view
|
||||
*/
|
||||
virtual void drawContent() = 0;
|
||||
|
||||
/**
|
||||
* @brief Draws content that should always be visible, even if the view is not open
|
||||
*/
|
||||
virtual void drawAlwaysVisibleContent() { }
|
||||
|
||||
/**
|
||||
* @brief Whether or not the view window should be drawn
|
||||
* @return True if the view window should be drawn, false otherwise
|
||||
*/
|
||||
[[nodiscard]] virtual bool shouldDraw() const;
|
||||
|
||||
/**
|
||||
* @brief Whether or not the entire view should be processed
|
||||
* If this returns false, the view will not be drawn and no shortcuts will be handled. This includes things
|
||||
* drawn in the drawAlwaysVisibleContent() function.
|
||||
* @return True if the view should be processed, false otherwise
|
||||
*/
|
||||
[[nodiscard]] virtual bool shouldProcess() const { return true; }
|
||||
|
||||
/**
|
||||
* @brief Whether or not the view should have an entry in the view menu
|
||||
* @return True if the view should have an entry in the view menu, false otherwise
|
||||
*/
|
||||
[[nodiscard]] virtual bool hasViewMenuItemEntry() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the minimum size of the view window
|
||||
* @return The minimum size of the view window
|
||||
*/
|
||||
[[nodiscard]] virtual ImVec2 getMinSize() const;
|
||||
|
||||
/**
|
||||
* @brief Gets the maximum size of the view window
|
||||
* @return The maximum size of the view window
|
||||
*/
|
||||
[[nodiscard]] virtual ImVec2 getMaxSize() const;
|
||||
|
||||
/**
|
||||
* @brief Gets additional window flags for the view window
|
||||
* @return Additional window flags for the view window
|
||||
*/
|
||||
[[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const;
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] bool &getWindowOpenState();
|
||||
[[nodiscard]] const bool &getWindowOpenState() const;
|
||||
|
||||
@ -46,18 +93,15 @@ namespace hex {
|
||||
[[nodiscard]] bool didWindowJustOpen();
|
||||
void setWindowJustOpened(bool state);
|
||||
|
||||
static void confirmButtons(const std::string &textLeft, const std::string &textRight, const std::function<void()> &leftButtonFn, const std::function<void()> &rightButtonFn);
|
||||
static void discardNavigationRequests();
|
||||
|
||||
static std::string toWindowName(const std::string &unlocalizedName) {
|
||||
return LangEntry(unlocalizedName) + "###" + unlocalizedName;
|
||||
}
|
||||
[[nodiscard]] static std::string toWindowName(const std::string &unlocalizedName);
|
||||
|
||||
static ImFontAtlas *getFontAtlas();
|
||||
static void setFontAtlas(ImFontAtlas *atlas);
|
||||
|
||||
static ImFontConfig getFontConfig();
|
||||
static void setFontConfig(ImFontConfig config);
|
||||
public:
|
||||
class Window;
|
||||
class Special;
|
||||
class Floating;
|
||||
class Modal;
|
||||
|
||||
private:
|
||||
std::string m_unlocalizedViewName;
|
||||
@ -68,4 +112,68 @@ namespace hex {
|
||||
friend class ShortcutManager;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief A view that draws a regular window. This should be the default for most views
|
||||
*/
|
||||
class View::Window : public View {
|
||||
public:
|
||||
explicit Window(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
||||
|
||||
if (ImGui::Begin(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
|
||||
this->drawContent();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A view that doesn't handle any window creation and just draws its content.
|
||||
* This should be used if you intend to draw your own special window
|
||||
*/
|
||||
class View::Special : public View {
|
||||
public:
|
||||
explicit Special(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
this->drawContent();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A view that draws a floating window. These are the same as regular windows but cannot be docked
|
||||
*/
|
||||
class View::Floating : public View::Window {
|
||||
public:
|
||||
explicit Floating(std::string unlocalizedName) : Window(std::move(unlocalizedName)) {}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const final { return ImGuiWindowFlags_NoDocking; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A view that draws a modal window. The window will always be drawn on top and will block input to other windows
|
||||
*/
|
||||
class View::Modal : public View {
|
||||
public:
|
||||
explicit Modal(std::string unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
|
||||
|
||||
if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
|
||||
this->drawContent();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -421,6 +421,12 @@ namespace hex {
|
||||
s_fontSize = size;
|
||||
}
|
||||
|
||||
static ImFontAtlas *s_fontAtlas;
|
||||
void setFontAtlas(ImFontAtlas* fontAtlas) {
|
||||
s_fontAtlas = fontAtlas;
|
||||
}
|
||||
|
||||
|
||||
static std::string s_gpuVendor;
|
||||
void setGPUVendor(const std::string &vendor) {
|
||||
s_gpuVendor = vendor;
|
||||
@ -508,6 +514,11 @@ namespace hex {
|
||||
return impl::s_fontSize;
|
||||
}
|
||||
|
||||
ImFontAtlas* getFontAtlas() {
|
||||
return impl::s_fontAtlas;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool s_systemThemeDetection;
|
||||
|
||||
|
@ -37,6 +37,10 @@ namespace hex {
|
||||
return vector * ImHexApi::System::getGlobalScale();
|
||||
}
|
||||
|
||||
ImVec2 scaled(float x, float y) {
|
||||
return ImVec2(x, y) * ImHexApi::System::getGlobalScale();
|
||||
}
|
||||
|
||||
std::string to_string(u128 value) {
|
||||
char data[45] = { 0 };
|
||||
|
||||
|
@ -917,6 +917,17 @@ namespace ImGuiExt {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void ConfirmButtons(const char *textLeft, const char *textRight, const std::function<void()> &leftButtonCallback, const std::function<void()> &rightButtonCallback) {
|
||||
auto width = ImGui::GetWindowWidth();
|
||||
ImGui::SetCursorPosX(width / 9);
|
||||
if (ImGui::Button(textLeft, ImVec2(width / 3, 0)))
|
||||
leftButtonCallback();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width / 9 * 5);
|
||||
if (ImGui::Button(textRight, ImVec2(width / 3, 0)))
|
||||
rightButtonCallback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ImGui {
|
||||
|
@ -7,16 +7,9 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
ImFontAtlas *s_fontAtlas;
|
||||
ImFontConfig s_fontConfig;
|
||||
|
||||
}
|
||||
|
||||
View::View(std::string unlocalizedName) : m_unlocalizedViewName(std::move(unlocalizedName)) { }
|
||||
|
||||
bool View::isAvailable() const {
|
||||
bool View::shouldDraw() const {
|
||||
return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable();
|
||||
}
|
||||
|
||||
@ -25,13 +18,18 @@ namespace hex {
|
||||
}
|
||||
|
||||
ImVec2 View::getMinSize() const {
|
||||
return scaled(ImVec2(300, 400));
|
||||
return scaled({ 300, 400 });
|
||||
}
|
||||
|
||||
ImVec2 View::getMaxSize() const {
|
||||
return { FLT_MAX, FLT_MAX };
|
||||
}
|
||||
|
||||
ImGuiWindowFlags View::getWindowFlags() const {
|
||||
return ImGuiWindowFlags_None;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool &View::getWindowOpenState() {
|
||||
return this->m_windowOpen;
|
||||
@ -50,11 +48,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool View::didWindowJustOpen() {
|
||||
bool result = this->m_windowJustOpened;
|
||||
|
||||
this->m_windowJustOpened = false;
|
||||
|
||||
return result;
|
||||
return std::exchange(this->m_windowJustOpened, false);
|
||||
}
|
||||
|
||||
void View::setWindowJustOpened(bool state) {
|
||||
@ -66,21 +60,8 @@ namespace hex {
|
||||
ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NavEnableKeyboard;
|
||||
}
|
||||
|
||||
void View::confirmButtons(const std::string &textLeft, const std::string &textRight, const std::function<void()> &leftButtonFn, const std::function<void()> &rightButtonFn) {
|
||||
auto width = ImGui::GetWindowWidth();
|
||||
ImGui::SetCursorPosX(width / 9);
|
||||
if (ImGui::Button(textLeft.c_str(), ImVec2(width / 3, 0)))
|
||||
leftButtonFn();
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(width / 9 * 5);
|
||||
if (ImGui::Button(textRight.c_str(), ImVec2(width / 3, 0)))
|
||||
rightButtonFn();
|
||||
std::string View::toWindowName(const std::string &unlocalizedName) {
|
||||
return LangEntry(unlocalizedName) + "###" + unlocalizedName;
|
||||
}
|
||||
|
||||
ImFontAtlas *View::getFontAtlas() { return s_fontAtlas; }
|
||||
void View::setFontAtlas(ImFontAtlas *atlas) { s_fontAtlas = atlas; }
|
||||
|
||||
ImFontConfig View::getFontConfig() { return s_fontConfig; }
|
||||
void View::setFontConfig(ImFontConfig config) { s_fontConfig = std::move(config); }
|
||||
|
||||
}
|
@ -325,8 +325,7 @@ namespace hex::init {
|
||||
}
|
||||
|
||||
// Configure ImGui to use the font atlas
|
||||
View::setFontAtlas(fonts);
|
||||
View::setFontConfig(cfg);
|
||||
ImHexApi::System::impl::setFontAtlas(fonts);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -810,17 +810,14 @@ namespace hex {
|
||||
ImGui::GetCurrentContext()->NextWindowData.ClearFlags();
|
||||
|
||||
// Draw always visible views
|
||||
view->drawAlwaysVisible();
|
||||
view->drawAlwaysVisibleContent();
|
||||
|
||||
// Skip views that shouldn't be processed currently
|
||||
if (!view->shouldProcess())
|
||||
continue;
|
||||
|
||||
// Draw view
|
||||
if (view->isAvailable()) {
|
||||
ImGui::SetNextWindowSizeConstraints(view->getMinSize(), view->getMaxSize());
|
||||
view->drawContent();
|
||||
}
|
||||
view->draw();
|
||||
|
||||
if (view->getWindowOpenState()) {
|
||||
auto window = ImGui::FindWindowByName(view->getName().c_str());
|
||||
@ -1137,7 +1134,7 @@ namespace hex {
|
||||
void Window::initImGui() {
|
||||
IMGUI_CHECKVERSION();
|
||||
|
||||
auto fonts = View::getFontAtlas();
|
||||
auto fonts = ImHexApi::System::getFontAtlas();
|
||||
|
||||
// Initialize ImGui and all other ImGui extensions
|
||||
GImGui = ImGui::CreateContext(fonts);
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewAbout : public View {
|
||||
class ViewAbout : public View::Modal {
|
||||
public:
|
||||
ViewAbout();
|
||||
|
||||
void drawContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return true; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
@ -22,8 +22,6 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_aboutWindowOpen = false;
|
||||
|
||||
void drawAboutPopup();
|
||||
|
||||
void drawAboutMainPage();
|
||||
|
@ -5,15 +5,15 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewAchievements : public View {
|
||||
class ViewAchievements : public View::Floating {
|
||||
public:
|
||||
ViewAchievements();
|
||||
~ViewAchievements() override;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisible() override;
|
||||
void drawAlwaysVisibleContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return true; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
@ -28,8 +28,6 @@ namespace hex::plugin::builtin {
|
||||
ImVec2 drawAchievementTree(ImDrawList *drawList, const AchievementManager::AchievementNode * prevNode, const std::vector<AchievementManager::AchievementNode*> &nodes, ImVec2 position);
|
||||
|
||||
private:
|
||||
bool m_viewOpen = false;
|
||||
|
||||
std::list<const Achievement*> m_achievementUnlockQueue;
|
||||
const Achievement *m_currAchievement = nullptr;
|
||||
const Achievement *m_achievementToGoto = nullptr;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewBookmarks : public View {
|
||||
class ViewBookmarks : public View::Window {
|
||||
public:
|
||||
ViewBookmarks();
|
||||
~ViewBookmarks() override;
|
||||
|
@ -8,14 +8,15 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewCommandPalette : public View {
|
||||
class ViewCommandPalette : public View::Special {
|
||||
public:
|
||||
ViewCommandPalette();
|
||||
~ViewCommandPalette() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
void drawContent() override {}
|
||||
void drawAlwaysVisibleContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return false; }
|
||||
[[nodiscard]] bool shouldProcess() const override { return true; }
|
||||
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
@ -20,21 +20,13 @@ namespace hex::plugin::builtin {
|
||||
std::string value;
|
||||
};
|
||||
|
||||
class ViewConstants : public View {
|
||||
class ViewConstants : public View::Window {
|
||||
public:
|
||||
explicit ViewConstants();
|
||||
~ViewConstants() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
|
||||
ImVec2 getMinSize() const override {
|
||||
return scaled(ImVec2(300, 400));
|
||||
}
|
||||
|
||||
ImVec2 getMaxSize() const override {
|
||||
return { FLT_MAX, 800_scaled };
|
||||
}
|
||||
|
||||
private:
|
||||
void reloadConstants();
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewDataInspector : public View {
|
||||
class ViewDataInspector : public View::Window {
|
||||
public:
|
||||
explicit ViewDataInspector();
|
||||
~ViewDataInspector() override;
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewDataProcessor : public View {
|
||||
class ViewDataProcessor : public View::Window {
|
||||
public:
|
||||
struct Workspace {
|
||||
Workspace() = default;
|
||||
|
@ -12,12 +12,13 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewDiff : public View {
|
||||
class ViewDiff : public View::Window {
|
||||
public:
|
||||
ViewDiff();
|
||||
~ViewDiff() override;
|
||||
|
||||
void drawContent() override;
|
||||
ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; }
|
||||
|
||||
public:
|
||||
struct Column {
|
||||
|
@ -21,7 +21,7 @@ namespace hex::plugin::builtin {
|
||||
std::string operators;
|
||||
};
|
||||
|
||||
class ViewDisassembler : public View {
|
||||
class ViewDisassembler : public View::Window {
|
||||
public:
|
||||
explicit ViewDisassembler();
|
||||
~ViewDisassembler() override;
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewFind : public View {
|
||||
class ViewFind : public View::Window {
|
||||
public:
|
||||
ViewFind();
|
||||
~ViewFind() override = default;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewHashes : public View {
|
||||
class ViewHashes : public View::Window {
|
||||
public:
|
||||
explicit ViewHashes();
|
||||
~ViewHashes() override;
|
||||
|
@ -9,12 +9,15 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewHexEditor : public View {
|
||||
class ViewHexEditor : public View::Window {
|
||||
public:
|
||||
ViewHexEditor();
|
||||
~ViewHexEditor() override;
|
||||
|
||||
void drawContent() override;
|
||||
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override {
|
||||
return ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||
}
|
||||
|
||||
class Popup {
|
||||
public:
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewInformation : public View {
|
||||
class ViewInformation : public View::Window {
|
||||
public:
|
||||
explicit ViewInformation();
|
||||
~ViewInformation() override;
|
||||
|
@ -4,19 +4,18 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewLogs : public View {
|
||||
class ViewLogs : public View::Floating {
|
||||
public:
|
||||
ViewLogs();
|
||||
~ViewLogs() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return true; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
private:
|
||||
int m_logLevel = 1;
|
||||
bool m_viewOpen = false;
|
||||
};
|
||||
|
||||
}
|
@ -6,13 +6,13 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewPatches : public View {
|
||||
class ViewPatches : public View::Window {
|
||||
public:
|
||||
explicit ViewPatches();
|
||||
~ViewPatches() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisible() override;
|
||||
void drawAlwaysVisibleContent() override;
|
||||
|
||||
private:
|
||||
u64 m_selectedPatch = 0x00;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewPatternData : public View {
|
||||
class ViewPatternData : public View::Window {
|
||||
public:
|
||||
ViewPatternData();
|
||||
~ViewPatternData() override;
|
||||
|
@ -24,13 +24,16 @@ namespace pl::ptrn { class Pattern; }
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewPatternEditor : public View {
|
||||
class ViewPatternEditor : public View::Window {
|
||||
public:
|
||||
ViewPatternEditor();
|
||||
~ViewPatternEditor() override;
|
||||
|
||||
void drawAlwaysVisible() override;
|
||||
void drawAlwaysVisibleContent() override;
|
||||
void drawContent() override;
|
||||
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override {
|
||||
return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
|
||||
}
|
||||
|
||||
private:
|
||||
enum class DangerousFunctionPerms : u8 {
|
||||
@ -79,7 +82,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("hex.builtin.view.pattern_editor.accept_pattern.question"_lang);
|
||||
|
||||
confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
|
||||
[this, provider] {
|
||||
this->m_view->loadPatternFile(this->m_view->m_possiblePatternFiles.get(provider)[this->m_selectedPatternFile], provider);
|
||||
this->close();
|
||||
|
@ -4,17 +4,16 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewProviderSettings : public hex::View {
|
||||
class ViewProviderSettings : public View::Modal {
|
||||
public:
|
||||
ViewProviderSettings();
|
||||
~ViewProviderSettings() override;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisible() override;
|
||||
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override;
|
||||
[[nodiscard]] bool shouldDraw() const override { return true; }
|
||||
};
|
||||
|
||||
}
|
@ -4,14 +4,15 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewSettings : public View {
|
||||
class ViewSettings : public View::Floating {
|
||||
public:
|
||||
explicit ViewSettings();
|
||||
~ViewSettings() override;
|
||||
|
||||
void drawContent() override;
|
||||
void drawContent() override {}
|
||||
void drawAlwaysVisibleContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return false; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override { return scaled({ 700, 400 }); }
|
||||
|
@ -44,14 +44,14 @@ namespace hex::plugin::builtin {
|
||||
std::function<void()> downloadCallback;
|
||||
};
|
||||
|
||||
class ViewStore : public View {
|
||||
class ViewStore : public View::Floating {
|
||||
public:
|
||||
ViewStore();
|
||||
~ViewStore() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return false; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override { return scaled({ 600, 400 }); }
|
||||
|
@ -6,19 +6,18 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewThemeManager : public View {
|
||||
class ViewThemeManager : public View::Floating {
|
||||
public:
|
||||
ViewThemeManager();
|
||||
~ViewThemeManager() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool shouldDraw() const override { return true; }
|
||||
[[nodiscard]] bool hasViewMenuItemEntry() const override { return false; }
|
||||
|
||||
private:
|
||||
std::string m_themeName;
|
||||
bool m_viewOpen = false;
|
||||
|
||||
std::optional<ImColor> m_startingColor;
|
||||
std::optional<u32> m_hoveredColorId;
|
||||
|
@ -7,13 +7,13 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewTools : public View {
|
||||
class ViewTools : public View::Window {
|
||||
public:
|
||||
ViewTools();
|
||||
~ViewTools() override = default;
|
||||
|
||||
void drawContent() override;
|
||||
void drawAlwaysVisible() override;
|
||||
void drawAlwaysVisibleContent() override;
|
||||
|
||||
private:
|
||||
std::vector<ContentRegistry::Tools::impl::Entry>::iterator m_dragStartIterator;
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewYara : public View {
|
||||
class ViewYara : public View::Window {
|
||||
public:
|
||||
ViewYara();
|
||||
~ViewYara() override;
|
||||
|
@ -16,12 +16,11 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewAbout::ViewAbout() : View("hex.builtin.view.help.about.name") {
|
||||
ViewAbout::ViewAbout() : View::Modal("hex.builtin.view.help.about.name") {
|
||||
|
||||
// Add "About" menu item to the help menu
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.help", "hex.builtin.view.help.about.name" }, 1000, Shortcut::None, [this] {
|
||||
TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.help.about.name").c_str()); });
|
||||
this->m_aboutWindowOpen = true;
|
||||
TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); });
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
|
||||
@ -481,38 +480,30 @@ namespace hex::plugin::builtin {
|
||||
Tab { "hex.builtin.view.help.about.license", &ViewAbout::drawLicensePage },
|
||||
};
|
||||
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.help.about.name").c_str(), &this->m_aboutWindowOpen)) {
|
||||
// Allow the window to be closed by pressing ESC
|
||||
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
|
||||
// Allow the window to be closed by pressing ESC
|
||||
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
if (ImGui::BeginTabBar("about_tab_bar")) {
|
||||
// Draw all tabs
|
||||
for (const auto &[unlocalizedName, function] : Tabs) {
|
||||
if (ImGui::BeginTabItem(LangEntry(unlocalizedName))) {
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginTabBar("about_tab_bar")) {
|
||||
// Draw all tabs
|
||||
for (const auto &[unlocalizedName, function] : Tabs) {
|
||||
if (ImGui::BeginTabItem(LangEntry(unlocalizedName))) {
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginChild(1)) {
|
||||
(this->*function)();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
if (ImGui::BeginChild(1)) {
|
||||
(this->*function)();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTabBar();
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewAbout::drawContent() {
|
||||
if (!this->m_aboutWindowOpen)
|
||||
this->getWindowOpenState() = false;
|
||||
|
||||
this->drawAboutPopup();
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,9 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewAchievements::ViewAchievements() : View("hex.builtin.view.achievements.name") {
|
||||
ViewAchievements::ViewAchievements() : View::Floating("hex.builtin.view.achievements.name") {
|
||||
// Add achievements menu item to Extas menu
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.achievements.name" }, 2600, Shortcut::None, [&, this] {
|
||||
this->m_viewOpen = true;
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
|
||||
@ -24,7 +23,6 @@ namespace hex::plugin::builtin {
|
||||
EventManager::subscribe<RequestOpenWindow>(this, [this](const std::string &name) {
|
||||
if (name == "Achievements") {
|
||||
TaskManager::doLater([this] {
|
||||
this->m_viewOpen = true;
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
}
|
||||
@ -302,113 +300,108 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewAchievements::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.achievements.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) {
|
||||
if (ImGui::BeginTabBar("##achievement_categories")) {
|
||||
auto &startNodes = AchievementManager::getAchievementStartNodes();
|
||||
if (ImGui::BeginTabBar("##achievement_categories")) {
|
||||
auto &startNodes = AchievementManager::getAchievementStartNodes();
|
||||
|
||||
// Get all achievement category names
|
||||
std::vector<std::string> categories;
|
||||
for (const auto &[categoryName, achievements] : startNodes) {
|
||||
categories.push_back(categoryName);
|
||||
}
|
||||
|
||||
std::reverse(categories.begin(), categories.end());
|
||||
|
||||
// Draw each individual achievement category
|
||||
for (const auto &categoryName : categories) {
|
||||
const auto &achievements = startNodes[categoryName];
|
||||
|
||||
// Check if any achievements in the category are unlocked or unlockable
|
||||
bool visible = false;
|
||||
for (const auto &achievement : achievements) {
|
||||
if (achievement->isUnlocked() || achievement->isUnlockable()) {
|
||||
visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all achievements in this category are invisible, don't draw it
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
ImGuiTabItemFlags flags = ImGuiTabItemFlags_None;
|
||||
|
||||
// Handle jumping to the category of an achievement
|
||||
if (this->m_achievementToGoto != nullptr) {
|
||||
if (this->m_achievementToGoto->getUnlocalizedCategory() == categoryName) {
|
||||
flags |= ImGuiTabItemFlags_SetSelected;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the achievement category
|
||||
if (ImGui::BeginTabItem(LangEntry(categoryName), nullptr, flags)) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
const auto cursorPos = ImGui::GetCursorPos();
|
||||
const auto windowPos = ImGui::GetWindowPos() + ImVec2(0, cursorPos.y);
|
||||
const auto windowSize = ImGui::GetWindowSize() - ImVec2(0, cursorPos.y);
|
||||
const float borderSize = 20.0_scaled;
|
||||
|
||||
const auto windowPadding = ImGui::GetStyle().WindowPadding;
|
||||
const auto innerWindowPos = windowPos + ImVec2(borderSize, borderSize);
|
||||
const auto innerWindowSize = windowSize - ImVec2(borderSize * 2, borderSize * 2) - ImVec2(0, ImGui::GetTextLineHeightWithSpacing());
|
||||
|
||||
// Prevent the achievement tree from being drawn outside of the window
|
||||
drawList->PushClipRect(innerWindowPos, innerWindowPos + innerWindowSize, true);
|
||||
|
||||
drawList->ChannelsSplit(4);
|
||||
|
||||
drawList->ChannelsSetCurrent(0);
|
||||
|
||||
// Draw achievement background
|
||||
drawBackground(drawList, innerWindowPos, innerWindowPos + innerWindowSize, this->m_offset);
|
||||
|
||||
// Draw the achievement tree
|
||||
auto maxPos = drawAchievementTree(drawList, nullptr, achievements, innerWindowPos + scaled({ 100, 100 }) + this->m_offset);
|
||||
|
||||
drawList->ChannelsSetCurrent(3);
|
||||
|
||||
// Draw the achievement overlay
|
||||
drawOverlay(drawList, innerWindowPos, innerWindowPos + innerWindowSize, categoryName);
|
||||
|
||||
drawList->ChannelsMerge();
|
||||
|
||||
// Handle dragging the achievement tree around
|
||||
if (ImGui::IsMouseHoveringRect(innerWindowPos, innerWindowPos + innerWindowSize)) {
|
||||
auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
this->m_offset += dragDelta;
|
||||
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
}
|
||||
|
||||
// Clamp the achievement tree to the window
|
||||
this->m_offset = -ImClamp(-this->m_offset, { 0, 0 }, ImMax(maxPos - innerWindowPos - innerWindowSize, { 0, 0 }));
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
// Draw settings below the window
|
||||
ImGui::SetCursorScreenPos(innerWindowPos + ImVec2(0, innerWindowSize.y + windowPadding.y));
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
if (ImGui::Checkbox("Show popup", &this->m_showPopup))
|
||||
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", this->m_showPopup);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
// Get all achievement category names
|
||||
std::vector<std::string> categories;
|
||||
for (const auto &[categoryName, achievements] : startNodes) {
|
||||
categories.push_back(categoryName);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
this->getWindowOpenState() = this->m_viewOpen;
|
||||
std::reverse(categories.begin(), categories.end());
|
||||
|
||||
// Draw each individual achievement category
|
||||
for (const auto &categoryName : categories) {
|
||||
const auto &achievements = startNodes[categoryName];
|
||||
|
||||
// Check if any achievements in the category are unlocked or unlockable
|
||||
bool visible = false;
|
||||
for (const auto &achievement : achievements) {
|
||||
if (achievement->isUnlocked() || achievement->isUnlockable()) {
|
||||
visible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If all achievements in this category are invisible, don't draw it
|
||||
if (!visible)
|
||||
continue;
|
||||
|
||||
ImGuiTabItemFlags flags = ImGuiTabItemFlags_None;
|
||||
|
||||
// Handle jumping to the category of an achievement
|
||||
if (this->m_achievementToGoto != nullptr) {
|
||||
if (this->m_achievementToGoto->getUnlocalizedCategory() == categoryName) {
|
||||
flags |= ImGuiTabItemFlags_SetSelected;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the achievement category
|
||||
if (ImGui::BeginTabItem(LangEntry(categoryName), nullptr, flags)) {
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
|
||||
const auto cursorPos = ImGui::GetCursorPos();
|
||||
const auto windowPos = ImGui::GetWindowPos() + ImVec2(0, cursorPos.y);
|
||||
const auto windowSize = ImGui::GetWindowSize() - ImVec2(0, cursorPos.y);
|
||||
const float borderSize = 20.0_scaled;
|
||||
|
||||
const auto windowPadding = ImGui::GetStyle().WindowPadding;
|
||||
const auto innerWindowPos = windowPos + ImVec2(borderSize, borderSize);
|
||||
const auto innerWindowSize = windowSize - ImVec2(borderSize * 2, borderSize * 2) - ImVec2(0, ImGui::GetTextLineHeightWithSpacing());
|
||||
|
||||
// Prevent the achievement tree from being drawn outside of the window
|
||||
drawList->PushClipRect(innerWindowPos, innerWindowPos + innerWindowSize, true);
|
||||
|
||||
drawList->ChannelsSplit(4);
|
||||
|
||||
drawList->ChannelsSetCurrent(0);
|
||||
|
||||
// Draw achievement background
|
||||
drawBackground(drawList, innerWindowPos, innerWindowPos + innerWindowSize, this->m_offset);
|
||||
|
||||
// Draw the achievement tree
|
||||
auto maxPos = drawAchievementTree(drawList, nullptr, achievements, innerWindowPos + scaled({ 100, 100 }) + this->m_offset);
|
||||
|
||||
drawList->ChannelsSetCurrent(3);
|
||||
|
||||
// Draw the achievement overlay
|
||||
drawOverlay(drawList, innerWindowPos, innerWindowPos + innerWindowSize, categoryName);
|
||||
|
||||
drawList->ChannelsMerge();
|
||||
|
||||
// Handle dragging the achievement tree around
|
||||
if (ImGui::IsMouseHoveringRect(innerWindowPos, innerWindowPos + innerWindowSize)) {
|
||||
auto dragDelta = ImGui::GetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
this->m_offset += dragDelta;
|
||||
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
}
|
||||
|
||||
// Clamp the achievement tree to the window
|
||||
this->m_offset = -ImClamp(-this->m_offset, { 0, 0 }, ImMax(maxPos - innerWindowPos - innerWindowSize, { 0, 0 }));
|
||||
|
||||
drawList->PopClipRect();
|
||||
|
||||
// Draw settings below the window
|
||||
ImGui::SetCursorScreenPos(innerWindowPos + ImVec2(0, innerWindowSize.y + windowPadding.y));
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
if (ImGui::Checkbox("Show popup", &this->m_showPopup))
|
||||
ContentRegistry::Settings::write("hex.builtin.setting.interface", "hex.builtin.setting.interface.achievement_popup", this->m_showPopup);
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
this->m_achievementToGoto = nullptr;
|
||||
}
|
||||
|
||||
void ViewAchievements::drawAlwaysVisible() {
|
||||
void ViewAchievements::drawAlwaysVisibleContent() {
|
||||
|
||||
// Handle showing the achievement unlock popup
|
||||
if (this->m_achievementUnlockQueueTimer >= 0 && this->m_showPopup) {
|
||||
@ -439,8 +432,7 @@ namespace hex::plugin::builtin {
|
||||
// Handle clicking on the popup
|
||||
if (ImGui::IsWindowHovered() && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
|
||||
// Open the achievement window and jump to the achievement
|
||||
this->m_viewOpen = true;
|
||||
this->getWindowOpenState() = this->m_viewOpen;
|
||||
this->getWindowOpenState() = true;
|
||||
this->m_achievementToGoto = this->m_currAchievement;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewBookmarks::ViewBookmarks() : View("hex.builtin.view.bookmarks.name") {
|
||||
ViewBookmarks::ViewBookmarks() : View::Window("hex.builtin.view.bookmarks.name") {
|
||||
|
||||
// Handle bookmark add requests sent by the API
|
||||
EventManager::subscribe<RequestAddBookmark>(this, [this](Region region, std::string name, std::string comment, color_t color) {
|
||||
@ -201,193 +201,190 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewBookmarks::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.bookmarks.name").c_str(), &this->getWindowOpenState())) {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
// Draw filter input
|
||||
ImGui::PushItemWidth(-1);
|
||||
ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, this->m_currFilter);
|
||||
ImGui::PopItemWidth();
|
||||
// Draw filter input
|
||||
ImGui::PushItemWidth(-1);
|
||||
ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, this->m_currFilter);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginChild("##bookmarks")) {
|
||||
if (this->m_bookmarks->empty()) {
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang);
|
||||
}
|
||||
|
||||
int id = 1;
|
||||
auto bookmarkToRemove = this->m_bookmarks->end();
|
||||
|
||||
// Draw all bookmarks
|
||||
for (auto iter = this->m_bookmarks->begin(); iter != this->m_bookmarks->end(); iter++) {
|
||||
auto &[region, name, comment, color, locked] = *iter;
|
||||
|
||||
// Apply filter
|
||||
if (!this->m_currFilter.empty()) {
|
||||
if (!name.contains(this->m_currFilter) && !comment.contains(this->m_currFilter))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto headerColor = ImColor(color);
|
||||
auto hoverColor = ImColor(color);
|
||||
hoverColor.Value.w *= 1.3F;
|
||||
|
||||
// Draw bookmark header in the same color as the bookmark was set to
|
||||
ImGui::PushID(id);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, u32(hoverColor));
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
ImGui::PopID();
|
||||
ImGui::PopStyleColor(3);
|
||||
id++;
|
||||
};
|
||||
|
||||
bool open = true;
|
||||
if (!ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), locked ? nullptr : &open)) {
|
||||
// Handle dragging bookmarks up and down when they're collapsed
|
||||
|
||||
// Set the currently held bookmark as the one being dragged
|
||||
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == this->m_bookmarks->end())
|
||||
this->m_dragStartIterator = iter;
|
||||
|
||||
// When the mouse moved away from the current bookmark, swap the dragged bookmark with the current one
|
||||
if (ImGui::IsItemHovered() && this->m_dragStartIterator != this->m_bookmarks->end()) {
|
||||
std::iter_swap(iter, this->m_dragStartIterator);
|
||||
this->m_dragStartIterator = iter;
|
||||
}
|
||||
|
||||
// When the mouse is released, reset the dragged bookmark
|
||||
if (!ImGui::IsMouseDown(0))
|
||||
this->m_dragStartIterator = this->m_bookmarks->end();
|
||||
} else {
|
||||
const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() + 2 * ImGui::GetStyle().FramePadding.y;
|
||||
if (ImGui::BeginTable("##bookmark_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("##name");
|
||||
ImGui::TableSetupColumn("##spacing", ImGuiTableColumnFlags_WidthFixed, 20);
|
||||
ImGui::TableSetupColumn("##value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw bookmark name
|
||||
ImGui::TextUnformatted("hex.builtin.view.bookmarks.header.name"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw lock/unlock button
|
||||
if (locked) {
|
||||
if (ImGuiExt::IconButton(ICON_VS_LOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = false;
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.unlock"_lang);
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_UNLOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = true;
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.lock"_lang);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw color button
|
||||
if (ImGui::ColorButton("hex.builtin.view.bookmarks.header.color"_lang, headerColor.Value, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoAlpha)) {
|
||||
if (!locked)
|
||||
ImGui::OpenPopup("hex.builtin.view.bookmarks.header.color"_lang);
|
||||
}
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.header.color"_lang);
|
||||
|
||||
// Draw color picker
|
||||
if (ImGui::BeginPopup("hex.builtin.view.bookmarks.header.color"_lang)) {
|
||||
drawColorPopup(headerColor);
|
||||
color = headerColor;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw bookmark name if the bookmark is locked or an input text box if it's unlocked
|
||||
if (locked)
|
||||
ImGui::TextUnformatted(name.data());
|
||||
else {
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputText("##nameInput", name);
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextUnformatted("hex.builtin.common.address"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw jump to address button
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_BACK, ImGui::GetStyleColorVec4(ImGuiCol_Text)))
|
||||
ImHexApi::HexEditor::setSelection(region);
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.jump_to"_lang);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw open in new view button
|
||||
if (ImGuiExt::IconButton(ICON_VS_GO_TO_FILE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
TaskManager::doLater([region, provider]{
|
||||
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true);
|
||||
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider); viewProvider != nullptr) {
|
||||
viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider);
|
||||
if (viewProvider->open()) {
|
||||
EventManager::post<EventProviderOpened>(viewProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.open_in_view"_lang);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw the address of the bookmark
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.bookmarks.address"_lang, region.getStartAddress(), region.getEndAddress());
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw size of the bookmark
|
||||
ImGui::TextUnformatted("hex.builtin.common.size"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted(hex::toByteString(region.size));
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Draw comment if the bookmark is locked or an input text box if it's unlocked
|
||||
if (locked) {
|
||||
if (!comment.empty()) {
|
||||
ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang);
|
||||
ImGuiExt::TextFormattedWrapped("{}", comment.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang);
|
||||
ImGui::InputTextMultiline("##commentInput", comment, ImVec2(ImGui::GetContentRegionAvail().x, 150_scaled));
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
// Mark a bookmark for removal when the user clicks the remove button
|
||||
if (!open)
|
||||
bookmarkToRemove = iter;
|
||||
}
|
||||
|
||||
// Remove the bookmark that was marked for removal
|
||||
if (bookmarkToRemove != this->m_bookmarks->end()) {
|
||||
this->m_bookmarks->erase(bookmarkToRemove);
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
if (ImGui::BeginChild("##bookmarks")) {
|
||||
if (this->m_bookmarks->empty()) {
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang);
|
||||
}
|
||||
|
||||
int id = 1;
|
||||
auto bookmarkToRemove = this->m_bookmarks->end();
|
||||
|
||||
// Draw all bookmarks
|
||||
for (auto iter = this->m_bookmarks->begin(); iter != this->m_bookmarks->end(); iter++) {
|
||||
auto &[region, name, comment, color, locked] = *iter;
|
||||
|
||||
// Apply filter
|
||||
if (!this->m_currFilter.empty()) {
|
||||
if (!name.contains(this->m_currFilter) && !comment.contains(this->m_currFilter))
|
||||
continue;
|
||||
}
|
||||
|
||||
auto headerColor = ImColor(color);
|
||||
auto hoverColor = ImColor(color);
|
||||
hoverColor.Value.w *= 1.3F;
|
||||
|
||||
// Draw bookmark header in the same color as the bookmark was set to
|
||||
ImGui::PushID(id);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, u32(hoverColor));
|
||||
|
||||
ON_SCOPE_EXIT {
|
||||
ImGui::PopID();
|
||||
ImGui::PopStyleColor(3);
|
||||
id++;
|
||||
};
|
||||
|
||||
bool open = true;
|
||||
if (!ImGui::CollapsingHeader(hex::format("{}###bookmark", name).c_str(), locked ? nullptr : &open)) {
|
||||
// Handle dragging bookmarks up and down when they're collapsed
|
||||
|
||||
// Set the currently held bookmark as the one being dragged
|
||||
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == this->m_bookmarks->end())
|
||||
this->m_dragStartIterator = iter;
|
||||
|
||||
// When the mouse moved away from the current bookmark, swap the dragged bookmark with the current one
|
||||
if (ImGui::IsItemHovered() && this->m_dragStartIterator != this->m_bookmarks->end()) {
|
||||
std::iter_swap(iter, this->m_dragStartIterator);
|
||||
this->m_dragStartIterator = iter;
|
||||
}
|
||||
|
||||
// When the mouse is released, reset the dragged bookmark
|
||||
if (!ImGui::IsMouseDown(0))
|
||||
this->m_dragStartIterator = this->m_bookmarks->end();
|
||||
} else {
|
||||
const auto rowHeight = ImGui::GetTextLineHeightWithSpacing() + 2 * ImGui::GetStyle().FramePadding.y;
|
||||
if (ImGui::BeginTable("##bookmark_table", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("##name");
|
||||
ImGui::TableSetupColumn("##spacing", ImGuiTableColumnFlags_WidthFixed, 20);
|
||||
ImGui::TableSetupColumn("##value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw bookmark name
|
||||
ImGui::TextUnformatted("hex.builtin.view.bookmarks.header.name"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw lock/unlock button
|
||||
if (locked) {
|
||||
if (ImGuiExt::IconButton(ICON_VS_LOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = false;
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.unlock"_lang);
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_UNLOCK, ImGui::GetStyleColorVec4(ImGuiCol_Text))) locked = true;
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.lock"_lang);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw color button
|
||||
if (ImGui::ColorButton("hex.builtin.view.bookmarks.header.color"_lang, headerColor.Value, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoAlpha)) {
|
||||
if (!locked)
|
||||
ImGui::OpenPopup("hex.builtin.view.bookmarks.header.color"_lang);
|
||||
}
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.header.color"_lang);
|
||||
|
||||
// Draw color picker
|
||||
if (ImGui::BeginPopup("hex.builtin.view.bookmarks.header.color"_lang)) {
|
||||
drawColorPopup(headerColor);
|
||||
color = headerColor;
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw bookmark name if the bookmark is locked or an input text box if it's unlocked
|
||||
if (locked)
|
||||
ImGui::TextUnformatted(name.data());
|
||||
else {
|
||||
ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
ImGui::InputText("##nameInput", name);
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextUnformatted("hex.builtin.common.address"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw jump to address button
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_BACK, ImGui::GetStyleColorVec4(ImGuiCol_Text)))
|
||||
ImHexApi::HexEditor::setSelection(region);
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.jump_to"_lang);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw open in new view button
|
||||
if (ImGuiExt::IconButton(ICON_VS_GO_TO_FILE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
TaskManager::doLater([region, provider]{
|
||||
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true);
|
||||
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider); viewProvider != nullptr) {
|
||||
viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider);
|
||||
if (viewProvider->open()) {
|
||||
EventManager::post<EventProviderOpened>(viewProvider);
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.hex_editor", "hex.builtin.achievement.hex_editor.open_new_view.name");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ImGuiExt::InfoTooltip("hex.builtin.view.bookmarks.tooltip.open_in_view"_lang);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Draw the address of the bookmark
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.bookmarks.address"_lang, region.getStartAddress(), region.getEndAddress());
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowHeight);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw size of the bookmark
|
||||
ImGui::TextUnformatted("hex.builtin.common.size"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted(hex::toByteString(region.size));
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Draw comment if the bookmark is locked or an input text box if it's unlocked
|
||||
if (locked) {
|
||||
if (!comment.empty()) {
|
||||
ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang);
|
||||
ImGuiExt::TextFormattedWrapped("{}", comment.data());
|
||||
}
|
||||
}
|
||||
else {
|
||||
ImGuiExt::Header("hex.builtin.view.bookmarks.header.comment"_lang);
|
||||
ImGui::InputTextMultiline("##commentInput", comment, ImVec2(ImGui::GetContentRegionAvail().x, 150_scaled));
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
// Mark a bookmark for removal when the user clicks the remove button
|
||||
if (!open)
|
||||
bookmarkToRemove = iter;
|
||||
}
|
||||
|
||||
// Remove the bookmark that was marked for removal
|
||||
if (bookmarkToRemove != this->m_bookmarks->end()) {
|
||||
this->m_bookmarks->erase(bookmarkToRemove);
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
bool ViewBookmarks::importBookmarks(prv::Provider *provider, const nlohmann::json &json) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewCommandPalette::ViewCommandPalette() : View("hex.builtin.view.command_palette.name") {
|
||||
ViewCommandPalette::ViewCommandPalette() : View::Special("hex.builtin.view.command_palette.name") {
|
||||
// Add global shortcut to open the command palette
|
||||
ShortcutManager::addGlobalShortcut(CTRLCMD + SHIFT + Keys::P, "hex.builtin.view.command_palette.name", [this] {
|
||||
EventManager::post<RequestOpenPopup>("hex.builtin.view.command_palette.name"_lang);
|
||||
@ -20,7 +20,7 @@ namespace hex::plugin::builtin {
|
||||
});
|
||||
}
|
||||
|
||||
void ViewCommandPalette::drawContent() {
|
||||
void ViewCommandPalette::drawAlwaysVisibleContent() {
|
||||
// If the command palette is hidden, don't draw it
|
||||
if (!this->m_commandPaletteOpen) return;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewConstants::ViewConstants() : View("hex.builtin.view.constants.name") {
|
||||
ViewConstants::ViewConstants() : View::Window("hex.builtin.view.constants.name") {
|
||||
this->reloadConstants();
|
||||
}
|
||||
|
||||
@ -63,92 +63,88 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewConstants::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.constants.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::PushItemWidth(-1);
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGuiExt::InputTextIcon("##search", ICON_VS_FILTER, this->m_filter)) {
|
||||
this->m_filterIndices.clear();
|
||||
|
||||
if (ImGuiExt::InputTextIcon("##search", ICON_VS_FILTER, this->m_filter)) {
|
||||
this->m_filterIndices.clear();
|
||||
|
||||
// Filter the constants according to the entered value
|
||||
for (u64 i = 0; i < this->m_constants.size(); i++) {
|
||||
auto &constant = this->m_constants[i];
|
||||
if (hex::containsIgnoreCase(constant.name, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.category, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.description, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.value, this->m_filter))
|
||||
this->m_filterIndices.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::BeginTable("##strings", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.category"_lang, 0, -1, ImGui::GetID("category"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.name"_lang, 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.desc"_lang, 0, -1, ImGui::GetID("desc"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.value"_lang, 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
// Handle table sorting
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(this->m_constants.begin(), this->m_constants.end(), [&sortSpecs](const Constant &left, const Constant &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("category")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.category > right.category;
|
||||
else
|
||||
return left.category < right.category;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.name > right.name;
|
||||
else
|
||||
return left.name < right.name;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("desc")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.description > right.description;
|
||||
else
|
||||
return left.description < right.description;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.value > right.value;
|
||||
else
|
||||
return left.value < right.value;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_filterIndices.size());
|
||||
|
||||
// Draw the constants table
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &constant = this->m_constants[this->m_filterIndices[i]];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.category.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.description.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.value.c_str());
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
// Filter the constants according to the entered value
|
||||
for (u64 i = 0; i < this->m_constants.size(); i++) {
|
||||
auto &constant = this->m_constants[i];
|
||||
if (hex::containsIgnoreCase(constant.name, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.category, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.description, this->m_filter) ||
|
||||
hex::containsIgnoreCase(constant.value, this->m_filter))
|
||||
this->m_filterIndices.push_back(i);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::BeginTable("##strings", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.category"_lang, 0, -1, ImGui::GetID("category"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.name"_lang, 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.desc"_lang, 0, -1, ImGui::GetID("desc"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.constants.row.value"_lang, 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
// Handle table sorting
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(this->m_constants.begin(), this->m_constants.end(), [&sortSpecs](const Constant &left, const Constant &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("category")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.category > right.category;
|
||||
else
|
||||
return left.category < right.category;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.name > right.name;
|
||||
else
|
||||
return left.name < right.name;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("desc")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.description > right.description;
|
||||
else
|
||||
return left.description < right.description;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.value > right.value;
|
||||
else
|
||||
return left.value < right.value;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_filterIndices.size());
|
||||
|
||||
// Draw the constants table
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &constant = this->m_constants[this->m_filterIndices[i]];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.category.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.description.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(constant.value.c_str());
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -19,7 +19,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
using NumberDisplayStyle = ContentRegistry::DataInspector::NumberDisplayStyle;
|
||||
|
||||
ViewDataInspector::ViewDataInspector() : View("hex.builtin.view.data_inspector.name") {
|
||||
ViewDataInspector::ViewDataInspector() : View::Window("hex.builtin.view.data_inspector.name") {
|
||||
// Handle region selection
|
||||
EventManager::subscribe<EventRegionSelected>(this, [this](const auto ®ion) {
|
||||
|
||||
@ -208,205 +208,202 @@ namespace hex::plugin::builtin {
|
||||
this->updateInspectorRows();
|
||||
}
|
||||
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_inspector.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_selectedProvider != nullptr && this->m_selectedProvider->isReadable() && this->m_validBytes > 0) {
|
||||
u32 validLineCount = this->m_cachedData.size();
|
||||
if (!this->m_tableEditingModeEnabled) {
|
||||
validLineCount = std::count_if(this->m_cachedData.begin(), this->m_cachedData.end(), [this](const auto &entry) {
|
||||
return !this->m_hiddenValues.contains(entry.filterValue);
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##datainspector", this->m_tableEditingModeEnabled ? 3 : 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * (validLineCount + 1)))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.name"_lang, ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.value"_lang, ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
if (this->m_tableEditingModeEnabled)
|
||||
ImGui::TableSetupColumn("##favorite", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
int inspectorRowId = 1;
|
||||
for (auto &[unlocalizedName, displayFunction, editingFunction, editing, filterValue] : this->m_cachedData) {
|
||||
bool grayedOut = false;
|
||||
if (this->m_hiddenValues.contains(filterValue)) {
|
||||
if (!this->m_tableEditingModeEnabled)
|
||||
continue;
|
||||
else
|
||||
grayedOut = true;
|
||||
}
|
||||
|
||||
ImGui::PushID(inspectorRowId);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::BeginDisabled(grayedOut);
|
||||
|
||||
// Render inspector row name
|
||||
ImGui::TextUnformatted(LangEntry(unlocalizedName));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (!editing) {
|
||||
// Handle regular display case
|
||||
|
||||
// Render inspector row value
|
||||
const auto ©Value = displayFunction();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Handle copying the value to the clipboard when clicking the row
|
||||
if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
||||
ImGui::SetClipboardText(copyValue.c_str());
|
||||
}
|
||||
|
||||
// Enter editing mode when double-clicking the row
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && editingFunction.has_value()) {
|
||||
editing = true;
|
||||
this->m_editingValue = copyValue;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Handle editing mode
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
|
||||
// Draw input text box
|
||||
if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
// Turn the entered value into bytes
|
||||
auto bytes = editingFunction.value()(this->m_editingValue, this->m_endian);
|
||||
|
||||
// Write those bytes to the selected provider at the current address
|
||||
this->m_selectedProvider->write(this->m_startAddress, bytes.data(), bytes.size());
|
||||
|
||||
// Disable editing mode
|
||||
this->m_editingValue.clear();
|
||||
editing = false;
|
||||
|
||||
// Reload all inspector rows
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// Disable editing mode when clicking outside the input text box
|
||||
if (!ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) {
|
||||
this->m_editingValue.clear();
|
||||
editing = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (this->m_tableEditingModeEnabled) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
|
||||
bool hidden = this->m_hiddenValues.contains(filterValue);
|
||||
if (ImGuiExt::DimmedButton(hidden ? ICON_VS_EYE : ICON_VS_EYE_CLOSED)) {
|
||||
if (hidden)
|
||||
this->m_hiddenValues.erase(filterValue);
|
||||
else
|
||||
this->m_hiddenValues.insert(filterValue);
|
||||
|
||||
{
|
||||
std::vector filterValues(this->m_hiddenValues.begin(), this->m_hiddenValues.end());
|
||||
|
||||
ContentRegistry::Settings::write("hex.builtin.setting.data_inspector", "hex.builtin.setting.data_inspector.hidden_rows", filterValues);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
inspectorRowId++;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGuiExt::DimmedButtonToggle("hex.builtin.common.edit"_lang, &this->m_tableEditingModeEnabled, ImVec2(ImGui::GetContentRegionAvail().x, 0));
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
// Draw inspector settings
|
||||
|
||||
// Draw endian setting
|
||||
{
|
||||
int selection = [this] {
|
||||
switch (this->m_endian) {
|
||||
default:
|
||||
case std::endian::little: return 0;
|
||||
case std::endian::big: return 1;
|
||||
}
|
||||
}();
|
||||
|
||||
std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: this->m_endian = std::endian::little; break;
|
||||
case 1: this->m_endian = std::endian::big; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw radix setting
|
||||
{
|
||||
int selection = [this] {
|
||||
switch (this->m_numberDisplayStyle) {
|
||||
default:
|
||||
case NumberDisplayStyle::Decimal: return 0;
|
||||
case NumberDisplayStyle::Hexadecimal: return 1;
|
||||
case NumberDisplayStyle::Octal: return 2;
|
||||
}
|
||||
}();
|
||||
std::array options = { "hex.builtin.common.decimal"_lang, "hex.builtin.common.hexadecimal"_lang, "hex.builtin.common.octal"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.common.number_format"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: this->m_numberDisplayStyle = NumberDisplayStyle::Decimal; break;
|
||||
case 1: this->m_numberDisplayStyle = NumberDisplayStyle::Hexadecimal; break;
|
||||
case 2: this->m_numberDisplayStyle = NumberDisplayStyle::Octal; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw invert setting
|
||||
{
|
||||
int selection = this->m_invert ? 1 : 0;
|
||||
std::array options = { "hex.builtin.common.no"_lang, "hex.builtin.common.yes"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.view.data_inspector.invert"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
this->m_invert = selection == 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Draw a message when no bytes are selected
|
||||
std::string text = "hex.builtin.view.data_inspector.no_data"_lang;
|
||||
auto textSize = ImGui::CalcTextSize(text.c_str());
|
||||
auto availableSpace = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::SetCursorPos((availableSpace - textSize) / 2.0F);
|
||||
ImGui::TextUnformatted(text.c_str());
|
||||
if (this->m_selectedProvider != nullptr && this->m_selectedProvider->isReadable() && this->m_validBytes > 0) {
|
||||
u32 validLineCount = this->m_cachedData.size();
|
||||
if (!this->m_tableEditingModeEnabled) {
|
||||
validLineCount = std::count_if(this->m_cachedData.begin(), this->m_cachedData.end(), [this](const auto &entry) {
|
||||
return !this->m_hiddenValues.contains(entry.filterValue);
|
||||
});
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("##datainspector", this->m_tableEditingModeEnabled ? 3 : 2, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg, ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * (validLineCount + 1)))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.name"_lang, ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.data_inspector.table.value"_lang, ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
if (this->m_tableEditingModeEnabled)
|
||||
ImGui::TableSetupColumn("##favorite", ImGuiTableColumnFlags_WidthFixed, ImGui::GetTextLineHeight());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
int inspectorRowId = 1;
|
||||
for (auto &[unlocalizedName, displayFunction, editingFunction, editing, filterValue] : this->m_cachedData) {
|
||||
bool grayedOut = false;
|
||||
if (this->m_hiddenValues.contains(filterValue)) {
|
||||
if (!this->m_tableEditingModeEnabled)
|
||||
continue;
|
||||
else
|
||||
grayedOut = true;
|
||||
}
|
||||
|
||||
ImGui::PushID(inspectorRowId);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::BeginDisabled(grayedOut);
|
||||
|
||||
// Render inspector row name
|
||||
ImGui::TextUnformatted(LangEntry(unlocalizedName));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (!editing) {
|
||||
// Handle regular display case
|
||||
|
||||
// Render inspector row value
|
||||
const auto ©Value = displayFunction();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Handle copying the value to the clipboard when clicking the row
|
||||
if (ImGui::Selectable("##InspectorLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
||||
ImGui::SetClipboardText(copyValue.c_str());
|
||||
}
|
||||
|
||||
// Enter editing mode when double-clicking the row
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && editingFunction.has_value()) {
|
||||
editing = true;
|
||||
this->m_editingValue = copyValue;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Handle editing mode
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::SetNextItemWidth(-1);
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
|
||||
// Draw input text box
|
||||
if (ImGui::InputText("##InspectorLineEditing", this->m_editingValue, ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
|
||||
// Turn the entered value into bytes
|
||||
auto bytes = editingFunction.value()(this->m_editingValue, this->m_endian);
|
||||
|
||||
// Write those bytes to the selected provider at the current address
|
||||
this->m_selectedProvider->write(this->m_startAddress, bytes.data(), bytes.size());
|
||||
|
||||
// Disable editing mode
|
||||
this->m_editingValue.clear();
|
||||
editing = false;
|
||||
|
||||
// Reload all inspector rows
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// Disable editing mode when clicking outside the input text box
|
||||
if (!ImGui::IsItemHovered() && ImGui::IsAnyMouseDown()) {
|
||||
this->m_editingValue.clear();
|
||||
editing = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (this->m_tableEditingModeEnabled) {
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_Text));
|
||||
|
||||
bool hidden = this->m_hiddenValues.contains(filterValue);
|
||||
if (ImGuiExt::DimmedButton(hidden ? ICON_VS_EYE : ICON_VS_EYE_CLOSED)) {
|
||||
if (hidden)
|
||||
this->m_hiddenValues.erase(filterValue);
|
||||
else
|
||||
this->m_hiddenValues.insert(filterValue);
|
||||
|
||||
{
|
||||
std::vector filterValues(this->m_hiddenValues.begin(), this->m_hiddenValues.end());
|
||||
|
||||
ContentRegistry::Settings::write("hex.builtin.setting.data_inspector", "hex.builtin.setting.data_inspector.hidden_rows", filterValues);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
inspectorRowId++;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGuiExt::DimmedButtonToggle("hex.builtin.common.edit"_lang, &this->m_tableEditingModeEnabled, ImVec2(ImGui::GetContentRegionAvail().x, 0));
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
// Draw inspector settings
|
||||
|
||||
// Draw endian setting
|
||||
{
|
||||
int selection = [this] {
|
||||
switch (this->m_endian) {
|
||||
default:
|
||||
case std::endian::little: return 0;
|
||||
case std::endian::big: return 1;
|
||||
}
|
||||
}();
|
||||
|
||||
std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: this->m_endian = std::endian::little; break;
|
||||
case 1: this->m_endian = std::endian::big; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw radix setting
|
||||
{
|
||||
int selection = [this] {
|
||||
switch (this->m_numberDisplayStyle) {
|
||||
default:
|
||||
case NumberDisplayStyle::Decimal: return 0;
|
||||
case NumberDisplayStyle::Hexadecimal: return 1;
|
||||
case NumberDisplayStyle::Octal: return 2;
|
||||
}
|
||||
}();
|
||||
std::array options = { "hex.builtin.common.decimal"_lang, "hex.builtin.common.hexadecimal"_lang, "hex.builtin.common.octal"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.common.number_format"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: this->m_numberDisplayStyle = NumberDisplayStyle::Decimal; break;
|
||||
case 1: this->m_numberDisplayStyle = NumberDisplayStyle::Hexadecimal; break;
|
||||
case 2: this->m_numberDisplayStyle = NumberDisplayStyle::Octal; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw invert setting
|
||||
{
|
||||
int selection = this->m_invert ? 1 : 0;
|
||||
std::array options = { "hex.builtin.common.no"_lang, "hex.builtin.common.yes"_lang };
|
||||
|
||||
if (ImGui::SliderInt("hex.builtin.view.data_inspector.invert"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
this->m_invert = selection == 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Draw a message when no bytes are selected
|
||||
std::string text = "hex.builtin.view.data_inspector.no_data"_lang;
|
||||
auto textSize = ImGui::CalcTextSize(text.c_str());
|
||||
auto availableSpace = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::SetCursorPos((availableSpace - textSize) / 2.0F);
|
||||
ImGui::TextUnformatted(text.c_str());
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
@ -347,7 +347,7 @@ namespace hex::plugin::builtin {
|
||||
ViewDataProcessor::Workspace m_workspace;
|
||||
};
|
||||
|
||||
ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") {
|
||||
ViewDataProcessor::ViewDataProcessor() : View::Window("hex.builtin.view.data_processor.name") {
|
||||
ContentRegistry::DataProcessorNode::add<NodeCustom>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.custom", this);
|
||||
ContentRegistry::DataProcessorNode::add<NodeCustomInput>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.input");
|
||||
ContentRegistry::DataProcessorNode::add<NodeCustomOutput>("hex.builtin.nodes.custom", "hex.builtin.nodes.custom.output");
|
||||
@ -842,180 +842,177 @@ namespace hex::plugin::builtin {
|
||||
auto &workspace = *this->m_workspaceStack->back();
|
||||
|
||||
bool popWorkspace = false;
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
// Set the ImNodes context to the current workspace context
|
||||
ImNodes::SetCurrentContext(workspace.context.get());
|
||||
// Set the ImNodes context to the current workspace context
|
||||
ImNodes::SetCurrentContext(workspace.context.get());
|
||||
|
||||
this->drawContextMenus(workspace);
|
||||
this->drawContextMenus(workspace);
|
||||
|
||||
// Draw error tooltip when hovering over a node that has an error
|
||||
{
|
||||
int nodeId;
|
||||
if (ImNodes::IsNodeHovered(&nodeId) && workspace.currNodeError.has_value() && workspace.currNodeError->node->getId() == nodeId) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted(workspace.currNodeError->message.c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
// Draw error tooltip when hovering over a node that has an error
|
||||
{
|
||||
int nodeId;
|
||||
if (ImNodes::IsNodeHovered(&nodeId) && workspace.currNodeError.has_value() && workspace.currNodeError->node->getId() == nodeId) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted(workspace.currNodeError->message.c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the main node editor workspace window
|
||||
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) {
|
||||
ImNodes::BeginNodeEditor();
|
||||
|
||||
// Loop over all nodes that have been placed in the workspace
|
||||
for (auto &node : workspace.nodes) {
|
||||
ImNodes::SnapNodeToGrid(node->getId());
|
||||
|
||||
// If the node has an error, draw it with a red outline
|
||||
const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get();
|
||||
if (hasError)
|
||||
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
|
||||
|
||||
// Draw the node
|
||||
this->drawNode(*node);
|
||||
|
||||
if (hasError)
|
||||
ImNodes::PopColorStyle();
|
||||
}
|
||||
|
||||
// Draw the main node editor workspace window
|
||||
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3F))) {
|
||||
ImNodes::BeginNodeEditor();
|
||||
this->m_updateNodePositions = false;
|
||||
|
||||
// Loop over all nodes that have been placed in the workspace
|
||||
for (auto &node : workspace.nodes) {
|
||||
ImNodes::SnapNodeToGrid(node->getId());
|
||||
|
||||
// If the node has an error, draw it with a red outline
|
||||
const bool hasError = workspace.currNodeError.has_value() && workspace.currNodeError->node == node.get();
|
||||
if (hasError)
|
||||
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
|
||||
|
||||
// Draw the node
|
||||
this->drawNode(*node);
|
||||
|
||||
if (hasError)
|
||||
ImNodes::PopColorStyle();
|
||||
}
|
||||
|
||||
this->m_updateNodePositions = false;
|
||||
|
||||
// Handle removing links that are connected to attributes that don't exist anymore
|
||||
{
|
||||
std::vector<int> linksToRemove;
|
||||
for (const auto &link : workspace.links) {
|
||||
if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getFromId()) == -1 ||
|
||||
ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getToId()) == -1) {
|
||||
|
||||
linksToRemove.push_back(link.getId());
|
||||
}
|
||||
}
|
||||
for (auto linkId : linksToRemove)
|
||||
this->eraseLink(workspace, linkId);
|
||||
}
|
||||
|
||||
// Draw links
|
||||
// Handle removing links that are connected to attributes that don't exist anymore
|
||||
{
|
||||
std::vector<int> linksToRemove;
|
||||
for (const auto &link : workspace.links) {
|
||||
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
|
||||
}
|
||||
if (ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getFromId()) == -1 ||
|
||||
ImNodes::ObjectPoolFind(ImNodes::EditorContextGet().Pins, link.getToId()) == -1) {
|
||||
|
||||
// Draw the mini map in the bottom right
|
||||
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
|
||||
|
||||
// Draw the help text if no nodes have been placed yet
|
||||
if (workspace.nodes.empty())
|
||||
ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
|
||||
|
||||
// Draw a close button if there is more than one workspace on the stack
|
||||
if (this->m_workspaceStack->size() > 1) {
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
|
||||
if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
|
||||
popWorkspace = true;
|
||||
linksToRemove.push_back(link.getId());
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImNodes::EndNodeEditor();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// Draw the control bar at the bottom
|
||||
{
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
|
||||
this->processNodes(workspace);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
|
||||
}
|
||||
|
||||
|
||||
// Erase links that have been distroyed
|
||||
{
|
||||
int linkId;
|
||||
if (ImNodes::IsLinkDestroyed(&linkId)) {
|
||||
for (auto linkId : linksToRemove)
|
||||
this->eraseLink(workspace, linkId);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle creation of new links
|
||||
{
|
||||
int from, to;
|
||||
if (ImNodes::IsLinkCreated(&from, &to)) {
|
||||
// Draw links
|
||||
for (const auto &link : workspace.links) {
|
||||
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
|
||||
}
|
||||
|
||||
do {
|
||||
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
||||
// Draw the mini map in the bottom right
|
||||
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
|
||||
|
||||
// Find the attributes that are connected by the link
|
||||
for (auto &node : workspace.nodes) {
|
||||
for (auto &attribute : node->getAttributes()) {
|
||||
if (attribute.getId() == from)
|
||||
fromAttr = &attribute;
|
||||
else if (attribute.getId() == to)
|
||||
toAttr = &attribute;
|
||||
}
|
||||
// Draw the help text if no nodes have been placed yet
|
||||
if (workspace.nodes.empty())
|
||||
ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
|
||||
|
||||
// Draw a close button if there is more than one workspace on the stack
|
||||
if (this->m_workspaceStack->size() > 1) {
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F));
|
||||
if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
|
||||
popWorkspace = true;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImNodes::EndNodeEditor();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
// Draw the control bar at the bottom
|
||||
{
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_continuousEvaluation)
|
||||
this->processNodes(workspace);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Checkbox("Continuous evaluation", &this->m_continuousEvaluation);
|
||||
}
|
||||
|
||||
|
||||
// Erase links that have been distroyed
|
||||
{
|
||||
int linkId;
|
||||
if (ImNodes::IsLinkDestroyed(&linkId)) {
|
||||
this->eraseLink(workspace, linkId);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle creation of new links
|
||||
{
|
||||
int from, to;
|
||||
if (ImNodes::IsLinkCreated(&from, &to)) {
|
||||
|
||||
do {
|
||||
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
|
||||
|
||||
// Find the attributes that are connected by the link
|
||||
for (auto &node : workspace.nodes) {
|
||||
for (auto &attribute : node->getAttributes()) {
|
||||
if (attribute.getId() == from)
|
||||
fromAttr = &attribute;
|
||||
else if (attribute.getId() == to)
|
||||
toAttr = &attribute;
|
||||
}
|
||||
|
||||
// If one of the attributes could not be found, the link is invalid and can't be created
|
||||
if (fromAttr == nullptr || toAttr == nullptr)
|
||||
break;
|
||||
|
||||
// If the attributes have different types, don't create the link
|
||||
if (fromAttr->getType() != toAttr->getType())
|
||||
break;
|
||||
|
||||
// If the link tries to connect two input or two output attributes, don't create the link
|
||||
if (fromAttr->getIOType() == toAttr->getIOType())
|
||||
break;
|
||||
|
||||
// If the link tries to connect to a input attribute that already has a link connected to it, don't create the link
|
||||
if (!toAttr->getConnectedAttributes().empty())
|
||||
break;
|
||||
|
||||
// Add a new link to the current workspace
|
||||
auto newLink = workspace.links.emplace_back(from, to);
|
||||
|
||||
// Add the link to the attributes that are connected by it
|
||||
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
|
||||
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
||||
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.data_processor", "hex.builtin.achievement.data_processor.create_connection.name");
|
||||
} while (false);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle deletion of links using the Delete key
|
||||
{
|
||||
const int selectedLinkCount = ImNodes::NumSelectedLinks();
|
||||
if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
|
||||
std::vector<int> selectedLinks;
|
||||
selectedLinks.resize(static_cast<size_t>(selectedLinkCount));
|
||||
ImNodes::GetSelectedLinks(selectedLinks.data());
|
||||
ImNodes::ClearLinkSelection();
|
||||
|
||||
for (const int id : selectedLinks) {
|
||||
eraseLink(workspace, id);
|
||||
}
|
||||
}
|
||||
|
||||
// If one of the attributes could not be found, the link is invalid and can't be created
|
||||
if (fromAttr == nullptr || toAttr == nullptr)
|
||||
break;
|
||||
|
||||
// If the attributes have different types, don't create the link
|
||||
if (fromAttr->getType() != toAttr->getType())
|
||||
break;
|
||||
|
||||
// If the link tries to connect two input or two output attributes, don't create the link
|
||||
if (fromAttr->getIOType() == toAttr->getIOType())
|
||||
break;
|
||||
|
||||
// If the link tries to connect to a input attribute that already has a link connected to it, don't create the link
|
||||
if (!toAttr->getConnectedAttributes().empty())
|
||||
break;
|
||||
|
||||
// Add a new link to the current workspace
|
||||
auto newLink = workspace.links.emplace_back(from, to);
|
||||
|
||||
// Add the link to the attributes that are connected by it
|
||||
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
|
||||
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
|
||||
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.data_processor", "hex.builtin.achievement.data_processor.create_connection.name");
|
||||
} while (false);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle deletion of noes using the Delete key
|
||||
{
|
||||
const int selectedNodeCount = ImNodes::NumSelectedNodes();
|
||||
if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
|
||||
std::vector<int> selectedNodes;
|
||||
selectedNodes.resize(static_cast<size_t>(selectedNodeCount));
|
||||
ImNodes::GetSelectedNodes(selectedNodes.data());
|
||||
ImNodes::ClearNodeSelection();
|
||||
// Handle deletion of links using the Delete key
|
||||
{
|
||||
const int selectedLinkCount = ImNodes::NumSelectedLinks();
|
||||
if (selectedLinkCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
|
||||
std::vector<int> selectedLinks;
|
||||
selectedLinks.resize(static_cast<size_t>(selectedLinkCount));
|
||||
ImNodes::GetSelectedLinks(selectedLinks.data());
|
||||
ImNodes::ClearLinkSelection();
|
||||
|
||||
this->eraseNodes(workspace, selectedNodes);
|
||||
for (const int id : selectedLinks) {
|
||||
eraseLink(workspace, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// Handle deletion of noes using the Delete key
|
||||
{
|
||||
const int selectedNodeCount = ImNodes::NumSelectedNodes();
|
||||
if (selectedNodeCount > 0 && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) {
|
||||
std::vector<int> selectedNodes;
|
||||
selectedNodes.resize(static_cast<size_t>(selectedNodeCount));
|
||||
ImNodes::GetSelectedNodes(selectedNodes.data());
|
||||
ImNodes::ClearNodeSelection();
|
||||
|
||||
this->eraseNodes(workspace, selectedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the top-most workspace from the stack if requested
|
||||
if (popWorkspace) {
|
||||
|
@ -15,7 +15,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
}
|
||||
|
||||
ViewDiff::ViewDiff() : View("hex.builtin.view.diff.name") {
|
||||
ViewDiff::ViewDiff() : View::Window("hex.builtin.view.diff.name") {
|
||||
|
||||
// Clear the selected diff providers when a provider is closed
|
||||
EventManager::subscribe<EventProviderClosed>(this, [this](prv::Provider *) {
|
||||
@ -183,147 +183,142 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewDiff::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.diff.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
auto &[a, b] = this->m_columns;
|
||||
|
||||
auto &[a, b] = this->m_columns;
|
||||
a.hexEditor.enableSyncScrolling(false);
|
||||
b.hexEditor.enableSyncScrolling(false);
|
||||
|
||||
a.hexEditor.enableSyncScrolling(false);
|
||||
b.hexEditor.enableSyncScrolling(false);
|
||||
if (a.scrollLock > 0) a.scrollLock--;
|
||||
if (b.scrollLock > 0) b.scrollLock--;
|
||||
|
||||
if (a.scrollLock > 0) a.scrollLock--;
|
||||
if (b.scrollLock > 0) b.scrollLock--;
|
||||
|
||||
// Change the hex editor providers if the user selected a new provider
|
||||
{
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
if (a.provider >= 0 && size_t(a.provider) < providers.size())
|
||||
a.hexEditor.setProvider(providers[a.provider]);
|
||||
else
|
||||
a.hexEditor.setProvider(nullptr);
|
||||
|
||||
if (b.provider >= 0 && size_t(b.provider) < providers.size())
|
||||
b.hexEditor.setProvider(providers[b.provider]);
|
||||
else
|
||||
b.hexEditor.setProvider(nullptr);
|
||||
}
|
||||
|
||||
// Analyze the providers if they are valid and the user selected a new provider
|
||||
if (!this->m_analyzed && a.provider != -1 && b.provider != -1 && !this->m_diffTask.isRunning()) {
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
auto providerA = providers[a.provider];
|
||||
auto providerB = providers[b.provider];
|
||||
|
||||
this->analyze(providerA, providerB);
|
||||
}
|
||||
|
||||
const auto height = ImGui::GetContentRegionAvail().y;
|
||||
|
||||
// Draw the two hex editor columns side by side
|
||||
if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::BeginDisabled(this->m_diffTask.isRunning());
|
||||
{
|
||||
// Draw first provider selector
|
||||
ImGui::TableNextColumn();
|
||||
if (drawProviderSelector(a)) this->m_analyzed = false;
|
||||
|
||||
// Draw second provider selector
|
||||
ImGui::TableNextColumn();
|
||||
if (drawProviderSelector(b)) this->m_analyzed = false;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Draw first hex editor column
|
||||
ImGui::TableNextColumn();
|
||||
bool scrollB = drawDiffColumn(a, height - 250_scaled);
|
||||
|
||||
// Draw second hex editor column
|
||||
ImGui::TableNextColumn();
|
||||
bool scrollA = drawDiffColumn(b, height - 250_scaled);
|
||||
|
||||
// Sync the scroll positions of the hex editors
|
||||
{
|
||||
if (scrollA && a.scrollLock == 0) {
|
||||
a.hexEditor.setScrollPosition(b.hexEditor.getScrollPosition());
|
||||
a.hexEditor.forceUpdateScrollPosition();
|
||||
}
|
||||
if (scrollB && b.scrollLock == 0) {
|
||||
b.hexEditor.setScrollPosition(a.hexEditor.getScrollPosition());
|
||||
b.hexEditor.forceUpdateScrollPosition();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Draw the differences table
|
||||
if (ImGui::BeginTable("##differences", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable, ImVec2(0, 200_scaled))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.begin"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.end"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.type"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Draw the differences if the providers have been analyzed
|
||||
if (this->m_analyzed) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(int(this->m_diffs.size()));
|
||||
|
||||
while (clipper.Step())
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Prevent the list from trying to access non-existing diffs
|
||||
if (size_t(i) >= this->m_diffs.size())
|
||||
break;
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
const auto &diff = this->m_diffs[i];
|
||||
|
||||
// Draw a clickable row for each difference that will select the difference in both hex editors
|
||||
|
||||
// Draw start address
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(hex::format("0x{:02X}", diff.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
a.hexEditor.setSelection(diff.region);
|
||||
a.hexEditor.jumpToSelection();
|
||||
b.hexEditor.setSelection(diff.region);
|
||||
b.hexEditor.jumpToSelection();
|
||||
}
|
||||
|
||||
// Draw end address
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(hex::format("0x{:02X}", diff.region.getEndAddress()).c_str());
|
||||
|
||||
// Draw difference type
|
||||
ImGui::TableNextColumn();
|
||||
switch (diff.type) {
|
||||
case DifferenceType::Modified:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffChanged), "hex.builtin.view.diff.modified"_lang);
|
||||
break;
|
||||
case DifferenceType::Added:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffAdded), "hex.builtin.view.diff.added"_lang);
|
||||
break;
|
||||
case DifferenceType::Removed:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffRemoved), "hex.builtin.view.diff.removed"_lang);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
// Change the hex editor providers if the user selected a new provider
|
||||
{
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
if (a.provider >= 0 && size_t(a.provider) < providers.size())
|
||||
a.hexEditor.setProvider(providers[a.provider]);
|
||||
else
|
||||
a.hexEditor.setProvider(nullptr);
|
||||
|
||||
if (b.provider >= 0 && size_t(b.provider) < providers.size())
|
||||
b.hexEditor.setProvider(providers[b.provider]);
|
||||
else
|
||||
b.hexEditor.setProvider(nullptr);
|
||||
}
|
||||
|
||||
// Analyze the providers if they are valid and the user selected a new provider
|
||||
if (!this->m_analyzed && a.provider != -1 && b.provider != -1 && !this->m_diffTask.isRunning()) {
|
||||
const auto &providers = ImHexApi::Provider::getProviders();
|
||||
auto providerA = providers[a.provider];
|
||||
auto providerB = providers[b.provider];
|
||||
|
||||
this->analyze(providerA, providerB);
|
||||
}
|
||||
|
||||
const auto height = ImGui::GetContentRegionAvail().y;
|
||||
|
||||
// Draw the two hex editor columns side by side
|
||||
if (ImGui::BeginTable("##binary_diff", 2, ImGuiTableFlags_None, ImVec2(0, height - 250_scaled))) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_a"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.diff.provider_b"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::BeginDisabled(this->m_diffTask.isRunning());
|
||||
{
|
||||
// Draw first provider selector
|
||||
ImGui::TableNextColumn();
|
||||
if (drawProviderSelector(a)) this->m_analyzed = false;
|
||||
|
||||
// Draw second provider selector
|
||||
ImGui::TableNextColumn();
|
||||
if (drawProviderSelector(b)) this->m_analyzed = false;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Draw first hex editor column
|
||||
ImGui::TableNextColumn();
|
||||
bool scrollB = drawDiffColumn(a, height - 250_scaled);
|
||||
|
||||
// Draw second hex editor column
|
||||
ImGui::TableNextColumn();
|
||||
bool scrollA = drawDiffColumn(b, height - 250_scaled);
|
||||
|
||||
// Sync the scroll positions of the hex editors
|
||||
{
|
||||
if (scrollA && a.scrollLock == 0) {
|
||||
a.hexEditor.setScrollPosition(b.hexEditor.getScrollPosition());
|
||||
a.hexEditor.forceUpdateScrollPosition();
|
||||
}
|
||||
if (scrollB && b.scrollLock == 0) {
|
||||
b.hexEditor.setScrollPosition(a.hexEditor.getScrollPosition());
|
||||
b.hexEditor.forceUpdateScrollPosition();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Draw the differences table
|
||||
if (ImGui::BeginTable("##differences", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Resizable, ImVec2(0, 200_scaled))) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.begin"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.end"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.type"_lang);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
// Draw the differences if the providers have been analyzed
|
||||
if (this->m_analyzed) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(int(this->m_diffs.size()));
|
||||
|
||||
while (clipper.Step())
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TableNextRow();
|
||||
|
||||
// Prevent the list from trying to access non-existing diffs
|
||||
if (size_t(i) >= this->m_diffs.size())
|
||||
break;
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
const auto &diff = this->m_diffs[i];
|
||||
|
||||
// Draw a clickable row for each difference that will select the difference in both hex editors
|
||||
|
||||
// Draw start address
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(hex::format("0x{:02X}", diff.region.getStartAddress()).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
a.hexEditor.setSelection(diff.region);
|
||||
a.hexEditor.jumpToSelection();
|
||||
b.hexEditor.setSelection(diff.region);
|
||||
b.hexEditor.jumpToSelection();
|
||||
}
|
||||
|
||||
// Draw end address
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(hex::format("0x{:02X}", diff.region.getEndAddress()).c_str());
|
||||
|
||||
// Draw difference type
|
||||
ImGui::TableNextColumn();
|
||||
switch (diff.type) {
|
||||
case DifferenceType::Modified:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffChanged), "hex.builtin.view.diff.modified"_lang);
|
||||
break;
|
||||
case DifferenceType::Added:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffAdded), "hex.builtin.view.diff.added"_lang);
|
||||
break;
|
||||
case DifferenceType::Removed:
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_DiffRemoved), "hex.builtin.view.diff.removed"_lang);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
@ -9,7 +9,7 @@ using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewDisassembler::ViewDisassembler() : View("hex.builtin.view.disassembler.name") {
|
||||
ViewDisassembler::ViewDisassembler() : View::Window("hex.builtin.view.disassembler.name") {
|
||||
EventManager::subscribe<EventProviderDeleted>(this, [this](const auto*) {
|
||||
this->m_disassembly.clear();
|
||||
});
|
||||
@ -91,366 +91,361 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewDisassembler::drawContent() {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
ImGuiExt::Header("hex.builtin.view.disassembler.position"_lang, true);
|
||||
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.disassembler.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
// Draw base address input
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
ImGuiExt::Header("hex.builtin.view.disassembler.position"_lang, true);
|
||||
// Draw region selection picker
|
||||
ui::regionSelectionPicker(&this->m_codeRegion, provider, &this->m_range);
|
||||
|
||||
// Draw base address input
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
// Draw settings
|
||||
{
|
||||
ImGuiExt::Header("hex.builtin.common.settings"_lang);
|
||||
|
||||
// Draw region selection picker
|
||||
ui::regionSelectionPicker(&this->m_codeRegion, provider, &this->m_range);
|
||||
// Draw architecture selector
|
||||
if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast<int *>(&this->m_architecture), Disassembler::ArchitectureNames.data(), Disassembler::getArchitectureSupportedCount()))
|
||||
this->m_mode = cs_mode(0);
|
||||
|
||||
// Draw settings
|
||||
{
|
||||
ImGuiExt::Header("hex.builtin.common.settings"_lang);
|
||||
// Draw sub-settings for each architecture
|
||||
if (ImGuiExt::BeginBox()) {
|
||||
|
||||
// Draw architecture selector
|
||||
if (ImGui::Combo("hex.builtin.view.disassembler.arch"_lang, reinterpret_cast<int *>(&this->m_architecture), Disassembler::ArchitectureNames.data(), Disassembler::getArchitectureSupportedCount()))
|
||||
this->m_mode = cs_mode(0);
|
||||
|
||||
// Draw sub-settings for each architecture
|
||||
if (ImGuiExt::BeginBox()) {
|
||||
|
||||
// Draw endian radio buttons. This setting is available for all architectures
|
||||
static int littleEndian = true;
|
||||
ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false);
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
// Draw architecture specific settings
|
||||
switch (this->m_architecture) {
|
||||
case Architecture::ARM:
|
||||
{
|
||||
static int mode = CS_MODE_ARM;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB);
|
||||
|
||||
static int extraMode = 0;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8);
|
||||
|
||||
this->m_mode = cs_mode(mode | extraMode);
|
||||
}
|
||||
break;
|
||||
case Architecture::MIPS:
|
||||
{
|
||||
static int mode = CS_MODE_MIPS32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6);
|
||||
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3);
|
||||
|
||||
static bool microMode;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode);
|
||||
|
||||
this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
case Architecture::X86:
|
||||
{
|
||||
static int mode = CS_MODE_32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64);
|
||||
|
||||
this->m_mode = cs_mode(mode);
|
||||
}
|
||||
break;
|
||||
case Architecture::PPC:
|
||||
{
|
||||
static int mode = CS_MODE_32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64);
|
||||
|
||||
static bool qpx = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx);
|
||||
|
||||
#if CS_API_MAJOR >= 5
|
||||
static bool spe = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe);
|
||||
static bool booke = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke);
|
||||
|
||||
this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0)));
|
||||
#else
|
||||
this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case Architecture::SPARC:
|
||||
{
|
||||
static bool v9Mode = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode);
|
||||
|
||||
this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0));
|
||||
}
|
||||
break;
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::RISCV:
|
||||
{
|
||||
static int mode = CS_MODE_RISCV32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64);
|
||||
|
||||
static bool compressed = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed);
|
||||
|
||||
this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case Architecture::M68K:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000},
|
||||
{ "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010},
|
||||
{ "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020},
|
||||
{ "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030},
|
||||
{ "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040},
|
||||
{ "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
case Architecture::M680X:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 },
|
||||
{ "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12},
|
||||
{ "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::MOS65XX:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::BPF:
|
||||
{
|
||||
static int mode = CS_MODE_BPF_CLASSIC;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED);
|
||||
|
||||
this->m_mode = cs_mode(mode);
|
||||
}
|
||||
break;
|
||||
case Architecture::SH:
|
||||
{
|
||||
static u32 selectionMode = 0;
|
||||
static bool fpu = false;
|
||||
static bool dsp = false;
|
||||
|
||||
std::pair<const char*, cs_mode> modes[] = {
|
||||
{ "hex.builtin.view.disassembler.sh.sh2"_lang, CS_MODE_SH2 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh2a"_lang, CS_MODE_SH2A },
|
||||
{ "hex.builtin.view.disassembler.sh.sh3"_lang, CS_MODE_SH3 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh4"_lang, CS_MODE_SH4 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh4a"_lang, CS_MODE_SH4A },
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectionMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sh.fpu"_lang, &fpu);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sh.dsp"_lang, &dsp);
|
||||
|
||||
this->m_mode = cs_mode(modes[selectionMode].second | (fpu ? CS_MODE_SHFPU : cs_mode(0)) | (dsp ? CS_MODE_SHDSP : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
case Architecture::TRICORE:
|
||||
{
|
||||
static u32 selectionMode = 0;
|
||||
|
||||
std::pair<const char*, cs_mode> modes[] = {
|
||||
{ "hex.builtin.view.disassembler.tricore.110"_lang, CS_MODE_TRICORE_110 },
|
||||
{ "hex.builtin.view.disassembler.tricore.120"_lang, CS_MODE_TRICORE_120 },
|
||||
{ "hex.builtin.view.disassembler.tricore.130"_lang, CS_MODE_TRICORE_130 },
|
||||
{ "hex.builtin.view.disassembler.tricore.131"_lang, CS_MODE_TRICORE_131 },
|
||||
{ "hex.builtin.view.disassembler.tricore.160"_lang, CS_MODE_TRICORE_160 },
|
||||
{ "hex.builtin.view.disassembler.tricore.161"_lang, CS_MODE_TRICORE_161 },
|
||||
{ "hex.builtin.view.disassembler.tricore.162"_lang, CS_MODE_TRICORE_162 },
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectionMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectionMode].second);
|
||||
}
|
||||
break;
|
||||
case Architecture::WASM:
|
||||
#endif
|
||||
case Architecture::EVM:
|
||||
case Architecture::TMS320C64X:
|
||||
case Architecture::ARM64:
|
||||
case Architecture::SYSZ:
|
||||
case Architecture::XCORE:
|
||||
this->m_mode = cs_mode(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGuiExt::EndBox();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw disassemble button
|
||||
ImGui::BeginDisabled(this->m_disassemblerTask.isRunning());
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang))
|
||||
this->disassemble();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Draw a spinner if the disassembler is running
|
||||
if (this->m_disassemblerTask.isRunning()) {
|
||||
// Draw endian radio buttons. This setting is available for all architectures
|
||||
static int littleEndian = true;
|
||||
ImGui::RadioButton("hex.builtin.common.little_endian"_lang, &littleEndian, true);
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang);
|
||||
}
|
||||
ImGui::RadioButton("hex.builtin.common.big_endian"_lang, &littleEndian, false);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang);
|
||||
ImGui::Separator();
|
||||
|
||||
// Draw disassembly table
|
||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang);
|
||||
|
||||
if (!this->m_disassemblerTask.isRunning()) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_disassembly.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &instruction = this->m_disassembly[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw a selectable label for the address
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable("##DisassemblyLine", false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// Draw instruction address
|
||||
// Draw architecture specific settings
|
||||
switch (this->m_architecture) {
|
||||
case Architecture::ARM:
|
||||
{
|
||||
static int mode = CS_MODE_ARM;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.arm"_lang, &mode, CS_MODE_ARM);
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("0x{0:X}", instruction.address);
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.thumb"_lang, &mode, CS_MODE_THUMB);
|
||||
|
||||
// Draw instruction offset
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:X}", instruction.offset);
|
||||
|
||||
// Draw instruction bytes
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(instruction.bytes.c_str());
|
||||
|
||||
// Draw instruction mnemonic and operands
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic);
|
||||
static int extraMode = 0;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.default"_lang, &extraMode, 0);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(instruction.operators.c_str());
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.cortex_m"_lang, &extraMode, CS_MODE_MCLASS);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.arm.armv8"_lang, &extraMode, CS_MODE_V8);
|
||||
|
||||
this->m_mode = cs_mode(mode | extraMode);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Architecture::MIPS:
|
||||
{
|
||||
static int mode = CS_MODE_MIPS32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32"_lang, &mode, CS_MODE_MIPS32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips64"_lang, &mode, CS_MODE_MIPS64);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips32R6"_lang, &mode, CS_MODE_MIPS32R6);
|
||||
|
||||
clipper.End();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips2"_lang, &mode, CS_MODE_MIPS2);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.mips.mips3"_lang, &mode, CS_MODE_MIPS3);
|
||||
|
||||
static bool microMode;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.mips.micro"_lang, µMode);
|
||||
|
||||
this->m_mode = cs_mode(mode | (microMode ? CS_MODE_MICRO : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
case Architecture::X86:
|
||||
{
|
||||
static int mode = CS_MODE_32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.16bit"_lang, &mode, CS_MODE_16);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64);
|
||||
|
||||
this->m_mode = cs_mode(mode);
|
||||
}
|
||||
break;
|
||||
case Architecture::PPC:
|
||||
{
|
||||
static int mode = CS_MODE_32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_64);
|
||||
|
||||
static bool qpx = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.qpx"_lang, &qpx);
|
||||
|
||||
#if CS_API_MAJOR >= 5
|
||||
static bool spe = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.spe"_lang, &spe);
|
||||
static bool booke = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.ppc.booke"_lang, &booke);
|
||||
|
||||
this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)) | (spe ? CS_MODE_SPE : cs_mode(0)) | (booke ? CS_MODE_BOOKE : cs_mode(0)));
|
||||
#else
|
||||
this->m_mode = cs_mode(mode | (qpx ? CS_MODE_QPX : cs_mode(0)));
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case Architecture::SPARC:
|
||||
{
|
||||
static bool v9Mode = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sparc.v9"_lang, &v9Mode);
|
||||
|
||||
this->m_mode = cs_mode(v9Mode ? CS_MODE_V9 : cs_mode(0));
|
||||
}
|
||||
break;
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::RISCV:
|
||||
{
|
||||
static int mode = CS_MODE_RISCV32;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.32bit"_lang, &mode, CS_MODE_RISCV32);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.64bit"_lang, &mode, CS_MODE_RISCV64);
|
||||
|
||||
static bool compressed = false;
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.riscv.compressed"_lang, &compressed);
|
||||
|
||||
this->m_mode = cs_mode(mode | (compressed ? CS_MODE_RISCVC : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case Architecture::M68K:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.m68k.000"_lang, CS_MODE_M68K_000},
|
||||
{ "hex.builtin.view.disassembler.m68k.010"_lang, CS_MODE_M68K_010},
|
||||
{ "hex.builtin.view.disassembler.m68k.020"_lang, CS_MODE_M68K_020},
|
||||
{ "hex.builtin.view.disassembler.m68k.030"_lang, CS_MODE_M68K_030},
|
||||
{ "hex.builtin.view.disassembler.m68k.040"_lang, CS_MODE_M68K_040},
|
||||
{ "hex.builtin.view.disassembler.m68k.060"_lang, CS_MODE_M68K_060},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
case Architecture::M680X:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.m680x.6301"_lang, CS_MODE_M680X_6301 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6309"_lang, CS_MODE_M680X_6309 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6800"_lang, CS_MODE_M680X_6800 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6801"_lang, CS_MODE_M680X_6801 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6805"_lang, CS_MODE_M680X_6805 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6808"_lang, CS_MODE_M680X_6808 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6809"_lang, CS_MODE_M680X_6809 },
|
||||
{ "hex.builtin.view.disassembler.m680x.6811"_lang, CS_MODE_M680X_6811 },
|
||||
{ "hex.builtin.view.disassembler.m680x.cpu12"_lang, CS_MODE_M680X_CPU12},
|
||||
{ "hex.builtin.view.disassembler.m680x.hcs08"_lang, CS_MODE_M680X_HCS08},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::MOS65XX:
|
||||
{
|
||||
static int selectedMode = 0;
|
||||
|
||||
std::pair<const char *, cs_mode> modes[] = {
|
||||
{"hex.builtin.view.disassembler.mos65xx.6502"_lang, CS_MODE_MOS65XX_6502 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65c02"_lang, CS_MODE_MOS65XX_65C02 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.w65c02"_lang, CS_MODE_MOS65XX_W65C02 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816"_lang, CS_MODE_MOS65XX_65816 },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_m"_lang, CS_MODE_MOS65XX_65816_LONG_M },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_x"_lang, CS_MODE_MOS65XX_65816_LONG_X },
|
||||
{ "hex.builtin.view.disassembler.mos65xx.65816_long_mx"_lang, CS_MODE_MOS65XX_65816_LONG_MX},
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectedMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectedMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectedMode].second);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CS_API_MAJOR >= 5
|
||||
case Architecture::BPF:
|
||||
{
|
||||
static int mode = CS_MODE_BPF_CLASSIC;
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.bpf.classic"_lang, &mode, CS_MODE_BPF_CLASSIC);
|
||||
ImGui::SameLine();
|
||||
ImGui::RadioButton("hex.builtin.view.disassembler.bpf.extended"_lang, &mode, CS_MODE_BPF_EXTENDED);
|
||||
|
||||
this->m_mode = cs_mode(mode);
|
||||
}
|
||||
break;
|
||||
case Architecture::SH:
|
||||
{
|
||||
static u32 selectionMode = 0;
|
||||
static bool fpu = false;
|
||||
static bool dsp = false;
|
||||
|
||||
std::pair<const char*, cs_mode> modes[] = {
|
||||
{ "hex.builtin.view.disassembler.sh.sh2"_lang, CS_MODE_SH2 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh2a"_lang, CS_MODE_SH2A },
|
||||
{ "hex.builtin.view.disassembler.sh.sh3"_lang, CS_MODE_SH3 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh4"_lang, CS_MODE_SH4 },
|
||||
{ "hex.builtin.view.disassembler.sh.sh4a"_lang, CS_MODE_SH4A },
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectionMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sh.fpu"_lang, &fpu);
|
||||
ImGui::SameLine();
|
||||
ImGui::Checkbox("hex.builtin.view.disassembler.sh.dsp"_lang, &dsp);
|
||||
|
||||
this->m_mode = cs_mode(modes[selectionMode].second | (fpu ? CS_MODE_SHFPU : cs_mode(0)) | (dsp ? CS_MODE_SHDSP : cs_mode(0)));
|
||||
}
|
||||
break;
|
||||
case Architecture::TRICORE:
|
||||
{
|
||||
static u32 selectionMode = 0;
|
||||
|
||||
std::pair<const char*, cs_mode> modes[] = {
|
||||
{ "hex.builtin.view.disassembler.tricore.110"_lang, CS_MODE_TRICORE_110 },
|
||||
{ "hex.builtin.view.disassembler.tricore.120"_lang, CS_MODE_TRICORE_120 },
|
||||
{ "hex.builtin.view.disassembler.tricore.130"_lang, CS_MODE_TRICORE_130 },
|
||||
{ "hex.builtin.view.disassembler.tricore.131"_lang, CS_MODE_TRICORE_131 },
|
||||
{ "hex.builtin.view.disassembler.tricore.160"_lang, CS_MODE_TRICORE_160 },
|
||||
{ "hex.builtin.view.disassembler.tricore.161"_lang, CS_MODE_TRICORE_161 },
|
||||
{ "hex.builtin.view.disassembler.tricore.162"_lang, CS_MODE_TRICORE_162 },
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.view.disassembler.settings.mode"_lang, modes[selectionMode].first)) {
|
||||
for (u32 i = 0; i < IM_ARRAYSIZE(modes); i++) {
|
||||
if (ImGui::Selectable(modes[i].first))
|
||||
selectionMode = i;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
this->m_mode = cs_mode(modes[selectionMode].second);
|
||||
}
|
||||
break;
|
||||
case Architecture::WASM:
|
||||
#endif
|
||||
case Architecture::EVM:
|
||||
case Architecture::TMS320C64X:
|
||||
case Architecture::ARM64:
|
||||
case Architecture::SYSZ:
|
||||
case Architecture::XCORE:
|
||||
this->m_mode = cs_mode(0);
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGuiExt::EndBox();
|
||||
}
|
||||
}
|
||||
|
||||
// Draw disassemble button
|
||||
ImGui::BeginDisabled(this->m_disassemblerTask.isRunning());
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.disassembler.disassemble"_lang))
|
||||
this->disassemble();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
// Draw a spinner if the disassembler is running
|
||||
if (this->m_disassemblerTask.isRunning()) {
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.disassembler.disassembling"_lang);
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::TextUnformatted("hex.builtin.view.disassembler.disassembly.title"_lang);
|
||||
ImGui::Separator();
|
||||
|
||||
// Draw disassembly table
|
||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.address"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.offset"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.bytes"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.disassembler.disassembly.title"_lang);
|
||||
|
||||
if (!this->m_disassemblerTask.isRunning()) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_disassembly.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &instruction = this->m_disassembly[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
// Draw a selectable label for the address
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable("##DisassemblyLine", false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
ImHexApi::HexEditor::setSelection(instruction.offset, instruction.size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// Draw instruction address
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("0x{0:X}", instruction.address);
|
||||
|
||||
// Draw instruction offset
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:X}", instruction.offset);
|
||||
|
||||
// Draw instruction bytes
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(instruction.bytes.c_str());
|
||||
|
||||
// Draw instruction mnemonic and operands
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImColor(0xFFD69C56), "{}", instruction.mnemonic);
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(instruction.operators.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewFind::ViewFind() : View("hex.builtin.view.find.name") {
|
||||
ViewFind::ViewFind() : View::Window("hex.builtin.view.find.name") {
|
||||
const static auto HighlightColor = [] { return (ImGuiExt::GetCustomColorU32(ImGuiCustomCol_FindHighlight) & 0x00FFFFFF) | 0x70000000; };
|
||||
|
||||
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size, bool) -> std::optional<color_t> {
|
||||
@ -599,347 +599,343 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewFind::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.find.name").c_str(), &this->getWindowOpenState())) {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
ImGui::BeginDisabled(this->m_searchTask.isRunning());
|
||||
{
|
||||
ui::regionSelectionPicker(&this->m_searchSettings.region, provider, &this->m_searchSettings.range, true, true);
|
||||
ImGui::BeginDisabled(this->m_searchTask.isRunning());
|
||||
{
|
||||
ui::regionSelectionPicker(&this->m_searchSettings.region, provider, &this->m_searchSettings.range, true, true);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginTabBar("SearchMethods")) {
|
||||
const std::array<std::string, 5> StringTypes = {
|
||||
"hex.builtin.common.encoding.ascii"_lang,
|
||||
"hex.builtin.common.encoding.utf16le"_lang,
|
||||
"hex.builtin.common.encoding.utf16be"_lang,
|
||||
hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16le"_lang),
|
||||
hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16be"_lang)
|
||||
};
|
||||
if (ImGui::BeginTabBar("SearchMethods")) {
|
||||
const std::array<std::string, 5> StringTypes = {
|
||||
"hex.builtin.common.encoding.ascii"_lang,
|
||||
"hex.builtin.common.encoding.utf16le"_lang,
|
||||
"hex.builtin.common.encoding.utf16be"_lang,
|
||||
hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16le"_lang),
|
||||
hex::format("{} + {}", "hex.builtin.common.encoding.ascii"_lang, "hex.builtin.common.encoding.utf16be"_lang)
|
||||
};
|
||||
|
||||
auto &mode = this->m_searchSettings.mode;
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.strings"_lang)) {
|
||||
auto &settings = this->m_searchSettings.strings;
|
||||
mode = SearchSettings::Mode::Strings;
|
||||
auto &mode = this->m_searchSettings.mode;
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.strings"_lang)) {
|
||||
auto &settings = this->m_searchSettings.strings;
|
||||
mode = SearchSettings::Mode::Strings;
|
||||
|
||||
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
||||
if (settings.minLength < 1)
|
||||
settings.minLength = 1;
|
||||
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
||||
if (settings.minLength < 1)
|
||||
settings.minLength = 1;
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < StringTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::StringType>(i);
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < StringTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::StringType>(i);
|
||||
|
||||
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
||||
settings.type = type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
||||
settings.type = type;
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("hex.builtin.view.find.strings.match_settings"_lang)) {
|
||||
ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination);
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.find.strings.chars"_lang);
|
||||
ImGui::Checkbox(hex::format("{} [a-z]", "hex.builtin.view.find.strings.lower_case"_lang.get()).c_str(), &settings.lowerCaseLetters);
|
||||
ImGui::Checkbox(hex::format("{} [A-Z]", "hex.builtin.view.find.strings.upper_case"_lang.get()).c_str(), &settings.upperCaseLetters);
|
||||
ImGui::Checkbox(hex::format("{} [0-9]", "hex.builtin.view.find.strings.numbers"_lang.get()).c_str(), &settings.numbers);
|
||||
ImGui::Checkbox(hex::format("{} [_]", "hex.builtin.view.find.strings.underscores"_lang.get()).c_str(), &settings.underscores);
|
||||
ImGui::Checkbox(hex::format("{} [!\"#$%...]", "hex.builtin.view.find.strings.symbols"_lang.get()).c_str(), &settings.symbols);
|
||||
ImGui::Checkbox(hex::format("{} [ \\f\\t\\v]", "hex.builtin.view.find.strings.spaces"_lang.get()).c_str(), &settings.spaces);
|
||||
ImGui::Checkbox(hex::format("{} [\\r\\n]", "hex.builtin.view.find.strings.line_feeds"_lang.get()).c_str(), &settings.lineFeeds);
|
||||
}
|
||||
|
||||
this->m_settingsValid = true;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.sequences"_lang)) {
|
||||
auto &settings = this->m_searchSettings.bytes;
|
||||
|
||||
mode = SearchSettings::Mode::Sequence;
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_KEY, settings.sequence);
|
||||
|
||||
this->m_settingsValid = !settings.sequence.empty() && !hex::decodeByteString(settings.sequence).empty();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.regex"_lang)) {
|
||||
auto &settings = this->m_searchSettings.regex;
|
||||
|
||||
mode = SearchSettings::Mode::Regex;
|
||||
|
||||
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
||||
if (settings.minLength < 1)
|
||||
settings.minLength = 1;
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < StringTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::StringType>(i);
|
||||
|
||||
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
||||
settings.type = type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("hex.builtin.view.find.strings.match_settings"_lang)) {
|
||||
ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGuiExt::Header("hex.builtin.view.find.strings.chars"_lang);
|
||||
ImGui::Checkbox(hex::format("{} [a-z]", "hex.builtin.view.find.strings.lower_case"_lang.get()).c_str(), &settings.lowerCaseLetters);
|
||||
ImGui::Checkbox(hex::format("{} [A-Z]", "hex.builtin.view.find.strings.upper_case"_lang.get()).c_str(), &settings.upperCaseLetters);
|
||||
ImGui::Checkbox(hex::format("{} [0-9]", "hex.builtin.view.find.strings.numbers"_lang.get()).c_str(), &settings.numbers);
|
||||
ImGui::Checkbox(hex::format("{} [_]", "hex.builtin.view.find.strings.underscores"_lang.get()).c_str(), &settings.underscores);
|
||||
ImGui::Checkbox(hex::format("{} [!\"#$%...]", "hex.builtin.view.find.strings.symbols"_lang.get()).c_str(), &settings.symbols);
|
||||
ImGui::Checkbox(hex::format("{} [ \\f\\t\\v]", "hex.builtin.view.find.strings.spaces"_lang.get()).c_str(), &settings.spaces);
|
||||
ImGui::Checkbox(hex::format("{} [\\r\\n]", "hex.builtin.view.find.strings.line_feeds"_lang.get()).c_str(), &settings.lineFeeds);
|
||||
}
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.find.regex.pattern"_lang, ICON_VS_REGEX, settings.pattern);
|
||||
this->m_settingsValid = true;
|
||||
|
||||
try {
|
||||
std::regex regex(settings.pattern);
|
||||
this->m_settingsValid = true;
|
||||
} catch (const std::regex_error &) {
|
||||
this->m_settingsValid = false;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.sequences"_lang)) {
|
||||
auto &settings = this->m_searchSettings.bytes;
|
||||
|
||||
mode = SearchSettings::Mode::Sequence;
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_KEY, settings.sequence);
|
||||
|
||||
this->m_settingsValid = !settings.sequence.empty() && !hex::decodeByteString(settings.sequence).empty();
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.regex"_lang)) {
|
||||
auto &settings = this->m_searchSettings.regex;
|
||||
|
||||
mode = SearchSettings::Mode::Regex;
|
||||
|
||||
ImGui::InputInt("hex.builtin.view.find.strings.min_length"_lang, &settings.minLength, 1, 1);
|
||||
if (settings.minLength < 1)
|
||||
settings.minLength = 1;
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, StringTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < StringTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::StringType>(i);
|
||||
|
||||
if (ImGui::Selectable(StringTypes[i].c_str(), type == settings.type))
|
||||
settings.type = type;
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.find.strings.null_term"_lang, &settings.nullTermination);
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.find.regex.pattern"_lang, ICON_VS_REGEX, settings.pattern);
|
||||
|
||||
try {
|
||||
std::regex regex(settings.pattern);
|
||||
this->m_settingsValid = true;
|
||||
} catch (const std::regex_error &) {
|
||||
this->m_settingsValid = false;
|
||||
}
|
||||
|
||||
if (settings.pattern.empty())
|
||||
this->m_settingsValid = false;
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.find.regex.full_match"_lang, &settings.fullMatch);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) {
|
||||
auto &settings = this->m_searchSettings.binaryPattern;
|
||||
|
||||
mode = SearchSettings::Mode::BinaryPattern;
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, settings.input);
|
||||
|
||||
constexpr static u32 min = 1, max = 0x1000;
|
||||
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
|
||||
|
||||
settings.pattern = hex::BinaryPattern(settings.input);
|
||||
this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.value"_lang)) {
|
||||
auto &settings = this->m_searchSettings.value;
|
||||
|
||||
mode = SearchSettings::Mode::Value;
|
||||
|
||||
bool edited = false;
|
||||
|
||||
if (settings.range) {
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.min"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) edited = true;
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.max"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMax)) edited = true;
|
||||
} else {
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) {
|
||||
edited = true;
|
||||
settings.inputMax = settings.inputMin;
|
||||
}
|
||||
|
||||
if (settings.pattern.empty())
|
||||
this->m_settingsValid = false;
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.find.regex.full_match"_lang, &settings.fullMatch);
|
||||
|
||||
ImGui::EndTabItem();
|
||||
ImGui::BeginDisabled();
|
||||
ImGuiExt::InputTextIcon("##placeholder_value", ICON_VS_SYMBOL_NUMERIC, settings.inputMax);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.binary_pattern"_lang)) {
|
||||
auto &settings = this->m_searchSettings.binaryPattern;
|
||||
|
||||
mode = SearchSettings::Mode::BinaryPattern;
|
||||
ImGui::Checkbox("hex.builtin.view.find.value.range"_lang, &settings.range);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.find.binary_pattern"_lang, ICON_VS_SYMBOL_NAMESPACE, settings.input);
|
||||
const std::array<std::string, 10> InputTypes = {
|
||||
"hex.builtin.common.type.u8"_lang,
|
||||
"hex.builtin.common.type.u16"_lang,
|
||||
"hex.builtin.common.type.u32"_lang,
|
||||
"hex.builtin.common.type.u64"_lang,
|
||||
"hex.builtin.common.type.i8"_lang,
|
||||
"hex.builtin.common.type.i16"_lang,
|
||||
"hex.builtin.common.type.i32"_lang,
|
||||
"hex.builtin.common.type.i64"_lang,
|
||||
"hex.builtin.common.type.f32"_lang,
|
||||
"hex.builtin.common.type.f64"_lang
|
||||
};
|
||||
|
||||
constexpr static u32 min = 1, max = 0x1000;
|
||||
ImGui::SliderScalar("hex.builtin.view.find.binary_pattern.alignment"_lang, ImGuiDataType_U32, &settings.alignment, &min, &max);
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < InputTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::Value::Type>(i);
|
||||
|
||||
settings.pattern = hex::BinaryPattern(settings.input);
|
||||
this->m_settingsValid = settings.pattern.isValid() && settings.alignment > 0;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.find.value"_lang)) {
|
||||
auto &settings = this->m_searchSettings.value;
|
||||
|
||||
mode = SearchSettings::Mode::Value;
|
||||
|
||||
bool edited = false;
|
||||
|
||||
if (settings.range) {
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.min"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) edited = true;
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.view.find.value.max"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMax)) edited = true;
|
||||
} else {
|
||||
if (ImGuiExt::InputTextIcon("hex.builtin.common.value"_lang, ICON_VS_SYMBOL_NUMERIC, settings.inputMin)) {
|
||||
if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) {
|
||||
settings.type = type;
|
||||
edited = true;
|
||||
settings.inputMax = settings.inputMin;
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled();
|
||||
ImGuiExt::InputTextIcon("##placeholder_value", ICON_VS_SYMBOL_NUMERIC, settings.inputMax);
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.find.value.range"_lang, &settings.range);
|
||||
ImGui::NewLine();
|
||||
|
||||
const std::array<std::string, 10> InputTypes = {
|
||||
"hex.builtin.common.type.u8"_lang,
|
||||
"hex.builtin.common.type.u16"_lang,
|
||||
"hex.builtin.common.type.u32"_lang,
|
||||
"hex.builtin.common.type.u64"_lang,
|
||||
"hex.builtin.common.type.i8"_lang,
|
||||
"hex.builtin.common.type.i16"_lang,
|
||||
"hex.builtin.common.type.i32"_lang,
|
||||
"hex.builtin.common.type.i64"_lang,
|
||||
"hex.builtin.common.type.f32"_lang,
|
||||
"hex.builtin.common.type.f64"_lang
|
||||
};
|
||||
|
||||
if (ImGui::BeginCombo("hex.builtin.common.type"_lang, InputTypes[std::to_underlying(settings.type)].c_str())) {
|
||||
for (size_t i = 0; i < InputTypes.size(); i++) {
|
||||
auto type = static_cast<SearchSettings::Value::Type>(i);
|
||||
|
||||
if (ImGui::Selectable(InputTypes[i].c_str(), type == settings.type)) {
|
||||
settings.type = type;
|
||||
edited = true;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
{
|
||||
int selection = [&] {
|
||||
switch (settings.endian) {
|
||||
default:
|
||||
case std::endian::little: return 0;
|
||||
case std::endian::big: return 1;
|
||||
}
|
||||
}();
|
||||
|
||||
std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang };
|
||||
if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
edited = true;
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: settings.endian = std::endian::little; break;
|
||||
case 1: settings.endian = std::endian::big; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Checkbox("hex.builtin.view.find.value.aligned"_lang, &settings.aligned);
|
||||
{
|
||||
int selection = [&] {
|
||||
switch (settings.endian) {
|
||||
default:
|
||||
case std::endian::little: return 0;
|
||||
case std::endian::big: return 1;
|
||||
}
|
||||
}();
|
||||
|
||||
if (edited) {
|
||||
auto [minValid, min, minSize] = parseNumericValueInput(settings.inputMin, settings.type);
|
||||
auto [maxValid, max, maxSize] = parseNumericValueInput(settings.inputMax, settings.type);
|
||||
|
||||
this->m_settingsValid = minValid && maxValid && minSize == maxSize;
|
||||
std::array options = { "hex.builtin.common.little"_lang, "hex.builtin.common.big"_lang };
|
||||
if (ImGui::SliderInt("hex.builtin.common.endian"_lang, &selection, 0, options.size() - 1, options[selection], ImGuiSliderFlags_NoInput)) {
|
||||
edited = true;
|
||||
switch (selection) {
|
||||
default:
|
||||
case 0: settings.endian = std::endian::little; break;
|
||||
case 1: settings.endian = std::endian::big; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.inputMin.empty())
|
||||
this->m_settingsValid = false;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
ImGui::Checkbox("hex.builtin.view.find.value.aligned"_lang, &settings.aligned);
|
||||
|
||||
ImGui::NewLine();
|
||||
if (edited) {
|
||||
auto [minValid, min, minSize] = parseNumericValueInput(settings.inputMin, settings.type);
|
||||
auto [maxValid, max, maxSize] = parseNumericValueInput(settings.inputMax, settings.type);
|
||||
|
||||
ImGui::BeginDisabled(!this->m_settingsValid);
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.find.search"_lang)) {
|
||||
this->runSearch();
|
||||
|
||||
this->m_decodeSettings = this->m_searchSettings;
|
||||
this->m_settingsValid = minValid && maxValid && minSize == maxSize;
|
||||
}
|
||||
|
||||
if (settings.inputMin.empty())
|
||||
this->m_settingsValid = false;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.find.search.entries"_lang, this->m_foundOccurrences->size());
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(this->m_foundOccurrences->empty());
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.find.search.reset"_lang)) {
|
||||
this->m_foundOccurrences->clear();
|
||||
this->m_sortedOccurrences->clear();
|
||||
this->m_occurrenceTree->clear();
|
||||
ImGui::NewLine();
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
ImGui::BeginDisabled(!this->m_settingsValid);
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.find.search"_lang)) {
|
||||
this->runSearch();
|
||||
|
||||
this->m_decodeSettings = this->m_searchSettings;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.find.search.entries"_lang, this->m_foundOccurrences->size());
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
ImGui::BeginDisabled(this->m_foundOccurrences->empty());
|
||||
{
|
||||
if (ImGui::Button("hex.builtin.view.find.search.reset"_lang)) {
|
||||
this->m_foundOccurrences->clear();
|
||||
this->m_sortedOccurrences->clear();
|
||||
this->m_occurrenceTree->clear();
|
||||
|
||||
auto &currOccurrences = *this->m_sortedOccurrences;
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
auto prevFilterLength = this->m_currFilter->length();
|
||||
if (ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, *this->m_currFilter)) {
|
||||
if (prevFilterLength > this->m_currFilter->length())
|
||||
*this->m_sortedOccurrences = *this->m_foundOccurrences;
|
||||
|
||||
if (this->m_filterTask.isRunning())
|
||||
this->m_filterTask.interrupt();
|
||||
|
||||
if (!this->m_currFilter->empty()) {
|
||||
this->m_filterTask = TaskManager::createTask("Filtering", currOccurrences.size(), [this, provider, &currOccurrences](Task &task) {
|
||||
u64 progress = 0;
|
||||
std::erase_if(currOccurrences, [this, provider, &task, &progress](const auto ®ion) {
|
||||
task.update(progress);
|
||||
progress += 1;
|
||||
|
||||
return !hex::containsIgnoreCase(this->decodeValue(provider, region), this->m_currFilter.get(provider));
|
||||
});
|
||||
});
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::BeginTable("##entries", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.offset"_lang, 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang, 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.value"_lang, 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.region.getStartAddress() > right.region.getStartAddress();
|
||||
else
|
||||
return left.region.getStartAddress() < right.region.getStartAddress();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.region.getSize() > right.region.getSize();
|
||||
else
|
||||
return left.region.getSize() < right.region.getSize();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return this->decodeValue(provider, left) > this->decodeValue(provider, right);
|
||||
else
|
||||
return this->decodeValue(provider, left) < this->decodeValue(provider, right);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(currOccurrences.size(), ImGui::GetTextLineHeightWithSpacing());
|
||||
|
||||
while (clipper.Step()) {
|
||||
for (size_t i = clipper.DisplayStart; i < std::min<size_t>(clipper.DisplayEnd, currOccurrences.size()); i++) {
|
||||
auto &foundItem = currOccurrences[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGuiExt::TextFormatted("0x{:08X}", foundItem.region.getStartAddress());
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", hex::toByteString(foundItem.region.getSize()));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
auto value = this->decodeValue(provider, foundItem, 256);
|
||||
ImGuiExt::TextFormatted("{}", value);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
if (ImGui::GetIO().KeyCtrl) {
|
||||
foundItem.selected = !foundItem.selected;
|
||||
} else {
|
||||
for (auto &occurrence : *this->m_sortedOccurrences)
|
||||
occurrence.selected = false;
|
||||
foundItem.selected = true;
|
||||
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
|
||||
}
|
||||
}
|
||||
drawContextMenu(foundItem, value);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
auto &currOccurrences = *this->m_sortedOccurrences;
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
auto prevFilterLength = this->m_currFilter->length();
|
||||
if (ImGuiExt::InputTextIcon("##filter", ICON_VS_FILTER, *this->m_currFilter)) {
|
||||
if (prevFilterLength > this->m_currFilter->length())
|
||||
*this->m_sortedOccurrences = *this->m_foundOccurrences;
|
||||
|
||||
if (this->m_filterTask.isRunning())
|
||||
this->m_filterTask.interrupt();
|
||||
|
||||
if (!this->m_currFilter->empty()) {
|
||||
this->m_filterTask = TaskManager::createTask("Filtering", currOccurrences.size(), [this, provider, &currOccurrences](Task &task) {
|
||||
u64 progress = 0;
|
||||
std::erase_if(currOccurrences, [this, provider, &task, &progress](const auto ®ion) {
|
||||
task.update(progress);
|
||||
progress += 1;
|
||||
|
||||
return !hex::containsIgnoreCase(this->decodeValue(provider, region), this->m_currFilter.get(provider));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::BeginTable("##entries", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.common.offset"_lang, 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang, 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.value"_lang, 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.region.getStartAddress() > right.region.getStartAddress();
|
||||
else
|
||||
return left.region.getStartAddress() < right.region.getStartAddress();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.region.getSize() > right.region.getSize();
|
||||
else
|
||||
return left.region.getSize() < right.region.getSize();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return this->decodeValue(provider, left) > this->decodeValue(provider, right);
|
||||
else
|
||||
return this->decodeValue(provider, left) < this->decodeValue(provider, right);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(currOccurrences.size(), ImGui::GetTextLineHeightWithSpacing());
|
||||
|
||||
while (clipper.Step()) {
|
||||
for (size_t i = clipper.DisplayStart; i < std::min<size_t>(clipper.DisplayEnd, currOccurrences.size()); i++) {
|
||||
auto &foundItem = currOccurrences[i];
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGuiExt::TextFormatted("0x{:08X}", foundItem.region.getStartAddress());
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", hex::toByteString(foundItem.region.getSize()));
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
auto value = this->decodeValue(provider, foundItem, 256);
|
||||
ImGuiExt::TextFormatted("{}", value);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
if (ImGui::GetIO().KeyCtrl) {
|
||||
foundItem.selected = !foundItem.selected;
|
||||
} else {
|
||||
for (auto &occurrence : *this->m_sortedOccurrences)
|
||||
occurrence.selected = false;
|
||||
foundItem.selected = true;
|
||||
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
|
||||
}
|
||||
}
|
||||
drawContextMenu(foundItem, value);
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,21 +42,13 @@ namespace hex::plugin::builtin {
|
||||
return ImGuiWindowFlags_AlwaysAutoResize;
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMinSize() const override {
|
||||
return scaled({ 400, 100 });
|
||||
}
|
||||
|
||||
[[nodiscard]] ImVec2 getMaxSize() const override {
|
||||
return scaled({ 600, 300 });
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_input;
|
||||
std::string m_result;
|
||||
ContentRegistry::Hashes::Hash::Function m_hash;
|
||||
};
|
||||
|
||||
ViewHashes::ViewHashes() : View("hex.builtin.view.hashes.name") {
|
||||
ViewHashes::ViewHashes() : View::Window("hex.builtin.view.hashes.name") {
|
||||
EventManager::subscribe<EventRegionSelected>(this, [this](const auto &providerRegion) {
|
||||
for (auto &function : this->m_hashFunctions.get(providerRegion.getProvider()))
|
||||
function.reset();
|
||||
@ -143,112 +135,109 @@ namespace hex::plugin::builtin {
|
||||
this->m_selectedHash = hashes.front().get();
|
||||
}
|
||||
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.hashes.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (ImGui::BeginCombo("hex.builtin.view.hashes.function"_lang, this->m_selectedHash != nullptr ? LangEntry(this->m_selectedHash->getUnlocalizedName()) : "")) {
|
||||
if (ImGui::BeginCombo("hex.builtin.view.hashes.function"_lang, this->m_selectedHash != nullptr ? LangEntry(this->m_selectedHash->getUnlocalizedName()) : "")) {
|
||||
|
||||
for (const auto &hash : hashes) {
|
||||
if (ImGui::Selectable(LangEntry(hash->getUnlocalizedName()), this->m_selectedHash == hash.get())) {
|
||||
this->m_selectedHash = hash.get();
|
||||
this->m_newHashName.clear();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
if (this->m_newHashName.empty() && this->m_selectedHash != nullptr)
|
||||
this->m_newHashName = hex::format("{} {}", LangEntry(this->m_selectedHash->getUnlocalizedName()), static_cast<const char *>("hex.builtin.view.hashes.hash"_lang));
|
||||
|
||||
if (ImGui::BeginChild("##settings", ImVec2(ImGui::GetContentRegionAvail().x, 200_scaled), true)) {
|
||||
if (this->m_selectedHash != nullptr) {
|
||||
auto startPos = ImGui::GetCursorPosY();
|
||||
this->m_selectedHash->draw();
|
||||
|
||||
// Check if no elements have been added
|
||||
if (startPos == ImGui::GetCursorPosY()) {
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.hashes.no_settings"_lang);
|
||||
}
|
||||
for (const auto &hash : hashes) {
|
||||
if (ImGui::Selectable(LangEntry(hash->getUnlocalizedName()), this->m_selectedHash == hash.get())) {
|
||||
this->m_selectedHash = hash.get();
|
||||
this->m_newHashName.clear();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGuiExt::InputTextIcon("##hash_name", ICON_VS_SYMBOL_KEY, this->m_newHashName);
|
||||
ImGui::SameLine();
|
||||
if (this->m_newHashName.empty() && this->m_selectedHash != nullptr)
|
||||
this->m_newHashName = hex::format("{} {}", LangEntry(this->m_selectedHash->getUnlocalizedName()), static_cast<const char *>("hex.builtin.view.hashes.hash"_lang));
|
||||
|
||||
ImGui::BeginDisabled(this->m_newHashName.empty() || this->m_selectedHash == nullptr);
|
||||
if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
if (this->m_selectedHash != nullptr) {
|
||||
this->m_hashFunctions->push_back(this->m_selectedHash->create(this->m_newHashName));
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.misc", "hex.builtin.achievement.misc.create_hash.name");
|
||||
if (ImGui::BeginChild("##settings", ImVec2(ImGui::GetContentRegionAvail().x, 200_scaled), true)) {
|
||||
if (this->m_selectedHash != nullptr) {
|
||||
auto startPos = ImGui::GetCursorPosY();
|
||||
this->m_selectedHash->draw();
|
||||
|
||||
// Check if no elements have been added
|
||||
if (startPos == ImGui::GetCursorPosY()) {
|
||||
ImGuiExt::TextFormattedCentered("hex.builtin.view.hashes.no_settings"_lang);
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::HelpHover("hex.builtin.view.hashes.hover_info"_lang);
|
||||
|
||||
if (ImGui::BeginTable("##hashes", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.name"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.type"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.result"_lang, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##buttons", ImGuiTableColumnFlags_WidthFixed, 50_scaled);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
std::optional<u32> indexToRemove;
|
||||
for (u32 i = 0; i < this->m_hashFunctions->size(); i++) {
|
||||
auto &function = (*this->m_hashFunctions)[i];
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0x00);
|
||||
ImGui::Selectable(function.getName().c_str(), false);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", LangEntry(function.getType()->getUnlocalizedName()));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
std::string result;
|
||||
if (provider != nullptr && selection.has_value())
|
||||
result = crypt::encode16(function.get(*selection, provider));
|
||||
else
|
||||
result = "???";
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
ImGui::InputText("##result", result, ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (ImGuiExt::IconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
PopupTextHash::open(function);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_X, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
|
||||
indexToRemove = i;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (indexToRemove.has_value()) {
|
||||
this->m_hashFunctions->erase(this->m_hashFunctions->begin() + indexToRemove.value());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::EndChild();
|
||||
|
||||
|
||||
ImGuiExt::InputTextIcon("##hash_name", ICON_VS_SYMBOL_KEY, this->m_newHashName);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginDisabled(this->m_newHashName.empty() || this->m_selectedHash == nullptr);
|
||||
if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
if (this->m_selectedHash != nullptr) {
|
||||
this->m_hashFunctions->push_back(this->m_selectedHash->create(this->m_newHashName));
|
||||
AchievementManager::unlockAchievement("hex.builtin.achievement.misc", "hex.builtin.achievement.misc.create_hash.name");
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::HelpHover("hex.builtin.view.hashes.hover_info"_lang);
|
||||
|
||||
if (ImGui::BeginTable("##hashes", 4, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.name"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.type"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.hashes.table.result"_lang, ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##buttons", ImGuiTableColumnFlags_WidthFixed, 50_scaled);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto selection = ImHexApi::HexEditor::getSelection();
|
||||
|
||||
std::optional<u32> indexToRemove;
|
||||
for (u32 i = 0; i < this->m_hashFunctions->size(); i++) {
|
||||
auto &function = (*this->m_hashFunctions)[i];
|
||||
|
||||
ImGui::PushID(i);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, 0x00);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, 0x00);
|
||||
ImGui::Selectable(function.getName().c_str(), false);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", LangEntry(function.getType()->getUnlocalizedName()));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
std::string result;
|
||||
if (provider != nullptr && selection.has_value())
|
||||
result = crypt::encode16(function.get(*selection, provider));
|
||||
else
|
||||
result = "???";
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
ImGui::InputText("##result", result, ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (ImGuiExt::IconButton(ICON_VS_OPEN_PREVIEW, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
PopupTextHash::open(function);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_X, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) {
|
||||
indexToRemove = i;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (indexToRemove.has_value()) {
|
||||
this->m_hashFunctions->erase(this->m_hashFunctions->begin() + indexToRemove.value());
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewHashes::importHashes(prv::Provider *provider, const nlohmann::json &json) {
|
||||
|
@ -347,7 +347,7 @@ namespace hex::plugin::builtin {
|
||||
editor->closePopup();
|
||||
}
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this]{
|
||||
setBaseAddress(this->m_baseAddress);
|
||||
editor->closePopup();
|
||||
@ -381,7 +381,7 @@ namespace hex::plugin::builtin {
|
||||
editor->closePopup();
|
||||
}
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this]{
|
||||
setPageSize(this->m_pageSize);
|
||||
editor->closePopup();
|
||||
@ -419,7 +419,7 @@ namespace hex::plugin::builtin {
|
||||
editor->closePopup();
|
||||
}
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this]{
|
||||
this->resize(this->m_size);
|
||||
editor->closePopup();
|
||||
@ -449,7 +449,7 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.common.address"_lang, &this->m_address);
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_size);
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this]{
|
||||
insert(this->m_address, this->m_size);
|
||||
editor->closePopup();
|
||||
@ -480,7 +480,7 @@ namespace hex::plugin::builtin {
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.common.address"_lang, &this->m_address);
|
||||
ImGuiExt::InputHexadecimal("hex.builtin.common.size"_lang, &this->m_size);
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this]{
|
||||
remove(this->m_address, this->m_size);
|
||||
editor->closePopup();
|
||||
@ -515,7 +515,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
ImGuiExt::InputTextIcon("hex.builtin.common.bytes"_lang, ICON_VS_SYMBOL_NAMESPACE, this->m_input);
|
||||
|
||||
View::confirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
ImGuiExt::ConfirmButtons("hex.builtin.common.set"_lang, "hex.builtin.common.cancel"_lang,
|
||||
[&, this] {
|
||||
fill(this->m_address, this->m_size, this->m_input);
|
||||
editor->closePopup();
|
||||
@ -552,7 +552,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
/* Hex Editor */
|
||||
|
||||
ViewHexEditor::ViewHexEditor() : View("hex.builtin.view.hex_editor.name") {
|
||||
ViewHexEditor::ViewHexEditor() : View::Window("hex.builtin.view.hex_editor.name") {
|
||||
this->m_hexEditor.setForegroundHighlightCallback([this](u64 address, const u8 *data, size_t size) -> std::optional<color_t> {
|
||||
if (auto highlight = this->m_foregroundHighlights->find(address); highlight != this->m_foregroundHighlights->end())
|
||||
return highlight->second;
|
||||
@ -675,14 +675,11 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewHexEditor::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
this->m_hexEditor.setProvider(ImHexApi::Provider::get());
|
||||
this->m_hexEditor.setProvider(ImHexApi::Provider::get());
|
||||
|
||||
this->m_hexEditor.draw();
|
||||
this->m_hexEditor.draw();
|
||||
|
||||
this->drawPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
this->drawPopup();
|
||||
}
|
||||
|
||||
static void save() {
|
||||
|
@ -20,7 +20,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
using namespace hex::literals;
|
||||
|
||||
ViewInformation::ViewInformation() : View("hex.builtin.view.information.name") {
|
||||
ViewInformation::ViewInformation() : View::Window("hex.builtin.view.information.name") {
|
||||
EventManager::subscribe<EventDataChanged>(this, [this] {
|
||||
this->m_dataValid = false;
|
||||
this->m_plainTextCharacterPercentage = -1.0;
|
||||
@ -136,232 +136,229 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewInformation::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.information.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav)) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav)) {
|
||||
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
ImGui::BeginDisabled(this->m_analyzerTask.isRunning());
|
||||
{
|
||||
ImGuiExt::Header("hex.builtin.common.settings"_lang, true);
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
ImGui::BeginDisabled(this->m_analyzerTask.isRunning());
|
||||
{
|
||||
ImGuiExt::Header("hex.builtin.common.settings"_lang, true);
|
||||
|
||||
ui::regionSelectionPicker(&this->m_analysisRegion, provider, &this->m_selectionType, false);
|
||||
ImGui::NewLine();
|
||||
ui::regionSelectionPicker(&this->m_analysisRegion, provider, &this->m_selectionType, false);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::InputInt("hex.builtin.view.information.block_size"_lang, &this->m_inputChunkSize, ImGuiInputTextFlags_CharsDecimal);
|
||||
ImGui::InputInt("hex.builtin.view.information.block_size"_lang, &this->m_inputChunkSize, ImGuiInputTextFlags_CharsDecimal);
|
||||
|
||||
if (ImGui::Button("hex.builtin.view.information.analyze"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
||||
this->analyze();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::Button("hex.builtin.view.information.analyze"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0)))
|
||||
this->analyze();
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (this->m_analyzerTask.isRunning()) {
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.information.analyzing"_lang);
|
||||
} else {
|
||||
ImGui::NewLine();
|
||||
if (this->m_analyzerTask.isRunning()) {
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.information.analyzing"_lang);
|
||||
} else {
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (!this->m_analyzerTask.isRunning() && this->m_dataValid) {
|
||||
|
||||
// Provider information
|
||||
ImGuiExt::Header("hex.builtin.view.information.provider_information"_lang, true);
|
||||
|
||||
if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoKeepColumnsVisible)) {
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
for (auto &[name, value] : provider->getDataDescription()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", name);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedWrapped("{}", value);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.region"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{:X} - 0x{:X}", this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getEndAddress());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (!this->m_analyzerTask.isRunning() && this->m_dataValid) {
|
||||
// Magic information
|
||||
if (!(this->m_dataDescription.empty() && this->m_dataMimeType.empty())) {
|
||||
ImGuiExt::Header("hex.builtin.view.information.magic"_lang);
|
||||
|
||||
// Provider information
|
||||
ImGuiExt::Header("hex.builtin.view.information.provider_information"_lang, true);
|
||||
|
||||
if (ImGui::BeginTable("information", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoKeepColumnsVisible)) {
|
||||
if (ImGui::BeginTable("magic", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
for (auto &[name, value] : provider->getDataDescription()) {
|
||||
if (!this->m_dataDescription.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", name);
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.description"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedWrapped("{}", value);
|
||||
|
||||
if (this->m_dataDescription == "data") {
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataDescription);
|
||||
} else {
|
||||
ImGuiExt::TextFormattedWrapped("{}", this->m_dataDescription);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.region"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{:X} - 0x{:X}", this->m_analyzedRegion.getStartAddress(), this->m_analyzedRegion.getEndAddress());
|
||||
if (!this->m_dataMimeType.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.mime"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (this->m_dataMimeType == "application/octet-stream") {
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataMimeType);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGuiExt::HelpHover("hex.builtin.view.information.octet_stream_warning"_lang);
|
||||
ImGui::PopStyleVar();
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("{}", this->m_dataMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Magic information
|
||||
if (!(this->m_dataDescription.empty() && this->m_dataMimeType.empty())) {
|
||||
ImGuiExt::Header("hex.builtin.view.information.magic"_lang);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (ImGui::BeginTable("magic", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
// Information analysis
|
||||
if (this->m_analyzedRegion.getSize() > 0) {
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGuiExt::Header("hex.builtin.view.information.info_analysis"_lang);
|
||||
|
||||
if (!this->m_dataDescription.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.description"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
|
||||
ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
|
||||
|
||||
if (this->m_dataDescription == "data") {
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataDescription);
|
||||
} else {
|
||||
ImGuiExt::TextFormattedWrapped("{}", this->m_dataDescription);
|
||||
}
|
||||
}
|
||||
// Display byte distribution analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.distribution"_lang);
|
||||
this->m_byteDistribution.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect
|
||||
);
|
||||
|
||||
if (!this->m_dataMimeType.empty()) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.mime"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (this->m_dataMimeType == "application/octet-stream") {
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{} ({})", "hex.builtin.view.information.octet_stream_text"_lang, this->m_dataMimeType);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGuiExt::HelpHover("hex.builtin.view.information.octet_stream_warning"_lang);
|
||||
ImGui::PopStyleVar();
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("{}", this->m_dataMimeType);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
// Information analysis
|
||||
if (this->m_analyzedRegion.getSize() > 0) {
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.information.info_analysis"_lang);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
|
||||
ImPlot::PushStyleColor(ImPlotCol_FrameBg, ImGui::GetColorU32(ImGuiCol_WindowBg));
|
||||
|
||||
// Display byte distribution analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.distribution"_lang);
|
||||
this->m_byteDistribution.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect
|
||||
);
|
||||
|
||||
// Display byte types distribution analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.byte_types"_lang);
|
||||
this->m_byteTypesDistribution.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect,
|
||||
true
|
||||
);
|
||||
|
||||
// Display chunk-based entropy analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.entropy"_lang);
|
||||
this->m_chunkBasedEntropy.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect,
|
||||
// Display byte types distribution analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.byte_types"_lang);
|
||||
this->m_byteTypesDistribution.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect,
|
||||
true
|
||||
);
|
||||
);
|
||||
|
||||
ImPlot::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
// Display chunk-based entropy analysis
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.entropy"_lang);
|
||||
this->m_chunkBasedEntropy.draw(
|
||||
ImVec2(-1, 0),
|
||||
ImPlotFlags_NoLegend | ImPlotFlags_NoMenus | ImPlotFlags_NoBoxSelect,
|
||||
true
|
||||
);
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
// Entropy information
|
||||
if (ImGui::BeginTable("entropy_info", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.block_size"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.information.block_size.desc"_lang, this->m_chunkBasedEntropy.getSize(), this->m_chunkBasedEntropy.getChunkSize());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.file_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_averageEntropy < 0) ImGui::TextUnformatted("???");
|
||||
else ImGuiExt::TextFormatted("{:.5f}", std::abs(this->m_averageEntropy));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.highest_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{:.5f} @", this->m_highestBlockEntropy);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
if (ImGui::Button(hex::format("0x{:06X}", this->m_highestBlockEntropyAddress).c_str())) {
|
||||
ImHexApi::HexEditor::setSelection(this->m_highestBlockEntropyAddress, this->m_inputChunkSize);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.lowest_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{:.5f} @", this->m_lowestBlockEntropy);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
if (ImGui::Button(hex::format("0x{:06X}", this->m_lowestBlockEntropyAddress).c_str())) {
|
||||
ImHexApi::HexEditor::setSelection(this->m_lowestBlockEntropyAddress, this->m_inputChunkSize);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.plain_text_percentage"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_plainTextCharacterPercentage < 0) ImGui::TextUnformatted("???");
|
||||
else ImGuiExt::TextFormatted("{:.2f}%", this->m_plainTextCharacterPercentage);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImPlot::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
// General information
|
||||
if (ImGui::BeginTable("info", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableNextRow();
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.encrypted"_lang);
|
||||
}
|
||||
|
||||
if (this->m_plainTextCharacterPercentage > 95) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.plain_text"_lang);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.digram"_lang);
|
||||
this->m_digram.draw(scaled(ImVec2(300, 300)));
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.layered_distribution"_lang);
|
||||
this->m_layeredDistribution.draw(scaled(ImVec2(300, 300)));
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
// Entropy information
|
||||
if (ImGui::BeginTable("entropy_info", 2, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("type");
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.block_size"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.information.block_size.desc"_lang, this->m_chunkBasedEntropy.getSize(), this->m_chunkBasedEntropy.getChunkSize());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.file_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_averageEntropy < 0) ImGui::TextUnformatted("???");
|
||||
else ImGuiExt::TextFormatted("{:.5f}", std::abs(this->m_averageEntropy));
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.highest_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{:.5f} @", this->m_highestBlockEntropy);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
if (ImGui::Button(hex::format("0x{:06X}", this->m_highestBlockEntropyAddress).c_str())) {
|
||||
ImHexApi::HexEditor::setSelection(this->m_highestBlockEntropyAddress, this->m_inputChunkSize);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.lowest_entropy"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{:.5f} @", this->m_lowestBlockEntropy);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
if (ImGui::Button(hex::format("0x{:06X}", this->m_lowestBlockEntropyAddress).c_str())) {
|
||||
ImHexApi::HexEditor::setSelection(this->m_lowestBlockEntropyAddress, this->m_inputChunkSize);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("{}", "hex.builtin.view.information.plain_text_percentage"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_plainTextCharacterPercentage < 0) ImGui::TextUnformatted("???");
|
||||
else ImGuiExt::TextFormatted("{:.2f}%", this->m_plainTextCharacterPercentage);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
// General information
|
||||
if (ImGui::BeginTable("info", 1, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("value", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableNextRow();
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.encrypted"_lang);
|
||||
}
|
||||
|
||||
if (this->m_plainTextCharacterPercentage > 95) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.information.plain_text"_lang);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.digram"_lang);
|
||||
this->m_digram.draw(scaled(ImVec2(300, 300)));
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::BeginGroup();
|
||||
{
|
||||
ImGui::TextUnformatted("hex.builtin.view.information.layered_distribution"_lang);
|
||||
this->m_layeredDistribution.draw(scaled(ImVec2(300, 300)));
|
||||
}
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,9 +5,8 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewLogs::ViewLogs() : View("hex.builtin.view.logs.name") {
|
||||
ViewLogs::ViewLogs() : View::Floating("hex.builtin.view.logs.name") {
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.logs.name" }, 2500, Shortcut::None, [&, this] {
|
||||
this->m_viewOpen = true;
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
}
|
||||
@ -41,48 +40,42 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewLogs::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.logs.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::Combo("hex.builtin.view.logs.log_level"_lang, &this->m_logLevel, "DEBUG\0INFO\0WARNING\0ERROR\0FATAL\0");
|
||||
|
||||
ImGui::Combo("hex.builtin.view.logs.log_level"_lang, &this->m_logLevel, "DEBUG\0INFO\0WARNING\0ERROR\0FATAL\0");
|
||||
if (ImGui::BeginTable("##logs", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.logs.component"_lang, ImGuiTableColumnFlags_WidthFixed, 100_scaled);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.logs.message"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
|
||||
if (ImGui::BeginTable("##logs", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupColumn("hex.builtin.view.logs.component"_lang, ImGuiTableColumnFlags_WidthFixed, 100_scaled);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.logs.message"_lang);
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
const auto &logs = log::impl::getLogEntries();
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(logs.size());
|
||||
|
||||
const auto &logs = log::impl::getLogEntries();
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(logs.size());
|
||||
while (clipper.Step()) {
|
||||
auto end = 0;
|
||||
for (size_t i = clipper.DisplayStart; i < std::min<size_t>(clipper.DisplayEnd + end, logs.size()); i++) {
|
||||
const auto &log = logs[logs.size() - 1 - i];
|
||||
|
||||
while (clipper.Step()) {
|
||||
auto end = 0;
|
||||
for (size_t i = clipper.DisplayStart; i < std::min<size_t>(clipper.DisplayEnd + end, logs.size()); i++) {
|
||||
const auto &log = logs[logs.size() - 1 - i];
|
||||
|
||||
if (!shouldDisplay(log.level, this->m_logLevel)) {
|
||||
end++;
|
||||
clipper.ItemsCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(log.project.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, getColor(log.level).Value);
|
||||
ImGui::TextUnformatted(log.message.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
if (!shouldDisplay(log.level, this->m_logLevel)) {
|
||||
end++;
|
||||
clipper.ItemsCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(log.project.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, getColor(log.level).Value);
|
||||
ImGui::TextUnformatted(log.message.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
this->getWindowOpenState() = this->m_viewOpen;
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -11,7 +11,7 @@ using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewPatches::ViewPatches() : View("hex.builtin.view.patches.name") {
|
||||
ViewPatches::ViewPatches() : View::Window("hex.builtin.view.patches.name") {
|
||||
|
||||
ProjectFile::registerPerProviderHandler({
|
||||
.basePath = "patches.json",
|
||||
@ -54,74 +54,71 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewPatches::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.patches.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
|
||||
|
||||
if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.offset"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.orig"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.patch"_lang);
|
||||
if (ImGui::BeginTable("##patchesTable", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.offset"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.orig"_lang);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.patches.patch"_lang);
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto &patches = provider->getPatches();
|
||||
u32 index = 0;
|
||||
auto &patches = provider->getPatches();
|
||||
u32 index = 0;
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
ImGuiListClipper clipper;
|
||||
|
||||
clipper.Begin(patches.size());
|
||||
while (clipper.Step()) {
|
||||
auto iter = patches.begin();
|
||||
for (auto i = 0; i < clipper.DisplayStart; i++)
|
||||
++iter;
|
||||
clipper.Begin(patches.size());
|
||||
while (clipper.Step()) {
|
||||
auto iter = patches.begin();
|
||||
for (auto i = 0; i < clipper.DisplayStart; i++)
|
||||
++iter;
|
||||
|
||||
for (auto i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &[address, patch] = *iter;
|
||||
for (auto i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &[address, patch] = *iter;
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
ImHexApi::HexEditor::setSelection(address, 1);
|
||||
}
|
||||
if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup("PatchContextMenu");
|
||||
this->m_selectedPatch = address;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("0x{0:08X}", address);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
u8 previousValue = 0x00;
|
||||
provider->readRaw(address, &previousValue, sizeof(u8));
|
||||
ImGuiExt::TextFormatted("0x{0:02X}", previousValue);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:02X}", patch);
|
||||
index += 1;
|
||||
|
||||
iter++;
|
||||
if (ImGui::Selectable(("##patchLine" + std::to_string(index)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
ImHexApi::HexEditor::setSelection(address, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("PatchContextMenu")) {
|
||||
if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) {
|
||||
patches.erase(this->m_selectedPatch);
|
||||
if (ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup("PatchContextMenu");
|
||||
this->m_selectedPatch = address;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextFormatted("0x{0:08X}", address);
|
||||
|
||||
ImGui::EndTable();
|
||||
ImGui::TableNextColumn();
|
||||
u8 previousValue = 0x00;
|
||||
provider->readRaw(address, &previousValue, sizeof(u8));
|
||||
ImGuiExt::TextFormatted("0x{0:02X}", previousValue);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:02X}", patch);
|
||||
index += 1;
|
||||
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("PatchContextMenu")) {
|
||||
if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) {
|
||||
patches.erase(this->m_selectedPatch);
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewPatches::drawAlwaysVisible() {
|
||||
void ViewPatches::drawAlwaysVisibleContent() {
|
||||
if (auto provider = ImHexApi::Provider::get(); provider != nullptr) {
|
||||
const auto &patches = provider->getPatches();
|
||||
if (this->m_numPatches.get(provider) != patches.size()) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewPatternData::ViewPatternData() : View("hex.builtin.view.pattern_data.name") {
|
||||
ViewPatternData::ViewPatternData() : View::Window("hex.builtin.view.pattern_data.name") {
|
||||
this->m_patternDrawer = std::make_unique<ui::PatternDrawer>();
|
||||
|
||||
// Handle tree style setting changes
|
||||
@ -42,22 +42,19 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewPatternData::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
// Draw the pattern tree if the provider is valid
|
||||
if (ImHexApi::Provider::isValid()) {
|
||||
// Make sure the runtime has finished evaluating and produced valid patterns
|
||||
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
if (!runtime.arePatternsValid()) {
|
||||
this->m_patternDrawer->draw({ });
|
||||
} else {
|
||||
// If the runtime has finished evaluating, draw the patterns
|
||||
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
|
||||
this->m_patternDrawer->draw(runtime.getPatterns(), &runtime);
|
||||
}
|
||||
// Draw the pattern tree if the provider is valid
|
||||
if (ImHexApi::Provider::isValid()) {
|
||||
// Make sure the runtime has finished evaluating and produced valid patterns
|
||||
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
if (!runtime.arePatternsValid()) {
|
||||
this->m_patternDrawer->draw({ });
|
||||
} else {
|
||||
// If the runtime has finished evaluating, draw the patterns
|
||||
if (TRY_LOCK(ContentRegistry::PatternLanguage::getRuntimeLock())) {
|
||||
this->m_patternDrawer->draw(runtime.getPatterns(), &runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ namespace hex::plugin::builtin {
|
||||
return langDef;
|
||||
}
|
||||
|
||||
ViewPatternEditor::ViewPatternEditor() : View("hex.builtin.view.pattern_editor.name") {
|
||||
ViewPatternEditor::ViewPatternEditor() : View::Window("hex.builtin.view.pattern_editor.name") {
|
||||
this->m_parserRuntime = std::make_unique<pl::PatternLanguage>();
|
||||
ContentRegistry::PatternLanguage::configureRuntime(*this->m_parserRuntime, nullptr);
|
||||
|
||||
@ -148,202 +148,199 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewPatternEditor::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_editor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
if (ImHexApi::Provider::isValid() && provider->isAvailable()) {
|
||||
static float height = 0;
|
||||
static bool dragging = false;
|
||||
if (ImHexApi::Provider::isValid() && provider->isAvailable()) {
|
||||
static float height = 0;
|
||||
static bool dragging = false;
|
||||
|
||||
auto availableSize = ImGui::GetContentRegionAvail();
|
||||
auto textEditorSize = availableSize;
|
||||
textEditorSize.y *= 3.5 / 5.0;
|
||||
textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing();
|
||||
textEditorSize.y += height;
|
||||
auto availableSize = ImGui::GetContentRegionAvail();
|
||||
auto textEditorSize = availableSize;
|
||||
textEditorSize.y *= 3.5 / 5.0;
|
||||
textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing();
|
||||
textEditorSize.y += height;
|
||||
|
||||
if (availableSize.y > 1)
|
||||
textEditorSize.y = std::clamp(textEditorSize.y, 1.0F, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3);
|
||||
if (availableSize.y > 1)
|
||||
textEditorSize.y = std::clamp(textEditorSize.y, 1.0F, availableSize.y - ImGui::GetTextLineHeightWithSpacing() * 3);
|
||||
|
||||
this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true);
|
||||
this->m_textEditor.Render("hex.builtin.view.pattern_editor.name"_lang, textEditorSize, true);
|
||||
|
||||
ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled));
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) {
|
||||
if (ImGui::IsItemHovered())
|
||||
dragging = true;
|
||||
} else {
|
||||
dragging = false;
|
||||
ImGui::Button("##settings_drag_bar", ImVec2(ImGui::GetContentRegionAvail().x, 2_scaled));
|
||||
if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, 0)) {
|
||||
if (ImGui::IsItemHovered())
|
||||
dragging = true;
|
||||
} else {
|
||||
dragging = false;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
||||
}
|
||||
|
||||
if (dragging) {
|
||||
height += ImGui::GetMouseDragDelta(ImGuiMouseButton_Left, 0).y;
|
||||
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
}
|
||||
|
||||
auto settingsSize = ImGui::GetContentRegionAvail();
|
||||
settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5F;
|
||||
|
||||
if (ImGui::BeginTabBar("##settings")) {
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) {
|
||||
this->drawConsole(settingsSize);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.env_vars"_lang)) {
|
||||
this->drawEnvVars(settingsSize, *this->m_envVarEntries);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.settings"_lang)) {
|
||||
this->drawVariableSettings(settingsSize, *this->m_patternVariables);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) {
|
||||
this->drawSectionSelector(settingsSize, *this->m_sections);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.debugger"_lang)) {
|
||||
this->drawDebugger(settingsSize);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (dragging) {
|
||||
height += ImGui::GetMouseDragDelta(ImGuiMouseButton_Left, 0).y;
|
||||
ImGui::ResetMouseDragDelta(ImGuiMouseButton_Left);
|
||||
}
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
auto settingsSize = ImGui::GetContentRegionAvail();
|
||||
settingsSize.y -= ImGui::GetTextLineHeightWithSpacing() * 2.5F;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
|
||||
|
||||
if (ImGui::BeginTabBar("##settings")) {
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.console"_lang)) {
|
||||
this->drawConsole(settingsSize);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.env_vars"_lang)) {
|
||||
this->drawEnvVars(settingsSize, *this->m_envVarEntries);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.settings"_lang)) {
|
||||
this->drawVariableSettings(settingsSize, *this->m_patternVariables);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.sections"_lang)) {
|
||||
this->drawSectionSelector(settingsSize, *this->m_sections);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("hex.builtin.view.pattern_editor.debugger"_lang)) {
|
||||
this->drawDebugger(settingsSize);
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
|
||||
|
||||
{
|
||||
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
if (runtime.isRunning()) {
|
||||
if (this->m_breakpointHit) {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_CONTINUE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow)))
|
||||
this->m_breakpointHit = false;
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_INTO, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) {
|
||||
runtime.getInternals().evaluator->pauseNextLine();
|
||||
this->m_breakpointHit = false;
|
||||
}
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
|
||||
runtime.abort();
|
||||
{
|
||||
auto &runtime = ContentRegistry::PatternLanguage::getRuntime();
|
||||
if (runtime.isRunning()) {
|
||||
if (this->m_breakpointHit) {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_CONTINUE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow)))
|
||||
this->m_breakpointHit = false;
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STEP_INTO, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarYellow))) {
|
||||
runtime.getInternals().evaluator->pauseNextLine();
|
||||
this->m_breakpointHit = false;
|
||||
}
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_triggerEvaluation) {
|
||||
this->m_triggerEvaluation = false;
|
||||
this->evaluatePattern(this->m_textEditor.GetText(), provider);
|
||||
}
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_STOP, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
|
||||
runtime.abort();
|
||||
}
|
||||
} else {
|
||||
if (ImGuiExt::IconButton(ICON_VS_DEBUG_START, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen)) || this->m_triggerEvaluation) {
|
||||
this->m_triggerEvaluation = false;
|
||||
this->evaluatePattern(this->m_textEditor.GetText(), provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (this->m_runningEvaluators > 0) {
|
||||
if (this->m_breakpointHit) {
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.pattern_editor.breakpoint_hit"_lang, runtime.getInternals().evaluator->getPauseLine().value_or(0));
|
||||
} else {
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.pattern_editor.evaluating"_lang);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (this->m_runningEvaluators > 0) {
|
||||
if (this->m_breakpointHit) {
|
||||
ImGuiExt::TextFormatted("hex.builtin.view.pattern_editor.breakpoint_hit"_lang, runtime.getInternals().evaluator->getPauseLine().value_or(0));
|
||||
} else {
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.pattern_editor.evaluating"_lang);
|
||||
}
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
const auto padding = ImGui::GetStyle().FramePadding.y;
|
||||
|
||||
const auto padding = ImGui::GetStyle().FramePadding.y;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
if (ImGui::BeginChild("##read_cursor", ImGui::GetContentRegionAvail() + ImVec2(0, padding), true)) {
|
||||
const auto startPos = ImGui::GetCursorScreenPos();
|
||||
const auto size = ImGui::GetContentRegionAvail();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
|
||||
if (ImGui::BeginChild("##read_cursor", ImGui::GetContentRegionAvail() + ImVec2(0, padding), true)) {
|
||||
const auto startPos = ImGui::GetCursorScreenPos();
|
||||
const auto size = ImGui::GetContentRegionAvail();
|
||||
const auto dataBaseAddress = runtime.getInternals().evaluator->getDataBaseAddress();
|
||||
const auto dataSize = runtime.getInternals().evaluator->getDataSize();
|
||||
|
||||
const auto dataBaseAddress = runtime.getInternals().evaluator->getDataBaseAddress();
|
||||
const auto dataSize = runtime.getInternals().evaluator->getDataSize();
|
||||
const auto insertPos = [&, this](u64 address, u32 color) {
|
||||
const auto progress = (address - dataBaseAddress) / float(dataSize);
|
||||
|
||||
const auto insertPos = [&, this](u64 address, u32 color) {
|
||||
const auto progress = (address - dataBaseAddress) / float(dataSize);
|
||||
this->m_accessHistory[this->m_accessHistoryIndex] = { progress, color };
|
||||
this->m_accessHistoryIndex = (this->m_accessHistoryIndex + 1) % this->m_accessHistory.size();
|
||||
};
|
||||
|
||||
this->m_accessHistory[this->m_accessHistoryIndex] = { progress, color };
|
||||
this->m_accessHistoryIndex = (this->m_accessHistoryIndex + 1) % this->m_accessHistory.size();
|
||||
};
|
||||
insertPos(runtime.getLastReadAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarBlue));
|
||||
insertPos(runtime.getLastWriteAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
|
||||
insertPos(runtime.getLastPatternPlaceAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen));
|
||||
|
||||
insertPos(runtime.getLastReadAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarBlue));
|
||||
insertPos(runtime.getLastWriteAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarRed));
|
||||
insertPos(runtime.getLastPatternPlaceAddress(), ImGuiExt::GetCustomColorU32(ImGuiCustomCol_ToolbarGreen));
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
for (const auto &[progress, color] : this->m_accessHistory) {
|
||||
if (progress <= 0) continue;
|
||||
|
||||
auto drawList = ImGui::GetWindowDrawList();
|
||||
for (const auto &[progress, color] : this->m_accessHistory) {
|
||||
if (progress <= 0) continue;
|
||||
const auto linePos = startPos + ImVec2(size.x * progress, 0);
|
||||
|
||||
const auto linePos = startPos + ImVec2(size.x * progress, 0);
|
||||
|
||||
drawList->AddLine(linePos, linePos + ImVec2(0, size.y), color, 2_scaled);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
} else {
|
||||
if (ImGui::Checkbox("hex.builtin.view.pattern_editor.auto"_lang, &this->m_runAutomatically)) {
|
||||
if (this->m_runAutomatically)
|
||||
this->m_hasUnevaluatedChanges = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (auto max = runtime.getMaximumPatternCount(); max >= std::numeric_limits<u32>::max()) {
|
||||
ImGuiExt::TextFormatted("{} / {}",
|
||||
runtime.getCreatedPatternCount(),
|
||||
ICON_FA_INFINITY);
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("{} / {}",
|
||||
runtime.getCreatedPatternCount(),
|
||||
runtime.getMaximumPatternCount());
|
||||
drawList->AddLine(linePos, linePos + ImVec2(0, size.y), color, 2_scaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_textEditor.IsTextChanged()) {
|
||||
this->m_hasUnevaluatedChanges = true;
|
||||
ImHexApi::Provider::markDirty();
|
||||
}
|
||||
|
||||
if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) {
|
||||
this->m_hasUnevaluatedChanges = false;
|
||||
|
||||
auto code = this->m_textEditor.GetText();
|
||||
EventManager::post<EventPatternEditorChanged>(code);
|
||||
|
||||
TaskManager::createBackgroundTask("Pattern Parsing", [this, code, provider](auto &){
|
||||
this->parsePattern(code, provider);
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
} else {
|
||||
if (ImGui::Checkbox("hex.builtin.view.pattern_editor.auto"_lang, &this->m_runAutomatically)) {
|
||||
if (this->m_runAutomatically)
|
||||
this->m_triggerAutoEvaluate = true;
|
||||
});
|
||||
}
|
||||
|
||||
if (this->m_triggerAutoEvaluate.exchange(false)) {
|
||||
this->evaluatePattern(this->m_textEditor.GetText(), provider);
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_dangerousFunctionCalled && !ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopup)) {
|
||||
PopupQuestion::open("hex.builtin.view.pattern_editor.dangerous_function.desc"_lang,
|
||||
[this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Allow;
|
||||
}, [this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Deny;
|
||||
this->m_hasUnevaluatedChanges = true;
|
||||
}
|
||||
);
|
||||
|
||||
this->m_dangerousFunctionCalled = false;
|
||||
ImGui::SameLine();
|
||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (auto max = runtime.getMaximumPatternCount(); max >= std::numeric_limits<u32>::max()) {
|
||||
ImGuiExt::TextFormatted("{} / {}",
|
||||
runtime.getCreatedPatternCount(),
|
||||
ICON_FA_INFINITY);
|
||||
} else {
|
||||
ImGuiExt::TextFormatted("{} / {}",
|
||||
runtime.getCreatedPatternCount(),
|
||||
runtime.getMaximumPatternCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
View::discardNavigationRequests();
|
||||
if (this->m_textEditor.IsTextChanged()) {
|
||||
this->m_hasUnevaluatedChanges = true;
|
||||
ImHexApi::Provider::markDirty();
|
||||
}
|
||||
|
||||
if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) {
|
||||
this->m_hasUnevaluatedChanges = false;
|
||||
|
||||
auto code = this->m_textEditor.GetText();
|
||||
EventManager::post<EventPatternEditorChanged>(code);
|
||||
|
||||
TaskManager::createBackgroundTask("Pattern Parsing", [this, code, provider](auto &){
|
||||
this->parsePattern(code, provider);
|
||||
|
||||
if (this->m_runAutomatically)
|
||||
this->m_triggerAutoEvaluate = true;
|
||||
});
|
||||
}
|
||||
|
||||
if (this->m_triggerAutoEvaluate.exchange(false)) {
|
||||
this->evaluatePattern(this->m_textEditor.GetText(), provider);
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
if (this->m_dangerousFunctionCalled && !ImGui::IsPopupOpen(ImGuiID(0), ImGuiPopupFlags_AnyPopup)) {
|
||||
PopupQuestion::open("hex.builtin.view.pattern_editor.dangerous_function.desc"_lang,
|
||||
[this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Allow;
|
||||
}, [this] {
|
||||
this->m_dangerousFunctionsAllowed = DangerousFunctionPerms::Deny;
|
||||
}
|
||||
);
|
||||
|
||||
this->m_dangerousFunctionCalled = false;
|
||||
}
|
||||
|
||||
View::discardNavigationRequests();
|
||||
}
|
||||
|
||||
void ViewPatternEditor::drawConsole(ImVec2 size) {
|
||||
@ -689,7 +686,7 @@ namespace hex::plugin::builtin {
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void ViewPatternEditor::drawAlwaysVisible() {
|
||||
void ViewPatternEditor::drawAlwaysVisibleContent() {
|
||||
auto provider = ImHexApi::Provider::get();
|
||||
|
||||
auto open = this->m_sectionWindowDrawer.contains(provider);
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewProviderSettings::ViewProviderSettings() : hex::View("hex.builtin.view.provider_settings.name") {
|
||||
ViewProviderSettings::ViewProviderSettings() : View::Modal("hex.builtin.view.provider_settings.name") {
|
||||
EventManager::subscribe<EventProviderCreated>(this, [](const hex::prv::Provider *provider) {
|
||||
if (provider->hasLoadInterface() && !provider->shouldSkipLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
@ -29,48 +29,38 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewProviderSettings::drawContent() {
|
||||
auto provider = hex::ImHexApi::Provider::get();
|
||||
if (provider != nullptr) {
|
||||
bool settingsValid = provider->drawLoadInterface();
|
||||
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
void ViewProviderSettings::drawAlwaysVisible() {
|
||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.provider_settings.load_popup").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
|
||||
auto provider = hex::ImHexApi::Provider::get();
|
||||
if (provider != nullptr) {
|
||||
bool settingsValid = provider->drawLoadInterface();
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::BeginDisabled(!settingsValid);
|
||||
if (ImGui::Button("hex.builtin.common.open"_lang)) {
|
||||
if (provider->open()) {
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
else {
|
||||
ImGui::CloseCurrentPopup();
|
||||
auto errorMessage = provider->getErrorMessage();
|
||||
if (errorMessage.empty()) {
|
||||
PopupError::open("hex.builtin.view.provider_settings.load_error"_lang);
|
||||
} else {
|
||||
PopupError::open(hex::format("hex.builtin.view.provider_settings.load_error_details"_lang, errorMessage));
|
||||
}
|
||||
TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); });
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.cancel"_lang)) {
|
||||
ImGui::BeginDisabled(!settingsValid);
|
||||
if (ImGui::Button("hex.builtin.common.open"_lang)) {
|
||||
if (provider->open()) {
|
||||
EventManager::post<EventProviderOpened>(provider);
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
else {
|
||||
ImGui::CloseCurrentPopup();
|
||||
auto errorMessage = provider->getErrorMessage();
|
||||
if (errorMessage.empty()) {
|
||||
PopupError::open("hex.builtin.view.provider_settings.load_error"_lang);
|
||||
} else {
|
||||
PopupError::open(hex::format("hex.builtin.view.provider_settings.load_error_details"_lang, errorMessage));
|
||||
}
|
||||
TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); });
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::EndPopup();
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("hex.builtin.common.cancel"_lang)) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
TaskManager::doLater([=] { ImHexApi::Provider::remove(provider); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,8 +68,4 @@ namespace hex::plugin::builtin {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ViewProviderSettings::isAvailable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,12 +10,12 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewSettings::ViewSettings() : View("hex.builtin.view.settings.name") {
|
||||
ViewSettings::ViewSettings() : View::Floating("hex.builtin.view.settings.name") {
|
||||
// Handle window open requests
|
||||
EventManager::subscribe<RequestOpenWindow>(this, [this](const std::string &name) {
|
||||
if (name == "Settings") {
|
||||
TaskManager::doLater([this] {
|
||||
ImGui::OpenPopup(View::toWindowName("hex.builtin.view.settings.name").c_str());
|
||||
ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
}
|
||||
@ -24,7 +24,7 @@ namespace hex::plugin::builtin {
|
||||
// Add the settings menu item to the Extras menu
|
||||
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.extras" }, 3000);
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.settings.name"_lang }, 4000, Shortcut::None, [&, this] {
|
||||
TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.settings.name").c_str()); });
|
||||
TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); });
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
}
|
||||
@ -33,9 +33,8 @@ namespace hex::plugin::builtin {
|
||||
EventManager::unsubscribe<RequestOpenWindow>(this);
|
||||
}
|
||||
|
||||
void ViewSettings::drawContent() {
|
||||
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.settings.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) {
|
||||
void ViewSettings::drawAlwaysVisibleContent() {
|
||||
if (ImGui::BeginPopupModal(View::toWindowName(this->getUnlocalizedName()).c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoResize)) {
|
||||
if (ImGui::BeginTabBar("settings")) {
|
||||
auto &categories = ContentRegistry::Settings::impl::getSettings();
|
||||
|
||||
|
@ -27,11 +27,11 @@ namespace hex::plugin::builtin {
|
||||
using namespace std::literals::string_literals;
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
ViewStore::ViewStore() : View("hex.builtin.view.store.name") {
|
||||
ViewStore::ViewStore() : View::Floating("hex.builtin.view.store.name") {
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.store.name" }, 1000, Shortcut::None, [&, this] {
|
||||
if (this->m_requestStatus == RequestStatus::NotAttempted)
|
||||
this->refresh();
|
||||
TaskManager::doLater([] { ImGui::OpenPopup(View::toWindowName("hex.builtin.view.store.name").c_str()); });
|
||||
TaskManager::doLater([this] { ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str()); });
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
|
||||
@ -278,16 +278,10 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewStore::drawContent() {
|
||||
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.store.name").c_str(), &this->getWindowOpenState())) {
|
||||
if (this->m_requestStatus == RequestStatus::Failed)
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed), "hex.builtin.view.store.netfailed"_lang);
|
||||
|
||||
this->drawStore();
|
||||
if (this->m_requestStatus == RequestStatus::Failed)
|
||||
ImGuiExt::TextFormattedColored(ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed), "hex.builtin.view.store.netfailed"_lang);
|
||||
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
this->getWindowOpenState() = false;
|
||||
}
|
||||
this->drawStore();
|
||||
}
|
||||
|
||||
bool ViewStore::download(fs::ImHexPath pathType, const std::string &fileName, const std::string &url, bool update) {
|
||||
|
@ -7,129 +7,122 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewThemeManager::ViewThemeManager() : View("hex.builtin.view.theme_manager.name") {
|
||||
ViewThemeManager::ViewThemeManager() : View::Floating("hex.builtin.view.theme_manager.name") {
|
||||
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.extras", "hex.builtin.view.theme_manager.name" }, 2000, Shortcut::None, [&, this] {
|
||||
this->m_viewOpen = true;
|
||||
this->getWindowOpenState() = true;
|
||||
});
|
||||
}
|
||||
|
||||
void ViewThemeManager::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.theme_manager.name").c_str(), &this->m_viewOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking)) {
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.colors"_lang, true);
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.colors"_lang, true);
|
||||
|
||||
// Draw theme handlers
|
||||
ImGui::PushID(1);
|
||||
// Draw theme handlers
|
||||
ImGui::PushID(1);
|
||||
|
||||
|
||||
// Loop over each theme handler
|
||||
bool anyColorHovered = false;
|
||||
for (auto &[name, handler] : ThemeManager::getThemeHandlers()) {
|
||||
// Create a new collapsable header for each category
|
||||
if (ImGui::CollapsingHeader(name.c_str())) {
|
||||
// Loop over each theme handler
|
||||
bool anyColorHovered = false;
|
||||
for (auto &[name, handler] : ThemeManager::getThemeHandlers()) {
|
||||
// Create a new collapsable header for each category
|
||||
if (ImGui::CollapsingHeader(name.c_str())) {
|
||||
|
||||
// Loop over all the individual theme settings
|
||||
for (auto &[colorName, colorId] : handler.colorMap) {
|
||||
if (this->m_startingColor.has_value()) {
|
||||
if (this->m_hoveredColorId == colorId && this->m_hoveredHandlerName == name) {
|
||||
handler.setFunction(colorId, this->m_startingColor.value());
|
||||
}
|
||||
// Loop over all the individual theme settings
|
||||
for (auto &[colorName, colorId] : handler.colorMap) {
|
||||
if (this->m_startingColor.has_value()) {
|
||||
if (this->m_hoveredColorId == colorId && this->m_hoveredHandlerName == name) {
|
||||
handler.setFunction(colorId, this->m_startingColor.value());
|
||||
}
|
||||
}
|
||||
|
||||
// Get the current color value
|
||||
auto color = handler.getFunction(colorId);
|
||||
// Get the current color value
|
||||
auto color = handler.getFunction(colorId);
|
||||
|
||||
// Draw a color picker for the color
|
||||
if (ImGui::ColorEdit4(colorName.c_str(), reinterpret_cast<float*>(&color.Value), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf)) {
|
||||
// Update the color value
|
||||
handler.setFunction(colorId, color);
|
||||
// Draw a color picker for the color
|
||||
if (ImGui::ColorEdit4(colorName.c_str(), reinterpret_cast<float*>(&color.Value), ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf)) {
|
||||
// Update the color value
|
||||
handler.setFunction(colorId, color);
|
||||
EventManager::post<EventThemeChanged>();
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
anyColorHovered = true;
|
||||
|
||||
if (!this->m_hoveredColorId.has_value()) {
|
||||
this->m_hoveredColorId = colorId;
|
||||
this->m_startingColor = color;
|
||||
this->m_hoveredHandlerName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_hoveredHandlerName == name && this->m_startingColor.has_value() && this->m_hoveredColorId.has_value()) {
|
||||
auto flashingColor = this->m_startingColor.value();
|
||||
|
||||
const float flashProgress = std::min(1.0F, (1.0F + sinf(ImGui::GetTime() * 6)) / 2.0F);
|
||||
flashingColor.Value.x = std::lerp(flashingColor.Value.x / 2, 1.0F, flashProgress);
|
||||
flashingColor.Value.y = std::lerp(flashingColor.Value.y / 2, 1.0F, flashProgress);
|
||||
flashingColor.Value.z = flashingColor.Value.z / 2;
|
||||
flashingColor.Value.w = 1.0F;
|
||||
|
||||
handler.setFunction(*this->m_hoveredColorId, flashingColor);
|
||||
|
||||
if (!anyColorHovered) {
|
||||
handler.setFunction(this->m_hoveredColorId.value(), this->m_startingColor.value());
|
||||
this->m_startingColor.reset();
|
||||
this->m_hoveredColorId.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.styles"_lang);
|
||||
|
||||
// Draw style handlers
|
||||
ImGui::PushID(2);
|
||||
|
||||
// Loop over each style handler
|
||||
for (auto &[name, handler] : ThemeManager::getStyleHandlers()) {
|
||||
// Create a new collapsable header for each category
|
||||
if (ImGui::CollapsingHeader(name.c_str())) {
|
||||
|
||||
// Loop over all the individual style settings
|
||||
for (auto &[styleName, style] : handler.styleMap) {
|
||||
// Get the current style value
|
||||
auto &[value, min, max, needsScaling] = style;
|
||||
|
||||
// Styles can either be floats or ImVec2s
|
||||
// Determine which one it is and draw the appropriate slider
|
||||
if (auto floatValue = std::get_if<float*>(&value); floatValue != nullptr) {
|
||||
if (ImGui::SliderFloat(styleName.c_str(), *floatValue, min, max, "%.1f")) {
|
||||
EventManager::post<EventThemeChanged>();
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
anyColorHovered = true;
|
||||
|
||||
if (!this->m_hoveredColorId.has_value()) {
|
||||
this->m_hoveredColorId = colorId;
|
||||
this->m_startingColor = color;
|
||||
this->m_hoveredHandlerName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this->m_hoveredHandlerName == name && this->m_startingColor.has_value() && this->m_hoveredColorId.has_value()) {
|
||||
auto flashingColor = this->m_startingColor.value();
|
||||
|
||||
const float flashProgress = std::min(1.0F, (1.0F + sinf(ImGui::GetTime() * 6)) / 2.0F);
|
||||
flashingColor.Value.x = std::lerp(flashingColor.Value.x / 2, 1.0F, flashProgress);
|
||||
flashingColor.Value.y = std::lerp(flashingColor.Value.y / 2, 1.0F, flashProgress);
|
||||
flashingColor.Value.z = flashingColor.Value.z / 2;
|
||||
flashingColor.Value.w = 1.0F;
|
||||
|
||||
handler.setFunction(*this->m_hoveredColorId, flashingColor);
|
||||
|
||||
if (!anyColorHovered) {
|
||||
handler.setFunction(this->m_hoveredColorId.value(), this->m_startingColor.value());
|
||||
this->m_startingColor.reset();
|
||||
this->m_hoveredColorId.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.styles"_lang);
|
||||
|
||||
// Draw style handlers
|
||||
ImGui::PushID(2);
|
||||
|
||||
// Loop over each style handler
|
||||
for (auto &[name, handler] : ThemeManager::getStyleHandlers()) {
|
||||
// Create a new collapsable header for each category
|
||||
if (ImGui::CollapsingHeader(name.c_str())) {
|
||||
|
||||
// Loop over all the individual style settings
|
||||
for (auto &[styleName, style] : handler.styleMap) {
|
||||
// Get the current style value
|
||||
auto &[value, min, max, needsScaling] = style;
|
||||
|
||||
// Styles can either be floats or ImVec2s
|
||||
// Determine which one it is and draw the appropriate slider
|
||||
if (auto floatValue = std::get_if<float*>(&value); floatValue != nullptr) {
|
||||
if (ImGui::SliderFloat(styleName.c_str(), *floatValue, min, max, "%.1f")) {
|
||||
EventManager::post<EventThemeChanged>();
|
||||
}
|
||||
} else if (auto vecValue = std::get_if<ImVec2*>(&value); vecValue != nullptr) {
|
||||
if (ImGui::SliderFloat2(styleName.c_str(), &(*vecValue)->x, min, max, "%.1f")) {
|
||||
EventManager::post<EventThemeChanged>();
|
||||
}
|
||||
} else if (auto vecValue = std::get_if<ImVec2*>(&value); vecValue != nullptr) {
|
||||
if (ImGui::SliderFloat2(styleName.c_str(), &(*vecValue)->x, min, max, "%.1f")) {
|
||||
EventManager::post<EventThemeChanged>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// Draw export settings
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.export"_lang);
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.theme_manager.export.name"_lang, ICON_VS_SYMBOL_KEY, this->m_themeName);
|
||||
|
||||
// Draw the export buttons
|
||||
if (ImGui::Button("hex.builtin.view.theme_manager.save_theme"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, { { "ImHex Theme", "json" } }, [this](const std::fs::path &path){
|
||||
// Export the current theme as json
|
||||
auto json = ThemeManager::exportCurrentTheme(this->m_themeName);
|
||||
|
||||
// Write the json to the file
|
||||
wolv::io::File outputFile(path, wolv::io::File::Mode::Create);
|
||||
outputFile.writeString(json.dump(4));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::End();
|
||||
ImGui::PopID();
|
||||
|
||||
this->getWindowOpenState() = this->m_viewOpen;
|
||||
// Draw export settings
|
||||
ImGuiExt::Header("hex.builtin.view.theme_manager.export"_lang);
|
||||
ImGuiExt::InputTextIcon("hex.builtin.view.theme_manager.export.name"_lang, ICON_VS_SYMBOL_KEY, this->m_themeName);
|
||||
|
||||
// Draw the export buttons
|
||||
if (ImGui::Button("hex.builtin.view.theme_manager.save_theme"_lang, ImVec2(ImGui::GetContentRegionAvail().x, 0))) {
|
||||
fs::openFileBrowser(fs::DialogMode::Save, { { "ImHex Theme", "json" } }, [this](const std::fs::path &path){
|
||||
// Export the current theme as json
|
||||
auto json = ThemeManager::exportCurrentTheme(this->m_themeName);
|
||||
|
||||
// Write the json to the file
|
||||
wolv::io::File outputFile(path, wolv::io::File::Mode::Create);
|
||||
outputFile.writeString(json.dump(4));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -5,49 +5,45 @@
|
||||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
ViewTools::ViewTools() : View("hex.builtin.view.tools.name") {
|
||||
ViewTools::ViewTools() : View::Window("hex.builtin.view.tools.name") {
|
||||
this->m_dragStartIterator = ContentRegistry::Tools::impl::getEntries().end();
|
||||
}
|
||||
|
||||
void ViewTools::drawContent() {
|
||||
auto &tools = ContentRegistry::Tools::impl::getEntries();
|
||||
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.tools.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
// Draw all tools
|
||||
for (auto iter = tools.begin(); iter != tools.end(); ++iter) {
|
||||
auto &[name, function, detached] = *iter;
|
||||
|
||||
// Draw all tools
|
||||
for (auto iter = tools.begin(); iter != tools.end(); ++iter) {
|
||||
auto &[name, function, detached] = *iter;
|
||||
// If the tool has been detached from the main window, don't draw it here anymore
|
||||
if (detached) continue;
|
||||
|
||||
// If the tool has been detached from the main window, don't draw it here anymore
|
||||
if (detached) continue;
|
||||
// Draw the tool
|
||||
if (ImGui::CollapsingHeader(LangEntry(name))) {
|
||||
function();
|
||||
ImGui::NewLine();
|
||||
} else {
|
||||
// Handle dragging the tool out of the main window
|
||||
|
||||
// Draw the tool
|
||||
if (ImGui::CollapsingHeader(LangEntry(name))) {
|
||||
function();
|
||||
ImGui::NewLine();
|
||||
} else {
|
||||
// Handle dragging the tool out of the main window
|
||||
// If the user clicks on the header, start dragging the tool remember the iterator
|
||||
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == tools.end())
|
||||
this->m_dragStartIterator = iter;
|
||||
|
||||
// If the user clicks on the header, start dragging the tool remember the iterator
|
||||
if (ImGui::IsMouseClicked(0) && ImGui::IsItemActivated() && this->m_dragStartIterator == tools.end())
|
||||
this->m_dragStartIterator = iter;
|
||||
|
||||
// If the user released the mouse button, stop dragging the tool
|
||||
if (!ImGui::IsMouseDown(0))
|
||||
this->m_dragStartIterator = tools.end();
|
||||
|
||||
// Detach the tool if the user dragged it out of the main window
|
||||
if (!ImGui::IsItemHovered() && this->m_dragStartIterator == iter) {
|
||||
detached = true;
|
||||
}
|
||||
// If the user released the mouse button, stop dragging the tool
|
||||
if (!ImGui::IsMouseDown(0))
|
||||
this->m_dragStartIterator = tools.end();
|
||||
|
||||
// Detach the tool if the user dragged it out of the main window
|
||||
if (!ImGui::IsItemHovered() && this->m_dragStartIterator == iter) {
|
||||
detached = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewTools::drawAlwaysVisible() {
|
||||
void ViewTools::drawAlwaysVisibleContent() {
|
||||
// Make sure the tool windows never get drawn over the welcome screen
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return;
|
||||
|
@ -27,7 +27,7 @@ namespace hex::plugin::builtin {
|
||||
|
||||
using namespace wolv::literals;
|
||||
|
||||
ViewYara::ViewYara() : View("hex.builtin.view.yara.name") {
|
||||
ViewYara::ViewYara() : View::Window("hex.builtin.view.yara.name") {
|
||||
yr_initialize();
|
||||
|
||||
ContentRegistry::FileHandler::add({ ".yar", ".yara" }, [](const auto &path) {
|
||||
@ -99,153 +99,149 @@ namespace hex::plugin::builtin {
|
||||
}
|
||||
|
||||
void ViewYara::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.builtin.view.yara.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGuiExt::Header("hex.builtin.view.yara.header.rules"_lang, true);
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.yara.header.rules"_lang, true);
|
||||
|
||||
if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) {
|
||||
for (u32 i = 0; i < this->m_rules->size(); i++) {
|
||||
const bool selected = (this->m_selectedRule == i);
|
||||
if (ImGui::Selectable(wolv::util::toUTF8String((*this->m_rules)[i].first).c_str(), selected)) {
|
||||
this->m_selectedRule = i;
|
||||
}
|
||||
if (ImGui::BeginListBox("##rules", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 5))) {
|
||||
for (u32 i = 0; i < this->m_rules->size(); i++) {
|
||||
const bool selected = (this->m_selectedRule == i);
|
||||
if (ImGui::Selectable(wolv::util::toUTF8String((*this->m_rules)[i].first).c_str(), selected)) {
|
||||
this->m_selectedRule = i;
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Yara);
|
||||
std::vector<std::fs::path> paths;
|
||||
for (const auto &path : basePaths) {
|
||||
std::error_code error;
|
||||
for (const auto &entry : std::fs::recursive_directory_iterator(path, error)) {
|
||||
if (!entry.is_regular_file()) continue;
|
||||
if (entry.path().extension() != ".yara" && entry.path().extension() != ".yar") continue;
|
||||
|
||||
paths.push_back(entry);
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
if (ImGuiExt::IconButton(ICON_VS_ADD, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Yara);
|
||||
std::vector<std::fs::path> paths;
|
||||
for (const auto &path : basePaths) {
|
||||
std::error_code error;
|
||||
for (const auto &entry : std::fs::recursive_directory_iterator(path, error)) {
|
||||
if (!entry.is_regular_file()) continue;
|
||||
if (entry.path().extension() != ".yara" && entry.path().extension() != ".yar") continue;
|
||||
PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true,
|
||||
[&](const auto &path) {
|
||||
this->m_rules->push_back({ path.filename(), path });
|
||||
});
|
||||
}
|
||||
|
||||
paths.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Yara File", "yara" }, { "Yara File", "yar" } }, true,
|
||||
[&](const auto &path) {
|
||||
this->m_rules->push_back({ path.filename(), path });
|
||||
});
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
if (this->m_selectedRule < this->m_rules->size()) {
|
||||
this->m_rules->erase(this->m_rules->begin() + this->m_selectedRule);
|
||||
this->m_selectedRule = std::min(this->m_selectedRule, u32(this->m_rules->size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("hex.builtin.view.yara.match"_lang)) this->applyRules();
|
||||
ImGui::SameLine();
|
||||
|
||||
if (this->m_matcherTask.isRunning()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGuiExt::IconButton(ICON_VS_REMOVE, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
|
||||
if (this->m_selectedRule < this->m_rules->size()) {
|
||||
this->m_rules->erase(this->m_rules->begin() + this->m_selectedRule);
|
||||
this->m_selectedRule = std::min(this->m_selectedRule, u32(this->m_rules->size() - 1));
|
||||
}
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.yara.matching"_lang);
|
||||
}
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.yara.header.matches"_lang);
|
||||
|
||||
auto matchesTableSize = ImGui::GetContentRegionAvail();
|
||||
matchesTableSize.y *= 3.75 / 5.0;
|
||||
matchesTableSize.y -= ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
if (ImGui::BeginTable("matches", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, matchesTableSize)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.yara.matches.identifier"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("identifier"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.yara.matches.variable"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("variable"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.address"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("address"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size"));
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
if (!this->m_matches->empty() && (sortSpecs->SpecsDirty || this->m_sortedMatches->empty())) {
|
||||
this->m_sortedMatches->clear();
|
||||
std::transform(this->m_matches->begin(), this->m_matches->end(), std::back_inserter(*this->m_sortedMatches), [](auto &match) {
|
||||
return &match;
|
||||
});
|
||||
|
||||
std::sort(this->m_sortedMatches->begin(), this->m_sortedMatches->end(), [&sortSpecs](const YaraMatch *left, const YaraMatch *right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier"))
|
||||
return left->identifier < right->identifier;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable"))
|
||||
return left->variable < right->variable;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("address"))
|
||||
return left->address < right->address;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size"))
|
||||
return left->size < right->size;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending)
|
||||
std::reverse(this->m_sortedMatches->begin(), this->m_sortedMatches->end());
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("hex.builtin.view.yara.match"_lang)) this->applyRules();
|
||||
ImGui::SameLine();
|
||||
if (!this->m_matcherTask.isRunning()) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_sortedMatches->size());
|
||||
|
||||
if (this->m_matcherTask.isRunning()) {
|
||||
ImGui::SameLine();
|
||||
ImGuiExt::TextSpinner("hex.builtin.view.yara.matching"_lang);
|
||||
}
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *(*this->m_sortedMatches)[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(address, size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(identifier.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(variableName.c_str());
|
||||
|
||||
ImGuiExt::Header("hex.builtin.view.yara.header.matches"_lang);
|
||||
|
||||
auto matchesTableSize = ImGui::GetContentRegionAvail();
|
||||
matchesTableSize.y *= 3.75 / 5.0;
|
||||
matchesTableSize.y -= ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
if (ImGui::BeginTable("matches", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, matchesTableSize)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("hex.builtin.view.yara.matches.identifier"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("identifier"));
|
||||
ImGui::TableSetupColumn("hex.builtin.view.yara.matches.variable"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("variable"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.address"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("address"));
|
||||
ImGui::TableSetupColumn("hex.builtin.common.size"_lang, ImGuiTableColumnFlags_PreferSortAscending, 0, ImGui::GetID("size"));
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
if (!this->m_matches->empty() && (sortSpecs->SpecsDirty || this->m_sortedMatches->empty())) {
|
||||
this->m_sortedMatches->clear();
|
||||
std::transform(this->m_matches->begin(), this->m_matches->end(), std::back_inserter(*this->m_sortedMatches), [](auto &match) {
|
||||
return &match;
|
||||
});
|
||||
|
||||
std::sort(this->m_sortedMatches->begin(), this->m_sortedMatches->end(), [&sortSpecs](const YaraMatch *left, const YaraMatch *right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("identifier"))
|
||||
return left->identifier < right->identifier;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("variable"))
|
||||
return left->variable < right->variable;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("address"))
|
||||
return left->address < right->address;
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size"))
|
||||
return left->size < right->size;
|
||||
else
|
||||
return false;
|
||||
});
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Descending)
|
||||
std::reverse(this->m_sortedMatches->begin(), this->m_sortedMatches->end());
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
if (!this->m_matcherTask.isRunning()) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_sortedMatches->size());
|
||||
|
||||
while (clipper.Step()) {
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &[identifier, variableName, address, size, wholeDataMatch, highlightId, tooltipId] = *(*this->m_sortedMatches)[i];
|
||||
ImGui::TableNextRow();
|
||||
if (!wholeDataMatch) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable("match", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(address, size);
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(identifier.c_str());
|
||||
ImGuiExt::TextFormatted("0x{0:X} : 0x{1:X}", address, address + size - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(variableName.c_str());
|
||||
|
||||
if (!wholeDataMatch) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:X} : 0x{1:X}", address, address + size - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormatted("0x{0:X}", size);
|
||||
} else {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.yara.whole_data"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
ImGuiExt::TextFormatted("0x{0:X}", size);
|
||||
} else {
|
||||
ImGui::TableNextColumn();
|
||||
ImGuiExt::TextFormattedColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "{}", "hex.builtin.view.yara.whole_data"_lang);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("");
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
clipper.End();
|
||||
}
|
||||
|
||||
auto consoleSize = ImGui::GetContentRegionAvail();
|
||||
|
||||
if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
ImGuiListClipper clipper;
|
||||
|
||||
clipper.Begin(this->m_consoleMessages.size());
|
||||
while (clipper.Step())
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &message = this->m_consoleMessages[i];
|
||||
|
||||
if (ImGui::Selectable(message.c_str()))
|
||||
ImGui::SetClipboardText(message.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
auto consoleSize = ImGui::GetContentRegionAvail();
|
||||
|
||||
if (ImGui::BeginChild("##console", consoleSize, true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
ImGuiListClipper clipper;
|
||||
|
||||
clipper.Begin(this->m_consoleMessages.size());
|
||||
while (clipper.Step())
|
||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
const auto &message = this->m_consoleMessages[i];
|
||||
|
||||
if (ImGui::Selectable(message.c_str()))
|
||||
ImGui::SetClipboardText(message.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void ViewYara::clearResult() {
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
namespace hex::plugin::windows {
|
||||
|
||||
class ViewTTYConsole : public View {
|
||||
class ViewTTYConsole : public View::Window {
|
||||
public:
|
||||
ViewTTYConsole();
|
||||
~ViewTTYConsole() override = default;
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace hex::plugin::windows {
|
||||
|
||||
ViewTTYConsole::ViewTTYConsole() : View("hex.windows.view.tty_console.name") {
|
||||
ViewTTYConsole::ViewTTYConsole() : View::Window("hex.windows.view.tty_console.name") {
|
||||
this->m_comPorts = getAvailablePorts();
|
||||
this->m_transmitDataBuffer.resize(0xFFF, 0x00);
|
||||
this->m_receiveDataBuffer.reserve(0xFFF);
|
||||
@ -17,156 +17,152 @@ namespace hex::plugin::windows {
|
||||
}
|
||||
|
||||
void ViewTTYConsole::drawContent() {
|
||||
if (ImGui::Begin(View::toWindowName("hex.windows.view.tty_console.name").c_str(), &this->getWindowOpenState())) {
|
||||
ImGuiExt::Header("hex.windows.view.tty_console.config"_lang, true);
|
||||
|
||||
ImGuiExt::Header("hex.windows.view.tty_console.config"_lang, true);
|
||||
bool connected = this->m_portHandle != INVALID_HANDLE_VALUE;
|
||||
|
||||
bool connected = this->m_portHandle != INVALID_HANDLE_VALUE;
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, connected);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, connected ? 0.5F : 1.0F);
|
||||
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, connected);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, connected ? 0.5F : 1.0F);
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.port"_lang, &this->m_selectedPort, [](void *data, int idx) {
|
||||
auto &ports = *static_cast<std::vector<std::pair<std::string, std::string>> *>(data);
|
||||
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.port"_lang, &this->m_selectedPort, [](void *data, int idx) {
|
||||
auto &ports = *static_cast<std::vector<std::pair<std::string, std::string>> *>(data);
|
||||
return ports[idx].first.c_str();
|
||||
},
|
||||
&this->m_comPorts,
|
||||
this->m_comPorts.size());
|
||||
|
||||
return ports[idx].first.c_str();
|
||||
},
|
||||
&this->m_comPorts,
|
||||
this->m_comPorts.size());
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.windows.view.tty_console.reload"_lang))
|
||||
this->m_comPorts = getAvailablePorts();
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("hex.windows.view.tty_console.reload"_lang))
|
||||
this->m_comPorts = getAvailablePorts();
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.baud"_lang, &this->m_selectedBaudRate, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.baud"_lang, &this->m_selectedBaudRate, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
return ViewTTYConsole::BaudRates[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::BaudRates.size());
|
||||
|
||||
return ViewTTYConsole::BaudRates[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::BaudRates.size());
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.num_bits"_lang, &this->m_selectedNumBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.num_bits"_lang, &this->m_selectedNumBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
return ViewTTYConsole::NumBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::NumBits.size());
|
||||
|
||||
return ViewTTYConsole::NumBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::NumBits.size());
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.stop_bits"_lang, &this->m_selectedStopBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.stop_bits"_lang, &this->m_selectedStopBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
return ViewTTYConsole::StopBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::StopBits.size());
|
||||
|
||||
return ViewTTYConsole::StopBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::StopBits.size());
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.parity_bits"_lang, &this->m_selectedParityBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
|
||||
ImGui::Combo(
|
||||
"hex.windows.view.tty_console.parity_bits"_lang, &this->m_selectedParityBits, [](void *data, int idx) {
|
||||
hex::unused(data);
|
||||
return ViewTTYConsole::ParityBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::ParityBits.size());
|
||||
|
||||
return ViewTTYConsole::ParityBits[idx];
|
||||
},
|
||||
nullptr,
|
||||
ViewTTYConsole::ParityBits.size());
|
||||
ImGui::Checkbox("hex.windows.view.tty_console.cts"_lang, &this->m_hasCTSFlowControl);
|
||||
|
||||
ImGui::Checkbox("hex.windows.view.tty_console.cts"_lang, &this->m_hasCTSFlowControl);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::NewLine();
|
||||
if (this->m_portHandle == INVALID_HANDLE_VALUE) {
|
||||
if (ImGui::Button("hex.windows.view.tty_console.connect"_lang))
|
||||
if (!this->connect())
|
||||
EventManager::post<RequestOpenErrorPopup>("hex.windows.view.tty_console.connect_error"_lang);
|
||||
} else {
|
||||
if (ImGui::Button("hex.windows.view.tty_console.disconnect"_lang))
|
||||
this->disconnect();
|
||||
}
|
||||
|
||||
if (this->m_portHandle == INVALID_HANDLE_VALUE) {
|
||||
if (ImGui::Button("hex.windows.view.tty_console.connect"_lang))
|
||||
if (!this->connect())
|
||||
EventManager::post<RequestOpenErrorPopup>("hex.windows.view.tty_console.connect_error"_lang);
|
||||
} else {
|
||||
if (ImGui::Button("hex.windows.view.tty_console.disconnect"_lang))
|
||||
this->disconnect();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("hex.windows.view.tty_console.clear"_lang)) {
|
||||
std::scoped_lock lock(this->m_receiveBufferMutex);
|
||||
|
||||
if (ImGui::Button("hex.windows.view.tty_console.clear"_lang)) {
|
||||
this->m_receiveDataBuffer.clear();
|
||||
this->m_wrapPositions.clear();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Checkbox("hex.windows.view.tty_console.auto_scroll"_lang, &this->m_shouldAutoScroll);
|
||||
|
||||
ImGuiExt::Header("hex.windows.view.tty_console.console"_lang);
|
||||
|
||||
auto consoleSize = ImGui::GetContentRegionAvail();
|
||||
consoleSize.y -= ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 4;
|
||||
if (ImGui::BeginChild("##scrolling", consoleSize, true, ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_wrapPositions.size(), ImGui::GetTextLineHeight());
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
|
||||
while (clipper.Step()) {
|
||||
std::scoped_lock lock(this->m_receiveBufferMutex);
|
||||
|
||||
this->m_receiveDataBuffer.clear();
|
||||
this->m_wrapPositions.clear();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Checkbox("hex.windows.view.tty_console.auto_scroll"_lang, &this->m_shouldAutoScroll);
|
||||
|
||||
ImGuiExt::Header("hex.windows.view.tty_console.console"_lang);
|
||||
|
||||
auto consoleSize = ImGui::GetContentRegionAvail();
|
||||
consoleSize.y -= ImGui::GetTextLineHeight() + ImGui::GetStyle().FramePadding.y * 4;
|
||||
if (ImGui::BeginChild("##scrolling", consoleSize, true, ImGuiWindowFlags_HorizontalScrollbar)) {
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_wrapPositions.size(), ImGui::GetTextLineHeight());
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
|
||||
while (clipper.Step()) {
|
||||
std::scoped_lock lock(this->m_receiveBufferMutex);
|
||||
|
||||
for (int i = clipper.DisplayStart + 1; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions[i - 1], this->m_receiveDataBuffer.data() + this->m_wrapPositions[i]);
|
||||
}
|
||||
|
||||
if (!this->m_receiveDataBuffer.empty() && !this->m_wrapPositions.empty())
|
||||
if (static_cast<size_t>(clipper.DisplayEnd) >= this->m_wrapPositions.size() - 1)
|
||||
ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions.back());
|
||||
|
||||
if (this->m_shouldAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
||||
ImGui::SetScrollHereY(0.0F);
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGui::InputText("##transmit", this->m_transmitDataBuffer.data(), this->m_transmitDataBuffer.size() - 2, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
auto size = strlen(this->m_transmitDataBuffer.data());
|
||||
|
||||
this->m_transmitDataBuffer[size + 0] = '\n';
|
||||
this->m_transmitDataBuffer[size + 1] = 0x00;
|
||||
|
||||
this->transmitData(this->m_transmitDataBuffer);
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && this->m_portHandle != INVALID_HANDLE_VALUE && !this->m_transmitting)
|
||||
ImGui::OpenPopup("ConsoleMenu");
|
||||
|
||||
if (ImGui::BeginPopup("ConsoleMenu")) {
|
||||
|
||||
static std::vector<char> buffer(2, 0x00);
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_etx"_lang, "CTRL + C")) {
|
||||
buffer[0] = 0x03;
|
||||
this->transmitData(buffer);
|
||||
}
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_eot"_lang, "CTRL + D")) {
|
||||
buffer[0] = 0x04;
|
||||
this->transmitData(buffer);
|
||||
}
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_sub"_lang, "CTRL + Z")) {
|
||||
buffer[0] = 0x1A;
|
||||
this->transmitData(buffer);
|
||||
for (int i = clipper.DisplayStart + 1; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions[i - 1], this->m_receiveDataBuffer.data() + this->m_wrapPositions[i]);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
if (!this->m_receiveDataBuffer.empty() && !this->m_wrapPositions.empty())
|
||||
if (static_cast<size_t>(clipper.DisplayEnd) >= this->m_wrapPositions.size() - 1)
|
||||
ImGui::TextUnformatted(this->m_receiveDataBuffer.data() + this->m_wrapPositions.back());
|
||||
|
||||
if (this->m_shouldAutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
|
||||
ImGui::SetScrollHereY(0.0F);
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::PushItemWidth(-1);
|
||||
if (ImGui::InputText("##transmit", this->m_transmitDataBuffer.data(), this->m_transmitDataBuffer.size() - 2, ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
auto size = strlen(this->m_transmitDataBuffer.data());
|
||||
|
||||
this->m_transmitDataBuffer[size + 0] = '\n';
|
||||
this->m_transmitDataBuffer[size + 1] = 0x00;
|
||||
|
||||
this->transmitData(this->m_transmitDataBuffer);
|
||||
ImGui::SetKeyboardFocusHere(0);
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsItemHovered() && this->m_portHandle != INVALID_HANDLE_VALUE && !this->m_transmitting)
|
||||
ImGui::OpenPopup("ConsoleMenu");
|
||||
|
||||
if (ImGui::BeginPopup("ConsoleMenu")) {
|
||||
|
||||
static std::vector<char> buffer(2, 0x00);
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_etx"_lang, "CTRL + C")) {
|
||||
buffer[0] = 0x03;
|
||||
this->transmitData(buffer);
|
||||
}
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_eot"_lang, "CTRL + D")) {
|
||||
buffer[0] = 0x04;
|
||||
this->transmitData(buffer);
|
||||
}
|
||||
if (ImGui::MenuItem("hex.windows.view.tty_console.send_sub"_lang, "CTRL + Z")) {
|
||||
buffer[0] = 0x1A;
|
||||
this->transmitData(buffer);
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> ViewTTYConsole::getAvailablePorts() const {
|
||||
|
Loading…
Reference in New Issue
Block a user