mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 15:57:03 -05:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4402120ffc | ||
|
|
b3fffdf530 | ||
|
|
8d6d959e17 | ||
|
|
43f5cc622e | ||
|
|
5f025bcbcc | ||
|
|
8297e22f10 | ||
|
|
989eade5d7 | ||
|
|
cd4de2ff96 | ||
|
|
73d66365e9 | ||
|
|
fbd4e593d2 | ||
|
|
033ef3889a | ||
|
|
0ce1b5d40b | ||
|
|
ed4ed6b433 | ||
|
|
4cd18b8358 | ||
|
|
fb85f272a1 | ||
|
|
28bb28b79c | ||
|
|
e3b6cfd54f | ||
|
|
bf6ed3d540 | ||
|
|
9c0a270d90 | ||
|
|
5112c3aa1e | ||
|
|
57dcf6cc93 | ||
|
|
48296775ae | ||
|
|
e3cb078306 | ||
|
|
2f78a10e4c | ||
|
|
f3e2e35533 | ||
|
|
0d9175dc15 | ||
|
|
302caba403 | ||
|
|
920b32b432 | ||
|
|
81e5c945b4 | ||
|
|
78ea4276ae | ||
|
|
12a36d08e2 | ||
|
|
e4879f7546 | ||
|
|
34b8f481e1 | ||
|
|
f36014194d | ||
|
|
763d1f0e2d | ||
|
|
5c6fb302d9 | ||
|
|
f748b75a19 | ||
|
|
6a815d5ebb | ||
|
|
bfb079cb4f | ||
|
|
89afbd1aef | ||
|
|
ed9922c8a9 | ||
|
|
3fe231cdb0 | ||
|
|
269af11eb4 | ||
|
|
6ed3936424 | ||
|
|
24c8fc6957 | ||
|
|
9965322505 | ||
|
|
9b04373809 | ||
|
|
6fffc589bf | ||
|
|
0889764bcc | ||
|
|
e40bb5c498 | ||
|
|
a255e062be | ||
|
|
02c3821ea7 | ||
|
|
e61dfa0927 | ||
|
|
c8304eb497 | ||
|
|
6e21f703ab | ||
|
|
7550cf394c |
@@ -5,9 +5,11 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLFW REQUIRED glfw3)
|
||||
pkg_search_module(GLM REQUIRED glm)
|
||||
pkg_search_module(CAPSTONE REQUIRED capstone)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD")
|
||||
|
||||
if (WIN32)
|
||||
@@ -24,6 +26,7 @@ add_executable(ImHex
|
||||
source/lang/lexer.cpp
|
||||
source/lang/parser.cpp
|
||||
source/lang/validator.cpp
|
||||
source/lang/evaluator.cpp
|
||||
|
||||
source/provider/file_provider.cpp
|
||||
|
||||
@@ -35,22 +38,27 @@ add_executable(ImHex
|
||||
source/views/view_help.cpp
|
||||
source/views/view_tools.cpp
|
||||
source/views/view_strings.cpp
|
||||
source/views/view_data_inspector.cpp
|
||||
source/views/view_disassembler.cpp
|
||||
|
||||
libs/glad/source/glad.c
|
||||
|
||||
libs/ImGui/source/imgui.cpp
|
||||
libs/ImGui/source/imgui_draw.cpp
|
||||
libs/ImGui/source/imgui_widgets.cpp
|
||||
libs/ImGui/source/imgui_demo.cpp
|
||||
libs/ImGui/source/imgui_impl_glfw.cpp
|
||||
libs/ImGui/source/imgui_impl_opengl3.cpp
|
||||
libs/ImGui/source/ImGuiFileBrowser.cpp
|
||||
libs/ImGui/source/TextEditor.cpp
|
||||
|
||||
res.rc
|
||||
resource.rc
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a)
|
||||
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib libcrypto.a libwinpthread.a libcapstone.a)
|
||||
endif (WIN32)
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so)
|
||||
target_link_libraries(ImHex libglfw.so libmagic.so libcrypto.so libdl.so libcapstone.so)
|
||||
endif (UNIX)
|
||||
@@ -4,4 +4,4 @@ A Hex editor written in C++ using OpenGL, GLFW and Dear ImGui
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -7,25 +7,27 @@ namespace hex {
|
||||
|
||||
enum class Events {
|
||||
DataChanged,
|
||||
PatternChanged
|
||||
PatternChanged,
|
||||
FileDropped,
|
||||
ByteSelected
|
||||
};
|
||||
|
||||
struct EventHandler {
|
||||
void *sender;
|
||||
Events eventType;
|
||||
std::function<void(void*)> callback;
|
||||
std::function<void(const void*)> callback;
|
||||
};
|
||||
|
||||
class EventManager {
|
||||
public:
|
||||
|
||||
void post(Events eventType, void *userData) {
|
||||
void post(Events eventType, const void *userData) {
|
||||
for (auto &handler : this->m_eventHandlers)
|
||||
if (eventType == handler.eventType)
|
||||
handler.callback(userData);
|
||||
}
|
||||
|
||||
void subscribe(Events eventType, void *sender, std::function<void(void*)> callback) {
|
||||
void subscribe(Events eventType, void *sender, std::function<void(const void*)> callback) {
|
||||
for (auto &handler : this->m_eventHandlers)
|
||||
if (eventType == handler.eventType && sender == handler.sender)
|
||||
return;
|
||||
|
||||
@@ -16,4 +16,10 @@ using s64 = std::int64_t;
|
||||
using s128 = __int128_t;
|
||||
|
||||
#include "lang/result.hpp"
|
||||
#include "lang/results.hpp"
|
||||
#include "lang/results.hpp"
|
||||
|
||||
#if defined(__EMX__) || defined (WIN32)
|
||||
#define MAGIC_PATH_SEPARATOR ";"
|
||||
#else
|
||||
#define MAGIC_PATH_SEPARATOR ":"
|
||||
#endif
|
||||
@@ -14,7 +14,9 @@ namespace hex::lang {
|
||||
VariableDecl,
|
||||
TypeDecl,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Bitfield,
|
||||
Scope,
|
||||
};
|
||||
|
||||
@@ -29,20 +31,24 @@ namespace hex::lang {
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeVariableDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1)
|
||||
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize) { }
|
||||
explicit ASTNodeVariableDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "", std::optional<u64> offset = { }, size_t arraySize = 1, std::optional<std::string> arraySizeVariable = { }, std::optional<u8> pointerSize = { })
|
||||
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize) { }
|
||||
|
||||
const Token::TypeToken::Type& getVariableType() const { return this->m_type; }
|
||||
const std::string& getCustomVariableTypeName() const { return this->m_customTypeName; }
|
||||
const std::string& getVariableName() const { return this->m_name; };
|
||||
std::optional<u64> getOffset() const { return this->m_offset; }
|
||||
size_t getArraySize() const { return this->m_arraySize; }
|
||||
std::optional<std::string> getArraySizeVariable() const { return this->m_arraySizeVariable; }
|
||||
std::optional<u8> getPointerSize() const { return this->m_pointerSize; }
|
||||
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
std::optional<u64> m_offset;
|
||||
size_t m_arraySize;
|
||||
std::optional<std::string> m_arraySizeVariable;
|
||||
std::optional<u8> m_pointerSize;
|
||||
};
|
||||
|
||||
class ASTNodeScope : public ASTNode {
|
||||
@@ -66,6 +72,30 @@ namespace hex::lang {
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeUnion : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeUnion(std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Union), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeBitField : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeBitField(std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
||||
: ASTNode(Type::Bitfield), m_name(name), m_fields(fields) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<std::pair<std::string, size_t>> &getFields() { return this->m_fields; }
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
};
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
|
||||
39
include/lang/evaluator.hpp
Normal file
39
include/lang/evaluator.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator(prv::Provider* &provider, std::endian dataEndianess);
|
||||
|
||||
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, ASTNode*> m_types;
|
||||
prv::Provider* &m_provider;
|
||||
std::endian m_dataEndianess;
|
||||
|
||||
|
||||
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
598
include/lang/pattern_data.hpp
Normal file
598
include/lang/pattern_data.hpp
Normal file
@@ -0,0 +1,598 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string makeDisplayable(u8 *data, size_t size) {
|
||||
std::string result;
|
||||
for (u8* c = data; c < (data + size); c++) {
|
||||
if (iscntrl(*c) || *c > 0x7F)
|
||||
result += " ";
|
||||
else
|
||||
result += *c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PatternData {
|
||||
public:
|
||||
enum class Type { Padding, Unsigned, Signed, Float, Character, String, Struct, Union, Array, Enum };
|
||||
|
||||
PatternData(Type type, u64 offset, size_t size, const std::string &name, u32 color = 0)
|
||||
: m_type(type), m_offset(offset), m_size(size), m_color(color), m_name(name) {
|
||||
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
|
||||
|
||||
if (color != 0)
|
||||
return;
|
||||
|
||||
this->m_color = Palette[PatternData::s_paletteOffset++];
|
||||
|
||||
if (PatternData::s_paletteOffset >= (sizeof(Palette) / sizeof(u32)))
|
||||
PatternData::s_paletteOffset = 0;
|
||||
}
|
||||
virtual ~PatternData() = default;
|
||||
|
||||
[[nodiscard]] Type getPatternType() const { return this->m_type; }
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
|
||||
[[nodiscard]] const std::string& getName() const { return this->m_name; }
|
||||
void setName(std::string name) { this->m_name = name; }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
void setColor(u32 color) { this->m_color = color; }
|
||||
|
||||
virtual void createEntry(prv::Provider* &provider) = 0;
|
||||
virtual std::string getTypeName() = 0;
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
return this->getColor();
|
||||
else
|
||||
return { };
|
||||
}
|
||||
|
||||
virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) { }
|
||||
|
||||
static bool sortPatternDataTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, lang::PatternData* left, lang::PatternData* right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getName() > right->getName();
|
||||
else
|
||||
return left->getName() < right->getName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (PatternData::s_endianess != std::endian::native) {
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
}
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
||||
static void setEndianess(std::endian endianess) { PatternData::s_endianess = endianess; }
|
||||
|
||||
protected:
|
||||
void createDefaultEntry(std::string value) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", value.c_str());
|
||||
}
|
||||
|
||||
protected:
|
||||
static inline std::endian s_endianess = std::endian::native;
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u64 m_offset;
|
||||
size_t m_size;
|
||||
|
||||
u32 m_color;
|
||||
std::string m_name;
|
||||
|
||||
static inline u8 s_paletteOffset = 0;
|
||||
|
||||
};
|
||||
|
||||
class PatternDataPadding : public PatternData {
|
||||
public:
|
||||
PatternDataPadding(u64 offset, size_t size) : PatternData(Type::Padding, offset, size, "", 0x00FFFFFF) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataPointer : public PatternData {
|
||||
public:
|
||||
PatternDataPointer(u64 offset, size_t size, const std::string &name, PatternData *pointedAt, u32 color = 0)
|
||||
: PatternData(Type::Unsigned, offset, size, name, color), m_pointedAt(pointedAt) {
|
||||
this->m_pointedAt->setName("*" + this->m_pointedAt->getName());
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
|
||||
|
||||
if (open) {
|
||||
this->m_pointedAt->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::optional<u32> highlightBytes(size_t offset) {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()))
|
||||
return this->getColor();
|
||||
else if (auto color = this->m_pointedAt->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
else
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "Pointer";
|
||||
}
|
||||
|
||||
private:
|
||||
PatternData *m_pointedAt;
|
||||
};
|
||||
|
||||
class PatternDataUnsigned : public PatternData {
|
||||
public:
|
||||
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Unsigned, offset, size, name, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
||||
|
||||
this->createDefaultEntry(hex::format("%lu (0x%0*lx)", data, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "u8";
|
||||
case 2: return "u16";
|
||||
case 4: return "u32";
|
||||
case 8: return "u64";
|
||||
case 16: return "u128";
|
||||
default: return "Unsigned data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataSigned : public PatternData {
|
||||
public:
|
||||
PatternDataSigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Signed, offset, size, name, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
|
||||
|
||||
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
||||
|
||||
this->createDefaultEntry(hex::format("%ld (0x%0*lx)", signedData, this->getSize() * 2, data));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "s8";
|
||||
case 2: return "s16";
|
||||
case 4: return "s32";
|
||||
case 8: return "s64";
|
||||
case 16: return "s128";
|
||||
default: return "Signed data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataFloat : public PatternData {
|
||||
public:
|
||||
PatternDataFloat(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Float, offset, size, name, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
double formatData = 0;
|
||||
if (this->getSize() == 4) {
|
||||
float data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
data = hex::changeEndianess(data, 4, PatternData::s_endianess);
|
||||
|
||||
formatData = data;
|
||||
} else if (this->getSize() == 8) {
|
||||
double data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
data = hex::changeEndianess(data, 8, PatternData::s_endianess);
|
||||
|
||||
formatData = data;
|
||||
}
|
||||
|
||||
this->createDefaultEntry(hex::format("%f (0x%0*lx)", formatData, this->getSize() * 2, formatData));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 4: return "float";
|
||||
case 8: return "double";
|
||||
default: return "Floating point data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataCharacter : public PatternData {
|
||||
public:
|
||||
PatternDataCharacter(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::Character, offset, size, name, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
char character;
|
||||
provider->read(this->getOffset(), &character, 1);
|
||||
|
||||
this->createDefaultEntry(hex::format("'%c'", character));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "Character";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataString : public PatternData {
|
||||
public:
|
||||
PatternDataString(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(Type::String, offset, size, name, color) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), this->getSize());
|
||||
buffer[this->getSize()] = '\0';
|
||||
|
||||
this->createDefaultEntry(hex::format("\"%s\"", makeDisplayable(buffer.data(), this->getSize()).c_str()));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "String";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataArray : public PatternData {
|
||||
public:
|
||||
PatternDataArray(u64 offset, size_t size, const std::string &name, const std::vector<PatternData*> & entries, u32 color = 0)
|
||||
: PatternData(Type::Array, offset, size, name, color), m_entries(entries) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_entries)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &entry : this->m_entries) {
|
||||
if (auto color = entry->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]";
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<PatternData*> m_entries;
|
||||
};
|
||||
|
||||
class PatternDataStruct : public PatternData {
|
||||
public:
|
||||
PatternDataStruct(u64 offset, size_t size, const std::string &name, const std::string &structName, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(Type::Struct, offset, size, name, color), m_structName(structName), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &member : this->m_members) {
|
||||
if (auto color = member->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers = this->m_members;
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) {
|
||||
return PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "struct " + this->m_structName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_structName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataUnion : public PatternData {
|
||||
public:
|
||||
PatternDataUnion(u64 offset, size_t size, const std::string &name, const std::string &unionName, const std::vector<PatternData*> & members, u32 color = 0)
|
||||
: PatternData(Type::Union, offset, size, name, color), m_unionName(unionName), m_members(members), m_sortedMembers(members) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->createEntry(provider);
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::optional<u32> highlightBytes(size_t offset) override{
|
||||
for (auto &member : this->m_members) {
|
||||
if (auto color = member->highlightBytes(offset); color.has_value())
|
||||
return color.value();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers = this->m_members;
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](PatternData *left, PatternData *right) {
|
||||
return PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "union " + this->m_unionName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_unionName;
|
||||
std::vector<PatternData*> m_members;
|
||||
std::vector<PatternData*> m_sortedMembers;
|
||||
};
|
||||
|
||||
class PatternDataEnum : public PatternData {
|
||||
public:
|
||||
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), PatternData::s_endianess);
|
||||
|
||||
std::string valueString = this->m_enumName + "::";
|
||||
|
||||
bool foundValue = false;
|
||||
for (auto &[entryValue, entryName] : this->m_enumValues) {
|
||||
if (value == entryValue) {
|
||||
valueString += entryName;
|
||||
foundValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundValue)
|
||||
valueString += "???";
|
||||
|
||||
this->createDefaultEntry(hex::format("%s (0x%0*lx)", valueString.c_str(), this->getSize() * 2, value));
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "enum " + this->m_enumName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_enumName;
|
||||
std::vector<std::pair<u64, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
class PatternDataBitfield : public PatternData {
|
||||
public:
|
||||
PatternDataBitfield(u64 offset, size_t size, const std::string &name, const std::string &bitfieldName, std::vector<std::pair<std::string, size_t>> fields, u32 color = 0)
|
||||
: PatternData(Type::Enum, offset, size, name, color), m_bitfieldName(bitfieldName), m_fields(fields) { }
|
||||
|
||||
void createEntry(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), PatternData::s_endianess);
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(0x00FFFFFF), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_AlphaPreview);
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("{ %llx }", value);
|
||||
|
||||
if (open) {
|
||||
u16 bitOffset = 0;
|
||||
for (auto &[entryName, entrySize] : this->m_fields) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
|
||||
ImGui::TableNextColumn();
|
||||
if (entrySize == 1)
|
||||
ImGui::Text("%llu bit", entrySize);
|
||||
else
|
||||
ImGui::Text("%llu bits", entrySize);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", entryName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llx", hex::extract((bitOffset + entrySize) - 1, bitOffset, value));
|
||||
bitOffset += entrySize;
|
||||
}
|
||||
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "bitfield " + this->m_bitfieldName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_bitfieldName;
|
||||
std::vector<std::pair<std::string, size_t>> m_fields;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
@@ -14,10 +16,16 @@ namespace hex::lang {
|
||||
public:
|
||||
Preprocessor();
|
||||
|
||||
std::pair<Result, std::string> preprocess(const std::string& code, bool applyDefines = true);
|
||||
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = true);
|
||||
|
||||
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
|
||||
void addDefaultPragramHandlers();
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
|
||||
|
||||
std::set<std::pair<std::string, std::string>> m_defines;
|
||||
std::set<std::pair<std::string, std::string>> m_pragmas;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -9,5 +9,6 @@ namespace hex::lang {
|
||||
constexpr Result ResultPreprocessingError(1, 1);
|
||||
constexpr Result ResultLexicalError(2, 1);
|
||||
constexpr Result ResultParseError(3, 1);
|
||||
constexpr Result ResultEvaluatorError(4, 1);
|
||||
|
||||
}
|
||||
@@ -25,8 +25,10 @@ namespace hex::lang {
|
||||
struct KeywordToken {
|
||||
enum class Keyword {
|
||||
Struct,
|
||||
Union,
|
||||
Using,
|
||||
Enum
|
||||
Enum,
|
||||
Bitfield
|
||||
} keyword;
|
||||
} keywordToken;
|
||||
struct IdentifierToken {
|
||||
@@ -36,7 +38,8 @@ namespace hex::lang {
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit
|
||||
Inherit,
|
||||
Star
|
||||
} op;
|
||||
} operatorToken;
|
||||
struct IntegerToken {
|
||||
@@ -56,7 +59,8 @@ namespace hex::lang {
|
||||
Signed128Bit = 0x101,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
CustomType = 0x00
|
||||
CustomType = 0x00,
|
||||
Padding = 0x1F
|
||||
} type;
|
||||
} typeToken;
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace hex::prv {
|
||||
|
||||
void read(u64 offset, void *buffer, size_t size) override;
|
||||
void write(u64 offset, void *buffer, size_t size) override;
|
||||
size_t getSize() override;
|
||||
size_t getActualSize() override;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getDataInformation() override;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -9,6 +10,8 @@ namespace hex::prv {
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
constexpr static size_t PageSize = 0x1000'0000;
|
||||
|
||||
Provider() = default;
|
||||
virtual ~Provider() = default;
|
||||
|
||||
@@ -18,9 +21,24 @@ namespace hex::prv {
|
||||
|
||||
virtual void read(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual void write(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual size_t getSize() = 0;
|
||||
virtual size_t getActualSize() = 0;
|
||||
|
||||
u32 getPageCount() { return std::ceil(this->getActualSize() / double(PageSize)); }
|
||||
u32 getCurrentPage() const { return this->m_currPage; }
|
||||
void setCurrentPage(u32 page) { if (page < getPageCount()) this->m_currPage = page; }
|
||||
|
||||
virtual size_t getBaseAddress() {
|
||||
return PageSize * this->m_currPage;
|
||||
}
|
||||
|
||||
virtual size_t getSize() {
|
||||
return std::min(this->getActualSize() - PageSize * this->m_currPage, PageSize);
|
||||
}
|
||||
|
||||
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
|
||||
|
||||
protected:
|
||||
u32 m_currPage = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -3,11 +3,18 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "lang/token.hpp"
|
||||
|
||||
namespace hex {
|
||||
@@ -74,5 +81,59 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const u64 &value) {
|
||||
u64 mask = (std::numeric_limits<u64>::max() >> (63 - (from - to))) << to;
|
||||
return (value & mask) >> to;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct always_false : std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
constexpr T changeEndianess(T value, std::endian endian) {
|
||||
if (endian == std::endian::native)
|
||||
return value;
|
||||
|
||||
if constexpr (sizeof(T) == 1)
|
||||
return value;
|
||||
else if constexpr (sizeof(T) == 2)
|
||||
return __builtin_bswap16(value);
|
||||
else if constexpr (sizeof(T) == 4)
|
||||
return __builtin_bswap32(value);
|
||||
else if constexpr (sizeof(T) == 8)
|
||||
return __builtin_bswap64(value);
|
||||
else
|
||||
static_assert(always_false<T>::value, "Invalid type provided!");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T changeEndianess(T value, size_t size, std::endian endian) {
|
||||
if (endian == std::endian::native)
|
||||
return value;
|
||||
|
||||
if (size == 1)
|
||||
return value;
|
||||
else if (size == 2)
|
||||
return __builtin_bswap16(value);
|
||||
else if (size == 4)
|
||||
return __builtin_bswap32(value);
|
||||
else if (size == 8)
|
||||
return __builtin_bswap64(value);
|
||||
else
|
||||
throw std::invalid_argument("Invalid value size!");
|
||||
}
|
||||
|
||||
|
||||
class ScopeExit {
|
||||
public:
|
||||
ScopeExit(std::function<void()> func) : m_func(func) {}
|
||||
~ScopeExit() { if (this->m_func != nullptr) this->m_func(); }
|
||||
|
||||
void release() {
|
||||
this->m_func = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void()> m_func;
|
||||
};
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string makeDisplayable(u8 *data, size_t size) {
|
||||
std::string result;
|
||||
for (u8* c = data; c < (data + size); c++) {
|
||||
if (iscntrl(*c) || *c > 0x7F)
|
||||
result += " ";
|
||||
else
|
||||
result += *c;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PatternData {
|
||||
public:
|
||||
PatternData(u64 offset, size_t size, const std::string &name, u32 color = 0)
|
||||
: m_offset(offset), m_size(size), m_color(color), m_name(name) {
|
||||
constexpr u32 Palette[] = { 0x50b4771f, 0x500e7fff, 0x502ca02c, 0x502827d6, 0x50bd6794, 0x504b568c, 0x50c277e3, 0x507f7f7f, 0x5022bdbc, 0x50cfbe17 };
|
||||
|
||||
this->m_color = Palette[PatternData::s_paletteOffset++];
|
||||
|
||||
if (PatternData::s_paletteOffset >= (sizeof(Palette) / sizeof(u32)))
|
||||
PatternData::s_paletteOffset = 0;
|
||||
}
|
||||
virtual ~PatternData() = default;
|
||||
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
[[nodiscard]] const std::string& getName() const { return this->m_name; }
|
||||
|
||||
virtual std::string format(prv::Provider* &provider) = 0;
|
||||
virtual std::string getTypeName() = 0;
|
||||
|
||||
static void resetPalette() { PatternData::s_paletteOffset = 0; }
|
||||
|
||||
private:
|
||||
u64 m_offset;
|
||||
size_t m_size;
|
||||
|
||||
u32 m_color;
|
||||
std::string m_name;
|
||||
|
||||
static inline u8 s_paletteOffset = 0;
|
||||
};
|
||||
|
||||
|
||||
class PatternDataUnsigned : public PatternData {
|
||||
public:
|
||||
PatternDataUnsigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(offset, size, name, color) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
|
||||
return hex::format("%lu (0x%08lx)", data, data);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "u8";
|
||||
case 2: return "u16";
|
||||
case 4: return "u32";
|
||||
case 8: return "u64";
|
||||
case 16: return "u128";
|
||||
default: return "Unsigned data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataSigned : public PatternData {
|
||||
public:
|
||||
PatternDataSigned(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(offset, size, name, color) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
|
||||
s64 signedData = signedData = hex::signExtend(data, this->getSize(), 64);
|
||||
|
||||
return hex::format("%ld (0x%08lx)", signedData, data);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 1: return "s8";
|
||||
case 2: return "s16";
|
||||
case 4: return "s32";
|
||||
case 8: return "s64";
|
||||
case 16: return "s128";
|
||||
default: return "Signed data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataFloat : public PatternData {
|
||||
public:
|
||||
PatternDataFloat(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(offset, size, name, color) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
double formatData = 0;
|
||||
if (this->getSize() == 4) {
|
||||
float data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
formatData = data;
|
||||
} else if (this->getSize() == 8) {
|
||||
double data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
formatData = data;
|
||||
}
|
||||
|
||||
return hex::format("%f (0x%08lx)", formatData, formatData);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
switch (this->getSize()) {
|
||||
case 4: return "float";
|
||||
case 8: return "double";
|
||||
default: return "Floating point data";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataCharacter : public PatternData {
|
||||
public:
|
||||
PatternDataCharacter(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(offset, size, name, color) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
char character;
|
||||
provider->read(this->getOffset(), &character, 1);
|
||||
|
||||
return hex::format("'%c'", character);
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "Character";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataString : public PatternData {
|
||||
public:
|
||||
PatternDataString(u64 offset, size_t size, const std::string &name, u32 color = 0) : PatternData(offset, size, name, color) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
std::vector<u8> buffer(this->getSize() + 1, 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), this->getSize());
|
||||
buffer[this->getSize()] = '\0';
|
||||
|
||||
return hex::format("\"%s\"", makeDisplayable(buffer.data(), this->getSize()).c_str());
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "String";
|
||||
}
|
||||
};
|
||||
|
||||
class PatternDataEnum : public PatternData {
|
||||
public:
|
||||
PatternDataEnum(u64 offset, size_t size, const std::string &name, const std::string &enumName, std::vector<std::pair<u64, std::string>> enumValues, u32 color = 0)
|
||||
: PatternData(offset, size, name, color), m_enumName(enumName), m_enumValues(enumValues) { }
|
||||
|
||||
std::string format(prv::Provider* &provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
|
||||
for (auto [enumValue, name] : this->m_enumValues) {
|
||||
if (value == enumValue)
|
||||
return hex::format("%lu (0x%08lx) : %s::%s", value, value, this->m_enumName.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
return hex::format("%lu (0x%08lx) : %s::???", value, value, this->m_enumName.c_str());
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
return "enum " + this->m_enumName;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_enumName;
|
||||
std::vector<std::pair<u64, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -25,8 +25,12 @@ namespace hex {
|
||||
return View::s_deferedCalls;
|
||||
}
|
||||
|
||||
static void postEvent(Events eventType, const void *userData = nullptr) {
|
||||
View::s_eventManager.post(eventType, userData);
|
||||
}
|
||||
|
||||
protected:
|
||||
void subscribeEvent(Events eventType, std::function<void(void*)> callback) {
|
||||
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
|
||||
View::s_eventManager.subscribe(eventType, this, callback);
|
||||
}
|
||||
|
||||
@@ -34,10 +38,6 @@ namespace hex {
|
||||
View::s_eventManager.unsubscribe(eventType, this);
|
||||
}
|
||||
|
||||
void postEvent(Events eventType, void *userData = nullptr) {
|
||||
View::s_eventManager.post(eventType, userData);
|
||||
}
|
||||
|
||||
void doLater(std::function<void()> &&function) {
|
||||
View::s_deferedCalls.push_back(function);
|
||||
}
|
||||
|
||||
64
include/views/view_data_inspector.hpp
Normal file
64
include/views/view_data_inspector.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct GUID {
|
||||
u32 data1;
|
||||
u16 data2;
|
||||
u16 data3;
|
||||
u8 data4[8];
|
||||
};
|
||||
|
||||
union PreviewData {
|
||||
u8 unsigned8;
|
||||
s8 signed8;
|
||||
u16 unsigned16;
|
||||
s16 signed16;
|
||||
u32 unsigned32;
|
||||
s32 signed32;
|
||||
u64 unsigned64;
|
||||
s64 signed64;
|
||||
char8_t ansiChar;
|
||||
char16_t wideChar;
|
||||
u8 utf8Char[4];
|
||||
float float32;
|
||||
double float64;
|
||||
#if defined(_WIN64)
|
||||
__time32_t time32;
|
||||
__time64_t time64;
|
||||
#else
|
||||
time_t time;
|
||||
#endif
|
||||
GUID guid;
|
||||
};
|
||||
|
||||
class ViewDataInspector : public View {
|
||||
public:
|
||||
explicit ViewDataInspector(prv::Provider* &dataProvider);
|
||||
~ViewDataInspector() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
bool m_shouldInvalidate = true;
|
||||
|
||||
std::endian m_endianess = std::endian::native;
|
||||
|
||||
PreviewData m_previewData = { 0 };
|
||||
size_t m_validBytes = 0;
|
||||
std::vector<std::pair<std::string, std::string>> m_cachedData;
|
||||
};
|
||||
|
||||
}
|
||||
49
include/views/view_disassembler.hpp
Normal file
49
include/views/view_disassembler.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct Disassembly {
|
||||
u64 address;
|
||||
u64 offset;
|
||||
std::string bytes;
|
||||
std::string opcodeString;
|
||||
};
|
||||
|
||||
class ViewDisassembler : public View {
|
||||
public:
|
||||
explicit ViewDisassembler(prv::Provider* &dataProvider);
|
||||
~ViewDisassembler() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
u64 m_baseAddress = 0;
|
||||
u64 m_codeOffset = 0;
|
||||
u64 m_codeSize = 0;
|
||||
|
||||
cs_arch m_architecture = CS_ARCH_ARM;
|
||||
cs_mode m_modeBasicARM = cs_mode(0), m_modeExtraARM = cs_mode(0), m_modeBasicMIPS = cs_mode(0), m_modeBasicPPC = cs_mode(0), m_modeBasicX86 = cs_mode(0);
|
||||
bool m_littleEndianMode = true, m_micoMode = false, m_sparcV9Mode = false;
|
||||
|
||||
std::vector<Disassembly> m_disassembly;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "imgui.h"
|
||||
#include "views/view.hpp"
|
||||
#include "views/pattern_data.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include "imgui_memory_editor.h"
|
||||
#include "imfilebrowser.h"
|
||||
#include "ImGuiFileBrowser.h"
|
||||
|
||||
#include <tuple>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "views/pattern_data.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace hex {
|
||||
|
||||
class ViewHexEditor : public View {
|
||||
public:
|
||||
ViewHexEditor(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData);
|
||||
ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewHexEditor() override;
|
||||
|
||||
void createView() override;
|
||||
@@ -29,12 +29,10 @@ namespace hex {
|
||||
|
||||
private:
|
||||
MemoryEditor m_memoryEditor;
|
||||
|
||||
ImGui::FileBrowser m_fileBrowser;
|
||||
|
||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::vector<hex::PatternData*> &m_patternData;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
|
||||
char m_searchStringBuffer[0xFFFF] = { 0 };
|
||||
char m_searchHexBuffer[0xFFFF] = { 0 };
|
||||
@@ -45,14 +43,20 @@ namespace hex {
|
||||
std::vector<std::pair<u64, u64>> m_lastStringSearch;
|
||||
std::vector<std::pair<u64, u64>> m_lastHexSearch;
|
||||
|
||||
u64 m_gotoAddress = 0;
|
||||
s64 m_gotoAddress = 0;
|
||||
|
||||
|
||||
void drawSearchPopup();
|
||||
void drawGotoPopup();
|
||||
|
||||
void openFile(std::string path);
|
||||
|
||||
enum class Language { C, Cpp, CSharp, Rust, Python, Java, JavaScript };
|
||||
void copyBytes();
|
||||
void copyString();
|
||||
void copyLanguageArray(Language language);
|
||||
void copyHexView();
|
||||
void copyHexViewHTML();
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -23,13 +23,16 @@ namespace hex {
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
bool m_dataValid = false;
|
||||
u32 m_blockSize = 0;
|
||||
float m_averageEntropy = 0;
|
||||
float m_highestBlockEntropy = 0;
|
||||
std::vector<float> m_blockEntropy;
|
||||
|
||||
std::array<float, 256> m_valueCounts = { 0 };
|
||||
bool m_shouldInvalidate = true;
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
std::pair<u64, u64> m_analyzedRegion = { 0, 0 };
|
||||
|
||||
std::string m_fileDescription;
|
||||
std::string m_mimeType;
|
||||
|
||||
@@ -3,43 +3,39 @@
|
||||
#include "lang/ast_node.hpp"
|
||||
|
||||
#include "views/view.hpp"
|
||||
#include "views/pattern_data.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
#include "imfilebrowser.h"
|
||||
#include "ImGuiFileBrowser.h"
|
||||
#include "TextEditor.h"
|
||||
|
||||
namespace hex {
|
||||
|
||||
class ViewPattern : public View {
|
||||
public:
|
||||
explicit ViewPattern(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData);
|
||||
explicit ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewPattern() override;
|
||||
|
||||
void createMenu() override;
|
||||
void createView() override;
|
||||
|
||||
private:
|
||||
char *m_buffer = nullptr;
|
||||
|
||||
std::vector<hex::PatternData*> &m_patternData;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::filesystem::path m_possiblePatternFile;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
ImGui::FileBrowser m_fileBrowser;
|
||||
TextEditor m_textEditor;
|
||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||
|
||||
|
||||
void addPatternData(PatternData *patternData);
|
||||
void loadPatternFile(std::string path);
|
||||
void clearPatternData();
|
||||
void parsePattern(char *buffer);
|
||||
|
||||
s32 highlightUsingDecls(std::vector<lang::ASTNode*> &ast, lang::ASTNodeTypeDecl* currTypeDeclNode, lang::ASTNodeVariableDecl* currVarDec, u64 offset, std::string name);
|
||||
s32 highlightStruct(std::vector<lang::ASTNode*> &ast, lang::ASTNodeStruct* currStructNode, u64 offset, std::string name);
|
||||
s32 highlightEnum(std::vector<lang::ASTNode*> &ast, lang::ASTNodeEnum* currEnumNode, u64 offset, std::string name);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "imgui.h"
|
||||
#include "views/view.hpp"
|
||||
#include "views/pattern_data.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <tuple>
|
||||
@@ -16,7 +16,7 @@ namespace hex {
|
||||
|
||||
class ViewPatternData : public View {
|
||||
public:
|
||||
ViewPatternData(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData);
|
||||
ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData);
|
||||
~ViewPatternData() override;
|
||||
|
||||
void createView() override;
|
||||
@@ -24,8 +24,8 @@ namespace hex {
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::vector<hex::PatternData*> &m_patternData;
|
||||
std::vector<hex::PatternData*> m_sortedPatternData;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
std::vector<lang::PatternData*> m_sortedPatternData;
|
||||
bool m_windowOpen = true;
|
||||
};
|
||||
|
||||
|
||||
1160
libs/ImGui/include/Dirent/dirent.h
Normal file
1160
libs/ImGui/include/Dirent/dirent.h
Normal file
File diff suppressed because it is too large
Load Diff
123
libs/ImGui/include/ImGuiFileBrowser.h
Normal file
123
libs/ImGui/include/ImGuiFileBrowser.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#ifndef IMGUIFILEBROWSER_H
|
||||
#define IMGUIFILEBROWSER_H
|
||||
|
||||
#include <imgui.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace imgui_addons
|
||||
{
|
||||
class ImGuiFileBrowser
|
||||
{
|
||||
public:
|
||||
ImGuiFileBrowser();
|
||||
~ImGuiFileBrowser();
|
||||
|
||||
enum class DialogMode
|
||||
{
|
||||
SELECT, //Select Directory Mode
|
||||
OPEN, //Open File mode
|
||||
SAVE //Save File mode.
|
||||
};
|
||||
|
||||
/* Use this to show an open file dialog. The function takes label for the window,
|
||||
* the size, a DialogMode enum value defining in which mode the dialog should operate and optionally the extensions that are valid for opening.
|
||||
* Note that the select directory mode doesn't need any extensions.
|
||||
*/
|
||||
bool showFileDialog(const std::string& label, const DialogMode mode, const ImVec2& sz_xy = ImVec2(0,0), const std::string& valid_types = "*.*");
|
||||
|
||||
/* Store the opened/saved file name or dir name (incase of selectDirectoryDialog) and the absolute path to the selection
|
||||
* Should only be accessed when above functions return true else may contain garbage.
|
||||
*/
|
||||
std::string selected_fn;
|
||||
std::string selected_path;
|
||||
std::string ext; // Store the saved file extension
|
||||
|
||||
|
||||
private:
|
||||
struct Info
|
||||
{
|
||||
Info(std::string name, bool is_hidden) : name(name), is_hidden(is_hidden)
|
||||
{
|
||||
}
|
||||
std::string name;
|
||||
bool is_hidden;
|
||||
};
|
||||
|
||||
//Enum used as bit flags.
|
||||
enum FilterMode
|
||||
{
|
||||
FilterMode_Files = 0x01,
|
||||
FilterMode_Dirs = 0x02
|
||||
};
|
||||
|
||||
//Helper Functions
|
||||
static std::string wStringToString(const wchar_t* wchar_arr);
|
||||
static bool alphaSortComparator(const Info& a, const Info& b);
|
||||
ImVec2 getButtonSize(std::string button_text);
|
||||
|
||||
/* Helper Functions that render secondary modals
|
||||
* and help in validating file extensions and for filtering, parsing top navigation bar.
|
||||
*/
|
||||
void setValidExtTypes(const std::string& valid_types_string);
|
||||
bool validateFile();
|
||||
void showErrorModal();
|
||||
void showInvalidFileModal();
|
||||
bool showReplaceFileModal();
|
||||
void showHelpMarker(std::string desc);
|
||||
void parsePathTabs(std::string str);
|
||||
void filterFiles(int filter_mode);
|
||||
|
||||
/* Core Functions that render the 4 different regions making up
|
||||
* a simple file dialog
|
||||
*/
|
||||
bool renderNavAndSearchBarRegion();
|
||||
bool renderFileListRegion();
|
||||
bool renderInputTextAndExtRegion();
|
||||
bool renderButtonsAndCheckboxRegion();
|
||||
bool renderInputComboBox();
|
||||
void renderExtBox();
|
||||
|
||||
/* Core Functions that handle navigation and
|
||||
* reading directories/files
|
||||
*/
|
||||
bool readDIR(std::string path);
|
||||
bool onNavigationButtonClick(int idx);
|
||||
bool onDirClick(int idx);
|
||||
|
||||
// Functions that reset state and/or clear file list when reading new directory
|
||||
void clearFileList();
|
||||
void closeDialog();
|
||||
|
||||
#if defined (WIN32) || defined (_WIN32) || defined (__WIN32)
|
||||
bool loadWindowsDrives(); // Helper Function for Windows to load Drive Letters.
|
||||
#endif
|
||||
|
||||
#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__)
|
||||
void initCurrentPath(); // Helper function for UNIX based system to load Absolute path using realpath
|
||||
#endif
|
||||
|
||||
ImVec2 min_size, max_size, input_combobox_pos, input_combobox_sz;
|
||||
DialogMode dialog_mode;
|
||||
int filter_mode, col_items_limit, selected_idx, selected_ext_idx;
|
||||
float col_width, ext_box_width;
|
||||
bool show_hidden, show_inputbar_combobox, is_dir, is_appearing, filter_dirty, validate_file, path_input_enabled;
|
||||
char input_fn[256];
|
||||
char temp_dir_input[256];
|
||||
|
||||
std::vector<std::string> valid_exts;
|
||||
std::vector<std::string> current_dirlist;
|
||||
std::vector<Info> subdirs;
|
||||
std::vector<Info> subfiles;
|
||||
std::string current_path, error_msg, error_title, invfile_modal_id, repfile_modal_id;
|
||||
|
||||
ImGuiTextFilter filter;
|
||||
std::string valid_types;
|
||||
std::vector<const Info*> filtered_dirs; // Note: We don't need to call delete. It's just for storing filtered items from subdirs and subfiles so we don't use PassFilter every frame.
|
||||
std::vector<const Info*> filtered_files;
|
||||
std::vector< std::reference_wrapper<std::string> > inputcb_filter_files;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif // IMGUIFILEBROWSER_H
|
||||
395
libs/ImGui/include/TextEditor.h
Normal file
395
libs/ImGui/include/TextEditor.h
Normal file
@@ -0,0 +1,395 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include "imgui.h"
|
||||
|
||||
class TextEditor
|
||||
{
|
||||
public:
|
||||
enum class PaletteIndex
|
||||
{
|
||||
Default,
|
||||
Keyword,
|
||||
Number,
|
||||
String,
|
||||
CharLiteral,
|
||||
Punctuation,
|
||||
Preprocessor,
|
||||
Identifier,
|
||||
KnownIdentifier,
|
||||
PreprocIdentifier,
|
||||
Comment,
|
||||
MultiLineComment,
|
||||
Background,
|
||||
Cursor,
|
||||
Selection,
|
||||
ErrorMarker,
|
||||
Breakpoint,
|
||||
LineNumber,
|
||||
CurrentLineFill,
|
||||
CurrentLineFillInactive,
|
||||
CurrentLineEdge,
|
||||
Max
|
||||
};
|
||||
|
||||
enum class SelectionMode
|
||||
{
|
||||
Normal,
|
||||
Word,
|
||||
Line
|
||||
};
|
||||
|
||||
struct Breakpoint
|
||||
{
|
||||
int mLine;
|
||||
bool mEnabled;
|
||||
std::string mCondition;
|
||||
|
||||
Breakpoint()
|
||||
: mLine(-1)
|
||||
, mEnabled(false)
|
||||
{}
|
||||
};
|
||||
|
||||
// Represents a character coordinate from the user's point of view,
|
||||
// i. e. consider an uniform grid (assuming fixed-width font) on the
|
||||
// screen as it is rendered, and each cell has its own coordinate, starting from 0.
|
||||
// Tabs are counted as [1..mTabSize] count empty spaces, depending on
|
||||
// how many space is necessary to reach the next tab stop.
|
||||
// For example, coordinate (1, 5) represents the character 'B' in a line "\tABC", when mTabSize = 4,
|
||||
// because it is rendered as " ABC" on the screen.
|
||||
struct Coordinates
|
||||
{
|
||||
int mLine, mColumn;
|
||||
Coordinates() : mLine(0), mColumn(0) {}
|
||||
Coordinates(int aLine, int aColumn) : mLine(aLine), mColumn(aColumn)
|
||||
{
|
||||
assert(aLine >= 0);
|
||||
assert(aColumn >= 0);
|
||||
}
|
||||
static Coordinates Invalid() { static Coordinates invalid(-1, -1); return invalid; }
|
||||
|
||||
bool operator ==(const Coordinates& o) const
|
||||
{
|
||||
return
|
||||
mLine == o.mLine &&
|
||||
mColumn == o.mColumn;
|
||||
}
|
||||
|
||||
bool operator !=(const Coordinates& o) const
|
||||
{
|
||||
return
|
||||
mLine != o.mLine ||
|
||||
mColumn != o.mColumn;
|
||||
}
|
||||
|
||||
bool operator <(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn < o.mColumn;
|
||||
}
|
||||
|
||||
bool operator >(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn > o.mColumn;
|
||||
}
|
||||
|
||||
bool operator <=(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine < o.mLine;
|
||||
return mColumn <= o.mColumn;
|
||||
}
|
||||
|
||||
bool operator >=(const Coordinates& o) const
|
||||
{
|
||||
if (mLine != o.mLine)
|
||||
return mLine > o.mLine;
|
||||
return mColumn >= o.mColumn;
|
||||
}
|
||||
};
|
||||
|
||||
struct Identifier
|
||||
{
|
||||
Coordinates mLocation;
|
||||
std::string mDeclaration;
|
||||
};
|
||||
|
||||
typedef std::string String;
|
||||
typedef std::unordered_map<std::string, Identifier> Identifiers;
|
||||
typedef std::unordered_set<std::string> Keywords;
|
||||
typedef std::map<int, std::string> ErrorMarkers;
|
||||
typedef std::unordered_set<int> Breakpoints;
|
||||
typedef std::array<ImU32, (unsigned)PaletteIndex::Max> Palette;
|
||||
typedef uint8_t Char;
|
||||
|
||||
struct Glyph
|
||||
{
|
||||
Char mChar;
|
||||
PaletteIndex mColorIndex = PaletteIndex::Default;
|
||||
bool mComment : 1;
|
||||
bool mMultiLineComment : 1;
|
||||
bool mPreprocessor : 1;
|
||||
|
||||
Glyph(Char aChar, PaletteIndex aColorIndex) : mChar(aChar), mColorIndex(aColorIndex),
|
||||
mComment(false), mMultiLineComment(false), mPreprocessor(false) {}
|
||||
};
|
||||
|
||||
typedef std::vector<Glyph> Line;
|
||||
typedef std::vector<Line> Lines;
|
||||
|
||||
struct LanguageDefinition
|
||||
{
|
||||
typedef std::pair<std::string, PaletteIndex> TokenRegexString;
|
||||
typedef std::vector<TokenRegexString> TokenRegexStrings;
|
||||
typedef bool(*TokenizeCallback)(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end, PaletteIndex & paletteIndex);
|
||||
|
||||
std::string mName;
|
||||
Keywords mKeywords;
|
||||
Identifiers mIdentifiers;
|
||||
Identifiers mPreprocIdentifiers;
|
||||
std::string mCommentStart, mCommentEnd, mSingleLineComment;
|
||||
char mPreprocChar;
|
||||
bool mAutoIndentation;
|
||||
|
||||
TokenizeCallback mTokenize;
|
||||
|
||||
TokenRegexStrings mTokenRegexStrings;
|
||||
|
||||
bool mCaseSensitive;
|
||||
|
||||
LanguageDefinition()
|
||||
: mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
|
||||
{
|
||||
}
|
||||
|
||||
static const LanguageDefinition& CPlusPlus();
|
||||
static const LanguageDefinition& HLSL();
|
||||
static const LanguageDefinition& GLSL();
|
||||
static const LanguageDefinition& C();
|
||||
static const LanguageDefinition& SQL();
|
||||
static const LanguageDefinition& AngelScript();
|
||||
static const LanguageDefinition& Lua();
|
||||
};
|
||||
|
||||
TextEditor();
|
||||
~TextEditor();
|
||||
|
||||
void SetLanguageDefinition(const LanguageDefinition& aLanguageDef);
|
||||
const LanguageDefinition& GetLanguageDefinition() const { return mLanguageDefinition; }
|
||||
|
||||
const Palette& GetPalette() const { return mPaletteBase; }
|
||||
void SetPalette(const Palette& aValue);
|
||||
|
||||
void SetErrorMarkers(const ErrorMarkers& aMarkers) { mErrorMarkers = aMarkers; }
|
||||
void SetBreakpoints(const Breakpoints& aMarkers) { mBreakpoints = aMarkers; }
|
||||
|
||||
void Render(const char* aTitle, const ImVec2& aSize = ImVec2(), bool aBorder = false);
|
||||
void SetText(const std::string& aText);
|
||||
std::string GetText() const;
|
||||
|
||||
void SetTextLines(const std::vector<std::string>& aLines);
|
||||
std::vector<std::string> GetTextLines() const;
|
||||
|
||||
std::string GetSelectedText() const;
|
||||
std::string GetCurrentLineText()const;
|
||||
|
||||
int GetTotalLines() const { return (int)mLines.size(); }
|
||||
bool IsOverwrite() const { return mOverwrite; }
|
||||
|
||||
void SetReadOnly(bool aValue);
|
||||
bool IsReadOnly() const { return mReadOnly; }
|
||||
bool IsTextChanged() const { return mTextChanged; }
|
||||
bool IsCursorPositionChanged() const { return mCursorPositionChanged; }
|
||||
|
||||
bool IsColorizerEnabled() const { return mColorizerEnabled; }
|
||||
void SetColorizerEnable(bool aValue);
|
||||
|
||||
Coordinates GetCursorPosition() const { return GetActualCursorCoordinates(); }
|
||||
void SetCursorPosition(const Coordinates& aPosition);
|
||||
|
||||
inline void SetHandleMouseInputs (bool aValue){ mHandleMouseInputs = aValue;}
|
||||
inline bool IsHandleMouseInputsEnabled() const { return mHandleKeyboardInputs; }
|
||||
|
||||
inline void SetHandleKeyboardInputs (bool aValue){ mHandleKeyboardInputs = aValue;}
|
||||
inline bool IsHandleKeyboardInputsEnabled() const { return mHandleKeyboardInputs; }
|
||||
|
||||
inline void SetImGuiChildIgnored (bool aValue){ mIgnoreImGuiChild = aValue;}
|
||||
inline bool IsImGuiChildIgnored() const { return mIgnoreImGuiChild; }
|
||||
|
||||
inline void SetShowWhitespaces(bool aValue) { mShowWhitespaces = aValue; }
|
||||
inline bool IsShowingWhitespaces() const { return mShowWhitespaces; }
|
||||
|
||||
void SetTabSize(int aValue);
|
||||
inline int GetTabSize() const { return mTabSize; }
|
||||
|
||||
void InsertText(const std::string& aValue);
|
||||
void InsertText(const char* aValue);
|
||||
|
||||
void MoveUp(int aAmount = 1, bool aSelect = false);
|
||||
void MoveDown(int aAmount = 1, bool aSelect = false);
|
||||
void MoveLeft(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveRight(int aAmount = 1, bool aSelect = false, bool aWordMode = false);
|
||||
void MoveTop(bool aSelect = false);
|
||||
void MoveBottom(bool aSelect = false);
|
||||
void MoveHome(bool aSelect = false);
|
||||
void MoveEnd(bool aSelect = false);
|
||||
|
||||
void SetSelectionStart(const Coordinates& aPosition);
|
||||
void SetSelectionEnd(const Coordinates& aPosition);
|
||||
void SetSelection(const Coordinates& aStart, const Coordinates& aEnd, SelectionMode aMode = SelectionMode::Normal);
|
||||
void SelectWordUnderCursor();
|
||||
void SelectAll();
|
||||
bool HasSelection() const;
|
||||
|
||||
void Copy();
|
||||
void Cut();
|
||||
void Paste();
|
||||
void Delete();
|
||||
|
||||
bool CanUndo() const;
|
||||
bool CanRedo() const;
|
||||
void Undo(int aSteps = 1);
|
||||
void Redo(int aSteps = 1);
|
||||
|
||||
static const Palette& GetDarkPalette();
|
||||
static const Palette& GetLightPalette();
|
||||
static const Palette& GetRetroBluePalette();
|
||||
|
||||
private:
|
||||
typedef std::vector<std::pair<std::regex, PaletteIndex>> RegexList;
|
||||
|
||||
struct EditorState
|
||||
{
|
||||
Coordinates mSelectionStart;
|
||||
Coordinates mSelectionEnd;
|
||||
Coordinates mCursorPosition;
|
||||
};
|
||||
|
||||
class UndoRecord
|
||||
{
|
||||
public:
|
||||
UndoRecord() {}
|
||||
~UndoRecord() {}
|
||||
|
||||
UndoRecord(
|
||||
const std::string& aAdded,
|
||||
const TextEditor::Coordinates aAddedStart,
|
||||
const TextEditor::Coordinates aAddedEnd,
|
||||
|
||||
const std::string& aRemoved,
|
||||
const TextEditor::Coordinates aRemovedStart,
|
||||
const TextEditor::Coordinates aRemovedEnd,
|
||||
|
||||
TextEditor::EditorState& aBefore,
|
||||
TextEditor::EditorState& aAfter);
|
||||
|
||||
void Undo(TextEditor* aEditor);
|
||||
void Redo(TextEditor* aEditor);
|
||||
|
||||
std::string mAdded;
|
||||
Coordinates mAddedStart;
|
||||
Coordinates mAddedEnd;
|
||||
|
||||
std::string mRemoved;
|
||||
Coordinates mRemovedStart;
|
||||
Coordinates mRemovedEnd;
|
||||
|
||||
EditorState mBefore;
|
||||
EditorState mAfter;
|
||||
};
|
||||
|
||||
typedef std::vector<UndoRecord> UndoBuffer;
|
||||
|
||||
void ProcessInputs();
|
||||
void Colorize(int aFromLine = 0, int aCount = -1);
|
||||
void ColorizeRange(int aFromLine = 0, int aToLine = 0);
|
||||
void ColorizeInternal();
|
||||
float TextDistanceToLineStart(const Coordinates& aFrom) const;
|
||||
void EnsureCursorVisible();
|
||||
int GetPageSize() const;
|
||||
std::string GetText(const Coordinates& aStart, const Coordinates& aEnd) const;
|
||||
Coordinates GetActualCursorCoordinates() const;
|
||||
Coordinates SanitizeCoordinates(const Coordinates& aValue) const;
|
||||
void Advance(Coordinates& aCoordinates) const;
|
||||
void DeleteRange(const Coordinates& aStart, const Coordinates& aEnd);
|
||||
int InsertTextAt(Coordinates& aWhere, const char* aValue);
|
||||
void AddUndo(UndoRecord& aValue);
|
||||
Coordinates ScreenPosToCoordinates(const ImVec2& aPosition) const;
|
||||
Coordinates FindWordStart(const Coordinates& aFrom) const;
|
||||
Coordinates FindWordEnd(const Coordinates& aFrom) const;
|
||||
Coordinates FindNextWord(const Coordinates& aFrom) const;
|
||||
int GetCharacterIndex(const Coordinates& aCoordinates) const;
|
||||
int GetCharacterColumn(int aLine, int aIndex) const;
|
||||
int GetLineCharacterCount(int aLine) const;
|
||||
int GetLineMaxColumn(int aLine) const;
|
||||
bool IsOnWordBoundary(const Coordinates& aAt) const;
|
||||
void RemoveLine(int aStart, int aEnd);
|
||||
void RemoveLine(int aIndex);
|
||||
Line& InsertLine(int aIndex);
|
||||
void EnterCharacter(ImWchar aChar, bool aShift);
|
||||
void Backspace();
|
||||
void DeleteSelection();
|
||||
std::string GetWordUnderCursor() const;
|
||||
std::string GetWordAt(const Coordinates& aCoords) const;
|
||||
ImU32 GetGlyphColor(const Glyph& aGlyph) const;
|
||||
|
||||
void HandleKeyboardInputs();
|
||||
void HandleMouseInputs();
|
||||
void Render();
|
||||
|
||||
float mLineSpacing;
|
||||
Lines mLines;
|
||||
EditorState mState;
|
||||
UndoBuffer mUndoBuffer;
|
||||
int mUndoIndex;
|
||||
|
||||
int mTabSize;
|
||||
bool mOverwrite;
|
||||
bool mReadOnly;
|
||||
bool mWithinRender;
|
||||
bool mScrollToCursor;
|
||||
bool mScrollToTop;
|
||||
bool mTextChanged;
|
||||
bool mColorizerEnabled;
|
||||
float mTextStart; // position (in pixels) where a code line starts relative to the left of the TextEditor.
|
||||
int mLeftMargin;
|
||||
bool mCursorPositionChanged;
|
||||
int mColorRangeMin, mColorRangeMax;
|
||||
SelectionMode mSelectionMode;
|
||||
bool mHandleKeyboardInputs;
|
||||
bool mHandleMouseInputs;
|
||||
bool mIgnoreImGuiChild;
|
||||
bool mShowWhitespaces;
|
||||
|
||||
Palette mPaletteBase;
|
||||
Palette mPalette;
|
||||
LanguageDefinition mLanguageDefinition;
|
||||
RegexList mRegexList;
|
||||
|
||||
bool mCheckComments;
|
||||
Breakpoints mBreakpoints;
|
||||
ErrorMarkers mErrorMarkers;
|
||||
ImVec2 mCharAdvance;
|
||||
Coordinates mInteractiveStart, mInteractiveEnd;
|
||||
std::string mLineBuffer;
|
||||
uint64_t mStartTime;
|
||||
|
||||
float mLastClick;
|
||||
};
|
||||
|
||||
bool TokenizeCStyleString(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleCharacterLiteral(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleIdentifier(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStyleNumber(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
bool TokenizeCStylePunctuation(const char * in_begin, const char * in_end, const char *& out_begin, const char *& out_end);
|
||||
@@ -1,721 +0,0 @@
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019-2020 Zhuang Guan
|
||||
|
||||
https://github.com/AirGuanZ
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef IMGUI_VERSION
|
||||
# error "include imgui.h before this header"
|
||||
#endif
|
||||
|
||||
using ImGuiFileBrowserFlags = int;
|
||||
|
||||
enum ImGuiFileBrowserFlags_
|
||||
{
|
||||
ImGuiFileBrowserFlags_SelectDirectory = 1 << 0, // select directory instead of regular file
|
||||
ImGuiFileBrowserFlags_EnterNewFilename = 1 << 1, // allow user to enter new filename when selecting regular file
|
||||
ImGuiFileBrowserFlags_NoModal = 1 << 2, // file browsing window is modal by default. specify this to use a popup window
|
||||
ImGuiFileBrowserFlags_NoTitleBar = 1 << 3, // hide window title bar
|
||||
ImGuiFileBrowserFlags_NoStatusBar = 1 << 4, // hide status bar at the bottom of browsing window
|
||||
ImGuiFileBrowserFlags_CloseOnEsc = 1 << 5, // close file browser when pressing 'ESC'
|
||||
ImGuiFileBrowserFlags_CreateNewDir = 1 << 6, // allow user to create new directory
|
||||
ImGuiFileBrowserFlags_MultipleSelection = 1 << 7, // allow user to select multiple files. this will hide ImGuiFileBrowserFlags_EnterNewFilename
|
||||
};
|
||||
|
||||
namespace ImGui
|
||||
{
|
||||
class FileBrowser
|
||||
{
|
||||
public:
|
||||
|
||||
// pwd is set to current working directory by default
|
||||
explicit FileBrowser(ImGuiFileBrowserFlags flags = 0);
|
||||
|
||||
FileBrowser(const FileBrowser ©From);
|
||||
|
||||
FileBrowser &operator=(const FileBrowser ©From);
|
||||
|
||||
// set the window size (in pixels)
|
||||
// default is (700, 450)
|
||||
void SetWindowSize(int width, int height) noexcept;
|
||||
|
||||
// set the window title text
|
||||
void SetTitle(std::string title);
|
||||
|
||||
// open the browsing window
|
||||
void Open();
|
||||
|
||||
// close the browsing window
|
||||
void Close();
|
||||
|
||||
// the browsing window is opened or not
|
||||
bool IsOpened() const noexcept;
|
||||
|
||||
// display the browsing window if opened
|
||||
void Display();
|
||||
|
||||
// returns true when there is a selected filename and the "ok" button was clicked
|
||||
bool HasSelected() const noexcept;
|
||||
|
||||
// set current browsing directory
|
||||
bool SetPwd(const std::filesystem::path &pwd =
|
||||
std::filesystem::current_path());
|
||||
|
||||
// get current browsing directory
|
||||
const std::filesystem::path &GetPwd() const noexcept;
|
||||
|
||||
// returns selected filename. make sense only when HasSelected returns true
|
||||
// when ImGuiFileBrowserFlags_MultipleSelection is enabled, only one of
|
||||
// selected filename will be returned
|
||||
std::filesystem::path GetSelected() const;
|
||||
|
||||
// returns all selected filenames.
|
||||
// when ImGuiFileBrowserFlags_MultipleSelection is enabled, use this
|
||||
// instead of GetSelected
|
||||
std::vector<std::filesystem::path> GetMultiSelected() const;
|
||||
|
||||
// set selected filename to empty
|
||||
void ClearSelected();
|
||||
|
||||
// set file type filters. eg. { ".h", ".cpp", ".hpp", ".cc", ".inl" }
|
||||
void SetTypeFilters(const std::vector<const char*> &typeFilters);
|
||||
|
||||
private:
|
||||
|
||||
template <class Functor>
|
||||
struct ScopeGuard
|
||||
{
|
||||
ScopeGuard(Functor&& t) : func(std::move(t)) { }
|
||||
|
||||
~ScopeGuard()
|
||||
{
|
||||
func();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Functor func;
|
||||
};
|
||||
|
||||
void SetPwdUncatched(const std::filesystem::path &pwd);
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::uint32_t GetDrivesBitMask();
|
||||
#endif
|
||||
|
||||
// for c++17 compatibility
|
||||
|
||||
#if defined(__cpp_lib_char8_t)
|
||||
static std::string u8StrToStr(std::u8string s);
|
||||
#endif
|
||||
static std::string u8StrToStr(std::string s);
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
ImGuiFileBrowserFlags flags_;
|
||||
|
||||
std::string title_;
|
||||
std::string openLabel_;
|
||||
|
||||
bool openFlag_;
|
||||
bool closeFlag_;
|
||||
bool isOpened_;
|
||||
bool ok_;
|
||||
|
||||
std::string statusStr_;
|
||||
|
||||
std::vector<const char*> typeFilters_;
|
||||
int typeFilterIndex_;
|
||||
|
||||
std::filesystem::path pwd_;
|
||||
std::set<std::filesystem::path> selectedFilenames_;
|
||||
|
||||
struct FileRecord
|
||||
{
|
||||
bool isDir = false;
|
||||
std::filesystem::path name;
|
||||
std::string showName;
|
||||
std::filesystem::path extension;
|
||||
};
|
||||
std::vector<FileRecord> fileRecords_;
|
||||
|
||||
// IMPROVE: truncate when selectedFilename_.length() > inputNameBuf_.size() - 1
|
||||
static constexpr size_t INPUT_NAME_BUF_SIZE = 512;
|
||||
std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> inputNameBuf_;
|
||||
|
||||
std::string openNewDirLabel_;
|
||||
std::unique_ptr<std::array<char, INPUT_NAME_BUF_SIZE>> newDirNameBuf_;
|
||||
|
||||
#ifdef _WIN32
|
||||
uint32_t drives_;
|
||||
#endif
|
||||
};
|
||||
} // namespace ImGui
|
||||
|
||||
inline ImGui::FileBrowser::FileBrowser(ImGuiFileBrowserFlags flags)
|
||||
: width_(700), height_(450), flags_(flags),
|
||||
openFlag_(false), closeFlag_(false), isOpened_(false), ok_(false),
|
||||
inputNameBuf_(std::make_unique<std::array<char, INPUT_NAME_BUF_SIZE>>())
|
||||
{
|
||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||
newDirNameBuf_ = std::make_unique<
|
||||
std::array<char, INPUT_NAME_BUF_SIZE>>();
|
||||
|
||||
inputNameBuf_->front() = '\0';
|
||||
inputNameBuf_->back() = '\0';
|
||||
SetTitle("file browser");
|
||||
SetPwd(std::filesystem::current_path());
|
||||
|
||||
typeFilters_.clear();
|
||||
typeFilterIndex_ = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
drives_ = GetDrivesBitMask();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline ImGui::FileBrowser::FileBrowser(const FileBrowser ©From)
|
||||
: FileBrowser()
|
||||
{
|
||||
*this = copyFrom;
|
||||
}
|
||||
|
||||
inline ImGui::FileBrowser &ImGui::FileBrowser::operator=(
|
||||
const FileBrowser ©From)
|
||||
{
|
||||
flags_ = copyFrom.flags_;
|
||||
SetTitle(copyFrom.title_);
|
||||
|
||||
openFlag_ = copyFrom.openFlag_;
|
||||
closeFlag_ = copyFrom.closeFlag_;
|
||||
isOpened_ = copyFrom.isOpened_;
|
||||
ok_ = copyFrom.ok_;
|
||||
|
||||
statusStr_ = "";
|
||||
pwd_ = copyFrom.pwd_;
|
||||
selectedFilenames_ = copyFrom.selectedFilenames_;
|
||||
|
||||
fileRecords_ = copyFrom.fileRecords_;
|
||||
|
||||
*inputNameBuf_ = *copyFrom.inputNameBuf_;
|
||||
|
||||
if(flags_ & ImGuiFileBrowserFlags_CreateNewDir)
|
||||
{
|
||||
newDirNameBuf_ = std::make_unique<
|
||||
std::array<char, INPUT_NAME_BUF_SIZE>>();
|
||||
*newDirNameBuf_ = *copyFrom.newDirNameBuf_;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetWindowSize(int width, int height) noexcept
|
||||
{
|
||||
assert(width > 0 && height > 0);
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetTitle(std::string title)
|
||||
{
|
||||
title_ = std::move(title);
|
||||
openLabel_ = title_ + "##filebrowser_" +
|
||||
std::to_string(reinterpret_cast<size_t>(this));
|
||||
openNewDirLabel_ = "new dir##new_dir_" +
|
||||
std::to_string(reinterpret_cast<size_t>(this));
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Open()
|
||||
{
|
||||
ClearSelected();
|
||||
statusStr_ = std::string();
|
||||
openFlag_ = true;
|
||||
closeFlag_ = false;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Close()
|
||||
{
|
||||
ClearSelected();
|
||||
statusStr_ = std::string();
|
||||
closeFlag_ = true;
|
||||
openFlag_ = false;
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::IsOpened() const noexcept
|
||||
{
|
||||
return isOpened_;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::Display()
|
||||
{
|
||||
PushID(this);
|
||||
ScopeGuard exitThis([this]
|
||||
{
|
||||
openFlag_ = false;
|
||||
closeFlag_ = false;
|
||||
PopID();
|
||||
});
|
||||
|
||||
if(openFlag_)
|
||||
OpenPopup(openLabel_.c_str());
|
||||
isOpened_ = false;
|
||||
|
||||
// open the popup window
|
||||
|
||||
if(openFlag_ && (flags_ & ImGuiFileBrowserFlags_NoModal))
|
||||
{
|
||||
SetNextWindowSize(
|
||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetNextWindowSize(
|
||||
ImVec2(static_cast<float>(width_), static_cast<float>(height_)),
|
||||
ImGuiCond_FirstUseEver);
|
||||
}
|
||||
if(flags_ & ImGuiFileBrowserFlags_NoModal)
|
||||
{
|
||||
if(!BeginPopup(openLabel_.c_str()))
|
||||
return;
|
||||
}
|
||||
else if(!BeginPopupModal(openLabel_.c_str(), nullptr,
|
||||
flags_ & ImGuiFileBrowserFlags_NoTitleBar ?
|
||||
ImGuiWindowFlags_NoTitleBar : 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
isOpened_ = true;
|
||||
ScopeGuard endPopup([] { EndPopup(); });
|
||||
|
||||
// display elements in pwd
|
||||
|
||||
#ifdef _WIN32
|
||||
char currentDrive = static_cast<char>(pwd_.c_str()[0]);
|
||||
char driveStr[] = { currentDrive, ':', '\0' };
|
||||
|
||||
PushItemWidth(4 * GetFontSize());
|
||||
if(BeginCombo("##select_drive", driveStr))
|
||||
{
|
||||
ScopeGuard guard([&] { ImGui::EndCombo(); });
|
||||
for(int i = 0; i < 26; ++i)
|
||||
{
|
||||
if(!(drives_ & (1 << i)))
|
||||
continue;
|
||||
char driveCh = static_cast<char>('A' + i);
|
||||
char selectableStr[] = { driveCh, ':', '\0' };
|
||||
bool selected = currentDrive == driveCh;
|
||||
if(Selectable(selectableStr, selected) && !selected)
|
||||
{
|
||||
char newPwd[] = { driveCh, ':', '\\', '\0' };
|
||||
SetPwd(newPwd);
|
||||
}
|
||||
}
|
||||
}
|
||||
PopItemWidth();
|
||||
|
||||
SameLine();
|
||||
#endif
|
||||
|
||||
int secIdx = 0, newPwdLastSecIdx = -1;
|
||||
for(auto &sec : pwd_)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(secIdx == 1)
|
||||
{
|
||||
++secIdx;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
PushID(secIdx);
|
||||
if(secIdx > 0)
|
||||
SameLine();
|
||||
if(SmallButton(u8StrToStr(sec.u8string()).c_str()))
|
||||
newPwdLastSecIdx = secIdx;
|
||||
PopID();
|
||||
++secIdx;
|
||||
}
|
||||
|
||||
if(newPwdLastSecIdx >= 0)
|
||||
{
|
||||
int i = 0;
|
||||
std::filesystem::path newPwd;
|
||||
for(auto &sec : pwd_)
|
||||
{
|
||||
if(i++ > newPwdLastSecIdx)
|
||||
break;
|
||||
newPwd /= sec;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if(newPwdLastSecIdx == 0)
|
||||
newPwd /= "\\";
|
||||
#endif
|
||||
SetPwd(newPwd);
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
if(SmallButton("*"))
|
||||
SetPwd(pwd_);
|
||||
|
||||
if(newDirNameBuf_)
|
||||
{
|
||||
SameLine();
|
||||
if(SmallButton("+"))
|
||||
{
|
||||
OpenPopup(openNewDirLabel_.c_str());
|
||||
(*newDirNameBuf_)[0] = '\0';
|
||||
}
|
||||
|
||||
if(BeginPopup(openNewDirLabel_.c_str()))
|
||||
{
|
||||
ScopeGuard endNewDirPopup([] { EndPopup(); });
|
||||
|
||||
InputText("name", newDirNameBuf_->data(), newDirNameBuf_->size());
|
||||
SameLine();
|
||||
|
||||
if(Button("ok") && (*newDirNameBuf_)[0] != '\0')
|
||||
{
|
||||
ScopeGuard closeNewDirPopup([] { CloseCurrentPopup(); });
|
||||
if(create_directory(pwd_ / newDirNameBuf_->data()))
|
||||
SetPwd(pwd_);
|
||||
else
|
||||
{
|
||||
statusStr_ = "failed to create " +
|
||||
std::string(newDirNameBuf_->data());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// browse files in a child window
|
||||
|
||||
float reserveHeight = GetFrameHeightWithSpacing();
|
||||
std::filesystem::path newPwd; bool setNewPwd = false;
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
||||
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
||||
reserveHeight += GetFrameHeightWithSpacing();
|
||||
{
|
||||
BeginChild("ch", ImVec2(0, -reserveHeight), true,
|
||||
(flags_ & ImGuiFileBrowserFlags_NoModal) ?
|
||||
ImGuiWindowFlags_AlwaysHorizontalScrollbar : 0);
|
||||
ScopeGuard endChild([] { EndChild(); });
|
||||
|
||||
for(auto &rsc : fileRecords_)
|
||||
{
|
||||
if (!rsc.isDir && typeFilters_.size() > 0 &&
|
||||
static_cast<size_t>(typeFilterIndex_) < typeFilters_.size() &&
|
||||
!(rsc.extension == typeFilters_[typeFilterIndex_]))
|
||||
continue;
|
||||
|
||||
if(!rsc.name.empty() && rsc.name.c_str()[0] == '$')
|
||||
continue;
|
||||
|
||||
bool selected = selectedFilenames_.find(rsc.name)
|
||||
!= selectedFilenames_.end();
|
||||
|
||||
if(Selectable(rsc.showName.c_str(), selected,
|
||||
ImGuiSelectableFlags_DontClosePopups))
|
||||
{
|
||||
const bool multiSelect =
|
||||
(flags_ & ImGuiFileBrowserFlags_MultipleSelection) &&
|
||||
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||
(GetIO().KeyCtrl || GetIO().KeyShift);
|
||||
|
||||
if(selected)
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
else
|
||||
selectedFilenames_.erase(rsc.name);
|
||||
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
}
|
||||
else if(rsc.name != "..")
|
||||
{
|
||||
if((rsc.isDir && (flags_ & ImGuiFileBrowserFlags_SelectDirectory)) ||
|
||||
(!rsc.isDir && !(flags_ & ImGuiFileBrowserFlags_SelectDirectory)))
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
selectedFilenames_.insert(rsc.name);
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
strcpy_s(
|
||||
inputNameBuf_->data(), inputNameBuf_->size(),
|
||||
u8StrToStr(rsc.name.u8string()).c_str());
|
||||
#else
|
||||
std::strncpy(inputNameBuf_->data(),
|
||||
u8StrToStr(rsc.name.u8string()).c_str(),
|
||||
inputNameBuf_->size() - 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!multiSelect)
|
||||
selectedFilenames_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if(IsItemClicked(0) && IsMouseDoubleClicked(0))
|
||||
{
|
||||
if(rsc.isDir)
|
||||
{
|
||||
setNewPwd = true;
|
||||
newPwd = (rsc.name != "..") ? (pwd_ / rsc.name) :
|
||||
pwd_.parent_path();
|
||||
}
|
||||
else if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
selectedFilenames_ = { rsc.name };
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(setNewPwd)
|
||||
SetPwd(newPwd);
|
||||
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory) &&
|
||||
(flags_ & ImGuiFileBrowserFlags_EnterNewFilename))
|
||||
{
|
||||
PushID(this);
|
||||
ScopeGuard popTextID([] { PopID(); });
|
||||
|
||||
PushItemWidth(-1);
|
||||
if(InputText("", inputNameBuf_->data(), inputNameBuf_->size()) &&
|
||||
inputNameBuf_->at(0) != '\0')
|
||||
{
|
||||
selectedFilenames_ = { inputNameBuf_->data() };
|
||||
}
|
||||
PopItemWidth();
|
||||
}
|
||||
|
||||
if(!(flags_ & ImGuiFileBrowserFlags_SelectDirectory))
|
||||
{
|
||||
if(Button(" ok ") && !selectedFilenames_.empty())
|
||||
{
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(Button(" ok "))
|
||||
{
|
||||
ok_ = true;
|
||||
CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
SameLine();
|
||||
|
||||
int escIdx = GetIO().KeyMap[ImGuiKey_Escape];
|
||||
if(Button("cancel") || closeFlag_ ||
|
||||
((flags_ & ImGuiFileBrowserFlags_CloseOnEsc) &&
|
||||
IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) &&
|
||||
escIdx >= 0 && IsKeyPressed(escIdx)))
|
||||
CloseCurrentPopup();
|
||||
|
||||
if(!statusStr_.empty() && !(flags_ & ImGuiFileBrowserFlags_NoStatusBar))
|
||||
{
|
||||
SameLine();
|
||||
Text("%s", statusStr_.c_str());
|
||||
}
|
||||
|
||||
if(!typeFilters_.empty())
|
||||
{
|
||||
SameLine();
|
||||
PushItemWidth(8 * GetFontSize());
|
||||
Combo("##type_filters", &typeFilterIndex_,
|
||||
typeFilters_.data(), int(typeFilters_.size()));
|
||||
PopItemWidth();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::HasSelected() const noexcept
|
||||
{
|
||||
return ok_;
|
||||
}
|
||||
|
||||
inline bool ImGui::FileBrowser::SetPwd(const std::filesystem::path &pwd)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetPwdUncatched(pwd);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &err)
|
||||
{
|
||||
statusStr_ = std::string("last error: ") + err.what();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
statusStr_ = "last error: unknown";
|
||||
}
|
||||
|
||||
SetPwdUncatched(std::filesystem::current_path());
|
||||
return false;
|
||||
}
|
||||
|
||||
inline const class std::filesystem::path &ImGui::FileBrowser::GetPwd() const noexcept
|
||||
{
|
||||
return pwd_;
|
||||
}
|
||||
|
||||
inline std::filesystem::path ImGui::FileBrowser::GetSelected() const
|
||||
{
|
||||
// when ok_ is true, selectedFilenames_ may be empty if SelectDirectory
|
||||
// is enabled. return pwd in that case.
|
||||
if(selectedFilenames_.empty())
|
||||
return pwd_;
|
||||
return pwd_ / *selectedFilenames_.begin();
|
||||
}
|
||||
|
||||
inline std::vector<std::filesystem::path>
|
||||
ImGui::FileBrowser::GetMultiSelected() const
|
||||
{
|
||||
if(selectedFilenames_.empty())
|
||||
return { pwd_ };
|
||||
std::vector<std::filesystem::path> ret;
|
||||
ret.reserve(selectedFilenames_.size());
|
||||
for(auto &s : selectedFilenames_)
|
||||
ret.push_back(pwd_ / s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::ClearSelected()
|
||||
{
|
||||
selectedFilenames_.clear();
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
ok_ = false;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetTypeFilters(
|
||||
const std::vector<const char*> &typeFilters)
|
||||
{
|
||||
typeFilters_ = typeFilters;
|
||||
typeFilterIndex_ = 0;
|
||||
}
|
||||
|
||||
inline void ImGui::FileBrowser::SetPwdUncatched(const std::filesystem::path &pwd)
|
||||
{
|
||||
fileRecords_ = { FileRecord{ true, "..", "[D] ..", "" } };
|
||||
|
||||
for(auto &p : std::filesystem::directory_iterator(pwd))
|
||||
{
|
||||
FileRecord rcd;
|
||||
|
||||
if(p.is_regular_file())
|
||||
rcd.isDir = false;
|
||||
else if(p.is_directory())
|
||||
rcd.isDir = true;
|
||||
else
|
||||
continue;
|
||||
|
||||
rcd.name = p.path().filename();
|
||||
if(rcd.name.empty())
|
||||
continue;
|
||||
|
||||
rcd.extension = p.path().filename().extension();
|
||||
|
||||
rcd.showName = (rcd.isDir ? "[D] " : "[F] ") +
|
||||
u8StrToStr(p.path().filename().u8string());
|
||||
fileRecords_.push_back(rcd);
|
||||
}
|
||||
|
||||
std::sort(fileRecords_.begin(), fileRecords_.end(),
|
||||
[](const FileRecord &L, const FileRecord &R)
|
||||
{
|
||||
return (L.isDir ^ R.isDir) ? L.isDir : (L.name < R.name);
|
||||
});
|
||||
|
||||
pwd_ = absolute(pwd);
|
||||
selectedFilenames_.clear();
|
||||
(*inputNameBuf_)[0] = '\0';
|
||||
}
|
||||
|
||||
#if defined(__cpp_lib_char8_t)
|
||||
inline std::string ImGui::FileBrowser::u8StrToStr(std::u8string s)
|
||||
{
|
||||
return std::string(s.begin(), s.end());
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string ImGui::FileBrowser::u8StrToStr(std::string s)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef _INC_WINDOWS
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#define IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // #ifndef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#undef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif // #ifdef IMGUI_FILEBROWSER_UNDEF_WIN32_LEAN_AND_MEAN
|
||||
|
||||
#endif // #ifdef _INC_WINDOWS
|
||||
|
||||
inline std::uint32_t ImGui::FileBrowser::GetDrivesBitMask()
|
||||
{
|
||||
DWORD mask = GetLogicalDrives();
|
||||
uint32_t ret = 0;
|
||||
for(int i = 0; i < 26; ++i)
|
||||
{
|
||||
if(!(mask & (1 << i)))
|
||||
continue;
|
||||
char rootName[4] = { static_cast<char>('A' + i), ':', '\\', '\0' };
|
||||
UINT type = GetDriveTypeA(rootName);
|
||||
if(type == DRIVE_REMOVABLE || type == DRIVE_FIXED)
|
||||
ret |= (1 << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -47,6 +47,8 @@
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define _PRISizeT "I"
|
||||
#define ImSnprintf _snprintf
|
||||
@@ -60,6 +62,8 @@
|
||||
#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.
|
||||
#endif
|
||||
|
||||
ImU32 ImAlphaBlendColors(ImU32 col_a, ImU32 col_b);
|
||||
|
||||
struct MemoryEditor
|
||||
{
|
||||
enum DataFormat
|
||||
@@ -75,7 +79,6 @@ struct MemoryEditor
|
||||
bool ReadOnly; // = false // disable any editing.
|
||||
int Cols; // = 16 // number of columns to display.
|
||||
bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.
|
||||
bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.
|
||||
bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X".
|
||||
bool OptShowAscii; // = true // display ASCII representation on the right side.
|
||||
bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color.
|
||||
@@ -107,7 +110,6 @@ struct MemoryEditor
|
||||
ReadOnly = false;
|
||||
Cols = 16;
|
||||
OptShowOptions = true;
|
||||
OptShowDataPreview = false;
|
||||
OptShowHexII = false;
|
||||
OptShowAscii = true;
|
||||
OptGreyOutZeroes = true;
|
||||
@@ -187,8 +189,6 @@ struct MemoryEditor
|
||||
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||
{
|
||||
if (ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))
|
||||
ImGui::OpenPopup("context");
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged)
|
||||
{
|
||||
@@ -227,8 +227,18 @@ struct MemoryEditor
|
||||
float footer_height = 0;
|
||||
if (OptShowOptions)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;
|
||||
if (OptShowDataPreview)
|
||||
footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;
|
||||
|
||||
ImGui::BeginChild("offset", ImVec2(0, s.LineHeight), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::Text("% *c ", s.AddrDigitsCount, ' ');
|
||||
for (int i = 0; i < Cols; i++) {
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * i;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(i / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
ImGui::Text("%02X", i);
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, -footer_height), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
@@ -253,8 +263,6 @@ struct MemoryEditor
|
||||
if (DataPreviewAddrEnd >= mem_size)
|
||||
DataPreviewAddrEnd = (size_t)-1;
|
||||
|
||||
size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;
|
||||
|
||||
size_t data_editing_addr_backup = DataEditingAddr;
|
||||
size_t data_editing_addr_next = (size_t)-1;
|
||||
if (DataEditingAddr != (size_t)-1)
|
||||
@@ -309,13 +317,18 @@ struct MemoryEditor
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
float highlight_width = s.GlyphWidth * 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, true)));
|
||||
if (is_next_byte_highlighted || (n + 1 == Cols))
|
||||
if (is_next_byte_highlighted)
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), HighlightColor);
|
||||
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
@@ -405,13 +418,19 @@ struct MemoryEditor
|
||||
if (ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
} else {
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
hex::View::postEvent(hex::Events::ByteSelected, &DataPreviewAddr);
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
size_t dataPreviewStart = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
hex::View::postEvent(hex::Events::ByteSelected, &dataPreviewStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,13 +441,13 @@ struct MemoryEditor
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
ImGui::PushID(line_i);
|
||||
if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = addr + (size_t)((ImGui::GetIO().MousePos.x - pos.x) / s.GlyphWidth);
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
if (addr == DataEditingAddr)
|
||||
@@ -439,6 +458,42 @@ struct MemoryEditor
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (!ReadOnly && ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += s.GlyphWidth;
|
||||
}
|
||||
}
|
||||
@@ -458,19 +513,12 @@ struct MemoryEditor
|
||||
DataEditingAddr = DataPreviewAddr = data_editing_addr_next;
|
||||
}
|
||||
|
||||
const bool lock_show_data_preview = OptShowDataPreview;
|
||||
if (OptShowOptions)
|
||||
{
|
||||
ImGui::Separator();
|
||||
DrawOptionsLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
if (lock_show_data_preview)
|
||||
{
|
||||
ImGui::Separator();
|
||||
DrawPreviewLine(s, mem_data, mem_size, base_display_addr);
|
||||
}
|
||||
|
||||
// Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)
|
||||
ImGui::SetCursorPosX(s.WindowWidth);
|
||||
}
|
||||
@@ -483,13 +531,12 @@ struct MemoryEditor
|
||||
|
||||
// Options menu
|
||||
if (ImGui::Button("Options"))
|
||||
ImGui::OpenPopup("context");
|
||||
if (ImGui::BeginPopup("context"))
|
||||
{
|
||||
ImGui::OpenPopup("options");
|
||||
|
||||
if (ImGui::BeginPopup("options")) {
|
||||
ImGui::PushItemWidth(56);
|
||||
if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Checkbox("Show Data Preview", &OptShowDataPreview);
|
||||
ImGui::Checkbox("Show HexII", &OptShowHexII);
|
||||
if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; }
|
||||
ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes);
|
||||
@@ -500,18 +547,6 @@ struct MemoryEditor
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);
|
||||
if (ImGui::InputText("##addr", AddrInputBuf, 32, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))
|
||||
{
|
||||
size_t goto_addr;
|
||||
if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1)
|
||||
{
|
||||
GotoAddr = goto_addr - base_display_addr;
|
||||
HighlightMin = HighlightMax = (size_t)-1;
|
||||
}
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
if (GotoAddr != (size_t)-1)
|
||||
{
|
||||
@@ -527,66 +562,7 @@ struct MemoryEditor
|
||||
}
|
||||
}
|
||||
|
||||
void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)
|
||||
{
|
||||
IM_UNUSED(base_display_addr);
|
||||
ImU8* mem_data = (ImU8*)mem_data_void;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("Preview as:");
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))
|
||||
{
|
||||
for (int n = 0; n < ImGuiDataType_COUNT; n++)
|
||||
if (ImGui::Selectable(DataTypeGetDesc((ImGuiDataType)n), PreviewDataType == n))
|
||||
PreviewDataType = (ImGuiDataType)n;
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);
|
||||
ImGui::Combo("##combo_endianess", &PreviewEndianess, "LE\0BE\0\0");
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
char buf[128] = "";
|
||||
float x = s.GlyphWidth * 6.0f;
|
||||
bool has_value = DataPreviewAddr != (size_t)-1;
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
if (has_value)
|
||||
DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));
|
||||
buf[IM_ARRAYSIZE(buf) - 1] = 0;
|
||||
ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A");
|
||||
}
|
||||
|
||||
// Utilities for Data Preview
|
||||
const char* DataTypeGetDesc(ImGuiDataType data_type) const
|
||||
{
|
||||
const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
return descs[data_type];
|
||||
}
|
||||
|
||||
size_t DataTypeGetSize(ImGuiDataType data_type) const
|
||||
{
|
||||
const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };
|
||||
IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT);
|
||||
return sizes[data_type];
|
||||
}
|
||||
|
||||
const char* DataFormatGetDesc(DataFormat data_format) const
|
||||
{
|
||||
const char* descs[] = { "Bin", "Dec", "Hex" };
|
||||
IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);
|
||||
return descs[data_format];
|
||||
}
|
||||
|
||||
bool IsBigEndian() const
|
||||
static bool IsBigEndian()
|
||||
{
|
||||
uint16_t x = 1;
|
||||
char c[2];
|
||||
@@ -633,132 +609,6 @@ struct MemoryEditor
|
||||
fp = IsBigEndian() ? EndianessCopyBigEndian : EndianessCopyLittleEndian;
|
||||
return fp(dst, src, size, PreviewEndianess);
|
||||
}
|
||||
|
||||
const char* FormatBinary(const uint8_t* buf, int width) const
|
||||
{
|
||||
IM_ASSERT(width <= 64);
|
||||
size_t out_n = 0;
|
||||
static char out_buf[64 + 8 + 1];
|
||||
int n = width / 8;
|
||||
for (int j = n - 1; j >= 0; --j)
|
||||
{
|
||||
for (int i = 0; i < 8; ++i)
|
||||
out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';
|
||||
out_buf[out_n++] = ' ';
|
||||
}
|
||||
IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));
|
||||
out_buf[out_n] = 0;
|
||||
return out_buf;
|
||||
}
|
||||
|
||||
// [Internal]
|
||||
void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const
|
||||
{
|
||||
uint8_t buf[8];
|
||||
size_t elem_size = DataTypeGetSize(data_type);
|
||||
size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;
|
||||
if (ReadFn)
|
||||
for (int i = 0, n = (int)size; i < n; ++i)
|
||||
buf[i] = ReadFn(mem_data, addr + i);
|
||||
else
|
||||
memcpy(buf, mem_data + addr, size);
|
||||
|
||||
if (data_format == DataFormat_Bin)
|
||||
{
|
||||
uint8_t binbuf[8];
|
||||
EndianessCopy(binbuf, buf, size);
|
||||
ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8));
|
||||
return;
|
||||
}
|
||||
|
||||
out_buf[0] = 0;
|
||||
switch (data_type)
|
||||
{
|
||||
case ImGuiDataType_S8:
|
||||
{
|
||||
int8_t int8 = 0;
|
||||
EndianessCopy(&int8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", int8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", int8 & 0xFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U8:
|
||||
{
|
||||
uint8_t uint8 = 0;
|
||||
EndianessCopy(&uint8, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", uint8); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", uint8 & 0XFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S16:
|
||||
{
|
||||
int16_t int16 = 0;
|
||||
EndianessCopy(&int16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", int16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", int16 & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U16:
|
||||
{
|
||||
uint16_t uint16 = 0;
|
||||
EndianessCopy(&uint16, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", uint16); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", uint16 & 0xFFFF); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S32:
|
||||
{
|
||||
int32_t int32 = 0;
|
||||
EndianessCopy(&int32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", int32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", int32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U32:
|
||||
{
|
||||
uint32_t uint32 = 0;
|
||||
EndianessCopy(&uint32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", uint32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", uint32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_S64:
|
||||
{
|
||||
int64_t int64 = 0;
|
||||
EndianessCopy(&int64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)int64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)int64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_U64:
|
||||
{
|
||||
uint64_t uint64 = 0;
|
||||
EndianessCopy(&uint64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)uint64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)uint64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Float:
|
||||
{
|
||||
float float32 = 0.0f;
|
||||
EndianessCopy(&float32, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float32); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float32); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_Double:
|
||||
{
|
||||
double float64 = 0.0;
|
||||
EndianessCopy(&float64, buf, size);
|
||||
if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", float64); return; }
|
||||
if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", float64); return; }
|
||||
break;
|
||||
}
|
||||
case ImGuiDataType_COUNT:
|
||||
break;
|
||||
} // Switch
|
||||
IM_ASSERT(0); // Shouldn't reach
|
||||
}
|
||||
};
|
||||
|
||||
#undef _PRISizeT
|
||||
|
||||
1214
libs/ImGui/source/ImGuiFileBrowser.cpp
Normal file
1214
libs/ImGui/source/ImGuiFileBrowser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
3160
libs/ImGui/source/TextEditor.cpp
Normal file
3160
libs/ImGui/source/TextEditor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
7377
libs/ImGui/source/imgui_demo.cpp
Normal file
7377
libs/ImGui/source/imgui_demo.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1
resource.rc
Normal file
1
resource.rc
Normal file
@@ -0,0 +1 @@
|
||||
GLFW_ICON ICON icon.ico
|
||||
398
source/lang/evaluator.cpp
Normal file
398
source/lang/evaluator.cpp
Normal file
@@ -0,0 +1,398 @@
|
||||
#include "lang/evaluator.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Evaluator::Evaluator(prv::Provider* &provider, std::endian dataEndianess) : m_provider(provider), m_dataEndianess(dataEndianess) {
|
||||
PatternData::setEndianess(dataEndianess);
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> members;
|
||||
|
||||
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (structNode == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
size_t structSize = 0;
|
||||
for (const auto &node : structNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
u64 memberOffset = 0;
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + structSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), this->m_dataEndianess);
|
||||
}
|
||||
else
|
||||
memberOffset = offset + structSize;
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
u64 memberSize = 0;
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), this->m_dataEndianess);
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arraySize.has_value())
|
||||
return { nullptr, 0 };
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset + structSize, member->getPointerSize().value(), member->getVariableName(), pattern));
|
||||
structSize += member->getPointerSize().value();
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
structSize += memberSize;
|
||||
}
|
||||
}
|
||||
|
||||
return { new PatternDataStruct(offset, structSize, varDeclNode->getVariableName(), structNode->getName(), members, 0x00FFFFFF), structSize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> members;
|
||||
|
||||
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (unionNode == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
size_t unionSize = 0;
|
||||
for (const auto &node : unionNode->getNodes()) {
|
||||
const auto &member = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
u64 memberOffset = 0;
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
this->m_provider->read(offset + unionSize, &memberOffset, member->getPointerSize().value());
|
||||
|
||||
memberOffset = hex::changeEndianess(memberOffset, member->getPointerSize().value(), this->m_dataEndianess);
|
||||
}
|
||||
else
|
||||
memberOffset = offset;
|
||||
|
||||
const auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(this->m_types[member->getCustomVariableTypeName()]);
|
||||
|
||||
PatternData *pattern = nullptr;
|
||||
u64 memberSize = 0;
|
||||
|
||||
if (member->getVariableType() == Token::TypeToken::Type::Signed8Bit && member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
|
||||
for (auto &prevMember : members) {
|
||||
if (prevMember->getPatternType() == PatternData::Type::Unsigned && prevMember->getName() == member->getArraySizeVariable()) {
|
||||
u64 value = 0;
|
||||
this->m_provider->read(prevMember->getOffset(), &value, prevMember->getSize());
|
||||
|
||||
value = hex::changeEndianess(value, prevMember->getSize(), this->m_dataEndianess);
|
||||
|
||||
arraySize = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arraySize.has_value())
|
||||
return { nullptr, 0 };
|
||||
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
|
||||
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
|
||||
}
|
||||
else if (member->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
if (member->getPointerSize().has_value()) {
|
||||
members.push_back(new PatternDataPointer(offset, member->getPointerSize().value(), member->getVariableName(), pattern));
|
||||
unionSize = std::max(size_t(member->getPointerSize().value()), unionSize);
|
||||
}
|
||||
else {
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(memberSize, unionSize);
|
||||
}
|
||||
}
|
||||
|
||||
return { new PatternDataUnion(offset, unionSize, varDeclNode->getVariableName(), unionNode->getName(), members, 0x00FFFFFF), unionSize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createEnumPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<std::pair<u64, std::string>> enumValues;
|
||||
|
||||
auto *enumType = static_cast<ASTNodeEnum*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (enumType == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
size_t size = getTypeSize(enumType->getUnderlyingType());
|
||||
|
||||
return { new PatternDataEnum(offset, size, varDeclNode->getVariableName(), enumType->getName(), enumType->getValues()), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBitfieldPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
|
||||
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (bitfieldType == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
size_t size = 0;
|
||||
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
||||
size += fieldSize;
|
||||
|
||||
size = std::bit_ceil(size) / 8;
|
||||
|
||||
return { new PatternDataBitfield(offset, size, varDeclNode->getVariableName(), bitfieldType->getName(), bitfieldType->getFields()), size };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createArrayPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
std::vector<PatternData*> entries;
|
||||
|
||||
auto arraySizeVariable = varDeclNode->getArraySizeVariable();
|
||||
|
||||
size_t arrayOffset = 0;
|
||||
std::optional<u32> arrayColor;
|
||||
for (u32 i = 0; i < varDeclNode->getArraySize(); i++) {
|
||||
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getVariableType(), "[" + std::to_string(i) + "]", varDeclNode->getCustomVariableTypeName(), varDeclNode->getOffset(), 1);
|
||||
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Padding) {
|
||||
return { new PatternDataPadding(offset, varDeclNode->getArraySize()), varDeclNode->getArraySize() };
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto& [pattern, size] = this->createBuiltInTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
} else {
|
||||
const auto &[pattern, size] = this->createCustomTypePattern(nonArrayVarDeclNode, offset + arrayOffset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
}
|
||||
|
||||
delete nonArrayVarDeclNode;
|
||||
}
|
||||
|
||||
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), entries, 0x00FFFFFF), arrayOffset };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
|
||||
return { new PatternDataString(offset, arraySize, varDeclNode->getVariableName()), arraySize };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
|
||||
|
||||
if (currType == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
switch (currType->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
return this->createStructPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Union:
|
||||
return this->createUnionPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Enum:
|
||||
return this->createEnumPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::Bitfield:
|
||||
return this->createBitfieldPattern(varDeclNode, offset);
|
||||
case ASTNode::Type::TypeDecl:
|
||||
return this->createBuiltInTypePattern(varDeclNode, offset);
|
||||
}
|
||||
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createBuiltInTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto type = varDeclNode->getVariableType();
|
||||
if (type == Token::TypeToken::Type::CustomType) {
|
||||
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
if (currType == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
type = currType->getAssignedType();
|
||||
}
|
||||
|
||||
size_t typeSize = getTypeSize(type);
|
||||
size_t arraySize = varDeclNode->getArraySize();
|
||||
|
||||
if (isSigned(type)) {
|
||||
if (typeSize == 1 && arraySize == 1)
|
||||
return { new PatternDataCharacter(offset, typeSize, varDeclNode->getVariableName()), 1 };
|
||||
else if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataSigned(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
|
||||
} else if (isUnsigned(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataUnsigned(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
|
||||
} else if (isFloatingPoint(varDeclNode->getVariableType())) {
|
||||
if (arraySize > 1)
|
||||
return createArrayPattern(varDeclNode, offset);
|
||||
else
|
||||
return { new PatternDataFloat(offset, typeSize, varDeclNode->getVariableName()), typeSize * arraySize };
|
||||
}
|
||||
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
std::pair<Result, std::vector<PatternData*>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
|
||||
|
||||
// Evaluate types
|
||||
for (const auto &node : ast) {
|
||||
|
||||
switch(node->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
auto *structNode = static_cast<ASTNodeStruct*>(node);
|
||||
this->m_types.emplace(structNode->getName(), structNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Union:
|
||||
{
|
||||
auto *unionNode = static_cast<ASTNodeUnion*>(node);
|
||||
this->m_types.emplace(unionNode->getName(), unionNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
auto *enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
this->m_types.emplace(enumNode->getName(), enumNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
auto *bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
this->m_types.emplace(bitfieldNode->getName(), bitfieldNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
auto *typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType)
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), this->m_types[typeDeclNode->getAssignedCustomTypeName()]);
|
||||
else
|
||||
this->m_types.emplace(typeDeclNode->getTypeName(), typeDeclNode);
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::VariableDecl: break;
|
||||
case ASTNode::Type::Scope: break;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate variable declarations
|
||||
|
||||
std::vector<PatternData*> variables;
|
||||
for (const auto &node : ast) {
|
||||
if (node->getType() != ASTNode::Type::VariableDecl)
|
||||
continue;
|
||||
|
||||
auto *varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (varDeclNode->getVariableType() == Token::TypeToken::Type::Signed8Bit && varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = createStringPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
else if (varDeclNode->getArraySize() > 1) {
|
||||
const auto &[pattern, _] = this->createArrayPattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
|
||||
} else if (varDeclNode->getVariableType() != Token::TypeToken::Type::CustomType) {
|
||||
const auto &[pattern, _] = this->createBuiltInTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
} else {
|
||||
const auto &[pattern, _] = this->createCustomTypePattern(varDeclNode, varDeclNode->getOffset().value());
|
||||
variables.push_back(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &var : variables)
|
||||
if (var == nullptr)
|
||||
return { ResultEvaluatorError, { } };
|
||||
|
||||
return { ResultSuccess, variables };
|
||||
}
|
||||
|
||||
}
|
||||
@@ -102,31 +102,34 @@ namespace hex::lang {
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({.type = Token::Type::EndOfExpression});
|
||||
tokens.push_back({ .type = Token::Type::EndOfExpression });
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({.type = Token::Type::ScopeOpen});
|
||||
tokens.push_back({ .type = Token::Type::ScopeOpen });
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({.type = Token::Type::ScopeClose});
|
||||
tokens.push_back({ .type = Token::Type::ScopeClose });
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.push_back({.type = Token::Type::ArrayOpen});
|
||||
tokens.push_back({ .type = Token::Type::ArrayOpen });
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.push_back({.type = Token::Type::ArrayClose});
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({.type = Token::Type::Separator});
|
||||
tokens.push_back({ .type = Token::Type::Separator });
|
||||
offset += 1;
|
||||
} else if (c == '@') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration}});
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::AtDeclaration } });
|
||||
offset += 1;
|
||||
} else if (c == '=') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment}});
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Assignment } });
|
||||
offset += 1;
|
||||
} else if (c == ':') {
|
||||
tokens.push_back({.type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit}});
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Inherit } });
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star } });
|
||||
offset += 1;
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
@@ -134,42 +137,48 @@ namespace hex::lang {
|
||||
// Check for reserved keywords
|
||||
|
||||
if (identifier == "struct")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct}});
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Struct } });
|
||||
else if (identifier == "union")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union } });
|
||||
else if (identifier == "using")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using}});
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Using } });
|
||||
else if (identifier == "enum")
|
||||
tokens.push_back({.type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum}});
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Enum } });
|
||||
else if (identifier == "bitfield")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield } });
|
||||
|
||||
// Check for built-in types
|
||||
else if (identifier == "u8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned8Bit } });
|
||||
else if (identifier == "s8")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed8Bit } });
|
||||
else if (identifier == "u16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned16Bit } });
|
||||
else if (identifier == "s16")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed16Bit } });
|
||||
else if (identifier == "u32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned32Bit } });
|
||||
else if (identifier == "s32")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed32Bit } });
|
||||
else if (identifier == "u64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned64Bit } });
|
||||
else if (identifier == "s64")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed64Bit } });
|
||||
else if (identifier == "u128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Unsigned128Bit } });
|
||||
else if (identifier == "s128")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Signed128Bit } });
|
||||
else if (identifier == "float")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Float } });
|
||||
else if (identifier == "double")
|
||||
tokens.push_back({.type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double }});
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Double } });
|
||||
else if (identifier == "padding")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding } });
|
||||
|
||||
// If it's not a keyword and a builtin type, it has to be an identifier
|
||||
|
||||
else
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier}});
|
||||
tokens.push_back({.type = Token::Type::Identifier, .identifierToken = { .identifier = identifier } });
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
@@ -181,12 +190,12 @@ namespace hex::lang {
|
||||
if (!integer.has_value())
|
||||
return { ResultLexicalError, {}};
|
||||
|
||||
tokens.push_back({.type = Token::Type::Integer, .integerToken = { .integer = integer.value() }});
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() } });
|
||||
offset += (end - &code[offset]);
|
||||
} else return { ResultLexicalError, {}};
|
||||
}
|
||||
|
||||
tokens.push_back({.type = Token::Type::EndOfProgram});
|
||||
tokens.push_back({ .type = Token::Type::EndOfProgram });
|
||||
|
||||
return { ResultSuccess, tokens };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "lang/parser.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace hex::lang {
|
||||
@@ -35,6 +37,24 @@ namespace hex::lang {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseBuiltinPointerVariableDecl(TokenIter &curr) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
||||
return nullptr;
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypePointerVariableDecl(TokenIter &curr) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
||||
return nullptr;
|
||||
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
@@ -43,6 +63,18 @@ namespace hex::lang {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseBuiltinVariableArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeVariableArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parsePaddingDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
||||
}
|
||||
@@ -64,6 +96,20 @@ namespace hex::lang {
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Identifier, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression})) {
|
||||
if (curr[-5].typeToken.type != Token::TypeToken::Type::Padding) {
|
||||
for(auto &node : nodes) delete node;
|
||||
return nullptr;
|
||||
}
|
||||
nodes.push_back(parsePaddingDecl(curr));
|
||||
} else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr));
|
||||
else break;
|
||||
}
|
||||
|
||||
@@ -75,6 +121,34 @@ namespace hex::lang {
|
||||
return new ASTNodeStruct(structName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* parseUnion(TokenIter &curr) {
|
||||
const std::string &unionName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Identifier, Token::Type::ArrayOpen, Token::Type::Integer, Token::Type::ArrayClose, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypeArrayDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Type, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinPointerVariableDecl(curr));
|
||||
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseCustomTypePointerVariableDecl(curr));
|
||||
else break;
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
for(auto &node : nodes) delete node;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeUnion(unionName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* parseEnum(TokenIter &curr) {
|
||||
const std::string &enumName = curr[-4].identifierToken.identifier;
|
||||
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
|
||||
@@ -121,6 +195,26 @@ namespace hex::lang {
|
||||
return enumNode;
|
||||
}
|
||||
|
||||
ASTNode *parseBitField(TokenIter &curr) {
|
||||
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
|
||||
std::vector<std::pair<std::string, size_t>> fields;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
||||
return nullptr;
|
||||
|
||||
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression}))
|
||||
return nullptr;
|
||||
|
||||
return new ASTNodeBitField(bitfieldName, fields);
|
||||
}
|
||||
|
||||
ASTNode *parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
|
||||
}
|
||||
@@ -163,12 +257,30 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
program.push_back(structAst);
|
||||
} else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Union) {
|
||||
auto unionAst = parseUnion(curr);
|
||||
|
||||
if (unionAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(unionAst);
|
||||
} else if (curr[-3].keywordToken.keyword == Token::KeywordToken::Keyword::Bitfield) {
|
||||
auto bitfieldAst = parseBitField(curr);
|
||||
|
||||
if (bitfieldAst == nullptr) {
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
program.push_back(bitfieldAst);
|
||||
}
|
||||
|
||||
return program;
|
||||
|
||||
} // Enum
|
||||
if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) {
|
||||
else if (tryConsume(curr, { Token::Type::Keyword, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::ScopeOpen })) {
|
||||
if (curr[-5].keywordToken.keyword == Token::KeywordToken::Keyword::Enum) {
|
||||
auto enumAst = parseEnum(curr);
|
||||
|
||||
@@ -181,7 +293,6 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
return program;
|
||||
|
||||
// Scope
|
||||
} else if (tryConsume(curr, { Token::Type::ScopeOpen })) {
|
||||
program.push_back(parseScope(curr));
|
||||
@@ -257,7 +368,7 @@ namespace hex::lang {
|
||||
|
||||
auto program = parseTillToken(currentToken, Token::Type::EndOfProgram);
|
||||
|
||||
if (program.empty())
|
||||
if (program.empty() || currentToken != tokens.end())
|
||||
return { ResultParseError, { } };
|
||||
|
||||
return { ResultSuccess, program };
|
||||
|
||||
@@ -6,11 +6,13 @@ namespace hex::lang {
|
||||
|
||||
}
|
||||
|
||||
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool applyDefines) {
|
||||
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
|
||||
u32 offset = 0;
|
||||
|
||||
if (applyDefines)
|
||||
if (initialRun) {
|
||||
this->m_defines.clear();
|
||||
this->m_pragmas.clear();
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.reserve(code.length());
|
||||
@@ -76,45 +78,103 @@ namespace hex::lang {
|
||||
std::string defineName;
|
||||
while (!std::isblank(code[offset])) {
|
||||
defineName += code[offset];
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { ResultPreprocessingError, "" };
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string replaceValue;
|
||||
do {
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
replaceValue += code[offset];
|
||||
offset += 1;
|
||||
} while (code[offset] != '\n' && code[offset] != '\r');
|
||||
}
|
||||
|
||||
if (replaceValue.empty())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
this->m_defines.emplace(defineName, replaceValue);
|
||||
}
|
||||
} else if (code.substr(offset, 6) == "pragma") {
|
||||
offset += 6;
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaKey;
|
||||
while (!std::isblank(code[offset])) {
|
||||
pragmaKey += code[offset];
|
||||
|
||||
if (offset >= code.length() || code[offset] == '\n' || code[offset] == '\r')
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
while (std::isblank(code[offset]))
|
||||
offset += 1;
|
||||
|
||||
std::string pragmaValue;
|
||||
while (code[offset] != '\n' && code[offset] != '\r') {
|
||||
if (offset >= code.length())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
pragmaValue += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (pragmaValue.empty())
|
||||
return { ResultPreprocessingError, "" };
|
||||
|
||||
this->m_pragmas.emplace(pragmaKey, pragmaValue);
|
||||
} else
|
||||
return { ResultPreprocessingError, "" };
|
||||
}
|
||||
|
||||
output += code[offset];
|
||||
offset += 1;
|
||||
}
|
||||
|
||||
if (applyDefines) {
|
||||
if (initialRun) {
|
||||
// Apply defines
|
||||
for (const auto &[define, value] : this->m_defines) {
|
||||
s32 index = 0;
|
||||
while((index = output.find(define, index)) != std::string::npos) {
|
||||
if (index > 0) {
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
}
|
||||
output.replace(index, define.length(), value);
|
||||
index += value.length();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle pragmas
|
||||
for (const auto &[type, value] : this->m_pragmas) {
|
||||
if (this->m_pragmaHandlers.contains(type)) {
|
||||
if (!this->m_pragmaHandlers[type](value))
|
||||
return { ResultPreprocessingError, { } };
|
||||
} else
|
||||
return { ResultPreprocessingError, { } };
|
||||
}
|
||||
}
|
||||
|
||||
return { ResultSuccess, output };
|
||||
}
|
||||
|
||||
void Preprocessor::addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function) {
|
||||
if (!this->m_pragmaHandlers.contains(pragmaType))
|
||||
this->m_pragmaHandlers.emplace(pragmaType, function);
|
||||
}
|
||||
|
||||
void Preprocessor::addDefaultPragramHandlers() {
|
||||
this->addPragmaHandler("MIME", [](std::string value) {
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
this->addPragmaHandler("endian", [](std::string value) {
|
||||
return value == "big" || value == "little" || value == "native";
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,10 @@ namespace hex::lang {
|
||||
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
if (!typeNames.insert(varDeclNode->getVariableName()).second)
|
||||
return false;
|
||||
|
||||
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
||||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value())
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
@@ -33,7 +37,7 @@ namespace hex::lang {
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName()))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
@@ -47,7 +51,7 @@ namespace hex::lang {
|
||||
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
@@ -61,7 +65,29 @@ namespace hex::lang {
|
||||
if (!constantNames.insert(name).second)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
if (!typeNames.insert(bitfieldNode->getName()).second)
|
||||
return false;
|
||||
|
||||
size_t bitfieldSize = 0;
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> flagNames;
|
||||
for (const auto &[name, size] : bitfieldNode->getFields()) {
|
||||
if (!flagNames.insert(name).second)
|
||||
return false;
|
||||
|
||||
bitfieldSize += size;
|
||||
}
|
||||
|
||||
if (bitfieldSize > 64)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "window.hpp"
|
||||
|
||||
#include "views/pattern_data.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
#include "views/view_hexeditor.hpp"
|
||||
#include "views/view_pattern.hpp"
|
||||
#include "views/view_pattern_data.hpp"
|
||||
@@ -9,6 +9,8 @@
|
||||
#include "views/view_help.hpp"
|
||||
#include "views/view_tools.hpp"
|
||||
#include "views/view_strings.hpp"
|
||||
#include "views/view_data_inspector.hpp"
|
||||
#include "views/view_disassembler.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
@@ -18,18 +20,19 @@ int main() {
|
||||
hex::Window window;
|
||||
|
||||
// Shared Data
|
||||
std::vector<hex::PatternData*> patternData;
|
||||
std::vector<hex::lang::PatternData*> patternData;
|
||||
hex::prv::Provider *dataProvider = nullptr;
|
||||
|
||||
// Create views
|
||||
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
|
||||
window.addView<hex::ViewPattern>(dataProvider, patternData);
|
||||
window.addView<hex::ViewPatternData>(dataProvider, patternData);
|
||||
window.addView<hex::ViewDataInspector>(dataProvider);
|
||||
window.addView<hex::ViewHashes>(dataProvider);
|
||||
window.addView<hex::ViewInformation>(dataProvider);
|
||||
window.addView<hex::ViewStrings>(dataProvider);
|
||||
window.addView<hex::ViewHelp>();
|
||||
window.addView<hex::ViewDisassembler>(dataProvider);
|
||||
window.addView<hex::ViewTools>();
|
||||
window.addView<hex::ViewHelp>();
|
||||
|
||||
window.loop();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "providers/file_provider.hpp"
|
||||
|
||||
#undef __STRICT_ANSI__
|
||||
#include <cstdio>
|
||||
|
||||
#include <sys/stat.h>
|
||||
@@ -21,7 +22,6 @@ namespace hex::prv {
|
||||
this->m_file = fopen(path.data(), "rb");
|
||||
this->m_writable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FileProvider::~FileProvider() {
|
||||
@@ -47,7 +47,7 @@ namespace hex::prv {
|
||||
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseek(this->m_file, offset, SEEK_SET);
|
||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||
fread(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
@@ -55,20 +55,20 @@ namespace hex::prv {
|
||||
if (buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseek(this->m_file, offset, SEEK_SET);
|
||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||
fwrite(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
size_t FileProvider::getSize() {
|
||||
fseek(this->m_file, 0, SEEK_END);
|
||||
return ftell(this->m_file);
|
||||
size_t FileProvider::getActualSize() {
|
||||
fseeko64(this->m_file, 0, SEEK_END);
|
||||
return ftello64(this->m_file);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> FileProvider::getDataInformation() {
|
||||
std::vector<std::pair<std::string, std::string>> result;
|
||||
|
||||
result.emplace_back("File path", this->m_path);
|
||||
result.emplace_back("Size", hex::toByteString(this->getSize()));
|
||||
result.emplace_back("Size", hex::toByteString(this->getActualSize()));
|
||||
|
||||
if (this->m_fileStatsValid) {
|
||||
result.emplace_back("Creation time", ctime(&this->m_fileStats.st_ctime));
|
||||
|
||||
164
source/views/view_data_inspector.cpp
Normal file
164
source/views/view_data_inspector.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "views/view_data_inspector.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end);
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::ByteSelected, [this](const void* userData){
|
||||
size_t offset = *static_cast<const size_t*>(userData);
|
||||
|
||||
this->m_validBytes = std::min(this->m_dataProvider->getSize() - offset, sizeof(PreviewData));
|
||||
std::memset(&this->m_previewData, 0x00, sizeof(PreviewData));
|
||||
this->m_dataProvider->read(offset, &this->m_previewData, sizeof(PreviewData));
|
||||
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
}
|
||||
|
||||
ViewDataInspector::~ViewDataInspector() {
|
||||
View::unsubscribeEvent(Events::ByteSelected);
|
||||
}
|
||||
|
||||
void ViewDataInspector::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_shouldInvalidate = false;
|
||||
|
||||
this->m_cachedData.clear();
|
||||
|
||||
{
|
||||
std::string binary;
|
||||
for (u8 i = 0; i < 8; i++)
|
||||
binary += ((this->m_previewData.unsigned8 << i) & 0x80) == 0 ? '0' : '1';
|
||||
this->m_cachedData.emplace_back("Binary (8 bit)", binary);
|
||||
}
|
||||
|
||||
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned8, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int8_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed8, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", hex::changeEndianess(this->m_previewData.unsigned16, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int16_t", hex::format("%d", hex::changeEndianess(this->m_previewData.signed16, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", hex::changeEndianess(this->m_previewData.signed32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", hex::changeEndianess(this->m_previewData.unsigned64, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", hex::changeEndianess(this->m_previewData.signed64, this->m_endianess)));
|
||||
|
||||
this->m_cachedData.emplace_back("ANSI Character / char8_t", hex::format("%c", hex::changeEndianess(this->m_previewData.ansiChar, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", hex::changeEndianess(this->m_previewData.wideChar, this->m_endianess)));
|
||||
{
|
||||
char buffer[5] = { 0 };
|
||||
char codepointString[5] = { 0 };
|
||||
u32 codepoint = 0;
|
||||
|
||||
std::memcpy(buffer, &this->m_previewData.utf8Char, 4);
|
||||
u8 codepointSize = ImTextCharFromUtf8(&codepoint, buffer, buffer + 4);
|
||||
|
||||
std::memcpy(codepointString, &codepoint, std::min(codepointSize, u8(4)));
|
||||
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("'%s' (U+%04lx)", codepoint == 0xFFFD ? "Invalid" : codepointString, codepoint));
|
||||
}
|
||||
|
||||
this->m_cachedData.emplace_back("float (32 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float32, this->m_endianess)));
|
||||
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", hex::changeEndianess(this->m_previewData.float64, this->m_endianess)));
|
||||
|
||||
#if defined(_WIN64)
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time32, this->m_endianess);
|
||||
std::tm * ptm = _localtime32(&endianAdjustedTime);
|
||||
char buffer[32];
|
||||
if (std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm))
|
||||
this->m_cachedData.emplace_back("__time32_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("__time32_t", "Invalid");
|
||||
}
|
||||
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time64, this->m_endianess);
|
||||
std::tm * ptm = _localtime64(&endianAdjustedTime);
|
||||
char buffer[64];
|
||||
if (std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
|
||||
this->m_cachedData.emplace_back("__time64_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("__time64_t", "Invalid");
|
||||
}
|
||||
#else
|
||||
{
|
||||
auto endianAdjustedTime = hex::changeEndianess(this->m_previewData.time, this->m_endianess);
|
||||
std::tm * ptm = localtime(&endianAdjustedTime);
|
||||
char buffer[64];
|
||||
if (std::strftime(buffer, 64, "%a, %d.%m.%Y %H:%M:%S", ptm) != 0)
|
||||
this->m_cachedData.emplace_back("time_t", buffer);
|
||||
else
|
||||
this->m_cachedData.emplace_back("time_t", "Invalid");
|
||||
}
|
||||
#endif
|
||||
|
||||
this->m_cachedData.emplace_back("GUID", hex::format("{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
|
||||
hex::changeEndianess(this->m_previewData.guid.data1, this->m_endianess),
|
||||
hex::changeEndianess(this->m_previewData.guid.data2, this->m_endianess),
|
||||
hex::changeEndianess(this->m_previewData.guid.data3, this->m_endianess),
|
||||
this->m_previewData.guid.data4[0], this->m_previewData.guid.data4[1], this->m_previewData.guid.data4[2], this->m_previewData.guid.data4[3],
|
||||
this->m_previewData.guid.data4[4], this->m_previewData.guid.data4[5], this->m_previewData.guid.data4[6], this->m_previewData.guid.data4[7]));
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Data Inspector", &this->m_windowOpen)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
|
||||
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||
ImGui::TableSetupColumn("Name");
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &[name, value] : this->m_cachedData) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(value.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("RGBA Color");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("##nolabel", ImColor(hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)),
|
||||
ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), 15));
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
if (ImGui::RadioButton("Little Endian", this->m_endianess == std::endian::little)) {
|
||||
this->m_endianess = std::endian::little;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Big Endian", this->m_endianess == std::endian::big)) {
|
||||
this->m_endianess = std::endian::big;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewDataInspector::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Data Preview View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
268
source/views/view_disassembler.cpp
Normal file
268
source/views/view_disassembler.cpp
Normal file
@@ -0,0 +1,268 @@
|
||||
#include "views/view_disassembler.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
}
|
||||
|
||||
ViewDisassembler::~ViewDisassembler() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
}
|
||||
|
||||
void ViewDisassembler::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_disassembly.clear();
|
||||
|
||||
csh capstoneHandle;
|
||||
cs_insn *instructions = nullptr;
|
||||
|
||||
cs_mode mode = cs_mode(this->m_modeBasicARM | this->m_modeExtraARM | this->m_modeBasicMIPS | this->m_modeBasicX86 | this->m_modeBasicPPC);
|
||||
|
||||
if (this->m_littleEndianMode)
|
||||
mode = cs_mode(mode | CS_MODE_LITTLE_ENDIAN);
|
||||
else
|
||||
mode = cs_mode(mode | CS_MODE_BIG_ENDIAN);
|
||||
|
||||
if (this->m_micoMode)
|
||||
mode = cs_mode(mode | CS_MODE_MICRO);
|
||||
|
||||
if (this->m_sparcV9Mode)
|
||||
mode = cs_mode(mode | CS_MODE_V9);
|
||||
|
||||
if (cs_open(this->m_architecture, mode, &capstoneHandle) == CS_ERR_OK) {
|
||||
|
||||
std::vector<u8> buffer(2048, 0x00);
|
||||
for (u64 address = 0; address < this->m_codeSize; address += 2048) {
|
||||
size_t bufferSize = std::min(u64(2048), this->m_codeSize - address);
|
||||
this->m_dataProvider->read(this->m_codeOffset + address, buffer.data(), bufferSize);
|
||||
|
||||
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), buffer.size(), this->m_baseAddress + address, 0, &instructions);
|
||||
|
||||
if (instructionCount == 0)
|
||||
break;
|
||||
|
||||
u64 usedBytes = 0;
|
||||
for (u32 instr = 0; instr < instructionCount; instr++) {
|
||||
Disassembly disassembly = { 0 };
|
||||
disassembly.address = instructions[instr].address;
|
||||
disassembly.offset = this->m_codeOffset + address + usedBytes;
|
||||
disassembly.opcodeString = instructions[instr].mnemonic + " "s + instructions[instr].op_str;
|
||||
|
||||
for (u8 i = 0; i < instructions[instr].size; i++)
|
||||
disassembly.bytes += hex::format("%02X ", instructions[instr].bytes[i]);
|
||||
disassembly.bytes.pop_back();
|
||||
|
||||
this->m_disassembly.push_back(disassembly);
|
||||
|
||||
usedBytes += instructions[instr].size;
|
||||
}
|
||||
|
||||
if (instructionCount < bufferSize)
|
||||
address -= (bufferSize - usedBytes);
|
||||
|
||||
cs_free(instructions, instructionCount);
|
||||
}
|
||||
|
||||
cs_close(&capstoneHandle);
|
||||
}
|
||||
|
||||
this->m_shouldInvalidate = false;
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Disassembler", &this->m_windowOpen)) {
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
constexpr static const char * const ArchitectureNames[] = { "ARM32", "ARM64", "MIPS", "x86", "PowerPC", "Sparc", "SystemZ", "XCore", "68K", "TMS320C64x", "680X", "Ethereum" };
|
||||
|
||||
ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::InputScalar("Code start offset", ImGuiDataType_U64, &this->m_codeOffset, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::InputScalar("Code size", ImGuiDataType_U64, &this->m_codeSize, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Combo("Architecture", reinterpret_cast<int*>(&this->m_architecture), ArchitectureNames, 12);
|
||||
|
||||
|
||||
if (ImGui::BeginChild("modes", ImVec2(0, 100), true)) {
|
||||
|
||||
if (ImGui::RadioButton("Little Endian", this->m_littleEndianMode))
|
||||
this->m_littleEndianMode = true;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Big Endian", !this->m_littleEndianMode))
|
||||
this->m_littleEndianMode = false;
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
switch (this->m_architecture) {
|
||||
case CS_ARCH_ARM:
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (ImGui::RadioButton("ARM mode", this->m_modeBasicARM == CS_MODE_ARM))
|
||||
this->m_modeBasicARM = CS_MODE_ARM;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Thumb mode", this->m_modeBasicARM == CS_MODE_THUMB))
|
||||
this->m_modeBasicARM = CS_MODE_THUMB;
|
||||
|
||||
if (ImGui::RadioButton("Default mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == 0))
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("Cortex-M mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_MCLASS))
|
||||
this->m_modeExtraARM = CS_MODE_MCLASS;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("ARMv8 mode", (this->m_modeExtraARM & (CS_MODE_MCLASS | CS_MODE_V8)) == CS_MODE_V8))
|
||||
this->m_modeExtraARM = CS_MODE_V8;
|
||||
break;
|
||||
case CS_ARCH_MIPS:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (ImGui::RadioButton("MIPS32 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("MIPS64 mode", this->m_modeBasicMIPS == CS_MODE_MIPS64))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS64;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("MIPS32R6 mode", this->m_modeBasicMIPS == CS_MODE_MIPS32R6))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32R6;
|
||||
|
||||
ImGui::Checkbox("Micro Mode", &this->m_micoMode);
|
||||
break;
|
||||
case CS_ARCH_X86:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (ImGui::RadioButton("16-bit mode", this->m_modeBasicX86 == CS_MODE_16))
|
||||
this->m_modeBasicX86 = CS_MODE_16;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicX86 == CS_MODE_32))
|
||||
this->m_modeBasicX86 = CS_MODE_32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicX86 == CS_MODE_64))
|
||||
this->m_modeBasicX86 = CS_MODE_64;
|
||||
break;
|
||||
case CS_ARCH_PPC:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
|
||||
if (ImGui::RadioButton("32-bit mode", this->m_modeBasicPPC == CS_MODE_32))
|
||||
this->m_modeBasicPPC = CS_MODE_32;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::RadioButton("64-bit mode", this->m_modeBasicPPC == CS_MODE_64))
|
||||
this->m_modeBasicPPC = CS_MODE_64;
|
||||
break;
|
||||
case CS_ARCH_SPARC:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
|
||||
ImGui::Checkbox("Sparc V9 mode", &this->m_sparcV9Mode);
|
||||
break;
|
||||
case CS_ARCH_ARM64:
|
||||
case CS_ARCH_SYSZ:
|
||||
case CS_ARCH_XCORE:
|
||||
case CS_ARCH_M68K:
|
||||
case CS_ARCH_TMS320C64X:
|
||||
case CS_ARCH_M680X:
|
||||
case CS_ARCH_EVM:
|
||||
this->m_modeBasicARM = cs_mode(0);
|
||||
this->m_modeExtraARM = cs_mode(0);
|
||||
this->m_modeBasicMIPS = cs_mode(0);
|
||||
this->m_modeBasicX86 = cs_mode(0);
|
||||
this->m_modeBasicPPC = cs_mode(0);
|
||||
this->m_micoMode = false;
|
||||
this->m_sparcV9Mode = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("Disassemble"))
|
||||
this->m_shouldInvalidate = true;
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::BeginChild("##scrolling")) {
|
||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||
ImGui::TableSetupColumn("Address");
|
||||
ImGui::TableSetupColumn("Offset");
|
||||
ImGui::TableSetupColumn("Bytes");
|
||||
ImGui::TableSetupColumn("Disassembly");
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_disassembly.size());
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%llx", this->m_disassembly[i].address);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%llx", this->m_disassembly[i].offset);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(this->m_disassembly[i].bytes.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(this->m_disassembly[i].opcodeString.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewDisassembler::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Disassembler View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,12 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](void*){
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
}
|
||||
@@ -21,7 +23,7 @@ namespace hex {
|
||||
|
||||
static void formatBigHexInt(auto dataArray, char *buffer, size_t bufferSize) {
|
||||
for (int i = 0; i < dataArray.size(); i++)
|
||||
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", __builtin_bswap32(dataArray[i]));
|
||||
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big));
|
||||
}
|
||||
|
||||
void ViewHashes::createView() {
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHelp::drawAboutPopup() {
|
||||
ImGui::OpenPopup("About");
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
||||
if (ImGui::BeginPopupModal("About", &this->m_aboutWindowOpen, ImGuiWindowFlags_NoResize)) {
|
||||
|
||||
@@ -31,7 +30,9 @@ namespace hex {
|
||||
ImGui::NewLine();
|
||||
ImGui::BulletText("ImGui by ocornut");
|
||||
ImGui::BulletText("imgui_club by ocornut");
|
||||
ImGui::BulletText("imgui-filebrowser by AirGuanZ");
|
||||
ImGui::BulletText("ImGui-Addons by gallickgunner");
|
||||
ImGui::BulletText("ImGuiColorTextEdit by BalazsJako");
|
||||
ImGui::BulletText("capstone by aquynh");
|
||||
ImGui::NewLine();
|
||||
ImGui::BulletText("GNU libmagic");
|
||||
ImGui::BulletText("OpenSSL libcrypto");
|
||||
@@ -44,7 +45,6 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHelp::drawPatternHelpPopup() {
|
||||
ImGui::OpenPopup("PatternLanguage");
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(450, 300));
|
||||
|
||||
constexpr static auto DrawTitle = [](const std::string &title) {
|
||||
@@ -64,11 +64,30 @@ namespace hex {
|
||||
};
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(450, 300), ImVec2(2000, 1000));
|
||||
if (ImGui::BeginPopupModal("Pattern Language Cheat Sheet", &this->m_patternHelpWindowOpen)) {
|
||||
if (ImGui::BeginPopupModal("Cheat Sheet", &this->m_patternHelpWindowOpen)) {
|
||||
ImGui::Text("ImHex Pattern Language Cheat Sheet");
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
DrawTitle("Preprocessor directives");
|
||||
ImGui::TextWrapped(
|
||||
"Preprocessor directives can be used to alter the code before it's being parsed. Supported are "
|
||||
"#define to replace one string with another and #include to include a separate file");
|
||||
DrawCodeSegment("preprocessor", 40,
|
||||
"#define HEADER_OFFSET 0x100\n"
|
||||
"#include <cstdint.hexpat>\n"
|
||||
"#include \"mypattern.hexpat\""
|
||||
);
|
||||
|
||||
DrawTitle("Pragma directives");
|
||||
ImGui::TextWrapped(
|
||||
"Pragma directives are used to give ImHex additional information about the code being read. Currently "
|
||||
"the following directives are supported.");
|
||||
DrawCodeSegment("pragma", 30,
|
||||
"// Allow this file to be loaded as pattern when a file identified as application/x-executable gets loaded"
|
||||
"#pragma MIME application/x-executable\n"
|
||||
);
|
||||
|
||||
DrawTitle("Built-in types");
|
||||
ImGui::TextWrapped(
|
||||
"The following built-in types are available for use");
|
||||
@@ -83,21 +102,58 @@ namespace hex {
|
||||
|
||||
DrawTitle("Variables and Arrays");
|
||||
ImGui::TextWrapped(
|
||||
"Normal variables as well as arrays are used to highlight and display values.");
|
||||
DrawCodeSegment("vars arrays", 30,
|
||||
"Normal variables as well as arrays are used to highlight and display values. "
|
||||
"It is possible to create arrays within structs and unions that use the value of a previously "
|
||||
"declared variable as size.");
|
||||
DrawCodeSegment("vars arrays", 45,
|
||||
"u32 variable;\n"
|
||||
"s8 string[16];"
|
||||
"s8 string[16];\n"
|
||||
"u8 customSizedArray[variable];"
|
||||
);
|
||||
|
||||
DrawTitle("Structs");
|
||||
ImGui::TextWrapped(
|
||||
"To bundle multiple variables together, a struct can be used.");
|
||||
DrawCodeSegment("struct", 70,
|
||||
"To bundle multiple variables together, a struct can be used. To insert padding bytes which won't show "
|
||||
"up in the pattern data view or be highlighted, use the padding[size] syntax.");
|
||||
DrawCodeSegment("struct", 85,
|
||||
"struct Header {\n"
|
||||
" u32 magic;\n"
|
||||
" u8 version;\n"
|
||||
" padding[4];\n"
|
||||
" Flags flags;\n"
|
||||
"}"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Unions");
|
||||
ImGui::TextWrapped(
|
||||
"A union is used to make two or more variables occupy the same region of memory. "
|
||||
"The union will have the size of the biggest contained variable.");
|
||||
DrawCodeSegment("union", 55,
|
||||
"union Color {\n"
|
||||
" u32 rgba;\n"
|
||||
" Components components;\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Pointers");
|
||||
ImGui::TextWrapped(
|
||||
"\"Another possible type of member in structs and unions are pointers. They are variables"
|
||||
"whose value is used as an offset from the start of the file to locate the actual offset. "
|
||||
"The leading type is treated as the data being pointed to and the trailing type as the size of the pointer.");
|
||||
DrawCodeSegment("pointer", 55,
|
||||
"Data *data : u16;"
|
||||
);
|
||||
|
||||
DrawTitle("Bitfields");
|
||||
ImGui::TextWrapped(
|
||||
"To decode values that are stored in fields that don't follow the typical 8 bit alignment, bitfields can be used. "
|
||||
"The size of these fields get specified in numbers of bits.");
|
||||
DrawCodeSegment("bitfield", 70,
|
||||
"bitfield Permission {\n"
|
||||
" r : 1;\n"
|
||||
" w : 1;\n"
|
||||
" x : 1;\n"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Enum");
|
||||
@@ -111,7 +167,7 @@ namespace hex {
|
||||
" Windows = 0x10,\n"
|
||||
" MacOSX,\n"
|
||||
" Linux\n"
|
||||
"}"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Using declarations");
|
||||
@@ -144,20 +200,23 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHelp::createView() {
|
||||
if (this->m_aboutWindowOpen)
|
||||
drawAboutPopup();
|
||||
|
||||
if (this->m_patternHelpWindowOpen)
|
||||
drawPatternHelpPopup();
|
||||
this->drawAboutPopup();
|
||||
this->drawPatternHelpPopup();
|
||||
}
|
||||
|
||||
void ViewHelp::createMenu() {
|
||||
if (ImGui::BeginMenu("Help")) {
|
||||
if (ImGui::MenuItem("About", ""))
|
||||
this->m_aboutWindowOpen = true;
|
||||
View::doLater([this]{
|
||||
ImGui::OpenPopup("About");
|
||||
this->m_aboutWindowOpen = true;
|
||||
});
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Cheat Sheet", ""))
|
||||
this->m_patternHelpWindowOpen = true;
|
||||
View::doLater([this]{
|
||||
ImGui::OpenPopup("Cheat Sheet");
|
||||
this->m_patternHelpWindowOpen = true;
|
||||
});
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData)
|
||||
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
|
||||
@@ -36,19 +36,26 @@ namespace hex {
|
||||
ViewHexEditor *_this = (ViewHexEditor *) data;
|
||||
|
||||
for (auto& pattern : _this->m_patternData) {
|
||||
if (next && off == (pattern->getOffset() + pattern->getSize())) {
|
||||
if (next && pattern->highlightBytes(off - 1) != pattern->highlightBytes(off)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (off >= pattern->getOffset() && off < (pattern->getOffset() + pattern->getSize())) {
|
||||
_this->m_memoryEditor.HighlightColor = pattern->getColor();
|
||||
if (auto color = pattern->highlightBytes(off); color.has_value()) {
|
||||
_this->m_memoryEditor.HighlightColor = color.value();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_this->m_memoryEditor.HighlightColor = 0x50C08080;
|
||||
_this->m_memoryEditor.HighlightColor = 0x60C08080;
|
||||
return false;
|
||||
};
|
||||
|
||||
View::subscribeEvent(Events::FileDropped, [this](const void *userData) {
|
||||
auto filePath = static_cast<const char*>(userData);
|
||||
|
||||
if (filePath != nullptr)
|
||||
this->openFile(filePath);
|
||||
});
|
||||
}
|
||||
|
||||
ViewHexEditor::~ViewHexEditor() {
|
||||
@@ -63,26 +70,50 @@ namespace hex {
|
||||
|
||||
size_t dataSize = (this->m_dataProvider == nullptr || !this->m_dataProvider->isReadable()) ? 0x00 : this->m_dataProvider->getSize();
|
||||
|
||||
this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize);
|
||||
this->m_memoryEditor.DrawWindow("Hex Editor", this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
|
||||
|
||||
if (dataSize != 0x00) {
|
||||
ImGui::Begin("Hex Editor");
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("Page %d / %d", this->m_dataProvider->getCurrentPage() + 1, this->m_dataProvider->getPageCount());
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
|
||||
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() - 1);
|
||||
|
||||
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
View::postEvent(Events::ByteSelected, &dataPreviewStart);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
|
||||
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
|
||||
|
||||
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
View::postEvent(Events::ByteSelected, &dataPreviewStart);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
this->drawSearchPopup();
|
||||
this->drawGotoPopup();
|
||||
}
|
||||
|
||||
this->m_fileBrowser.Display();
|
||||
|
||||
if (this->m_fileBrowser.HasSelected()) {
|
||||
if (this->m_dataProvider != nullptr)
|
||||
delete this->m_dataProvider;
|
||||
|
||||
this->m_dataProvider = new prv::FileProvider(this->m_fileBrowser.GetSelected().string());
|
||||
View::postEvent(Events::DataChanged);
|
||||
this->m_fileBrowser.ClearSelected();
|
||||
if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
|
||||
this->openFile(this->m_fileBrowser.selected_path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ViewHexEditor::openFile(std::string path) {
|
||||
if (this->m_dataProvider != nullptr)
|
||||
delete this->m_dataProvider;
|
||||
|
||||
this->m_dataProvider = new prv::FileProvider(path);
|
||||
View::postEvent(Events::DataChanged);
|
||||
}
|
||||
|
||||
void ViewHexEditor::copyBytes() {
|
||||
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
@@ -94,7 +125,7 @@ namespace hex {
|
||||
|
||||
std::string str;
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("%x ", byte);
|
||||
str += hex::format("%02X ", byte);
|
||||
str.pop_back();
|
||||
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
@@ -113,12 +144,219 @@ namespace hex {
|
||||
ImGui::SetClipboardText(buffer.c_str());
|
||||
}
|
||||
|
||||
void ViewHexEditor::copyLanguageArray(Language language) {
|
||||
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
|
||||
size_t copySize = (end - start) + 1;
|
||||
|
||||
std::vector<u8> buffer(copySize, 0x00);
|
||||
this->m_dataProvider->read(start, buffer.data(), buffer.size());
|
||||
|
||||
std::string str;
|
||||
switch (language) {
|
||||
case Language::C:
|
||||
str += "const unsigned char data[" + std::to_string(buffer.size()) + "] = { ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " };";
|
||||
break;
|
||||
case Language::Cpp:
|
||||
str += "constexpr std::array<unsigned char, " + std::to_string(buffer.size()) + "> data = { ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " };";
|
||||
break;
|
||||
case Language::Java:
|
||||
str += "final byte[] data = { ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " };";
|
||||
break;
|
||||
case Language::CSharp:
|
||||
str += "const byte[] data = { ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " };";
|
||||
break;
|
||||
case Language::Rust:
|
||||
str += "let data: [u8, " + std::to_string(buffer.size()) + "] = [ ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " ];";
|
||||
break;
|
||||
case Language::Python:
|
||||
str += "data = bytes([ ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " ]);";
|
||||
break;
|
||||
case Language::JavaScript:
|
||||
str += "const data = new Uint8Array([ ";
|
||||
|
||||
for (const auto &byte : buffer)
|
||||
str += hex::format("0x%02X, ", byte);
|
||||
|
||||
// Remove trailing comma
|
||||
str.pop_back();
|
||||
str.pop_back();
|
||||
|
||||
str += " ]);";
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
}
|
||||
|
||||
void ViewHexEditor::copyHexView() {
|
||||
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
|
||||
size_t copySize = (end - start) + 1;
|
||||
|
||||
std::vector<u8> buffer(copySize, 0x00);
|
||||
this->m_dataProvider->read(start, buffer.data(), buffer.size());
|
||||
|
||||
std::string str = "Hex View 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n\n";
|
||||
|
||||
|
||||
for (u32 col = start >> 4; col <= (end >> 4); col++) {
|
||||
str += hex::format("%08lX ", col << 4);
|
||||
for (u64 i = 0 ; i < 16; i++) {
|
||||
|
||||
if (col == (start >> 4) && i < (start & 0xF) || col == (end >> 4) && i > (end & 0xF))
|
||||
str += " ";
|
||||
else
|
||||
str += hex::format("%02lX ", buffer[((col << 4) - start) + i]);
|
||||
|
||||
if ((i & 0xF) == 0x7)
|
||||
str += " ";
|
||||
}
|
||||
|
||||
str += " ";
|
||||
|
||||
for (u64 i = 0 ; i < 16; i++) {
|
||||
|
||||
if (col == (start >> 4) && i < (start & 0xF) || col == (end >> 4) && i > (end & 0xF))
|
||||
str += " ";
|
||||
else {
|
||||
u8 c = buffer[((col << 4) - start) + i];
|
||||
char displayChar = (c < 32 || c >= 128) ? '.' : c;
|
||||
str += hex::format("%c", displayChar);
|
||||
}
|
||||
}
|
||||
|
||||
str += "\n";
|
||||
}
|
||||
|
||||
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
}
|
||||
|
||||
void ViewHexEditor::copyHexViewHTML() {
|
||||
size_t start = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
size_t end = std::max(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
|
||||
size_t copySize = (end - start) + 1;
|
||||
|
||||
std::vector<u8> buffer(copySize, 0x00);
|
||||
this->m_dataProvider->read(start, buffer.data(), buffer.size());
|
||||
|
||||
std::string str =
|
||||
R"(
|
||||
<div>
|
||||
<style type="text/css">
|
||||
.offsetheader { color:#0000A0; line-height:200% }
|
||||
.offsetcolumn { color:#0000A0 }
|
||||
.hexcolumn { color:#000000 }
|
||||
.textcolumn { color:#000000 }
|
||||
</style>
|
||||
|
||||
<code>
|
||||
<span class="offsetheader">Hex View  00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F</span><br/>
|
||||
)";
|
||||
|
||||
|
||||
for (u32 col = start >> 4; col <= (end >> 4); col++) {
|
||||
str += hex::format(" <span class=\"offsetcolumn\">%08lX</span>  <span class=\"hexcolumn\">", col << 4);
|
||||
for (u64 i = 0 ; i < 16; i++) {
|
||||
|
||||
if (col == (start >> 4) && i < (start & 0xF) || col == (end >> 4) && i > (end & 0xF))
|
||||
str += "   ";
|
||||
else
|
||||
str += hex::format("%02lX ", buffer[((col << 4) - start) + i]);
|
||||
|
||||
if ((i & 0xF) == 0x7)
|
||||
str += " ";
|
||||
}
|
||||
|
||||
str += "</span>  <span class=\"textcolumn\">";
|
||||
|
||||
for (u64 i = 0 ; i < 16; i++) {
|
||||
|
||||
if (col == (start >> 4) && i < (start & 0xF) || col == (end >> 4) && i > (end & 0xF))
|
||||
str += " ";
|
||||
else {
|
||||
u8 c = buffer[((col << 4) - start) + i];
|
||||
char displayChar = (c < 32 || c >= 128) ? '.' : c;
|
||||
str += hex::format("%c", displayChar);
|
||||
}
|
||||
}
|
||||
|
||||
str += "</span><br/>\n";
|
||||
}
|
||||
|
||||
str +=
|
||||
R"(
|
||||
</code>
|
||||
</div>
|
||||
)";
|
||||
|
||||
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
}
|
||||
|
||||
void ViewHexEditor::createMenu() {
|
||||
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
|
||||
this->m_fileBrowser.SetTitle("Open File");
|
||||
this->m_fileBrowser.Open();
|
||||
View::doLater([]{ ImGui::OpenPopup("Open File"); });
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
@@ -135,13 +373,39 @@ namespace hex {
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
if (ImGui::MenuItem("Copy bytes", "CTRL + ALT + C")) {
|
||||
this->copyBytes();
|
||||
if (ImGui::BeginMenu("Copy as...", this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
|
||||
if (ImGui::MenuItem("Bytes", "CTRL + ALT + C"))
|
||||
this->copyBytes();
|
||||
if (ImGui::MenuItem("Hex String", "CTRL + SHIFT + C"))
|
||||
this->copyString();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("C Array"))
|
||||
this->copyLanguageArray(Language::C);
|
||||
if (ImGui::MenuItem("C++ Array"))
|
||||
this->copyLanguageArray(Language::Cpp);
|
||||
if (ImGui::MenuItem("C# Array"))
|
||||
this->copyLanguageArray(Language::CSharp);
|
||||
if (ImGui::MenuItem("Rust Array"))
|
||||
this->copyLanguageArray(Language::Rust);
|
||||
if (ImGui::MenuItem("Python Array"))
|
||||
this->copyLanguageArray(Language::Python);
|
||||
if (ImGui::MenuItem("Java Array"))
|
||||
this->copyLanguageArray(Language::Java);
|
||||
if (ImGui::MenuItem("JavaScript Array"))
|
||||
this->copyLanguageArray(Language::JavaScript);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Editor View"))
|
||||
this->copyHexView();
|
||||
if (ImGui::MenuItem("HTML"))
|
||||
this->copyHexViewHTML();
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Copy string", "CTRL + SHIFT + C")) {
|
||||
this->copyString();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@@ -160,8 +424,7 @@ namespace hex {
|
||||
ImGui::OpenPopup("Goto");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
|
||||
this->m_fileBrowser.SetTitle("Open File");
|
||||
this->m_fileBrowser.Open();
|
||||
ImGui::OpenPopup("Open File");
|
||||
return true;
|
||||
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
|
||||
this->copyBytes();
|
||||
@@ -333,13 +596,58 @@ namespace hex {
|
||||
void ViewHexEditor::drawGotoPopup() {
|
||||
if (ImGui::BeginPopup("Goto")) {
|
||||
ImGui::TextUnformatted("Goto");
|
||||
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::BeginTabBar("gotoTabs")) {
|
||||
s64 newOffset = 0;
|
||||
if (ImGui::BeginTabItem("Begin")) {
|
||||
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
if (this->m_gotoAddress >= this->m_dataProvider->getSize())
|
||||
this->m_gotoAddress = this->m_dataProvider->getSize() - 1;
|
||||
if (this->m_gotoAddress >= this->m_dataProvider->getActualSize())
|
||||
this->m_gotoAddress = this->m_dataProvider->getActualSize() - 1;
|
||||
|
||||
if (ImGui::Button("Goto")) {
|
||||
this->m_memoryEditor.GotoAddr = this->m_gotoAddress;
|
||||
newOffset = this->m_gotoAddress;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("Current")) {
|
||||
ImGui::InputScalar("##nolabel", ImGuiDataType_S64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
if (this->m_memoryEditor.DataPreviewAddr == -1 || this->m_memoryEditor.DataPreviewAddrEnd == -1) {
|
||||
this->m_memoryEditor.DataPreviewAddr = 0;
|
||||
this->m_memoryEditor.DataPreviewAddrEnd = 0;
|
||||
}
|
||||
|
||||
s64 currHighlightStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
|
||||
newOffset = this->m_gotoAddress + currHighlightStart;
|
||||
if (newOffset >= this->m_dataProvider->getActualSize()) {
|
||||
newOffset = this->m_dataProvider->getActualSize() - 1;
|
||||
this->m_gotoAddress = (this->m_dataProvider->getActualSize() - 1) - currHighlightStart;
|
||||
} else if (newOffset < 0) {
|
||||
newOffset = 0;
|
||||
this->m_gotoAddress = -currHighlightStart;
|
||||
}
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
if (ImGui::BeginTabItem("End")) {
|
||||
ImGui::InputScalar("##nolabel", ImGuiDataType_U64, &this->m_gotoAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
|
||||
if (this->m_gotoAddress >= this->m_dataProvider->getActualSize())
|
||||
this->m_gotoAddress = this->m_dataProvider->getActualSize() - 1;
|
||||
|
||||
newOffset = (this->m_dataProvider->getActualSize() - 1) - this->m_gotoAddress;
|
||||
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Goto")) {
|
||||
this->m_dataProvider->setCurrentPage(std::floor(newOffset / double(prv::Provider::PageSize)));
|
||||
this->m_memoryEditor.GotoAddr = newOffset;
|
||||
this->m_memoryEditor.DataPreviewAddr = newOffset;
|
||||
this->m_memoryEditor.DataPreviewAddrEnd = newOffset;
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
|
||||
@@ -12,18 +12,20 @@
|
||||
|
||||
#include <magic.h>
|
||||
|
||||
|
||||
#if defined(__EMX__) || defined (WIN32)
|
||||
#define MAGIC_PATH_SEPARATOR ";"
|
||||
#else
|
||||
#define MAGIC_PATH_SEPARATOR ":"
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](void*) {
|
||||
this->m_shouldInvalidate = true;
|
||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
|
||||
: View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*) {
|
||||
this->m_dataValid = false;
|
||||
this->m_highestBlockEntropy = 0;
|
||||
this->m_blockEntropy.clear();
|
||||
this->m_averageEntropy = 0;
|
||||
this->m_blockSize = 0;
|
||||
this->m_valueCounts.fill(0x00);
|
||||
this->m_mimeType = "";
|
||||
this->m_fileDescription = "";
|
||||
this->m_analyzedRegion = { 0, 0 };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,6 +56,8 @@ namespace hex {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (this->m_shouldInvalidate) {
|
||||
|
||||
this->m_analyzedRegion = { this->m_dataProvider->getBaseAddress(), this->m_dataProvider->getBaseAddress() + this->m_dataProvider->getSize() };
|
||||
|
||||
{
|
||||
this->m_blockSize = std::ceil(this->m_dataProvider->getSize() / 2048.0F);
|
||||
std::vector<u8> buffer(this->m_blockSize, 0x00);
|
||||
@@ -95,12 +99,10 @@ namespace hex {
|
||||
|
||||
{
|
||||
magic_t cookie = magic_open(MAGIC_NONE);
|
||||
if (magic_load(cookie, magicFiles.c_str()) == -1)
|
||||
goto skip_description;
|
||||
|
||||
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
skip_description:
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
this->m_fileDescription = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
else
|
||||
this->m_fileDescription = "";
|
||||
|
||||
magic_close(cookie);
|
||||
}
|
||||
@@ -108,12 +110,10 @@ namespace hex {
|
||||
|
||||
{
|
||||
magic_t cookie = magic_open(MAGIC_MIME);
|
||||
if (magic_load(cookie, magicFiles.c_str()) == -1)
|
||||
goto skip_mime;
|
||||
|
||||
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
skip_mime:
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
this->m_mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
else
|
||||
this->m_mimeType = "";
|
||||
|
||||
magic_close(cookie);
|
||||
}
|
||||
@@ -122,53 +122,67 @@ namespace hex {
|
||||
|
||||
|
||||
this->m_shouldInvalidate = false;
|
||||
this->m_dataValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
for (auto &[name, value] : this->m_dataProvider->getDataInformation()) {
|
||||
ImGui::LabelText(name.c_str(), "%s", value.c_str());
|
||||
}
|
||||
if (ImGui::Button("Analyze current page"))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (!this->m_fileDescription.empty()) {
|
||||
ImGui::TextUnformatted("Description:");
|
||||
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
|
||||
if (this->m_dataValid) {
|
||||
|
||||
for (auto &[name, value] : this->m_dataProvider->getDataInformation()) {
|
||||
ImGui::LabelText(name.c_str(), "%s", value.c_str());
|
||||
}
|
||||
|
||||
ImGui::LabelText("Analyzed region", "0x%llx - 0x%llx", this->m_analyzedRegion.first, this->m_analyzedRegion.second);
|
||||
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
if (!this->m_mimeType.empty()) {
|
||||
ImGui::TextUnformatted("MIME Type:");
|
||||
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
if (!this->m_fileDescription.empty()) {
|
||||
ImGui::TextUnformatted("Description:");
|
||||
ImGui::TextWrapped("%s", this->m_fileDescription.c_str());
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
ImGui::Text("Byte Distribution");
|
||||
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
if (!this->m_mimeType.empty()) {
|
||||
ImGui::TextUnformatted("MIME Type:");
|
||||
ImGui::TextWrapped("%s", this->m_mimeType.c_str());
|
||||
ImGui::NewLine();
|
||||
}
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Entropy");
|
||||
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::LabelText("Block size", "2048 blocks à %lu bytes", this->m_blockSize);
|
||||
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
||||
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F), "This data is most likely encrypted or compressed!");
|
||||
|
||||
ImGui::Text("Byte Distribution");
|
||||
ImGui::PlotHistogram("##nolabel", this->m_valueCounts.data(), 256, 0, nullptr, FLT_MAX, FLT_MAX,ImVec2(0, 100));
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Entropy");
|
||||
ImGui::PlotLines("##nolabel", this->m_blockEntropy.data(), this->m_blockEntropy.size(), 0, nullptr, FLT_MAX, FLT_MAX, ImVec2(0, 100));
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::LabelText("Block size", "2048 blocks à %lu bytes", this->m_blockSize);
|
||||
ImGui::LabelText("Average entropy", "%.8f", this->m_averageEntropy);
|
||||
ImGui::LabelText("Highest entropy block", "%.8f", this->m_highestBlockEntropy);
|
||||
|
||||
if (this->m_averageEntropy > 0.83 && this->m_highestBlockEntropy > 0.9) {
|
||||
ImGui::NewLine();
|
||||
ImGui::TextColored(ImVec4(0.92F, 0.25F, 0.2F, 1.0F),"This data is most likely encrypted or compressed!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,27 +4,146 @@
|
||||
#include "lang/parser.hpp"
|
||||
#include "lang/lexer.hpp"
|
||||
#include "lang/validator.hpp"
|
||||
#include "lang/evaluator.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <magic.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData)
|
||||
static const TextEditor::LanguageDefinition& PatternLanguage() {
|
||||
static bool initialized = false;
|
||||
static TextEditor::LanguageDefinition langDef;
|
||||
if (!initialized) {
|
||||
static const char* const keywords[] = {
|
||||
"using", "struct", "union", "enum", "bitfield"
|
||||
};
|
||||
for (auto& k : keywords)
|
||||
langDef.mKeywords.insert(k);
|
||||
|
||||
static std::pair<const char* const, size_t> builtInTypes[] = {
|
||||
{ "u8", 1 }, { "u16", 2 }, { "u32", 4 }, { "u64", 8 }, { "u128", 16 },
|
||||
{ "s8", 1 }, { "s16", 2 }, { "s32", 4 }, { "s64", 8 }, { "s128", 16 },
|
||||
{ "float", 4 }, { "double", 8 }, { "padding", 1 }
|
||||
};
|
||||
for (const auto &[name, size] : builtInTypes) {
|
||||
TextEditor::Identifier id;
|
||||
id.mDeclaration = std::to_string(size);
|
||||
id.mDeclaration += size == 1 ? " byte" : " bytes";
|
||||
langDef.mIdentifiers.insert(std::make_pair(std::string(name), id));
|
||||
}
|
||||
|
||||
langDef.mTokenize = [](const char * inBegin, const char * inEnd, const char *& outBegin, const char *& outEnd, TextEditor::PaletteIndex & paletteIndex) -> bool {
|
||||
paletteIndex = TextEditor::PaletteIndex::Max;
|
||||
|
||||
while (inBegin < inEnd && isascii(*inBegin) && isblank(*inBegin))
|
||||
inBegin++;
|
||||
|
||||
if (inBegin == inEnd) {
|
||||
outBegin = inEnd;
|
||||
outEnd = inEnd;
|
||||
paletteIndex = TextEditor::PaletteIndex::Default;
|
||||
}
|
||||
else if (TokenizeCStyleIdentifier(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
||||
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::Number;
|
||||
|
||||
return paletteIndex != TextEditor::PaletteIndex::Max;
|
||||
};
|
||||
|
||||
langDef.mCommentStart = "/*";
|
||||
langDef.mCommentEnd = "*/";
|
||||
langDef.mSingleLineComment = "//";
|
||||
|
||||
langDef.mCaseSensitive = true;
|
||||
langDef.mAutoIndentation = true;
|
||||
langDef.mPreprocChar = '#';
|
||||
|
||||
langDef.mName = "Pattern Language";
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
return langDef;
|
||||
}
|
||||
|
||||
|
||||
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->m_buffer = new char[0xFF'FFFF];
|
||||
std::memset(this->m_buffer, 0x00, 0xFF'FFFF);
|
||||
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
|
||||
this->m_textEditor.SetShowWhitespaces(false);
|
||||
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void* userData) {
|
||||
lang::Preprocessor preprocessor;
|
||||
|
||||
std::string magicFiles;
|
||||
|
||||
std::error_code error;
|
||||
for (const auto &entry : std::filesystem::directory_iterator("magic", error)) {
|
||||
if (entry.is_regular_file() && entry.path().extension() == ".mgc")
|
||||
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
std::vector<u8> buffer(std::min(this->m_dataProvider->getSize(), size_t(0xFF'FFFF)), 0x00);
|
||||
this->m_dataProvider->read(0, buffer.data(), buffer.size());
|
||||
|
||||
std::string mimeType;
|
||||
|
||||
magic_t cookie = magic_open(MAGIC_MIME_TYPE);
|
||||
if (magic_load(cookie, magicFiles.c_str()) != -1)
|
||||
mimeType = magic_buffer(cookie, buffer.data(), buffer.size());
|
||||
|
||||
magic_close(cookie);
|
||||
|
||||
bool foundCorrectType = false;
|
||||
preprocessor.addPragmaHandler("MIME", [&mimeType, &foundCorrectType](std::string value) {
|
||||
if (value == mimeType) {
|
||||
foundCorrectType = true;
|
||||
return true;
|
||||
}
|
||||
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
|
||||
});
|
||||
preprocessor.addDefaultPragramHandlers();
|
||||
|
||||
|
||||
for (auto &entry : std::filesystem::directory_iterator("patterns")) {
|
||||
if (!entry.is_regular_file())
|
||||
continue;
|
||||
|
||||
FILE *file = fopen(entry.path().string().c_str(), "r");
|
||||
|
||||
if (file == nullptr)
|
||||
continue;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
std::vector<char> buffer( size + 1, 0x00);
|
||||
fread(buffer.data(), 1, size, file);
|
||||
fclose(file);
|
||||
|
||||
preprocessor.preprocess(buffer.data());
|
||||
|
||||
if (foundCorrectType) {
|
||||
this->m_possiblePatternFile = entry.path();
|
||||
ImGui::OpenPopup("Accept Pattern");
|
||||
ImGui::SetNextWindowSize(ImVec2(200, 100));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewPattern::~ViewPattern() {
|
||||
if (this->m_buffer != nullptr)
|
||||
delete[] this->m_buffer;
|
||||
|
||||
}
|
||||
|
||||
void ViewPattern::createMenu() {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Load pattern...")) {
|
||||
this->m_fileBrowser.SetTitle("Open Hex Pattern");
|
||||
this->m_fileBrowser.SetTypeFilters({ ".hexpat" });
|
||||
this->m_fileBrowser.Open();
|
||||
View::doLater([]{ ImGui::OpenPopup("Open Hex Pattern"); });
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@@ -40,57 +159,63 @@ namespace hex {
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
|
||||
if (this->m_buffer != nullptr && this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||
this->m_textEditor.Render("Pattern");
|
||||
|
||||
auto size = ImGui::GetWindowSize();
|
||||
size.y -= 50;
|
||||
ImGui::InputTextMultiline("Pattern", this->m_buffer, 0xFFFF, size,
|
||||
ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackEdit,
|
||||
[](ImGuiInputTextCallbackData *data) -> int {
|
||||
auto _this = static_cast<ViewPattern *>(data->UserData);
|
||||
|
||||
_this->parsePattern(data->Buf);
|
||||
|
||||
return 0;
|
||||
}, this
|
||||
);
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
if (this->m_textEditor.IsTextChanged()) {
|
||||
this->parsePattern(this->m_textEditor.GetText().data());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
this->m_fileBrowser.Display();
|
||||
if (this->m_fileBrowser.showFileDialog("Open Hex Pattern", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexpat")) {
|
||||
this->loadPatternFile(this->m_fileBrowser.selected_path);
|
||||
}
|
||||
|
||||
if (this->m_fileBrowser.HasSelected()) {
|
||||
if (ImGui::BeginPopupModal("Accept Pattern", nullptr, ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::TextUnformatted("A pattern compatible with this data type has been found:");
|
||||
ImGui::Text("%ls", this->m_possiblePatternFile.filename().c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Text("Do you want to load it?");
|
||||
ImGui::NewLine();
|
||||
|
||||
FILE *file = fopen(this->m_fileBrowser.GetSelected().string().c_str(), "rb");
|
||||
this->m_fileBrowser.ClearSelected();
|
||||
|
||||
if (file != nullptr) {
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
if (size >= 0xFF'FFFF) {
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
|
||||
fread(this->m_buffer, size, 1, file);
|
||||
|
||||
fclose(file);
|
||||
|
||||
this->parsePattern(this->m_buffer);
|
||||
if (ImGui::Button("Yes", ImVec2(40, 20))) {
|
||||
this->loadPatternFile(this->m_possiblePatternFile.string());
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("No", ImVec2(40, 20))) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViewPattern::addPatternData(PatternData *patternData) {
|
||||
this->m_patternData.push_back(patternData);
|
||||
void ViewPattern::loadPatternFile(std::string path) {
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
|
||||
if (file != nullptr) {
|
||||
char *buffer;
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
buffer = new char[size + 1];
|
||||
|
||||
fread(buffer, size, 1, file);
|
||||
buffer[size] = 0x00;
|
||||
|
||||
|
||||
fclose(file);
|
||||
|
||||
this->parsePattern(buffer);
|
||||
this->m_textEditor.SetText(buffer);
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewPattern::clearPatternData() {
|
||||
@@ -98,7 +223,7 @@ namespace hex {
|
||||
delete data;
|
||||
|
||||
this->m_patternData.clear();
|
||||
PatternData::resetPalette();
|
||||
lang::PatternData::resetPalette();
|
||||
}
|
||||
|
||||
template<std::derived_from<lang::ASTNode> T>
|
||||
@@ -113,294 +238,59 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewPattern::parsePattern(char *buffer) {
|
||||
static hex::lang::Preprocessor preprocessor;
|
||||
static hex::lang::Lexer lexer;
|
||||
static hex::lang::Parser parser;
|
||||
static hex::lang::Validator validator;
|
||||
|
||||
this->clearPatternData();
|
||||
this->postEvent(Events::PatternChanged);
|
||||
|
||||
hex::lang::Preprocessor preprocessor;
|
||||
std::endian dataEndianess = std::endian::native;
|
||||
|
||||
preprocessor.addPragmaHandler("endian", [&dataEndianess](std::string value) {
|
||||
if (value == "big") {
|
||||
dataEndianess = std::endian::big;
|
||||
return true;
|
||||
} else if (value == "little") {
|
||||
dataEndianess = std::endian::little;
|
||||
return true;
|
||||
} else if (value == "native") {
|
||||
dataEndianess = std::endian::native;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
});
|
||||
preprocessor.addDefaultPragramHandlers();
|
||||
|
||||
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
||||
if (preprocessingResult.failed())
|
||||
return;
|
||||
|
||||
hex::lang::Lexer lexer;
|
||||
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
||||
if (lexResult.failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Parser parser;
|
||||
auto [parseResult, ast] = parser.parse(tokens);
|
||||
if (parseResult.failed()) {
|
||||
for(auto &node : ast) delete node;
|
||||
return;
|
||||
}
|
||||
|
||||
hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast) delete node; });
|
||||
|
||||
hex::lang::Validator validator;
|
||||
auto validatorResult = validator.validate(ast);
|
||||
if (!validatorResult) {
|
||||
for(auto &node : ast) delete node;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto &varNode : findNodes<lang::ASTNodeVariableDecl>(lang::ASTNode::Type::VariableDecl, ast)) {
|
||||
if (!varNode->getOffset().has_value())
|
||||
continue;
|
||||
|
||||
u64 offset = varNode->getOffset().value();
|
||||
if (varNode->getVariableType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = getTypeSize(varNode->getVariableType()) * varNode->getArraySize();
|
||||
|
||||
if (isUnsigned(varNode->getVariableType()))
|
||||
this->addPatternData(new PatternDataUnsigned(offset, size, varNode->getVariableName()));
|
||||
else if (isSigned(varNode->getVariableType())) {
|
||||
if (getTypeSize(varNode->getVariableType()) == 1 && varNode->getArraySize() == 1)
|
||||
this->addPatternData(new PatternDataCharacter(offset, size, varNode->getVariableName()));
|
||||
else if (getTypeSize(varNode->getVariableType()) == 1 && varNode->getArraySize() > 1)
|
||||
this->addPatternData(new PatternDataString(offset, size, varNode->getVariableName()));
|
||||
else
|
||||
this->addPatternData(new PatternDataSigned(offset, size, varNode->getVariableName()));
|
||||
}
|
||||
else if (isFloatingPoint(varNode->getVariableType()))
|
||||
this->addPatternData(new PatternDataFloat(offset, size, varNode->getVariableName()));
|
||||
} else {
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast)) {
|
||||
if (varNode->getCustomVariableTypeName() == structNode->getName()) {
|
||||
for (u32 i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string name = varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
name += "[" + std::to_string(varNode->getArraySize()) + "]";
|
||||
|
||||
if (size_t size = this->highlightStruct(ast, structNode, offset, name); size == -1)
|
||||
this->clearPatternData();
|
||||
else
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &enumNode : findNodes<lang::ASTNodeEnum>(lang::ASTNode::Type::Enum, ast)) {
|
||||
if (varNode->getCustomVariableTypeName() == enumNode->getName()) {
|
||||
for (u32 i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string name = varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
name += "[" + std::to_string(varNode->getArraySize()) + "]";
|
||||
|
||||
if (size_t size = this->highlightEnum(ast, enumNode, offset, name); size == -1)
|
||||
this->clearPatternData();
|
||||
else
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &usingNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast)) {
|
||||
if (varNode->getCustomVariableTypeName() == usingNode->getTypeName()) {
|
||||
for (u32 i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string name = varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
name += "[" + std::to_string(varNode->getArraySize()) + "]";
|
||||
|
||||
if (size_t size = this->highlightUsingDecls(ast, usingNode, varNode, offset, name); size == -1)
|
||||
this->clearPatternData();
|
||||
else
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hex::lang::Evaluator evaluator(this->m_dataProvider, dataEndianess);
|
||||
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
|
||||
if (evaluateResult.failed()) {
|
||||
return;
|
||||
}
|
||||
this->m_patternData = patternData;
|
||||
|
||||
for(auto &node : ast) delete node;
|
||||
this->postEvent(Events::PatternChanged);
|
||||
}
|
||||
|
||||
s32 ViewPattern::highlightUsingDecls(std::vector<lang::ASTNode*> &ast, lang::ASTNodeTypeDecl* currTypeDeclNode, lang::ASTNodeVariableDecl* currVarDecl, u64 offset, std::string name) {
|
||||
u64 startOffset = offset;
|
||||
|
||||
if (currTypeDeclNode->getAssignedType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = (static_cast<u32>(currTypeDeclNode->getAssignedType()) >> 4);
|
||||
|
||||
if (isUnsigned(currTypeDeclNode->getAssignedType()))
|
||||
this->addPatternData(new PatternDataUnsigned(offset, size, name));
|
||||
else if (isSigned(currTypeDeclNode->getAssignedType()))
|
||||
this->addPatternData(new PatternDataSigned(offset, size, name));
|
||||
else if (isFloatingPoint(currTypeDeclNode->getAssignedType()))
|
||||
this->addPatternData(new PatternDataFloat(offset, size, name));
|
||||
|
||||
offset += size;
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast)) {
|
||||
if (structNode->getName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
for (size_t i = 0; i < currVarDecl->getArraySize(); i++) {
|
||||
size_t size = this->highlightStruct(ast, structNode, offset, name);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &enumNode : findNodes<lang::ASTNodeEnum>(lang::ASTNode::Type::Enum, ast)) {
|
||||
if (enumNode->getName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
for (size_t i = 0; i < currVarDecl->getArraySize(); i++) {
|
||||
size_t size = this->highlightEnum(ast, enumNode, offset, name);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast)) {
|
||||
if (typeDeclNode->getTypeName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
for (size_t i = 0; i < currVarDecl->getArraySize(); i++) {
|
||||
size_t size = this->highlightUsingDecls(ast, typeDeclNode, currVarDecl, offset, name);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return offset - startOffset;
|
||||
}
|
||||
|
||||
s32 ViewPattern::highlightStruct(std::vector<lang::ASTNode*> &ast, lang::ASTNodeStruct* currStructNode, u64 offset, std::string name) {
|
||||
u64 startOffset = offset;
|
||||
|
||||
for (auto &node : currStructNode->getNodes()) {
|
||||
auto varNode = static_cast<lang::ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (varNode->getVariableType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = (static_cast<u32>(varNode->getVariableType()) >> 4);
|
||||
for (size_t i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string memberName = name + "." + varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
memberName += "[" + std::to_string(i) + "]";
|
||||
|
||||
if (isUnsigned(varNode->getVariableType()))
|
||||
this->addPatternData(new PatternDataUnsigned(offset, size, memberName));
|
||||
else if (isSigned(varNode->getVariableType())) {
|
||||
if (getTypeSize(varNode->getVariableType()) == 1 && varNode->getArraySize() == 1)
|
||||
this->addPatternData(new PatternDataCharacter(offset, size, memberName));
|
||||
else if (getTypeSize(varNode->getVariableType()) == 1 && varNode->getArraySize() > 1) {
|
||||
this->addPatternData(new PatternDataString(offset, size * varNode->getArraySize(), name + "." + varNode->getVariableName()));
|
||||
offset += size * varNode->getArraySize();
|
||||
break;
|
||||
}
|
||||
else
|
||||
this->addPatternData(new PatternDataSigned(offset, size, memberName));
|
||||
}
|
||||
else if (isFloatingPoint(varNode->getVariableType()))
|
||||
this->addPatternData(new PatternDataFloat(offset, size, memberName));
|
||||
|
||||
offset += size;
|
||||
}
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast)) {
|
||||
if (structNode->getName() == varNode->getCustomVariableTypeName()) {
|
||||
for (size_t i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string memberName = name + "." + varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
memberName += "[" + std::to_string(i) + "]";
|
||||
|
||||
size_t size = this->highlightStruct(ast, structNode, offset, memberName);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &enumNode : findNodes<lang::ASTNodeEnum>(lang::ASTNode::Type::Enum, ast)) {
|
||||
if (enumNode->getName() == varNode->getCustomVariableTypeName()) {
|
||||
for (size_t i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string memberName = name + "." + varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
memberName += "[" + std::to_string(i) + "]";
|
||||
|
||||
size_t size = this->highlightEnum(ast, enumNode, offset, memberName);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast)) {
|
||||
if (typeDeclNode->getTypeName() == varNode->getCustomVariableTypeName()) {
|
||||
for (size_t i = 0; i < varNode->getArraySize(); i++) {
|
||||
std::string memberName = name + "." + varNode->getVariableName();
|
||||
if (varNode->getArraySize() > 1)
|
||||
memberName += "[" + std::to_string(i) + "]";
|
||||
|
||||
size_t size = this->highlightUsingDecls(ast, typeDeclNode, varNode, offset, memberName);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
}
|
||||
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return offset - startOffset;
|
||||
}
|
||||
|
||||
s32 ViewPattern::highlightEnum(std::vector<lang::ASTNode*> &ast, lang::ASTNodeEnum* currEnumNode, u64 offset, std::string name) {
|
||||
if (!isUnsigned(currEnumNode->getUnderlyingType()))
|
||||
return -1;
|
||||
|
||||
s32 size = static_cast<u32>(currEnumNode->getUnderlyingType()) >> 4;
|
||||
|
||||
if (size > 8)
|
||||
return -1;
|
||||
|
||||
this->addPatternData(new PatternDataEnum(offset, size, name, currEnumNode->getName(), currEnumNode->getValues()));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
#include "views/view_pattern_data.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "lang/pattern_data.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<hex::PatternData*> &patternData)
|
||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->subscribeEvent(Events::PatternChanged, [this](auto data) {
|
||||
@@ -16,6 +17,36 @@ namespace hex {
|
||||
this->unsubscribeEvent(Events::PatternChanged);
|
||||
}
|
||||
|
||||
static bool beginPatternDataTable(prv::Provider* &provider, const std::vector<lang::PatternData*> &patterns, std::vector<lang::PatternData*> &sortedPatterns) {
|
||||
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
||||
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
||||
ImGui::TableSetupColumn("Value", 0, -1, ImGui::GetID("value"));
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
|
||||
if (sortSpecs->SpecsDirty || sortedPatterns.empty()) {
|
||||
sortedPatterns = patterns;
|
||||
|
||||
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](lang::PatternData* left, lang::PatternData* right) -> bool {
|
||||
return lang::PatternData::sortPatternDataTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &pattern : sortedPatterns)
|
||||
pattern->sort(sortSpecs, provider);
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ViewPatternData::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
@@ -25,87 +56,13 @@ namespace hex {
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
|
||||
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
||||
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("Position", 0, -1, ImGui::GetID("position"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
||||
ImGui::TableSetupColumn("Value", 0, -1, ImGui::GetID("value"));
|
||||
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
|
||||
if (this->m_sortedPatternData.size() > 0) {
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
auto sortSpecs = ImGui::TableGetSortSpecs();
|
||||
for (auto &patternData : this->m_sortedPatternData)
|
||||
patternData->createEntry(this->m_dataProvider);
|
||||
|
||||
if (sortSpecs->SpecsDirty || this->m_sortedPatternData.empty()) {
|
||||
this->m_sortedPatternData = this->m_patternData;
|
||||
|
||||
std::sort(this->m_sortedPatternData.begin(), this->m_sortedPatternData.end(), [this, &sortSpecs](PatternData* left, PatternData* right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getName() > right->getName();
|
||||
else
|
||||
return left->getName() < right->getName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("position")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
this->m_dataProvider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
this->m_dataProvider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
}
|
||||
else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
sortSpecs->SpecsDirty = false;
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
u32 rowCount = 0;
|
||||
for (auto& patternData : this->m_sortedPatternData) {
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(patternData->getColor()), ImGuiColorEditFlags_NoTooltip);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", patternData->getName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", patternData->getOffset(), patternData->getOffset() + patternData->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx", patternData->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", patternData->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", patternData->format(this->m_dataProvider).c_str());
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
namespace hex {
|
||||
|
||||
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_foundStrings.clear();
|
||||
});
|
||||
|
||||
this->m_filter = new char[0xFFFF];
|
||||
@@ -64,6 +64,8 @@ namespace hex {
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::InputText("Filter", this->m_filter, 0xFFFF);
|
||||
if (ImGui::Button("Extract"))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
@@ -72,8 +74,8 @@ namespace hex {
|
||||
|
||||
if (ImGui::BeginTable("##strings", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg)) {
|
||||
ImGui::TableSetupColumn("Position", 0, -1, ImGui::GetID("position"));
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
|
||||
|
||||
@@ -82,7 +84,7 @@ namespace hex {
|
||||
if (sortSpecs->SpecsDirty) {
|
||||
std::sort(this->m_foundStrings.begin(), this->m_foundStrings.end(),
|
||||
[&sortSpecs](FoundString &left, FoundString &right) -> bool {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("position")) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left.offset > right.offset;
|
||||
else
|
||||
@@ -106,30 +108,27 @@ namespace hex {
|
||||
}
|
||||
|
||||
ImGui::TableHeadersRow();
|
||||
u32 rowCount = 0;
|
||||
|
||||
ImGuiListClipper clipper;
|
||||
clipper.Begin(this->m_foundStrings.size(), 14);
|
||||
clipper.Step();
|
||||
clipper.Begin(this->m_foundStrings.size());
|
||||
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &foundString = this->m_foundStrings[i];
|
||||
while (clipper.Step()) {
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &foundString = this->m_foundStrings[i];
|
||||
|
||||
if (strlen(this->m_filter) != 0 && foundString.string.find(this->m_filter) == std::string::npos)
|
||||
continue;
|
||||
if (strlen(this->m_filter) != 0 &&
|
||||
foundString.string.find(this->m_filter) == std::string::npos)
|
||||
continue;
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx:0x%08lx", foundString.offset, foundString.offset + foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%08lx", foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", foundString.string.c_str());
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
|
||||
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", foundString.offset, foundString.offset + foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04lx", foundString.size);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", foundString.string.c_str());
|
||||
}
|
||||
}
|
||||
clipper.Step();
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace hex {
|
||||
|
||||
void Window::frameEnd() {
|
||||
ImGui::Render();
|
||||
|
||||
int display_w, display_h;
|
||||
glfwGetFramebufferSize(this->m_window, &display_w, &display_h);
|
||||
glViewport(0, 0, display_w, display_h);
|
||||
@@ -176,6 +177,13 @@ namespace hex {
|
||||
Window::s_currShortcut = { key, mods };
|
||||
});
|
||||
|
||||
glfwSetDropCallback(this->m_window, [](GLFWwindow *window, int count, const char **paths) {
|
||||
if (count != 1)
|
||||
return;
|
||||
|
||||
View::postEvent(Events::FileDropped, paths[0]);
|
||||
});
|
||||
|
||||
glfwSetWindowSizeLimits(this->m_window, 720, 480, GLFW_DONT_CARE, GLFW_DONT_CARE);
|
||||
|
||||
if (gladLoadGL() == 0)
|
||||
|
||||
Reference in New Issue
Block a user