mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-30 13:05:25 -05:00
Added hex editor and basic pattern parsing/highlighting
This commit is contained in:
18
include/hex.hpp
Normal file
18
include/hex.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using u128 = __uint128_t;
|
||||
|
||||
using s8 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
using s128 = __int128_t;
|
||||
|
||||
#include "parser/result.hpp"
|
||||
#include "parser/results.hpp"
|
||||
79
include/parser/ast_node.hpp
Normal file
79
include/parser/ast_node.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class ASTNode {
|
||||
public:
|
||||
enum class Type {
|
||||
VariableDecl,
|
||||
TypeDecl,
|
||||
Struct,
|
||||
Scope,
|
||||
};
|
||||
|
||||
explicit ASTNode(Type type) : m_type(type) {}
|
||||
virtual ~ASTNode() {}
|
||||
|
||||
Type getType() { return this->m_type; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeVariableDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
|
||||
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; };
|
||||
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
};
|
||||
|
||||
class ASTNodeScope : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScope(std::vector<ASTNode*> nodes) : ASTNode(Type::Scope), m_nodes(nodes) { }
|
||||
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
};
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeStruct(std::string name, std::vector<ASTNode*> nodes, std::optional<u64> offset = { })
|
||||
: ASTNode(Type::Struct), m_name(name), m_nodes(nodes), m_offset(offset) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
std::optional<u64> getOffset() { return this->m_offset; };
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<ASTNode*> m_nodes;
|
||||
std::optional<u64> m_offset;
|
||||
};
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeTypeDecl(const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::TypeDecl), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
|
||||
const std::string& getTypeName() const { return this->m_name; };
|
||||
|
||||
const Token::TypeToken::Type& getAssignedType() const { return this->m_type; }
|
||||
const std::string& getAssignedCustomTypeName() const { return this->m_customTypeName; }
|
||||
private:
|
||||
Token::TypeToken::Type m_type;
|
||||
std::string m_name, m_customTypeName;
|
||||
};
|
||||
|
||||
}
|
||||
19
include/parser/lexer.hpp
Normal file
19
include/parser/lexer.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "token.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Lexer {
|
||||
public:
|
||||
Lexer();
|
||||
|
||||
std::pair<Result, std::vector<Token>> lex(const std::string& code);
|
||||
};
|
||||
|
||||
}
|
||||
19
include/parser/parser.hpp
Normal file
19
include/parser/parser.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
#include "token.hpp"
|
||||
#include "ast_node.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
21
include/parser/result.hpp
Normal file
21
include/parser/result.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
class Result {
|
||||
public:
|
||||
constexpr Result(const std::uint8_t module, const std::uint32_t desc) noexcept : m_result((module << 24) | (desc & 0x00FFFFFF)) { }
|
||||
|
||||
constexpr std::uint32_t getResult() const noexcept { return this->m_result; }
|
||||
constexpr std::uint8_t getModule() const noexcept { return this->m_result >> 24; }
|
||||
constexpr std::uint32_t getDescription() const noexcept { return this->m_result & 0x00FFFFFF; }
|
||||
|
||||
constexpr bool succeeded() const noexcept { return this->m_result == 0; }
|
||||
constexpr bool failed() const noexcept { return !succeeded(); }
|
||||
private:
|
||||
const std::uint32_t m_result;
|
||||
};
|
||||
|
||||
}
|
||||
12
include/parser/results.hpp
Normal file
12
include/parser/results.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "result.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
constexpr Result ResultSuccess(0, 0);
|
||||
|
||||
constexpr Result ResultLexicalError(1, 1);
|
||||
constexpr Result ResultParseError(2, 1);
|
||||
|
||||
}
|
||||
59
include/parser/token.hpp
Normal file
59
include/parser/token.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
struct Token {
|
||||
enum class Type : u64 {
|
||||
Keyword,
|
||||
Type,
|
||||
Operator,
|
||||
Integer,
|
||||
Identifier,
|
||||
EndOfExpression,
|
||||
ScopeOpen,
|
||||
ScopeClose,
|
||||
Separator,
|
||||
EndOfProgram
|
||||
} type;
|
||||
|
||||
struct KeywordToken {
|
||||
enum class Keyword {
|
||||
Struct,
|
||||
Using
|
||||
} keyword;
|
||||
} keywordToken;
|
||||
struct IdentifierToken {
|
||||
std::string identifier;
|
||||
} identifierToken;
|
||||
struct OperatorToken {
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment
|
||||
} op;
|
||||
} operatorToken;
|
||||
struct IntegerToken {
|
||||
s128 integer;
|
||||
} integerToken;
|
||||
struct TypeToken {
|
||||
enum class Type {
|
||||
Unsigned8Bit = 0x10,
|
||||
Signed8Bit = 0x11,
|
||||
Unsigned16Bit = 0x20,
|
||||
Signed16Bit = 0x21,
|
||||
Unsigned32Bit = 0x40,
|
||||
Signed32Bit = 0x41,
|
||||
Unsigned64Bit = 0x80,
|
||||
Signed64Bit = 0x81,
|
||||
Unsigned128Bit = 0x100,
|
||||
Signed128Bit = 0x101,
|
||||
Float = 0x42,
|
||||
Double = 0x82,
|
||||
CustomType = 0x00
|
||||
} type;
|
||||
} typeToken;
|
||||
};
|
||||
}
|
||||
51
include/utils.hpp
Normal file
51
include/utils.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::optional<std::string> openFileDialog() {
|
||||
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (SUCCEEDED(hr)) {
|
||||
IFileOpenDialog *pFileOpen;
|
||||
|
||||
hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = pFileOpen->Show(nullptr);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
IShellItem *pItem;
|
||||
hr = pFileOpen->GetResult(&pItem);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
PWSTR pszFilePath;
|
||||
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
|
||||
std::string result = converter.to_bytes(pszFilePath);
|
||||
|
||||
CoTaskMemFree(pszFilePath);
|
||||
|
||||
return result;
|
||||
}
|
||||
pItem->Release();
|
||||
}
|
||||
}
|
||||
pFileOpen->Release();
|
||||
}
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
||||
|
||||
}
|
||||
18
include/views/view.hpp
Normal file
18
include/views/view.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View {
|
||||
public:
|
||||
View() { }
|
||||
virtual ~View() { }
|
||||
|
||||
virtual void createView() = 0;
|
||||
virtual void createMenu() { }
|
||||
};
|
||||
|
||||
}
|
||||
110
include/views/view_hexeditor.hpp
Normal file
110
include/views/view_hexeditor.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#include "utils.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include "imgui_memory_editor.h"
|
||||
|
||||
#include <tuple>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
class ViewHexEditor : public View {
|
||||
public:
|
||||
ViewHexEditor() : View() {
|
||||
this->m_memoryEditor.ReadFn = [](const ImU8* data, size_t off) -> ImU8 {
|
||||
ViewHexEditor *_this = (ViewHexEditor*)data;
|
||||
|
||||
if (_this->m_file == nullptr)
|
||||
return 0x00;
|
||||
|
||||
fseek(_this->m_file, off, SEEK_SET);
|
||||
|
||||
ImU8 byte;
|
||||
fread(&byte, sizeof(ImU8), 1, _this->m_file);
|
||||
|
||||
return byte;
|
||||
};
|
||||
|
||||
this->m_memoryEditor.WriteFn = [](ImU8* data, size_t off, ImU8 d) -> void {
|
||||
ViewHexEditor *_this = (ViewHexEditor*)data;
|
||||
|
||||
if (_this->m_file == nullptr)
|
||||
return;
|
||||
|
||||
fseek(_this->m_file, off, SEEK_SET);
|
||||
|
||||
fwrite(&d, sizeof(ImU8), 1, _this->m_file);
|
||||
|
||||
};
|
||||
|
||||
this->m_memoryEditor.HighlightFn = [](const ImU8* data, size_t off, bool next) -> bool {
|
||||
ViewHexEditor *_this = (ViewHexEditor*)data;
|
||||
|
||||
for (auto& [offset, size, color] : _this->m_highlights) {
|
||||
if (next && off == (offset + size))
|
||||
return false;
|
||||
|
||||
if (off >= offset && off < (offset + size)) {
|
||||
_this->m_memoryEditor.HighlightColor = color;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
virtual ~ViewHexEditor() { }
|
||||
|
||||
virtual void createView() override {
|
||||
this->m_memoryEditor.DrawWindow("Hex Editor", this, this->m_file == nullptr ? 0x00 : this->m_fileSize);
|
||||
}
|
||||
|
||||
virtual void createMenu() override {
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open File...")) {
|
||||
auto filePath = openFileDialog();
|
||||
if (filePath.has_value()) {
|
||||
if (this->m_file != nullptr)
|
||||
fclose(this->m_file);
|
||||
|
||||
this->m_file = fopen(filePath->c_str(), "r+b");
|
||||
|
||||
fseek(this->m_file, 0, SEEK_END);
|
||||
this->m_fileSize = ftell(this->m_file);
|
||||
rewind(this->m_file);
|
||||
}
|
||||
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void setHighlight(u64 offset, size_t size, u32 color = 0) {
|
||||
if (color == 0)
|
||||
color = std::mt19937(std::random_device()())();
|
||||
|
||||
color |= 0xFF00'0000;
|
||||
|
||||
this->m_highlights.emplace_back(offset, size, color);
|
||||
}
|
||||
|
||||
void clearHighlights() {
|
||||
this->m_highlights.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryEditor m_memoryEditor;
|
||||
|
||||
FILE *m_file = nullptr;
|
||||
size_t m_fileSize = 0;
|
||||
|
||||
std::vector<std::tuple<u64, size_t, u32>> m_highlights;
|
||||
};
|
||||
|
||||
}
|
||||
163
include/views/view_pattern.hpp
Normal file
163
include/views/view_pattern.hpp
Normal file
@@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include "parser/ast_node.hpp"
|
||||
#include "parser/parser.hpp"
|
||||
#include "parser/lexer.hpp"
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
|
||||
#include "views/view_hexeditor.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
class ViewPattern : public View {
|
||||
public:
|
||||
ViewPattern(ViewHexEditor *hexEditor) : View(), m_hexEditor(hexEditor) {
|
||||
this->m_buffer = new char[0xFFFF];
|
||||
std::memset(this->m_buffer, 0x00, 0xFFFF);
|
||||
}
|
||||
virtual ~ViewPattern() {
|
||||
delete[] this->m_buffer;
|
||||
}
|
||||
|
||||
void createView() override {
|
||||
ImGui::Begin("Pattern", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
auto size = ImGui::GetWindowSize();
|
||||
size.y -= 50;
|
||||
ImGui::InputTextMultiline("Pattern", this->m_buffer, 0xFFFF, size, ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_CallbackEdit,
|
||||
[](ImGuiInputTextCallbackData* data) -> int {
|
||||
static hex::lang::Lexer lexer;
|
||||
static hex::lang::Parser parser;
|
||||
|
||||
auto _this = static_cast<ViewPattern*>(data->UserData);
|
||||
_this->m_hexEditor->clearHighlights();
|
||||
|
||||
auto [lexResult, tokens] = lexer.lex(data->Buf);
|
||||
|
||||
if (lexResult.failed()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto [parseResult, ast] = parser.parse(tokens);
|
||||
if (parseResult.failed()) {
|
||||
for(auto &node : ast) delete node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
for (auto &structNode : _this->findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast)) {
|
||||
if (!structNode->getOffset().has_value())
|
||||
continue;
|
||||
|
||||
u64 offset = structNode->getOffset().value();
|
||||
if (_this->highlightStruct(ast, structNode, offset) == -1)
|
||||
_this->m_hexEditor->clearHighlights();
|
||||
}
|
||||
|
||||
for(auto &node : ast) delete node;
|
||||
|
||||
return 1;
|
||||
}, this
|
||||
);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
private:
|
||||
char *m_buffer;
|
||||
|
||||
ViewHexEditor *m_hexEditor;
|
||||
|
||||
template<std::derived_from<lang::ASTNode> T>
|
||||
std::vector<T*> findNodes(const lang::ASTNode::Type type, const std::vector<lang::ASTNode*> &nodes) const noexcept {
|
||||
std::vector<T*> result;
|
||||
|
||||
for (const auto & node : nodes)
|
||||
if (node->getType() == type)
|
||||
result.push_back(static_cast<T*>(node));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
s32 highlightUsingDecls(std::vector<lang::ASTNode*> &ast, lang::ASTNodeTypeDecl* currTypeDeclNode, u64 offset) {
|
||||
if (currTypeDeclNode->getAssignedType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = static_cast<u32>(currTypeDeclNode->getAssignedType()) >> 4;
|
||||
|
||||
this->m_hexEditor->setHighlight(offset, size);
|
||||
offset += size;
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast))
|
||||
if (structNode->getName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
offset = this->highlightStruct(ast, structNode, offset);
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast))
|
||||
if (typeDeclNode->getTypeName() == currTypeDeclNode->getAssignedCustomTypeName()) {
|
||||
offset = this->highlightUsingDecls(ast, typeDeclNode, offset);
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
s32 highlightStruct(std::vector<lang::ASTNode*> &ast, lang::ASTNodeStruct* currStructNode, u64 offset) {
|
||||
u64 startOffset = offset;
|
||||
|
||||
for (auto &node : currStructNode->getNodes()) {
|
||||
auto var = static_cast<lang::ASTNodeVariableDecl*>(node);
|
||||
|
||||
if (var->getVariableType() != lang::Token::TypeToken::Type::CustomType) {
|
||||
size_t size = static_cast<u32>(var->getVariableType()) >> 4;
|
||||
|
||||
this->m_hexEditor->setHighlight(offset, size);
|
||||
offset += size;
|
||||
} else {
|
||||
bool foundType = false;
|
||||
for (auto &structNode : findNodes<lang::ASTNodeStruct>(lang::ASTNode::Type::Struct, ast))
|
||||
if (structNode->getName() == var->getCustomVariableTypeName()) {
|
||||
auto size = this->highlightStruct(ast, structNode, offset);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &typeDeclNode : findNodes<lang::ASTNodeTypeDecl>(lang::ASTNode::Type::TypeDecl, ast))
|
||||
if (typeDeclNode->getTypeName() == var->getCustomVariableTypeName()) {
|
||||
auto size = this->highlightUsingDecls(ast, typeDeclNode, offset);
|
||||
|
||||
if (size == -1)
|
||||
return -1;
|
||||
|
||||
offset += size;
|
||||
foundType = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundType)
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return offset - startOffset;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
41
include/window.hpp
Normal file
41
include/window.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace hex {
|
||||
|
||||
class Window {
|
||||
public:
|
||||
Window();
|
||||
~Window();
|
||||
|
||||
void loop();
|
||||
|
||||
template<std::derived_from<View> T, typename ... Args>
|
||||
T* addView(Args& ... args) {
|
||||
this->m_views.emplace_back(new T(args...));
|
||||
|
||||
return static_cast<T*>(this->m_views.back());
|
||||
}
|
||||
|
||||
private:
|
||||
void frameBegin();
|
||||
void frameEnd();
|
||||
|
||||
void initGLFW();
|
||||
void initImGui();
|
||||
void deinitGLFW();
|
||||
void deinitImGui();
|
||||
|
||||
GLFWwindow* m_window;
|
||||
|
||||
std::vector<View*> m_views;
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user