mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-29 08:20:02 -05:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1395c95831 | ||
|
|
33b70a550f | ||
|
|
985e924e9d | ||
|
|
3827919a32 | ||
|
|
d55bea7c46 | ||
|
|
2efe326fdb | ||
|
|
d43bd23e1a | ||
|
|
015ec12215 | ||
|
|
fde9dc7961 | ||
|
|
ed572ececf | ||
|
|
fd2b79bea9 | ||
|
|
acc10930c2 | ||
|
|
f17d6c2359 | ||
|
|
e21211f3f6 | ||
|
|
58deaa6b29 | ||
|
|
fcd88b4b3b | ||
|
|
3bd987ff2c | ||
|
|
45bcdc8c46 | ||
|
|
0d0b2d6962 | ||
|
|
fd6a41d219 | ||
|
|
b052429a73 | ||
|
|
1996f401e0 | ||
|
|
3b3f2226f1 | ||
|
|
5c0f6a1e50 | ||
|
|
b7438f6ab8 | ||
|
|
84f80b3e06 | ||
|
|
c281372b8d | ||
|
|
788a3cfc5e | ||
|
|
01eeb53af0 | ||
|
|
7cf69128ea | ||
|
|
c977baecc4 | ||
|
|
2ab2f5e675 | ||
|
|
6a38f1e9f3 | ||
|
|
f0eba69c4a | ||
|
|
cea366e135 | ||
|
|
d752c7434f | ||
|
|
4402120ffc | ||
|
|
b3fffdf530 | ||
|
|
8d6d959e17 | ||
|
|
43f5cc622e | ||
|
|
5f025bcbcc | ||
|
|
8297e22f10 | ||
|
|
989eade5d7 | ||
|
|
cd4de2ff96 | ||
|
|
73d66365e9 | ||
|
|
fbd4e593d2 | ||
|
|
033ef3889a | ||
|
|
0ce1b5d40b | ||
|
|
ed4ed6b433 | ||
|
|
4cd18b8358 | ||
|
|
fb85f272a1 | ||
|
|
28bb28b79c |
@@ -5,20 +5,31 @@ set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_search_module(GLFW REQUIRED glfw3)
|
||||
pkg_search_module(GLM REQUIRED glm)
|
||||
pkg_search_module(CRYPTO REQUIRED libcrypto)
|
||||
pkg_search_module(CAPSTONE REQUIRED capstone)
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
|
||||
llvm_map_components_to_libnames(demangler)
|
||||
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} ${CAPSTONE_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
||||
|
||||
include_directories(include ${GLFW_INCLUDE_DIRS} libs/ImGui/include libs/glad/include)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -DIMGUI_IMPL_OPENGL_LOADER_GLAD")
|
||||
|
||||
if (WIN32)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -static-libgcc -static")
|
||||
endif (WIN32)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE")
|
||||
SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
|
||||
|
||||
add_executable(ImHex
|
||||
source/main.cpp
|
||||
source/window.cpp
|
||||
source/utils.cpp
|
||||
source/crypto.cpp
|
||||
source/patches.cpp
|
||||
|
||||
source/lang/preprocessor.cpp
|
||||
source/lang/lexer.cpp
|
||||
@@ -26,7 +37,7 @@ add_executable(ImHex
|
||||
source/lang/validator.cpp
|
||||
source/lang/evaluator.cpp
|
||||
|
||||
source/provider/file_provider.cpp
|
||||
source/providers/file_provider.cpp
|
||||
|
||||
source/views/view_hexeditor.cpp
|
||||
source/views/view_pattern.cpp
|
||||
@@ -37,6 +48,8 @@ add_executable(ImHex
|
||||
source/views/view_tools.cpp
|
||||
source/views/view_strings.cpp
|
||||
source/views/view_data_inspector.cpp
|
||||
source/views/view_disassembler.cpp
|
||||
source/views/view_bookmarks.cpp
|
||||
|
||||
libs/glad/source/glad.c
|
||||
|
||||
@@ -53,9 +66,9 @@ add_executable(ImHex
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(ImHex libglfw3.a libgcc.a libstdc++.a libmagic.a libgnurx.a libtre.a libintl.a libiconv.a shlwapi.lib ws2_32.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 libLLVMDemangle.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 libLLVMDemangle.so)
|
||||
endif (UNIX)
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -21,4 +22,6 @@ namespace hex {
|
||||
std::array<u32, 12> sha384(prv::Provider* &data, u64 offset, size_t size);
|
||||
std::array<u32, 16> sha512(prv::Provider* &data, u64 offset, size_t size);
|
||||
|
||||
std::vector<u8> decode64(const std::vector<u8> &input);
|
||||
std::vector<u8> encode64(const std::vector<u8> &input);
|
||||
}
|
||||
@@ -6,10 +6,15 @@
|
||||
namespace hex {
|
||||
|
||||
enum class Events {
|
||||
FileLoaded,
|
||||
DataChanged,
|
||||
PatternChanged,
|
||||
FileDropped,
|
||||
ByteSelected
|
||||
RegionSelected,
|
||||
|
||||
SelectionChangeRequest,
|
||||
|
||||
AddBookmark
|
||||
};
|
||||
|
||||
struct EventHandler {
|
||||
|
||||
@@ -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
|
||||
@@ -20,36 +20,42 @@ namespace hex::lang {
|
||||
Scope,
|
||||
};
|
||||
|
||||
explicit ASTNode(Type type) : m_type(type) {}
|
||||
explicit ASTNode(Type type, u32 lineNumber) : m_type(type), m_lineNumber(lineNumber) {}
|
||||
virtual ~ASTNode() = default;
|
||||
|
||||
Type getType() { return this->m_type; }
|
||||
u32 getLineNumber() { return this->m_lineNumber; }
|
||||
|
||||
private:
|
||||
Type m_type;
|
||||
u32 m_lineNumber;
|
||||
};
|
||||
|
||||
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(u32 lineNumber, 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, lineNumber), 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 {
|
||||
public:
|
||||
explicit ASTNodeScope(std::vector<ASTNode*> nodes) : ASTNode(Type::Scope), m_nodes(nodes) { }
|
||||
explicit ASTNodeScope(u32 lineNumber, std::vector<ASTNode*> nodes) : ASTNode(Type::Scope, lineNumber), m_nodes(nodes) { }
|
||||
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
private:
|
||||
@@ -58,8 +64,8 @@ namespace hex::lang {
|
||||
|
||||
class ASTNodeStruct : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeStruct(std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Struct), m_name(name), m_nodes(nodes) { }
|
||||
explicit ASTNodeStruct(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Struct, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
@@ -70,8 +76,8 @@ namespace hex::lang {
|
||||
|
||||
class ASTNodeUnion : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeUnion(std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Union), m_name(name), m_nodes(nodes) { }
|
||||
explicit ASTNodeUnion(u32 lineNumber, std::string name, std::vector<ASTNode*> nodes)
|
||||
: ASTNode(Type::Union, lineNumber), m_name(name), m_nodes(nodes) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; }
|
||||
std::vector<ASTNode*> &getNodes() { return this->m_nodes; }
|
||||
@@ -82,8 +88,8 @@ namespace hex::lang {
|
||||
|
||||
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) { }
|
||||
explicit ASTNodeBitField(u32 lineNumber, std::string name, std::vector<std::pair<std::string, size_t>> fields)
|
||||
: ASTNode(Type::Bitfield, lineNumber), 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; }
|
||||
@@ -94,8 +100,8 @@ namespace hex::lang {
|
||||
|
||||
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) { }
|
||||
explicit ASTNodeTypeDecl(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name, const std::string& customTypeName = "")
|
||||
: ASTNode(Type::TypeDecl, lineNumber), m_type(type), m_name(name), m_customTypeName(customTypeName) { }
|
||||
|
||||
const std::string& getTypeName() const { return this->m_name; };
|
||||
|
||||
@@ -108,8 +114,8 @@ namespace hex::lang {
|
||||
|
||||
class ASTNodeEnum : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeEnum(const Token::TypeToken::Type &type, const std::string &name)
|
||||
: ASTNode(Type::Enum), m_type(type), m_name(name) { }
|
||||
explicit ASTNodeEnum(u32 lineNumber, const Token::TypeToken::Type &type, const std::string &name)
|
||||
: ASTNode(Type::Enum, lineNumber), m_type(type), m_name(name) { }
|
||||
|
||||
const std::string& getName() const { return this->m_name; };
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
#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>
|
||||
@@ -13,12 +15,18 @@ namespace hex::lang {
|
||||
|
||||
class Evaluator {
|
||||
public:
|
||||
Evaluator();
|
||||
Evaluator(prv::Provider* &provider, std::endian dataEndianess);
|
||||
|
||||
std::pair<Result, std::vector<PatternData*>> evaluate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, ASTNode*> m_types;
|
||||
prv::Provider* &m_provider;
|
||||
std::endian m_dataEndianess;
|
||||
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
std::pair<PatternData*, size_t> createStructPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
std::pair<PatternData*, size_t> createUnionPattern(ASTNodeVariableDecl *varDeclNode, u64 offset);
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace hex::lang {
|
||||
Lexer();
|
||||
|
||||
std::pair<Result, std::vector<Token>> lex(const std::string& code);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -12,8 +12,37 @@ namespace hex::lang {
|
||||
public:
|
||||
Parser();
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
std::pair<Result, std::vector<ASTNode*>> parse(const std::vector<Token> &tokens);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseBuiltinPointerVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseCustomTypePointerVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr);
|
||||
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr);
|
||||
ASTNode* parseBuiltinVariableArrayDecl(TokenIter &curr);
|
||||
ASTNode* parseCustomTypeVariableArrayDecl(TokenIter &curr);
|
||||
ASTNode* parsePaddingDecl(TokenIter &curr);
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr);
|
||||
ASTNode* parseStruct(TokenIter &curr);
|
||||
ASTNode* parseUnion(TokenIter &curr);
|
||||
ASTNode* parseEnum(TokenIter &curr);
|
||||
ASTNode *parseBitField(TokenIter &curr);
|
||||
ASTNode *parseScope(TokenIter &curr);
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr);
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr);
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
using namespace ::std::literals::string_literals;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string makeDisplayable(u8 *data, size_t size) {
|
||||
@@ -51,8 +53,11 @@ namespace hex::lang {
|
||||
[[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; }
|
||||
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;
|
||||
@@ -92,6 +97,11 @@ namespace hex::lang {
|
||||
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
|
||||
@@ -114,25 +124,34 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
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::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
Region selectRegion = { this->getOffset(), this->getSize() };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
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::TextColored(ImColor(0xFF9BC64D), "%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;
|
||||
@@ -142,6 +161,7 @@ namespace hex::lang {
|
||||
std::string m_name;
|
||||
|
||||
static inline u8 s_paletteOffset = 0;
|
||||
|
||||
};
|
||||
|
||||
class PatternDataPadding : public PatternData {
|
||||
@@ -156,6 +176,56 @@ namespace hex::lang {
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
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::TextColored(ImColor(0xFF9BC64D), "%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) { }
|
||||
@@ -163,6 +233,7 @@ namespace hex::lang {
|
||||
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));
|
||||
}
|
||||
@@ -186,6 +257,7 @@ namespace hex::lang {
|
||||
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);
|
||||
|
||||
@@ -213,10 +285,14 @@ namespace hex::lang {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -271,17 +347,25 @@ namespace hex::lang {
|
||||
: 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);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
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::TextColored(ImColor(0xFF9BC64D), "%s", this->m_entries[0]->getTypeName().c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::TextUnformatted("[");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextColored(ImColor(0xFF00FF00), "%llu", this->m_entries.size());
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted("]");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
@@ -316,17 +400,16 @@ namespace hex::lang {
|
||||
: 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::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
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::TextColored(ImColor(0xFFD69C56), "struct"); ImGui::SameLine(); ImGui::Text("%s", this->m_structName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
@@ -375,17 +458,17 @@ namespace hex::lang {
|
||||
: 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::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
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::TextColored(ImColor(0xFFD69C56), "union"); ImGui::SameLine(); ImGui::Text("%s", this->m_unionName.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", "{ ... }");
|
||||
|
||||
@@ -436,6 +519,7 @@ namespace hex::lang {
|
||||
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 + "::";
|
||||
|
||||
@@ -451,7 +535,25 @@ namespace hex::lang {
|
||||
if (!foundValue)
|
||||
valueString += "???";
|
||||
|
||||
this->createDefaultEntry(hex::format("%s (0x0*lx)", valueString.c_str(), this->getSize() * 2, value));
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternDataLine"s + std::to_string(this->getOffset())).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { this->getOffset(), this->getSize() };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", this->getName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
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::TextColored(ImColor(0xFFD69C56), "enum"); ImGui::SameLine(); ImGui::Text("%s", this->m_enumName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", hex::format("%s (0x%0*lx)", valueString.c_str(), this->getSize() * 2, value).c_str());
|
||||
}
|
||||
|
||||
std::string getTypeName() override {
|
||||
@@ -471,18 +573,18 @@ namespace hex::lang {
|
||||
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::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
bool open = ImGui::TreeNodeEx(this->getName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
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::TextColored(ImColor(0xFFD69C56), "bitfield"); ImGui::SameLine(); ImGui::Text("%s", this->m_bitfieldName.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("{ %llx }", value);
|
||||
|
||||
@@ -492,10 +594,10 @@ namespace hex::lang {
|
||||
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::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), 14));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%08lx : 0x%08lx", this->getOffset() + (bitOffset >> 3), this->getOffset() + ((bitOffset + entrySize) >> 3) - 1);
|
||||
ImGui::TableNextColumn();
|
||||
if (entrySize == 1)
|
||||
|
||||
@@ -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,20 @@ 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 addDefaultPragmaHandlers();
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
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;
|
||||
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -38,7 +38,8 @@ namespace hex::lang {
|
||||
enum class Operator {
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit
|
||||
Inherit,
|
||||
Star
|
||||
} op;
|
||||
} operatorToken;
|
||||
struct IntegerToken {
|
||||
@@ -62,5 +63,7 @@ namespace hex::lang {
|
||||
Padding = 0x1F
|
||||
} type;
|
||||
} typeToken;
|
||||
|
||||
u32 lineNumber;
|
||||
};
|
||||
}
|
||||
@@ -15,6 +15,11 @@ namespace hex::lang {
|
||||
Validator();
|
||||
|
||||
bool validate(const std::vector<ASTNode*>& ast);
|
||||
|
||||
const std::pair<u32, std::string>& getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
std::pair<u32, std::string> m_error;
|
||||
};
|
||||
|
||||
}
|
||||
15
include/patches.hpp
Normal file
15
include/patches.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
using Patches = std::map<u64, u8>;
|
||||
|
||||
std::vector<u8> generateIPSPatch(const Patches &patches);
|
||||
std::vector<u8> generateIPS32Patch(const Patches &patches);
|
||||
|
||||
}
|
||||
@@ -18,8 +18,11 @@ namespace hex::prv {
|
||||
bool isWritable() override;
|
||||
|
||||
void read(u64 offset, void *buffer, size_t size) override;
|
||||
void write(u64 offset, void *buffer, size_t size) override;
|
||||
size_t getSize() override;
|
||||
void write(u64 offset, const void *buffer, size_t size) override;
|
||||
|
||||
void readRaw(u64 offset, void *buffer, size_t size) override;
|
||||
void writeRaw(u64 offset, const void *buffer, size_t size) override;
|
||||
size_t getActualSize() override;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> getDataInformation() override;
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -9,18 +13,58 @@ namespace hex::prv {
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
Provider() = default;
|
||||
constexpr static size_t PageSize = 0x1000'0000;
|
||||
|
||||
Provider() {
|
||||
this->m_patches.emplace_back();
|
||||
}
|
||||
|
||||
virtual ~Provider() = default;
|
||||
|
||||
virtual bool isAvailable() = 0;
|
||||
virtual bool isReadable() = 0;
|
||||
virtual bool isWritable() = 0;
|
||||
|
||||
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 void read(u64 offset, void *buffer, size_t size) { this->readRaw(offset, buffer, size); }
|
||||
virtual void write(u64 offset, const void *buffer, size_t size) { this->writeRaw(offset, buffer, size); }
|
||||
|
||||
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
|
||||
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
|
||||
virtual size_t getActualSize() = 0;
|
||||
|
||||
const std::map<u64, u8>& getPatches() { return this->m_patches.back(); }
|
||||
void applyPatches() {
|
||||
for (auto &[patchAddress, patch] : this->m_patches.back())
|
||||
this->writeRaw(patchAddress, &patch, 1);
|
||||
}
|
||||
|
||||
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::optional<u32> getPageOfAddress(u64 address) {
|
||||
u32 page = std::floor(address / double(PageSize));
|
||||
|
||||
if (page >= this->getPageCount())
|
||||
return { };
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
virtual std::vector<std::pair<std::string, std::string>> getDataInformation() = 0;
|
||||
|
||||
protected:
|
||||
u32 m_currPage = 0;
|
||||
|
||||
std::vector<std::map<u64, u8>> m_patches;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -9,13 +9,20 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock.h>
|
||||
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "lang/token.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
template<typename ... Args>
|
||||
inline std::string format(const std::string &format, Args ... args) {
|
||||
size_t size = snprintf( nullptr, 0, format.c_str(), args ... );
|
||||
ssize_t size = snprintf( nullptr, 0, format.c_str(), args ... );
|
||||
|
||||
if( size <= 0 )
|
||||
return "";
|
||||
@@ -26,60 +33,71 @@ namespace hex {
|
||||
return std::string(buffer.data(), buffer.data() + size);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
|
||||
u64 mask = 1LLU << (currWidth - 1);
|
||||
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
||||
}
|
||||
|
||||
constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||
}
|
||||
|
||||
constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||
}
|
||||
|
||||
constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||
}
|
||||
|
||||
constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
|
||||
return static_cast<u32>(type) >> 4;
|
||||
}
|
||||
|
||||
inline std::string toByteString(u64 bytes) {
|
||||
double value = bytes;
|
||||
u8 unitIndex = 0;
|
||||
|
||||
while (value > 1024) {
|
||||
value /= 1024;
|
||||
unitIndex++;
|
||||
|
||||
if (unitIndex == 6)
|
||||
break;
|
||||
}
|
||||
|
||||
std::string result = std::to_string(value);
|
||||
|
||||
switch (unitIndex) {
|
||||
case 0: result += " Bytes"; break;
|
||||
case 1: result += " kB"; break;
|
||||
case 2: result += " MB"; break;
|
||||
case 3: result += " GB"; break;
|
||||
case 4: result += " TB"; break;
|
||||
case 5: result += " PB"; break;
|
||||
case 6: result += " EB"; break;
|
||||
default: result = "A lot!";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u64 signExtend(u64 value, u8 currWidth, u8 targetWidth) {
|
||||
u64 mask = 1LLU << (currWidth - 1);
|
||||
return (((value ^ mask) - mask) << (64 - targetWidth)) >> (64 - targetWidth);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x00;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isSigned(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x01;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline bool isFloatingPoint(const lang::Token::TypeToken::Type type) {
|
||||
return (static_cast<u32>(type) & 0x0F) == 0x02;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr inline u32 getTypeSize(const lang::Token::TypeToken::Type type) {
|
||||
return static_cast<u32>(type) >> 4;
|
||||
}
|
||||
|
||||
std::string toByteString(u64 bytes);
|
||||
std::string makePrintable(char c);
|
||||
|
||||
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:
|
||||
@@ -93,4 +111,9 @@ namespace hex {
|
||||
private:
|
||||
std::function<void()> m_func;
|
||||
};
|
||||
|
||||
struct Region {
|
||||
u64 address;
|
||||
size_t size;
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "event.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace hex {
|
||||
|
||||
class View {
|
||||
public:
|
||||
View() { }
|
||||
View(std::string viewName) : m_viewName(viewName) { }
|
||||
virtual ~View() { }
|
||||
|
||||
virtual void createView() = 0;
|
||||
@@ -29,6 +30,36 @@ namespace hex {
|
||||
View::s_eventManager.post(eventType, userData);
|
||||
}
|
||||
|
||||
static void drawCommonInterfaces() {
|
||||
if (ImGui::BeginPopupModal("Error", nullptr, ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::NewLine();
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(300, 100))) {
|
||||
ImGui::SetCursorPosX((300 - ImGui::CalcTextSize(View::s_errorMessage.c_str(), nullptr, false).x) / 2.0F);
|
||||
ImGui::TextWrapped("%s", View::s_errorMessage.c_str());
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::SetCursorPosX(75);
|
||||
if (ImGui::Button("Okay", ImVec2(150, 20)))
|
||||
ImGui::CloseCurrentPopup();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
static void showErrorPopup(std::string_view errorMessage) {
|
||||
View::s_errorMessage = errorMessage;
|
||||
|
||||
ImGui::OpenPopup("Error");
|
||||
}
|
||||
|
||||
bool& getWindowOpenState() {
|
||||
return this->m_windowOpen;
|
||||
}
|
||||
|
||||
const std::string getName() const {
|
||||
return this->m_viewName;
|
||||
}
|
||||
|
||||
protected:
|
||||
void subscribeEvent(Events eventType, std::function<void(const void*)> callback) {
|
||||
View::s_eventManager.subscribe(eventType, this, callback);
|
||||
@@ -42,9 +73,16 @@ namespace hex {
|
||||
View::s_deferedCalls.push_back(function);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::string m_viewName;
|
||||
bool m_windowOpen = false;
|
||||
|
||||
static inline EventManager s_eventManager;
|
||||
static inline std::vector<std::function<void()>> s_deferedCalls;
|
||||
|
||||
static inline std::string s_errorMessage;
|
||||
};
|
||||
|
||||
}
|
||||
35
include/views/view_bookmarks.hpp
Normal file
35
include/views/view_bookmarks.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace prv { class Provider; }
|
||||
|
||||
struct Bookmark {
|
||||
Region region;
|
||||
|
||||
std::vector<char> name;
|
||||
std::vector<char> comment;
|
||||
};
|
||||
|
||||
class ViewBookmarks : public View {
|
||||
public:
|
||||
explicit ViewBookmarks(prv::Provider* &dataProvider);
|
||||
~ViewBookmarks() override;
|
||||
|
||||
void createView() override;
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
|
||||
std::list<Bookmark> m_bookmarks;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
@@ -50,9 +51,10 @@ namespace hex {
|
||||
|
||||
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;
|
||||
size_t size;
|
||||
std::string bytes;
|
||||
std::string mnemonic;
|
||||
std::string operators;
|
||||
};
|
||||
|
||||
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_shouldInvalidate = false;
|
||||
|
||||
u64 m_baseAddress = 0;
|
||||
u64 m_codeRegion[2] = { 0 };
|
||||
bool m_shouldMatchSelection = false;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -18,11 +18,12 @@ namespace hex {
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
|
||||
bool m_shouldInvalidate = true;
|
||||
int m_currHashFunction = 0;
|
||||
int m_hashStart = 0, m_hashEnd = 0;
|
||||
u64 m_hashRegion[2] = { 0 };
|
||||
bool m_shouldMatchSelection = false;
|
||||
|
||||
static constexpr const char* HashFunctionNames[] = { "CRC16", "CRC32", "MD4", "MD5", "SHA-1", "SHA-224", "SHA-256", "SHA-384", "SHA-512" };
|
||||
};
|
||||
|
||||
|
||||
@@ -45,11 +45,14 @@ namespace hex {
|
||||
|
||||
s64 m_gotoAddress = 0;
|
||||
|
||||
std::vector<u8> m_dataToSave;
|
||||
|
||||
void drawSearchPopup();
|
||||
void drawGotoPopup();
|
||||
|
||||
void openFile(std::string path);
|
||||
bool saveToFile(std::string path, const std::vector<u8>& data);
|
||||
bool loadFromFile(std::string path, std::vector<u8>& data);
|
||||
|
||||
enum class Language { C, Cpp, CSharp, Rust, Python, Java, JavaScript };
|
||||
void copyBytes();
|
||||
|
||||
@@ -21,15 +21,17 @@ namespace hex {
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "ImGuiFileBrowser.h"
|
||||
#include "TextEditor.h"
|
||||
@@ -27,11 +27,12 @@ namespace hex {
|
||||
private:
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
std::filesystem::path m_possiblePatternFile;
|
||||
|
||||
TextEditor m_textEditor;
|
||||
imgui_addons::ImGuiFileBrowser m_fileBrowser;
|
||||
|
||||
void loadPatternFile(std::string path);
|
||||
void clearPatternData();
|
||||
void parsePattern(char *buffer);
|
||||
};
|
||||
|
||||
@@ -23,10 +23,10 @@ namespace hex {
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
|
||||
prv::Provider* &m_dataProvider;
|
||||
std::vector<lang::PatternData*> &m_patternData;
|
||||
std::vector<lang::PatternData*> m_sortedPatternData;
|
||||
bool m_windowOpen = true;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -25,12 +25,16 @@ namespace hex {
|
||||
|
||||
private:
|
||||
prv::Provider* &m_dataProvider;
|
||||
bool m_windowOpen = true;
|
||||
bool m_shouldInvalidate = false;
|
||||
|
||||
std::vector<FoundString> m_foundStrings;
|
||||
int m_minimumLength = 5;
|
||||
char *m_filter;
|
||||
|
||||
std::string m_selectedString;
|
||||
std::string m_demangledName;
|
||||
|
||||
void createStringContextMenu(const FoundString &foundString);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -21,10 +21,8 @@ namespace hex {
|
||||
void createMenu() override;
|
||||
|
||||
private:
|
||||
bool m_windowOpen = true;
|
||||
|
||||
char *m_mangledBuffer = nullptr;
|
||||
char *m_demangledName = nullptr;
|
||||
std::string m_demangledName;
|
||||
|
||||
bool m_asciiTableShowOctal = false;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "views/view.hpp"
|
||||
|
||||
struct GLFWwindow;
|
||||
struct ImGuiSettingsHandler;
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -24,8 +25,10 @@ namespace hex {
|
||||
return static_cast<T*>(this->m_views.back());
|
||||
}
|
||||
|
||||
public:
|
||||
float m_globalScale = 1.0f, m_fontScale = 1.0f;
|
||||
friend void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *);
|
||||
friend void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line);
|
||||
friend void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler);
|
||||
friend void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf);
|
||||
|
||||
private:
|
||||
void frameBegin();
|
||||
@@ -38,7 +41,10 @@ namespace hex {
|
||||
|
||||
GLFWwindow* m_window;
|
||||
std::vector<View*> m_views;
|
||||
|
||||
float m_globalScale = 1.0f, m_fontScale = 1.0f;
|
||||
bool m_fpsVisible = false;
|
||||
bool m_demoWindowOpen = false;
|
||||
|
||||
static inline std::tuple<int, int> s_currShortcut = { -1, -1 };
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
#include <stdio.h> // sprintf, scanf
|
||||
#include <stdint.h> // uint8_t, etc.
|
||||
#include "utils.hpp"
|
||||
|
||||
#include "views/view.hpp"
|
||||
|
||||
@@ -75,7 +76,6 @@ struct MemoryEditor
|
||||
};
|
||||
|
||||
// Settings
|
||||
bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow().
|
||||
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.
|
||||
@@ -106,7 +106,6 @@ struct MemoryEditor
|
||||
MemoryEditor()
|
||||
{
|
||||
// Settings
|
||||
Open = true;
|
||||
ReadOnly = false;
|
||||
Cols = 16;
|
||||
OptShowOptions = true;
|
||||
@@ -181,13 +180,12 @@ struct MemoryEditor
|
||||
}
|
||||
|
||||
// Standalone Memory Editor window
|
||||
void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
void DrawWindow(const char* title, bool *p_open, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)
|
||||
{
|
||||
Sizes s;
|
||||
CalcSizes(s, mem_size, base_display_addr);
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));
|
||||
|
||||
if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))
|
||||
if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse))
|
||||
{
|
||||
DrawContents(mem_data, mem_size, base_display_addr);
|
||||
if (ContentsWidthChanged)
|
||||
@@ -423,14 +421,16 @@ struct MemoryEditor
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
hex::View::postEvent(hex::Events::ByteSelected, &DataPreviewAddr);
|
||||
hex::Region selectionRegion { addr, 1 };
|
||||
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
|
||||
}
|
||||
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);
|
||||
hex::Region selectionRegion { std::min(DataPreviewAddr, DataPreviewAddrEnd), std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd) + 1 };
|
||||
hex::View::postEvent(hex::Events::RegionSelected, &selectionRegion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -547,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)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
@@ -215,4 +217,24 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> decode64(const std::vector<u8> &input) {
|
||||
size_t outputSize = (3 * input.size()) / 4;
|
||||
std::vector<u8> output(outputSize + 1, 0x00);
|
||||
|
||||
if (EVP_DecodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
|
||||
return { };
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<u8> encode64(const std::vector<u8> &input) {
|
||||
size_t outputSize = 4 * ((input.size() + 2) / 3);
|
||||
std::vector<u8> output(outputSize + 1, 0x00);
|
||||
|
||||
if (EVP_EncodeBlock(output.data(), reinterpret_cast<const unsigned char *>(input.data()), input.size()) != outputSize)
|
||||
return { };
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Evaluator::Evaluator() {
|
||||
|
||||
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) {
|
||||
@@ -14,61 +14,82 @@ namespace hex::lang {
|
||||
|
||||
auto structNode = static_cast<ASTNodeStruct*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (structNode == nullptr)
|
||||
if (structNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
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) {
|
||||
const auto &[pattern, size] = this->createStringPattern(member, offset + structSize);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
structSize += size;
|
||||
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) {
|
||||
|
||||
const auto &[pattern, size] = this->createStringPattern(member, offset + structSize);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
structSize += size;
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
const auto &[pattern, size] = this->createArrayPattern(member, offset + structSize);
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
}
|
||||
else if (member->getArraySizeVariable().has_value()) {
|
||||
std::optional<size_t> arraySize;
|
||||
|
||||
if (pattern == nullptr)
|
||||
|
||||
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()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
members.push_back(pattern);
|
||||
structSize += size;
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), 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) {
|
||||
const auto &[pattern, size] = this->createBuiltInTypePattern(member, offset + structSize);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
structSize += size;
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
const auto &[pattern, size] = this->createCustomTypePattern(member, offset + structSize);
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
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 += size;
|
||||
structSize += memberSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,61 +101,85 @@ namespace hex::lang {
|
||||
|
||||
auto unionNode = static_cast<ASTNodeUnion*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (unionNode == nullptr)
|
||||
if (unionNode == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
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) {
|
||||
const auto &[pattern, size] = this->createStringPattern(member, offset);
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(size, unionSize);
|
||||
} else if (member->getVariableType() == Token::TypeToken::Type::CustomType
|
||||
&& typeDeclNode != nullptr && typeDeclNode->getAssignedType() == Token::TypeToken::Type::Signed8Bit
|
||||
&& member->getArraySize() > 1) {
|
||||
|
||||
const auto &[pattern, size] = this->createStringPattern(member, offset);
|
||||
std::tie(pattern, memberSize) = this->createStringPattern(member, memberOffset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(size, unionSize);
|
||||
}
|
||||
else if (member->getArraySize() > 1) {
|
||||
const auto &[pattern, size] = this->createArrayPattern(member, offset);
|
||||
std::tie(pattern, memberSize) = this->createArrayPattern(member, memberOffset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
}
|
||||
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()) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a previous member of '%s'", member->getArraySizeVariable().value().c_str(), varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(size, unionSize);
|
||||
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), 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) {
|
||||
const auto &[pattern, size] = this->createBuiltInTypePattern(member, offset);
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
members.push_back(pattern);
|
||||
unionSize = std::max(size, unionSize);
|
||||
std::tie(pattern, memberSize) = this->createBuiltInTypePattern(member, memberOffset);
|
||||
}
|
||||
else {
|
||||
const auto &[pattern, size] = this->createCustomTypePattern(member, offset);
|
||||
std::tie(pattern, memberSize) = this->createCustomTypePattern(member, memberOffset);
|
||||
}
|
||||
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
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(size, unionSize);
|
||||
unionSize = std::max(memberSize, unionSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,12 +187,12 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
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)
|
||||
if (enumType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = getTypeSize(enumType->getUnderlyingType());
|
||||
|
||||
@@ -158,8 +203,10 @@ namespace hex::lang {
|
||||
|
||||
auto *bitfieldType = static_cast<ASTNodeBitField*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
|
||||
if (bitfieldType == nullptr)
|
||||
if (bitfieldType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
size_t size = 0;
|
||||
for (auto &[fieldName, fieldSize] : bitfieldType->getFields())
|
||||
@@ -173,18 +220,27 @@ namespace hex::lang {
|
||||
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);
|
||||
ASTNodeVariableDecl *nonArrayVarDeclNode = new ASTNodeVariableDecl(varDeclNode->getLineNumber(), 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);
|
||||
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 {
|
||||
@@ -193,6 +249,11 @@ namespace hex::lang {
|
||||
if (pattern == nullptr)
|
||||
return { nullptr, 0 };
|
||||
|
||||
if (!arrayColor.has_value())
|
||||
arrayColor = pattern->getColor();
|
||||
|
||||
pattern->setColor(arrayColor.value());
|
||||
|
||||
entries.push_back(pattern);
|
||||
arrayOffset += size;
|
||||
}
|
||||
@@ -200,7 +261,7 @@ namespace hex::lang {
|
||||
delete nonArrayVarDeclNode;
|
||||
}
|
||||
|
||||
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), entries, 0x00FFFFFF), arrayOffset };
|
||||
return { new PatternDataArray(offset, arrayOffset, varDeclNode->getVariableName(), entries, arrayColor.value()), arrayOffset };
|
||||
}
|
||||
|
||||
std::pair<PatternData*, size_t> Evaluator::createStringPattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
@@ -212,8 +273,10 @@ namespace hex::lang {
|
||||
std::pair<PatternData*, size_t> Evaluator::createCustomTypePattern(ASTNodeVariableDecl *varDeclNode, u64 offset) {
|
||||
auto &currType = this->m_types[varDeclNode->getCustomVariableTypeName()];
|
||||
|
||||
if (currType == nullptr)
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
switch (currType->getType()) {
|
||||
case ASTNode::Type::Struct:
|
||||
@@ -235,8 +298,10 @@ namespace hex::lang {
|
||||
auto type = varDeclNode->getVariableType();
|
||||
if (type == Token::TypeToken::Type::CustomType) {
|
||||
const auto &currType = static_cast<ASTNodeTypeDecl*>(this->m_types[varDeclNode->getCustomVariableTypeName()]);
|
||||
if (currType == nullptr)
|
||||
if (currType == nullptr) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("'%s' does not name a type", varDeclNode->getCustomVariableTypeName().c_str()) };
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
|
||||
type = currType->getAssignedType();
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ namespace hex::lang {
|
||||
std::vector<Token> tokens;
|
||||
u32 offset = 0;
|
||||
|
||||
u32 lineNumber = 1;
|
||||
|
||||
while (offset < code.length()) {
|
||||
|
||||
// Handle comments
|
||||
@@ -85,6 +87,8 @@ namespace hex::lang {
|
||||
} else if (offset < code.length() && code[offset] == '*') {
|
||||
offset++;
|
||||
while (offset < (code.length() - 1)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
|
||||
if (code[offset] == '*' && code[offset + 1] == '/')
|
||||
break;
|
||||
offset++;
|
||||
@@ -100,82 +104,127 @@ namespace hex::lang {
|
||||
break;
|
||||
|
||||
if (std::isblank(c) || std::isspace(c)) {
|
||||
if (code[offset] == '\n') lineNumber++;
|
||||
offset += 1;
|
||||
} else if (c == ';') {
|
||||
tokens.push_back({ .type = Token::Type::EndOfExpression });
|
||||
tokens.push_back({ .type = Token::Type::EndOfExpression, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '{') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeOpen });
|
||||
tokens.push_back({ .type = Token::Type::ScopeOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '}') {
|
||||
tokens.push_back({ .type = Token::Type::ScopeClose });
|
||||
tokens.push_back({ .type = Token::Type::ScopeClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '[') {
|
||||
tokens.push_back({ .type = Token::Type::ArrayOpen });
|
||||
tokens.push_back({ .type = Token::Type::ArrayOpen, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ']') {
|
||||
tokens.push_back({.type = Token::Type::ArrayClose});
|
||||
tokens.push_back({.type = Token::Type::ArrayClose, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == ',') {
|
||||
tokens.push_back({ .type = Token::Type::Separator });
|
||||
tokens.push_back({ .type = Token::Type::Separator, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '*') {
|
||||
tokens.push_back({ .type = Token::Type::Operator, .operatorToken = { .op = Token::OperatorToken::Operator::Star }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
} else if (c == '\'') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
char character = code[offset];
|
||||
|
||||
if (character == '\\') {
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length()) {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
if (code[offset] != '\\' && code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Invalid escape sequence" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
character = code[offset];
|
||||
} else {
|
||||
if (code[offset] == '\\' || code[offset] == '\'' || character == '\n' || character == '\r') {
|
||||
this->m_error = { lineNumber, "Invalid character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
}
|
||||
|
||||
offset += 1;
|
||||
|
||||
if (offset >= code.length() || code[offset] != '\'') {
|
||||
this->m_error = { lineNumber, "Missing terminating ' after character literal" };
|
||||
return { ResultLexicalError, { } };
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = character }, .lineNumber = lineNumber });
|
||||
offset += 1;
|
||||
|
||||
} else if (std::isalpha(c)) {
|
||||
std::string identifier = matchTillInvalid(&code[offset], [](char c) -> bool { return std::isalnum(c) || c == '_'; });
|
||||
|
||||
// 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 }, .lineNumber = lineNumber });
|
||||
else if (identifier == "union")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union } });
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Union }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
else if (identifier == "bitfield")
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield } });
|
||||
tokens.push_back({ .type = Token::Type::Keyword, .keywordToken = { .keyword = Token::KeywordToken::Keyword::Bitfield }, .lineNumber = lineNumber });
|
||||
|
||||
// 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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
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 }, .lineNumber = lineNumber });
|
||||
else if (identifier == "padding")
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding } });
|
||||
tokens.push_back({ .type = Token::Type::Type, .typeToken = { .type = Token::TypeToken::Type::Padding }, .lineNumber = lineNumber });
|
||||
|
||||
// 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 }, .lineNumber = lineNumber });
|
||||
|
||||
offset += identifier.length();
|
||||
} else if (std::isdigit(c)) {
|
||||
@@ -184,15 +233,20 @@ namespace hex::lang {
|
||||
|
||||
auto integer = parseInt(std::string_view(&code[offset], end));
|
||||
|
||||
if (!integer.has_value())
|
||||
if (!integer.has_value()) {
|
||||
this->m_error = { lineNumber, "Invalid integer literal" };
|
||||
return { ResultLexicalError, {}};
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() } });
|
||||
tokens.push_back({ .type = Token::Type::Integer, .integerToken = { .integer = integer.value() }, .lineNumber = lineNumber });
|
||||
offset += (end - &code[offset]);
|
||||
} else return { ResultLexicalError, {}};
|
||||
} else {
|
||||
this->m_error = { lineNumber, "Unknown token" };
|
||||
return { ResultLexicalError, {} };
|
||||
}
|
||||
}
|
||||
|
||||
tokens.push_back({ .type = Token::Type::EndOfProgram });
|
||||
tokens.push_back({ .type = Token::Type::EndOfProgram, .lineNumber = lineNumber });
|
||||
|
||||
return { ResultSuccess, tokens };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "lang/parser.hpp"
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace hex::lang {
|
||||
@@ -8,11 +10,7 @@ namespace hex::lang {
|
||||
|
||||
}
|
||||
|
||||
using TokenIter = std::vector<Token>::const_iterator;
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType);
|
||||
|
||||
bool tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||
bool Parser::tryConsume(TokenIter &curr, std::initializer_list<Token::Type> tokenTypes) {
|
||||
std::vector<Token>::const_iterator originalPosition = curr;
|
||||
|
||||
for (const auto& type : tokenTypes) {
|
||||
@@ -27,38 +25,90 @@ namespace hex::lang {
|
||||
}
|
||||
|
||||
|
||||
ASTNode* parseBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
||||
ASTNode* Parser::parseBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-3].lineNumber, curr[-3].typeToken.type, curr[-2].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
ASTNode* Parser::parseCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-3].lineNumber, Token::TypeToken::Type::CustomType, curr[-2].identifierToken.identifier, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseBuiltinArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
||||
ASTNode* Parser::parseBuiltinPointerVariableDecl(TokenIter &curr) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType)) {
|
||||
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
|
||||
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* parsePaddingDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
||||
ASTNode* Parser::parseCustomTypePointerVariableDecl(TokenIter &curr) {
|
||||
auto pointerType = curr[-2].typeToken.type;
|
||||
|
||||
if (!isUnsigned(pointerType)) {
|
||||
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star) {
|
||||
this->m_error = { curr->lineNumber, "Expected '*' for pointer definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr->lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
|
||||
}
|
||||
|
||||
ASTNode* parseCustomTypeArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
||||
ASTNode* Parser::parseBuiltinArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
||||
ASTNode* Parser::parseCustomTypeArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* parseFreeCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
ASTNode* Parser::parseBuiltinVariableArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* parseStruct(TokenIter &curr) {
|
||||
ASTNode* Parser::parseCustomTypeVariableArrayDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-6].lineNumber, Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parsePaddingDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFreeBuiltinVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseFreeCustomTypeVariableDecl(TokenIter &curr) {
|
||||
return new ASTNodeVariableDecl(curr[-5].lineNumber, Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-5].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
|
||||
ASTNode* Parser::parseStruct(TokenIter &curr) {
|
||||
const std::string &structName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr));
|
||||
@@ -68,28 +118,44 @@ 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;
|
||||
|
||||
this->m_error = { curr[-5].lineNumber, "No member name provided" };
|
||||
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 {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
this->m_error = { curr->lineNumber, "Expected ';' after struct definition" };
|
||||
for(auto &node : nodes) delete node;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeStruct(structName, nodes);
|
||||
return new ASTNodeStruct(startLineNumber, structName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* parseUnion(TokenIter &curr) {
|
||||
ASTNode* Parser::parseUnion(TokenIter &curr) {
|
||||
const std::string &unionName = curr[-2].identifierToken.identifier;
|
||||
std::vector<ASTNode*> nodes;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, {Token::Type::Type, Token::Type::Identifier, Token::Type::EndOfExpression}))
|
||||
nodes.push_back(parseBuiltinVariableDecl(curr));
|
||||
@@ -99,28 +165,43 @@ 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 break;
|
||||
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 {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
for(auto &node : nodes) delete node;
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after union definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeUnion(unionName, nodes);
|
||||
return new ASTNodeUnion(startLineNumber, unionName, nodes);
|
||||
}
|
||||
|
||||
ASTNode* parseEnum(TokenIter &curr) {
|
||||
ASTNode* Parser::parseEnum(TokenIter &curr) {
|
||||
const std::string &enumName = curr[-4].identifierToken.identifier;
|
||||
const Token::TypeToken::Type underlyingType = curr[-2].typeToken.type;
|
||||
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
|
||||
return nullptr;
|
||||
u32 startLineNumber = curr[-5].lineNumber;
|
||||
|
||||
if ((static_cast<u32>(underlyingType) & 0x0F) != 0x00)
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected ':' after enum name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto enumNode = new ASTNodeEnum(underlyingType, enumName);
|
||||
if (!isUnsigned(underlyingType)) {
|
||||
this->m_error = { curr[-3].lineNumber, "Underlying type needs to be an unsigned type" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto enumNode = new ASTNodeEnum(startLineNumber, underlyingType, enumName);
|
||||
|
||||
while (!tryConsume(curr, {Token::Type::ScopeClose})) {
|
||||
if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Separator }) || tryConsume(curr, { Token::Type::Identifier, Token::Type::ScopeClose })) {
|
||||
@@ -144,67 +225,83 @@ namespace hex::lang {
|
||||
}
|
||||
else {
|
||||
delete enumNode;
|
||||
this->m_error = { curr->lineNumber, "Expected constant identifier" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
delete enumNode;
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after enum definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return enumNode;
|
||||
}
|
||||
|
||||
ASTNode *parseBitField(TokenIter &curr) {
|
||||
ASTNode* Parser::parseBitField(TokenIter &curr) {
|
||||
const std::string &bitfieldName = curr[-2].identifierToken.identifier;
|
||||
std::vector<std::pair<std::string, size_t>> fields;
|
||||
|
||||
u32 startLineNumber = curr[-3].lineNumber;
|
||||
|
||||
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)
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected ':' after member name" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fields.emplace_back(curr[-4].identifierToken.identifier, curr[-2].integerToken.integer);
|
||||
}
|
||||
else break;
|
||||
else {
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence, expected member declaration" };
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression}))
|
||||
if (!tryConsume(curr, {Token::Type::EndOfExpression})) {
|
||||
this->m_error = { curr[-1].lineNumber, "Expected ';' after bitfield definition" };
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new ASTNodeBitField(bitfieldName, fields);
|
||||
return new ASTNodeBitField(startLineNumber, bitfieldName, fields);
|
||||
}
|
||||
|
||||
ASTNode *parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(parseTillToken(curr, Token::Type::ScopeClose));
|
||||
ASTNode* Parser::parseScope(TokenIter &curr) {
|
||||
return new ASTNodeScope(curr[-1].lineNumber, parseTillToken(curr, Token::Type::ScopeClose));
|
||||
}
|
||||
|
||||
std::optional<ASTNode*> parseUsingDeclaration(TokenIter &curr) {
|
||||
std::optional<ASTNode*> Parser::parseUsingDeclaration(TokenIter &curr) {
|
||||
auto keyword = curr[-5].keywordToken;
|
||||
auto name = curr[-4].identifierToken;
|
||||
auto op = curr[-3].operatorToken;
|
||||
|
||||
if (keyword.keyword != Token::KeywordToken::Keyword::Using)
|
||||
if (keyword.keyword != Token::KeywordToken::Keyword::Using) {
|
||||
this->m_error = { curr[-5].lineNumber, "Invalid keyword. Expected 'using'" };
|
||||
return { };
|
||||
}
|
||||
|
||||
if (op.op != Token::OperatorToken::Operator::Assignment)
|
||||
if (op.op != Token::OperatorToken::Operator::Assignment) {
|
||||
this->m_error = { curr[-3].lineNumber, "Invalid operator. Expected '='" };
|
||||
return { };
|
||||
}
|
||||
|
||||
if (curr[-2].type == Token::Type::Type) {
|
||||
auto type = curr[-2].typeToken;
|
||||
|
||||
return new ASTNodeTypeDecl(type.type, name.identifier);
|
||||
return new ASTNodeTypeDecl(curr[-2].lineNumber, type.type, name.identifier);
|
||||
} else if (curr[-2].type == Token::Type::Identifier) {
|
||||
auto customType = curr[-2].identifierToken;
|
||||
|
||||
return new ASTNodeTypeDecl(Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
|
||||
return new ASTNodeTypeDecl(curr[-2].lineNumber, Token::TypeToken::Type::CustomType, name.identifier, customType.identifier);
|
||||
}
|
||||
|
||||
this->m_error = { curr[-2].lineNumber, hex::format("'%s' does not name a type") };
|
||||
return { };
|
||||
}
|
||||
|
||||
std::optional<std::vector<ASTNode*>> parseStatement(TokenIter &curr) {
|
||||
std::optional<std::vector<ASTNode*>> Parser::parseStatement(TokenIter &curr) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
// Struct
|
||||
@@ -285,16 +382,28 @@ namespace hex::lang {
|
||||
program.push_back(usingDecl.value());
|
||||
|
||||
return program;
|
||||
// Variable declaration with built-in type
|
||||
// Variable placement declaration with built-in type
|
||||
} else if (tryConsume(curr, { Token::Type::Type, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
auto variableDecl = parseFreeBuiltinVariableDecl(curr);
|
||||
|
||||
program.push_back(variableDecl);
|
||||
|
||||
return program;
|
||||
|
||||
// Variable declaration with custom type
|
||||
// Variable placement declaration with custom type
|
||||
} else if (tryConsume(curr, { Token::Type::Identifier, Token::Type::Identifier, Token::Type::Operator, Token::Type::Integer, Token::Type::EndOfExpression})) {
|
||||
if (curr[-3].operatorToken.op != Token::OperatorToken::Operator::AtDeclaration) {
|
||||
this->m_error = { curr[-3].lineNumber, "Expected '@' after variable placement declaration" };
|
||||
for(auto &node : program) delete node;
|
||||
return { };
|
||||
}
|
||||
|
||||
auto variableDecl = parseFreeCustomTypeVariableDecl(curr);
|
||||
|
||||
program.push_back(variableDecl);
|
||||
@@ -303,11 +412,12 @@ namespace hex::lang {
|
||||
}
|
||||
else {
|
||||
for(auto &node : program) delete node;
|
||||
this->m_error = { curr->lineNumber, "Invalid sequence" };
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ASTNode*> parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||
std::vector<ASTNode*> Parser::parseTillToken(TokenIter &curr, Token::Type endTokenType) {
|
||||
std::vector<ASTNode*> program;
|
||||
|
||||
while (curr->type != endTokenType) {
|
||||
|
||||
@@ -6,11 +6,14 @@ 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;
|
||||
u32 lineNumber = 1;
|
||||
|
||||
if (applyDefines)
|
||||
if (initialRun) {
|
||||
this->m_defines.clear();
|
||||
this->m_pragmas.clear();
|
||||
}
|
||||
|
||||
std::string output;
|
||||
output.reserve(code.length());
|
||||
@@ -76,45 +79,106 @@ 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, "" };
|
||||
}
|
||||
|
||||
if (code[offset] == '\n')
|
||||
lineNumber++;
|
||||
|
||||
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::addDefaultPragmaHandlers() {
|
||||
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";
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <unordered_set>
|
||||
#include <string>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace hex::lang {
|
||||
|
||||
Validator::Validator() {
|
||||
@@ -19,70 +21,98 @@ namespace hex::lang {
|
||||
{
|
||||
// Check for duplicate variable names
|
||||
auto varDeclNode = static_cast<ASTNodeVariableDecl*>(node);
|
||||
if (!typeNames.insert(varDeclNode->getVariableName()).second)
|
||||
if (!typeNames.insert(varDeclNode->getVariableName()).second) {
|
||||
this->m_error = { varDeclNode->getLineNumber(), hex::format("Redefinition of variable '%s'", varDeclNode->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (varDeclNode->getArraySize() == 0 && !varDeclNode->getArraySizeVariable().has_value() ||
|
||||
varDeclNode->getArraySize() != 0 && varDeclNode->getArraySizeVariable().has_value()) {
|
||||
|
||||
this->m_error = { varDeclNode->getLineNumber(), "Invalid array size" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::TypeDecl:
|
||||
{
|
||||
// Check for duplicate type names
|
||||
auto typeDeclNode = static_cast<ASTNodeTypeDecl*>(node);
|
||||
if (!typeNames.insert(typeDeclNode->getTypeName()).second)
|
||||
if (!typeNames.insert(typeDeclNode->getTypeName()).second) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), hex::format("Redefinition of type '%s'", typeDeclNode->getTypeName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName()))
|
||||
if (typeDeclNode->getAssignedType() == Token::TypeToken::Type::CustomType && !typeNames.contains(typeDeclNode->getAssignedCustomTypeName())) {
|
||||
this->m_error = { typeDeclNode->getLineNumber(), "Type declaration without a name" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ASTNode::Type::Struct:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto structNode = static_cast<ASTNodeStruct*>(node);
|
||||
if (!typeNames.insert(structNode->getName()).second)
|
||||
if (!typeNames.insert(structNode->getName()).second) {
|
||||
this->m_error = { structNode->getLineNumber(), hex::format("Redeclaration of type '%s'", structNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate member names
|
||||
std::unordered_set<std::string> memberNames;
|
||||
for (const auto &member : structNode->getNodes())
|
||||
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second)
|
||||
if (!memberNames.insert(static_cast<ASTNodeVariableDecl*>(member)->getVariableName()).second) {
|
||||
this->m_error = { member->getLineNumber(), hex::format("Redeclaration of member '%s'", static_cast<ASTNodeVariableDecl*>(member)->getVariableName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case ASTNode::Type::Enum:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto enumNode = static_cast<ASTNodeEnum*>(node);
|
||||
if (!typeNames.insert(enumNode->getName()).second)
|
||||
if (!typeNames.insert(enumNode->getName()).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of type '%s'", enumNode->getName().c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for duplicate constant names
|
||||
std::unordered_set<std::string> constantNames;
|
||||
for (const auto &[value, name] : enumNode->getValues())
|
||||
if (!constantNames.insert(name).second)
|
||||
if (!constantNames.insert(name).second) {
|
||||
this->m_error = { enumNode->getLineNumber(), hex::format("Redeclaration of enum constant '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ASTNode::Type::Bitfield:
|
||||
{
|
||||
// Check for duplicate type name
|
||||
auto bitfieldNode = static_cast<ASTNodeBitField*>(node);
|
||||
if (!typeNames.insert(bitfieldNode->getName()).second)
|
||||
if (!typeNames.insert(bitfieldNode->getName()).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of type '%s'", bitfieldNode->getName().c_str()) };
|
||||
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)
|
||||
if (!flagNames.insert(name).second) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), hex::format("Redeclaration of member '%s'", name.c_str()) };
|
||||
return false;
|
||||
}
|
||||
|
||||
bitfieldSize += size;
|
||||
}
|
||||
|
||||
if (bitfieldSize > 64)
|
||||
if (bitfieldSize > 64) {
|
||||
this->m_error = { bitfieldNode->getLineNumber(), "Bitfield exceeds maximum size of 64 bits" };
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "views/view_tools.hpp"
|
||||
#include "views/view_strings.hpp"
|
||||
#include "views/view_data_inspector.hpp"
|
||||
#include "views/view_disassembler.hpp"
|
||||
#include "views/view_bookmarks.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
@@ -30,8 +32,10 @@ int main() {
|
||||
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::ViewBookmarks>(dataProvider);
|
||||
window.addView<hex::ViewTools>();
|
||||
window.addView<hex::ViewHelp>();
|
||||
|
||||
window.loop();
|
||||
|
||||
|
||||
116
source/patches.cpp
Normal file
116
source/patches.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "patches.hpp"
|
||||
|
||||
#include <concepts>
|
||||
#include <cstring>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
static void pushBytesBack(std::vector<u8> &buffer, const char* bytes) {
|
||||
std::string_view string(bytes);
|
||||
buffer.resize(buffer.size() + string.length());
|
||||
std::memcpy((&buffer.back() - string.length()) + 1, string.begin(), string.length());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static void pushBytesBack(std::vector<u8> &buffer, T bytes) {
|
||||
buffer.resize(buffer.size() + sizeof(T));
|
||||
std::memcpy((&buffer.back() - sizeof(T)) + 1, &bytes, sizeof(T));
|
||||
}
|
||||
|
||||
std::vector<u8> generateIPSPatch(const Patches &patches) {
|
||||
std::vector<u8> result;
|
||||
|
||||
pushBytesBack(result, "PATCH");
|
||||
|
||||
std::vector<u64> addresses;
|
||||
std::vector<u8> values;
|
||||
|
||||
for (const auto &[address, value] : patches) {
|
||||
addresses.push_back(address);
|
||||
values.push_back(value);
|
||||
}
|
||||
|
||||
std::optional<u64> startAddress;
|
||||
std::vector<u8> bytes;
|
||||
for (u32 i = 0; i < addresses.size(); i++) {
|
||||
if (!startAddress.has_value())
|
||||
startAddress = addresses[i];
|
||||
|
||||
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
|
||||
bytes.push_back(values[i]);
|
||||
} else {
|
||||
bytes.push_back(values[i]);
|
||||
|
||||
if (bytes.size() > 0xFFFF || startAddress > 0xFF'FFFF)
|
||||
return { };
|
||||
|
||||
u32 address = startAddress.value();
|
||||
auto addressBytes = reinterpret_cast<u8*>(&address);
|
||||
|
||||
result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
|
||||
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
|
||||
|
||||
for (auto byte : bytes)
|
||||
result.push_back(byte);
|
||||
|
||||
bytes.clear();
|
||||
startAddress = { };
|
||||
}
|
||||
}
|
||||
|
||||
pushBytesBack(result, "EOF");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> generateIPS32Patch(const Patches &patches) {
|
||||
std::vector<u8> result;
|
||||
|
||||
pushBytesBack(result, "IPS32");
|
||||
|
||||
std::vector<u64> addresses;
|
||||
std::vector<u8> values;
|
||||
|
||||
for (const auto &[address, value] : patches) {
|
||||
addresses.push_back(address);
|
||||
values.push_back(value);
|
||||
}
|
||||
|
||||
std::optional<u64> startAddress;
|
||||
std::vector<u8> bytes;
|
||||
for (u32 i = 0; i < addresses.size(); i++) {
|
||||
if (!startAddress.has_value())
|
||||
startAddress = addresses[i];
|
||||
|
||||
if (i != addresses.size() - 1 && addresses[i] == (addresses[i + 1] - 1)) {
|
||||
bytes.push_back(values[i]);
|
||||
} else {
|
||||
bytes.push_back(values[i]);
|
||||
|
||||
if (bytes.size() > 0xFFFF || startAddress > 0xFFFF'FFFF)
|
||||
return { };
|
||||
|
||||
u32 address = startAddress.value();
|
||||
auto addressBytes = reinterpret_cast<u8*>(&address);
|
||||
|
||||
result.push_back(addressBytes[3]); result.push_back(addressBytes[2]); result.push_back(addressBytes[1]); result.push_back(addressBytes[0]);
|
||||
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
|
||||
|
||||
for (auto byte : bytes)
|
||||
result.push_back(byte);
|
||||
|
||||
bytes.clear();
|
||||
startAddress = { };
|
||||
}
|
||||
}
|
||||
|
||||
pushBytesBack(result, "EEOF");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ namespace hex::prv {
|
||||
this->m_file = fopen(path.data(), "rb");
|
||||
this->m_writable = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FileProvider::~FileProvider() {
|
||||
@@ -48,19 +47,42 @@ namespace hex::prv {
|
||||
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseeko64(this->m_file, offset, SEEK_SET);
|
||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||
size_t readSize = fread(buffer, 1, size, this->m_file);
|
||||
|
||||
|
||||
|
||||
for (u64 i = 0; i < readSize; i++)
|
||||
if (this->m_patches.back().contains(offset + i))
|
||||
reinterpret_cast<u8*>(buffer)[i] = this->m_patches.back()[offset + i];
|
||||
}
|
||||
|
||||
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
|
||||
if (buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
this->m_patches.push_back(this->m_patches.back());
|
||||
|
||||
for (u64 i = 0; i < size; i++)
|
||||
this->m_patches.back()[offset + i] = reinterpret_cast<const u8*>(buffer)[i];
|
||||
}
|
||||
|
||||
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
|
||||
if ((offset + size) > this->getSize() || buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
|
||||
fread(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
void FileProvider::write(u64 offset, void *buffer, size_t size) {
|
||||
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
|
||||
if (buffer == nullptr || size == 0)
|
||||
return;
|
||||
|
||||
fseeko64(this->m_file, offset, SEEK_SET);
|
||||
fwrite(buffer, 1, size, this->m_file);
|
||||
}
|
||||
|
||||
size_t FileProvider::getSize() {
|
||||
size_t FileProvider::getActualSize() {
|
||||
fseeko64(this->m_file, 0, SEEK_END);
|
||||
return ftello64(this->m_file);
|
||||
}
|
||||
@@ -69,7 +91,7 @@ namespace hex::prv {
|
||||
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));
|
||||
@@ -5,4 +5,72 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::string toByteString(u64 bytes) {
|
||||
double value = bytes;
|
||||
u8 unitIndex = 0;
|
||||
|
||||
while (value > 1024) {
|
||||
value /= 1024;
|
||||
unitIndex++;
|
||||
|
||||
if (unitIndex == 6)
|
||||
break;
|
||||
}
|
||||
|
||||
std::string result = hex::format("%.2f", value);
|
||||
|
||||
switch (unitIndex) {
|
||||
case 0: result += " Bytes"; break;
|
||||
case 1: result += " kB"; break;
|
||||
case 2: result += " MB"; break;
|
||||
case 3: result += " GB"; break;
|
||||
case 4: result += " TB"; break;
|
||||
case 5: result += " PB"; break;
|
||||
case 6: result += " EB"; break;
|
||||
default: result = "A lot!";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string makePrintable(char c) {
|
||||
switch (c) {
|
||||
case 0: return "NUL";
|
||||
case 1: return "SOH";
|
||||
case 2: return "STX";
|
||||
case 3: return "ETX";
|
||||
case 4: return "EOT";
|
||||
case 5: return "ENQ";
|
||||
case 6: return "ACK";
|
||||
case 7: return "BEL";
|
||||
case 8: return "BS";
|
||||
case 9: return "TAB";
|
||||
case 10: return "LF";
|
||||
case 11: return "VT";
|
||||
case 12: return "FF";
|
||||
case 13: return "CR";
|
||||
case 14: return "SO";
|
||||
case 15: return "SI";
|
||||
case 16: return "DLE";
|
||||
case 17: return "DC1";
|
||||
case 18: return "DC2";
|
||||
case 19: return "DC3";
|
||||
case 20: return "DC4";
|
||||
case 21: return "NAK";
|
||||
case 22: return "SYN";
|
||||
case 23: return "ETB";
|
||||
case 24: return "CAN";
|
||||
case 25: return "EM";
|
||||
case 26: return "SUB";
|
||||
case 27: return "ESC";
|
||||
case 28: return "FS";
|
||||
case 29: return "GS";
|
||||
case 30: return "RS";
|
||||
case 31: return "US";
|
||||
case 32: return "Space";
|
||||
case 127: return "DEL";
|
||||
default: return std::string() + c;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
99
source/views/view_bookmarks.cpp
Normal file
99
source/views/view_bookmarks.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "views/view_bookmarks.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewBookmarks::ViewBookmarks(prv::Provider* &dataProvider) : View("Bookmarks"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::AddBookmark, [this](const void *userData) {
|
||||
Bookmark bookmark;
|
||||
bookmark.region = *reinterpret_cast<const Region*>(userData);
|
||||
bookmark.name.resize(64);
|
||||
bookmark.comment.resize(0xF'FFFF);
|
||||
|
||||
std::memset(bookmark.name.data(), 0x00, 64);
|
||||
std::memset(bookmark.comment.data(), 0x00, 0xF'FFFF);
|
||||
|
||||
std::strcpy(bookmark.name.data(), ("Bookmark " + std::to_string(this->m_bookmarks.size() + 1)).c_str());
|
||||
|
||||
this->m_bookmarks.push_back(bookmark);
|
||||
});
|
||||
}
|
||||
|
||||
ViewBookmarks::~ViewBookmarks() {
|
||||
View::unsubscribeEvent(Events::AddBookmark);
|
||||
}
|
||||
|
||||
void ViewBookmarks::createView() {
|
||||
if (ImGui::Begin("Bookmarks", &this->getWindowOpenState())) {
|
||||
if (ImGui::BeginChild("##scrolling")) {
|
||||
|
||||
u32 id = 1;
|
||||
std::list<Bookmark>::const_iterator bookmarkToRemove = this->m_bookmarks.end();
|
||||
for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) {
|
||||
auto &[region, name, comment] = *iter;
|
||||
|
||||
if (ImGui::CollapsingHeader((std::string(name.data()) + "###" + std::to_string((u64)comment.data())).c_str())) {
|
||||
ImGui::TextUnformatted("Information");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("0x%08lx : 0x%08lx (%lu bytes)", region.address, region.address + region.size - 1, region.size);
|
||||
|
||||
{
|
||||
u8 bytes[10] = { 0 };
|
||||
this->m_dataProvider->read(region.address, bytes, std::min(region.size, size_t(10)));
|
||||
|
||||
std::string bytesString;
|
||||
for (u8 i = 0; i < std::min(region.size, size_t(10)); i++) {
|
||||
bytesString += hex::format("%02X ", bytes[i]);
|
||||
}
|
||||
|
||||
if (region.size > 10) {
|
||||
bytesString.pop_back();
|
||||
bytesString += "...";
|
||||
}
|
||||
|
||||
ImGui::TextColored(ImColor(0xFF9BC64D), bytesString.c_str());
|
||||
}
|
||||
|
||||
if (ImGui::Button("Jump to"))
|
||||
View::postEvent(Events::SelectionChangeRequest, ®ion);
|
||||
|
||||
ImGui::SameLine(0, 15);
|
||||
|
||||
if (ImGui::Button("Remove"))
|
||||
bookmarkToRemove = iter;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Name");
|
||||
ImGui::Separator();
|
||||
ImGui::PushID(id);
|
||||
ImGui::InputText("##nolabel", name.data(), 64);
|
||||
ImGui::PopID();
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Comment");
|
||||
ImGui::Separator();
|
||||
ImGui::PushID(id + 1);
|
||||
ImGui::InputTextMultiline("##nolabel", comment.data(), 0xF'FFFF);
|
||||
ImGui::PopID();
|
||||
ImGui::NewLine();
|
||||
|
||||
id += 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (bookmarkToRemove != this->m_bookmarks.end())
|
||||
this->m_bookmarks.erase(bookmarkToRemove);
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewBookmarks::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,26 +9,23 @@ extern int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const
|
||||
|
||||
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);
|
||||
ViewDataInspector::ViewDataInspector(prv::Provider* &dataProvider) : View("Data Inspector"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void* userData){
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
this->m_validBytes = std::min(this->m_dataProvider->getSize() - offset, sizeof(PreviewData));
|
||||
this->m_validBytes = std::min(this->m_dataProvider->getSize() - region.address, sizeof(PreviewData));
|
||||
std::memset(&this->m_previewData, 0x00, sizeof(PreviewData));
|
||||
this->m_dataProvider->read(offset, &this->m_previewData, sizeof(PreviewData));
|
||||
this->m_dataProvider->read(region.address, &this->m_previewData, sizeof(PreviewData));
|
||||
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
}
|
||||
|
||||
ViewDataInspector::~ViewDataInspector() {
|
||||
View::unsubscribeEvent(Events::ByteSelected);
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
void ViewDataInspector::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_shouldInvalidate = false;
|
||||
|
||||
@@ -41,51 +38,58 @@ namespace hex {
|
||||
this->m_cachedData.emplace_back("Binary (8 bit)", binary);
|
||||
}
|
||||
|
||||
this->m_cachedData.emplace_back("uint8_t", hex::format("%u", this->m_previewData.unsigned8));
|
||||
this->m_cachedData.emplace_back("int8_t", hex::format("%d", this->m_previewData.unsigned8));
|
||||
this->m_cachedData.emplace_back("uint16_t", hex::format("%u", this->m_previewData.unsigned16));
|
||||
this->m_cachedData.emplace_back("int16_t", hex::format("%d", this->m_previewData.unsigned16));
|
||||
this->m_cachedData.emplace_back("uint32_t", hex::format("%lu", this->m_previewData.unsigned32));
|
||||
this->m_cachedData.emplace_back("int32_t", hex::format("%ld", this->m_previewData.unsigned32));
|
||||
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", this->m_previewData.unsigned64));
|
||||
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", this->m_previewData.unsigned64));
|
||||
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", this->m_previewData.ansiChar));
|
||||
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", this->m_previewData.wideChar));
|
||||
this->m_cachedData.emplace_back("ASCII Character", hex::format("'%s'", makePrintable(this->m_previewData.ansiChar).c_str()));
|
||||
this->m_cachedData.emplace_back("Wide Character", hex::format("'%lc'", this->m_previewData.wideChar == 0 ? '\x01' : 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);
|
||||
u32 utf8 = 0;
|
||||
ImTextCharFromUtf8(&utf8, buffer, buffer + 4);
|
||||
this->m_cachedData.emplace_back("UTF-8 code point", hex::format("U+%08lx", utf8));
|
||||
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", this->m_previewData.float32));
|
||||
this->m_cachedData.emplace_back("double (64 bit)", hex::format("%e", this->m_previewData.float64));
|
||||
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)
|
||||
{
|
||||
std::tm * ptm = _localtime32(&this->m_previewData.time32);
|
||||
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))
|
||||
if (ptm != nullptr && 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");
|
||||
}
|
||||
|
||||
{
|
||||
std::tm * ptm = _localtime64(&this->m_previewData.time64);
|
||||
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)
|
||||
if (ptm != nullptr && 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
|
||||
{
|
||||
std::tm * ptm = localtime(&this->m_previewData.time);
|
||||
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)
|
||||
if (ptm != nullptr && 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");
|
||||
@@ -93,30 +97,29 @@ namespace hex {
|
||||
#endif
|
||||
|
||||
this->m_cachedData.emplace_back("GUID", hex::format("{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
|
||||
this->m_previewData.guid.data1, this->m_previewData.guid.data2, this->m_previewData.guid.data3,
|
||||
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 Preview", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Data Inspector", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (ImGui::BeginChild("##scrolling")) {
|
||||
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg)) {
|
||||
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();
|
||||
u32 rowCount = 0;
|
||||
|
||||
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::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
|
||||
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -124,11 +127,8 @@ namespace hex {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted("RGBA Color");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("##nolabel", ImColor(this->m_previewData.unsigned32),
|
||||
ImGui::ColorButton("##nolabel", ImColor(hex::changeEndianess(this->m_previewData.unsigned32, this->m_endianess)),
|
||||
ImGuiColorEditFlags_None, ImVec2(ImGui::GetColumnWidth(), 15));
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
|
||||
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,16 +136,23 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
291
source/views/view_disassembler.cpp
Normal file
291
source/views/view_disassembler.cpp
Normal file
@@ -0,0 +1,291 @@
|
||||
#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("Disassembler"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
if (this->m_shouldMatchSelection) {
|
||||
this->m_codeRegion[0] = region.address;
|
||||
this->m_codeRegion[1] = region.address + region.size - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewDisassembler::~ViewDisassembler() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
void ViewDisassembler::createView() {
|
||||
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_codeRegion[1] - this->m_codeRegion[0] + 1); address += 2048) {
|
||||
size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address);
|
||||
this->m_dataProvider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize);
|
||||
|
||||
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, 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_codeRegion[0] + address + usedBytes;
|
||||
disassembly.size = instructions[instr].size;
|
||||
disassembly.mnemonic = instructions[instr].mnemonic;
|
||||
disassembly.operators = 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->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
|
||||
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::TextUnformatted("Position");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::InputScalarN("Code region", ImGuiDataType_U64, this->m_codeRegion, 2, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Settings");
|
||||
ImGui::Separator();
|
||||
|
||||
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 (this->m_modeBasicARM == cs_mode(0))
|
||||
this->m_modeBasicARM = CS_MODE_ARM;
|
||||
|
||||
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 (this->m_modeBasicMIPS == cs_mode(0))
|
||||
this->m_modeBasicMIPS = CS_MODE_MIPS32;
|
||||
|
||||
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 (this->m_modeBasicX86 == cs_mode(0))
|
||||
this->m_modeBasicX86 = CS_MODE_16;
|
||||
|
||||
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 (m_modeBasicPPC == cs_mode(0))
|
||||
this->m_modeBasicPPC = CS_MODE_32;
|
||||
|
||||
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::SetCursorPosX((ImGui::GetContentRegionAvailWidth() - 300) / 2);
|
||||
if (ImGui::Button("Disassemble", ImVec2(300, 20)))
|
||||
this->m_shouldInvalidate = true;
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::TextUnformatted("Disassembly");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_ScrollY | ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_Reorderable)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
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();
|
||||
if (ImGui::Selectable(("##DisassemblyLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { this->m_disassembly[i].offset, this->m_disassembly[i].size };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::SameLine();
|
||||
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::TextColored(ImColor(0xFFD69C56), "%s", this->m_disassembly[i].mnemonic.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(this->m_disassembly[i].operators.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewDisassembler::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,67 +6,70 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <winsock.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
ViewHashes::ViewHashes(prv::Provider* &dataProvider) : View("Hashes"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::RegionSelected, [this](const void *userData) {
|
||||
Region region = *static_cast<const Region*>(userData);
|
||||
|
||||
if (this->m_shouldMatchSelection) {
|
||||
this->m_hashRegion[0] = region.address;
|
||||
this->m_hashRegion[1] = region.address + region.size - 1;
|
||||
this->m_shouldInvalidate = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ViewHashes::~ViewHashes() {
|
||||
View::unsubscribeEvent(Events::DataChanged);
|
||||
View::unsubscribeEvent(Events::RegionSelected);
|
||||
}
|
||||
|
||||
|
||||
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", htonl(dataArray[i]));
|
||||
snprintf(buffer + 8 * i, bufferSize - 8 * i, "%08X", hex::changeEndianess(dataArray[i], std::endian::big));
|
||||
}
|
||||
|
||||
void ViewHashes::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Hashing", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Hashing", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
ImGui::NewLine();
|
||||
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||
|
||||
ImGui::TextUnformatted("Region");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::InputScalarN("##nolabel", ImGuiDataType_U64, this->m_hashRegion, 2, nullptr, nullptr, "%08X", ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::Checkbox("Match selection", &this->m_shouldMatchSelection);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Settings");
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Combo("Hash Function", &this->m_currHashFunction, HashFunctionNames,sizeof(HashFunctionNames) / sizeof(const char *)))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::InputInt("Begin", &this->m_hashStart, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::InputInt("End", &this->m_hashEnd, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
size_t dataSize = this->m_dataProvider->getSize();
|
||||
if (this->m_hashEnd >= dataSize)
|
||||
this->m_hashEnd = dataSize - 1;
|
||||
if (this->m_hashRegion[1] >= dataSize)
|
||||
this->m_hashRegion[1] = dataSize - 1;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
if (this->m_hashEnd >= this->m_hashStart) {
|
||||
if (this->m_hashRegion[1] >= this->m_hashRegion[0]) {
|
||||
|
||||
switch (this->m_currHashFunction) {
|
||||
case 0: // CRC16
|
||||
{
|
||||
int polynomial = 0, init = 0;
|
||||
static int polynomial = 0, init = 0;
|
||||
|
||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
@@ -74,23 +77,24 @@ namespace hex {
|
||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
static u16 result = 0;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = crc16(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
|
||||
result = crc16(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
snprintf(buffer, sizeof(buffer), "%04X", result);
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 1: // CRC32
|
||||
{
|
||||
int polynomial = 0, init = 0;
|
||||
static int polynomial = 0, init = 0;
|
||||
|
||||
ImGui::InputInt("Initial Value", &init, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
@@ -98,18 +102,19 @@ namespace hex {
|
||||
ImGui::InputInt("Polynomial", &polynomial, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
|
||||
if (ImGui::IsItemEdited()) this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
static u32 result = 0;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = crc32(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1, polynomial, init);
|
||||
result = crc32(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1, polynomial, init);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
snprintf(buffer, sizeof(buffer), "%08X", result);
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 2: // MD4
|
||||
@@ -117,11 +122,15 @@ namespace hex {
|
||||
static std::array<u32, 4> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = md4(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = md4(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 3: // MD5
|
||||
@@ -129,11 +138,15 @@ namespace hex {
|
||||
static std::array<u32, 4> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = md5(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = md5(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 4: // SHA-1
|
||||
@@ -141,11 +154,15 @@ namespace hex {
|
||||
static std::array<u32, 5> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha1(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha1(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 5: // SHA-224
|
||||
@@ -153,11 +170,15 @@ namespace hex {
|
||||
static std::array<u32, 7> result = { 0 };
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha224(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha224(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 6: // SHA-256
|
||||
@@ -165,11 +186,15 @@ namespace hex {
|
||||
static std::array<u32, 8> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha256(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha256(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 7: // SHA-384
|
||||
@@ -177,11 +202,15 @@ namespace hex {
|
||||
static std::array<u32, 12> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha384(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha384(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
case 8: // SHA-512
|
||||
@@ -189,11 +218,15 @@ namespace hex {
|
||||
static std::array<u32, 16> result;
|
||||
|
||||
if (this->m_shouldInvalidate)
|
||||
result = sha512(this->m_dataProvider, this->m_hashStart, this->m_hashEnd - this->m_hashStart + 1);
|
||||
result = sha512(this->m_dataProvider, this->m_hashRegion[0], this->m_hashRegion[1] - this->m_hashRegion[0] + 1);
|
||||
|
||||
char buffer[sizeof(result) * 2 + 1];
|
||||
formatBigHexInt(result, buffer, sizeof(buffer));
|
||||
ImGui::InputText("Hash value", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
|
||||
ImGui::NewLine();
|
||||
ImGui::TextUnformatted("Result");
|
||||
ImGui::Separator();
|
||||
ImGui::InputText("##nolabel", buffer, ImGuiInputTextFlags_ReadOnly);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -208,10 +241,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHashes::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Hash View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHelp::ViewHelp() {
|
||||
|
||||
ViewHelp::ViewHelp() : View("Help") {
|
||||
this->getWindowOpenState() = true;
|
||||
}
|
||||
|
||||
ViewHelp::~ViewHelp() {
|
||||
@@ -32,6 +32,7 @@ namespace hex {
|
||||
ImGui::BulletText("imgui_club by ocornut");
|
||||
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");
|
||||
@@ -78,6 +79,15 @@ namespace hex {
|
||||
"#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");
|
||||
@@ -92,10 +102,13 @@ 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");
|
||||
@@ -108,7 +121,7 @@ namespace hex {
|
||||
" u8 version;\n"
|
||||
" padding[4];\n"
|
||||
" Flags flags;\n"
|
||||
"}"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Unions");
|
||||
@@ -119,7 +132,16 @@ namespace hex {
|
||||
"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");
|
||||
@@ -131,7 +153,7 @@ namespace hex {
|
||||
" r : 1;\n"
|
||||
" w : 1;\n"
|
||||
" x : 1;\n"
|
||||
"}"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Enum");
|
||||
@@ -145,7 +167,7 @@ namespace hex {
|
||||
" Windows = 0x10,\n"
|
||||
" MacOSX,\n"
|
||||
" Linux\n"
|
||||
"}"
|
||||
"};"
|
||||
);
|
||||
|
||||
DrawTitle("Using declarations");
|
||||
|
||||
@@ -5,10 +5,16 @@
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "crypto.hpp"
|
||||
#include "patches.hpp"
|
||||
|
||||
#undef __STRICT_ANSI__
|
||||
#include <cstdio>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewHexEditor::ViewHexEditor(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
: View("Hex Editor"), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->m_memoryEditor.ReadFn = [](const ImU8 *data, size_t off) -> ImU8 {
|
||||
ViewHexEditor *_this = (ViewHexEditor *) data;
|
||||
@@ -56,6 +62,20 @@ namespace hex {
|
||||
if (filePath != nullptr)
|
||||
this->openFile(filePath);
|
||||
});
|
||||
|
||||
View::subscribeEvent(Events::SelectionChangeRequest, [this](const void *userData) {
|
||||
const Region ®ion = *reinterpret_cast<const Region*>(userData);
|
||||
|
||||
auto page = this->m_dataProvider->getPageOfAddress(region.address);
|
||||
if (!page.has_value())
|
||||
return;
|
||||
|
||||
this->m_dataProvider->setCurrentPage(page.value());
|
||||
this->m_memoryEditor.GotoAddr = region.address;
|
||||
this->m_memoryEditor.DataPreviewAddr = region.address;
|
||||
this->m_memoryEditor.DataPreviewAddrEnd = region.address + region.size - 1;
|
||||
View::postEvent(Events::RegionSelected, ®ion);
|
||||
});
|
||||
}
|
||||
|
||||
ViewHexEditor::~ViewHexEditor() {
|
||||
@@ -65,32 +85,265 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewHexEditor::createView() {
|
||||
if (!this->m_memoryEditor.Open)
|
||||
return;
|
||||
|
||||
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->getWindowOpenState(), 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);
|
||||
|
||||
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
|
||||
View::postEvent(Events::RegionSelected, &dataPreview);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
|
||||
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
|
||||
|
||||
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
|
||||
View::postEvent(Events::RegionSelected, &dataPreview);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
this->drawSearchPopup();
|
||||
this->drawGotoPopup();
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this->m_fileBrowser.showFileDialog("Open File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
|
||||
this->openFile(this->m_fileBrowser.selected_path);
|
||||
}
|
||||
|
||||
if (this->m_fileBrowser.showFileDialog("Open Base64 File", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN)) {
|
||||
std::vector<u8> base64;
|
||||
this->loadFromFile(this->m_fileBrowser.selected_path, base64);
|
||||
|
||||
if (!base64.empty()) {
|
||||
this->m_dataToSave = decode64(base64);
|
||||
|
||||
if (this->m_dataToSave.empty())
|
||||
View::showErrorPopup("File is not in a valid Base64 format!");
|
||||
else
|
||||
ImGui::OpenPopup("Save Data");
|
||||
} else View::showErrorPopup("Failed to open file!");
|
||||
|
||||
}
|
||||
|
||||
if (this->m_fileBrowser.showFileDialog("Export File", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
|
||||
this->saveToFile(this->m_fileBrowser.selected_path, this->m_dataToSave);
|
||||
}
|
||||
|
||||
if (this->m_fileBrowser.showFileDialog("Save As", imgui_addons::ImGuiFileBrowser::DialogMode::SAVE)) {
|
||||
FILE *file = fopen(this->m_fileBrowser.selected_path.c_str(), "wb");
|
||||
|
||||
if (file != nullptr) {
|
||||
std::vector<u8> buffer(0xFF'FFFF, 0x00);
|
||||
size_t bufferSize = buffer.size();
|
||||
|
||||
fseek(file, 0, SEEK_SET);
|
||||
|
||||
for (u64 offset = 0; offset < this->m_dataProvider->getActualSize(); offset += bufferSize) {
|
||||
if (bufferSize > this->m_dataProvider->getActualSize() - offset)
|
||||
bufferSize = this->m_dataProvider->getActualSize() - offset;
|
||||
|
||||
this->m_dataProvider->read(offset, buffer.data(), bufferSize);
|
||||
fwrite(buffer.data(), 1, bufferSize, file);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewHexEditor::createMenu() {
|
||||
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Open File"); });
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Save", "CTRL + S", false, this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
|
||||
for (const auto &[address, value] : this->m_dataProvider->getPatches())
|
||||
this->m_dataProvider->writeRaw(address, &value, sizeof(u8));
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Save As...", "CTRL + SHIFT + S", false, this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Save As"); });
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Import...")) {
|
||||
if (ImGui::MenuItem("Base64 File")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Open Base64 File"); });
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Export...", this->m_dataProvider != nullptr && this->m_dataProvider->isWritable())) {
|
||||
if (ImGui::MenuItem("IPS Patch")) {
|
||||
Patches patches = this->m_dataProvider->getPatches();
|
||||
if (!patches.contains(0x00454F45) && patches.contains(0x00454F46)) {
|
||||
u8 value = 0;
|
||||
this->m_dataProvider->read(0x00454F45, &value, sizeof(u8));
|
||||
patches[0x00454F45] = value;
|
||||
}
|
||||
|
||||
this->m_dataToSave = generateIPSPatch(patches);
|
||||
View::doLater([]{ ImGui::OpenPopup("Export File"); });
|
||||
}
|
||||
if (ImGui::MenuItem("IPS32 Patch")) {
|
||||
Patches patches = this->m_dataProvider->getPatches();
|
||||
if (!patches.contains(0x00454F45) && patches.contains(0x45454F46)) {
|
||||
u8 value = 0;
|
||||
this->m_dataProvider->read(0x45454F45, &value, sizeof(u8));
|
||||
patches[0x45454F45] = value;
|
||||
}
|
||||
|
||||
this->m_dataToSave = generateIPS32Patch(patches);
|
||||
View::doLater([]{ ImGui::OpenPopup("Export File"); });
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Search", "CTRL + F")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Search"); });
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Goto", "CTRL + G")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Goto"); });
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
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("Create bookmark", nullptr, false, this->m_memoryEditor.DataPreviewAddr != -1 && this->m_memoryEditor.DataPreviewAddrEnd != -1)) {
|
||||
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);
|
||||
Region selectionRegion = { start, end - start + 1 };
|
||||
|
||||
View::postEvent(Events::AddBookmark, &selectionRegion);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewHexEditor::handleShortcut(int key, int mods) {
|
||||
if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_S) {
|
||||
for (const auto &[address, value] : this->m_dataProvider->getPatches())
|
||||
this->m_dataProvider->writeRaw(address, &value, sizeof(u8));
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL | GLFW_MOD_SHIFT && key == GLFW_KEY_S) {
|
||||
ImGui::OpenPopup("Save As");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
|
||||
ImGui::OpenPopup("Search");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
|
||||
ImGui::OpenPopup("Goto");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
|
||||
ImGui::OpenPopup("Open File");
|
||||
return true;
|
||||
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
|
||||
this->copyBytes();
|
||||
return true;
|
||||
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_C) {
|
||||
this->copyString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void ViewHexEditor::openFile(std::string path) {
|
||||
if (this->m_dataProvider != nullptr)
|
||||
delete this->m_dataProvider;
|
||||
|
||||
this->m_dataProvider = new prv::FileProvider(path);
|
||||
this->m_memoryEditor.ReadOnly = !this->m_dataProvider->isWritable();
|
||||
View::postEvent(Events::FileLoaded);
|
||||
View::postEvent(Events::DataChanged);
|
||||
}
|
||||
|
||||
bool ViewHexEditor::saveToFile(std::string path, const std::vector<u8>& data) {
|
||||
FILE *file = fopen(path.c_str(), "wb");
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
fwrite(data.data(), 1, data.size(), file);
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewHexEditor::loadFromFile(std::string path, std::vector<u8>& data) {
|
||||
FILE *file = fopen(path.c_str(), "rb");
|
||||
|
||||
if (file == nullptr)
|
||||
return false;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftello64(file);
|
||||
rewind(file);
|
||||
|
||||
data.resize(size);
|
||||
fread(data.data(), 1, data.size(), file);
|
||||
fclose(file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -114,7 +367,7 @@ namespace hex {
|
||||
|
||||
size_t copySize = (end - start) + 1;
|
||||
|
||||
std::string buffer;
|
||||
std::string buffer(copySize, 0x00);
|
||||
buffer.reserve(copySize + 1);
|
||||
this->m_dataProvider->read(start, buffer.data(), copySize);
|
||||
|
||||
@@ -329,92 +582,6 @@ R"(
|
||||
ImGui::SetClipboardText(str.c_str());
|
||||
}
|
||||
|
||||
void ViewHexEditor::createMenu() {
|
||||
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open File...", "CTRL + O")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Open File"); });
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Search", "CTRL + F")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Search"); });
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Goto", "CTRL + G")) {
|
||||
View::doLater([]{ ImGui::OpenPopup("Goto"); });
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Edit")) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Hex View", "", &this->m_memoryEditor.Open);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewHexEditor::handleShortcut(int key, int mods) {
|
||||
if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_F) {
|
||||
ImGui::OpenPopup("Search");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_G) {
|
||||
ImGui::OpenPopup("Goto");
|
||||
return true;
|
||||
} else if (mods == GLFW_MOD_CONTROL && key == GLFW_KEY_O) {
|
||||
ImGui::OpenPopup("Open File");
|
||||
return true;
|
||||
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_ALT) && key == GLFW_KEY_C) {
|
||||
this->copyBytes();
|
||||
return true;
|
||||
} else if (mods == (GLFW_MOD_CONTROL | GLFW_MOD_SHIFT) && key == GLFW_KEY_C) {
|
||||
this->copyString();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static std::vector<std::pair<u64, u64>> findString(prv::Provider* &provider, std::string string) {
|
||||
std::vector<std::pair<u64, u64>> results;
|
||||
|
||||
@@ -578,8 +745,8 @@ R"(
|
||||
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;
|
||||
|
||||
newOffset = this->m_gotoAddress;
|
||||
|
||||
@@ -596,9 +763,9 @@ R"(
|
||||
s64 currHighlightStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
|
||||
|
||||
newOffset = this->m_gotoAddress + currHighlightStart;
|
||||
if (newOffset >= this->m_dataProvider->getSize()) {
|
||||
newOffset = this->m_dataProvider->getSize() - 1;
|
||||
this->m_gotoAddress = (this->m_dataProvider->getSize() - 1) - 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;
|
||||
@@ -609,15 +776,16 @@ R"(
|
||||
if (ImGui::BeginTabItem("End")) {
|
||||
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;
|
||||
|
||||
newOffset = (this->m_dataProvider->getSize() - 1) - this->m_gotoAddress;
|
||||
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;
|
||||
|
||||
@@ -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) {
|
||||
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
|
||||
: View("Information"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*) {
|
||||
this->m_shouldInvalidate = true;
|
||||
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 };
|
||||
});
|
||||
}
|
||||
|
||||
@@ -45,15 +47,14 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewInformation::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Data Information", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Data Information", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
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 +96,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 +107,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 +119,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 of %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!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,10 +189,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewInformation::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Data Information View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "lang/evaluator.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <magic.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
static const TextEditor::LanguageDefinition& PatternLanguage() {
|
||||
@@ -46,6 +48,8 @@ namespace hex {
|
||||
paletteIndex = TextEditor::PaletteIndex::Identifier;
|
||||
else if (TokenizeCStyleNumber(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::Number;
|
||||
else if (TokenizeCStyleCharacterLiteral(inBegin, inEnd, outBegin, outEnd))
|
||||
paletteIndex = TextEditor::PaletteIndex::CharLiteral;
|
||||
|
||||
return paletteIndex != TextEditor::PaletteIndex::Max;
|
||||
};
|
||||
@@ -67,10 +71,72 @@ namespace hex {
|
||||
|
||||
|
||||
ViewPattern::ViewPattern(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
: View("Pattern"), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->m_textEditor.SetLanguageDefinition(PatternLanguage());
|
||||
this->m_textEditor.SetShowWhitespaces(false);
|
||||
|
||||
View::subscribeEvent(Events::FileLoaded, [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(0xFFFF)), 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.addDefaultPragmaHandlers();
|
||||
|
||||
|
||||
std::error_code errorCode;
|
||||
for (auto &entry : std::filesystem::directory_iterator("patterns", errorCode)) {
|
||||
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() {
|
||||
@@ -84,18 +150,10 @@ namespace hex {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Pattern View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewPattern::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Pattern", &this->m_windowOpen, ImGuiWindowFlags_None)) {
|
||||
if (ImGui::Begin("Pattern", &this->getWindowOpenState(), ImGuiWindowFlags_None | ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isAvailable()) {
|
||||
this->m_textEditor.Render("Pattern");
|
||||
|
||||
@@ -107,32 +165,54 @@ namespace hex {
|
||||
ImGui::End();
|
||||
|
||||
if (this->m_fileBrowser.showFileDialog("Open Hex Pattern", imgui_addons::ImGuiFileBrowser::DialogMode::OPEN, ImVec2(0, 0), ".hexpat")) {
|
||||
this->loadPatternFile(this->m_fileBrowser.selected_path);
|
||||
}
|
||||
|
||||
FILE *file = fopen(this->m_fileBrowser.selected_path.c_str(), "rb");
|
||||
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();
|
||||
|
||||
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;
|
||||
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::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() {
|
||||
for (auto &data : this->m_patternData)
|
||||
delete data;
|
||||
@@ -153,38 +233,61 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewPattern::parsePattern(char *buffer) {
|
||||
hex::lang::Preprocessor preprocessor;
|
||||
hex::lang::Lexer lexer;
|
||||
hex::lang::Parser parser;
|
||||
hex::lang::Validator validator;
|
||||
hex::lang::Evaluator evaluator;
|
||||
|
||||
this->clearPatternData();
|
||||
this->m_textEditor.SetErrorMarkers({ });
|
||||
this->postEvent(Events::PatternChanged);
|
||||
|
||||
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
||||
if (preprocessingResult.failed())
|
||||
return;
|
||||
hex::lang::Preprocessor preprocessor;
|
||||
std::endian dataEndianess = std::endian::native;
|
||||
|
||||
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
||||
if (lexResult.failed()) {
|
||||
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.addDefaultPragmaHandlers();
|
||||
|
||||
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
|
||||
if (preprocessingResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ preprocessor.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Lexer lexer;
|
||||
auto [lexResult, tokens] = lexer.lex(preprocesedCode);
|
||||
if (lexResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ lexer.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Parser parser;
|
||||
auto [parseResult, ast] = parser.parse(tokens);
|
||||
if (parseResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ parser.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::ScopeExit deleteAst([&ast]{ for(auto &node : ast) delete node; });
|
||||
|
||||
hex::lang::Validator validator;
|
||||
auto validatorResult = validator.validate(ast);
|
||||
if (!validatorResult) {
|
||||
this->m_textEditor.SetErrorMarkers({ validator.getError() });
|
||||
return;
|
||||
}
|
||||
|
||||
hex::lang::Evaluator evaluator(this->m_dataProvider, dataEndianess);
|
||||
auto [evaluateResult, patternData] = evaluator.evaluate(ast);
|
||||
if (evaluateResult.failed()) {
|
||||
this->m_textEditor.SetErrorMarkers({ evaluator.getError() });
|
||||
return;
|
||||
}
|
||||
this->m_patternData = patternData;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace hex {
|
||||
|
||||
ViewPatternData::ViewPatternData(prv::Provider* &dataProvider, std::vector<lang::PatternData*> &patternData)
|
||||
: View(), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
: View("Pattern Data"), m_dataProvider(dataProvider), m_patternData(patternData) {
|
||||
|
||||
this->subscribeEvent(Events::PatternChanged, [this](auto data) {
|
||||
this->m_sortedPatternData.clear();
|
||||
@@ -18,9 +18,10 @@ namespace hex {
|
||||
}
|
||||
|
||||
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)) {
|
||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
||||
if (ImGui::BeginTable("##patterndatatable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Name", 0, -1, ImGui::GetID("name"));
|
||||
ImGui::TableSetupColumn("Color", 0, -1, ImGui::GetID("color"));
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("Type", 0, -1, ImGui::GetID("type"));
|
||||
@@ -48,25 +49,15 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewPatternData::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Pattern Data", &this->m_windowOpen)) {
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
if (ImGui::Begin("Pattern Data", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
|
||||
if (beginPatternDataTable(this->m_dataProvider, this->m_patternData, this->m_sortedPatternData)) {
|
||||
if (this->m_sortedPatternData.size() > 0) {
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
u32 rowCount = 0;
|
||||
for (auto &patternData : this->m_sortedPatternData) {
|
||||
for (auto &patternData : this->m_sortedPatternData)
|
||||
patternData->createEntry(this->m_dataProvider);
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
|
||||
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,17 +65,12 @@ namespace hex {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewPatternData::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Data View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
#include "views/view_strings.hpp"
|
||||
|
||||
#include "providers/provider.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
|
||||
ViewStrings::ViewStrings(prv::Provider* &dataProvider) : View("Strings"), m_dataProvider(dataProvider) {
|
||||
View::subscribeEvent(Events::DataChanged, [this](const void*){
|
||||
this->m_shouldInvalidate = true;
|
||||
this->m_foundStrings.clear();
|
||||
});
|
||||
|
||||
this->m_filter = new char[0xFFFF];
|
||||
@@ -20,22 +25,40 @@ namespace hex {
|
||||
delete[] this->m_filter;
|
||||
}
|
||||
|
||||
void ViewStrings::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
void ViewStrings::createStringContextMenu(const FoundString &foundString) {
|
||||
if (ImGui::TableGetHoveredColumn() == 2 && ImGui::IsMouseReleased(1) && ImGui::IsItemHovered()) {
|
||||
ImGui::OpenPopup("StringContextMenu");
|
||||
this->m_selectedString = foundString.string;
|
||||
}
|
||||
if (ImGui::BeginPopup("StringContextMenu")) {
|
||||
if (ImGui::MenuItem("Copy string")) {
|
||||
ImGui::SetClipboardText(this->m_selectedString.c_str());
|
||||
}
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Demangle")) {
|
||||
this->m_demangledName = llvm::demangle(this->m_selectedString);
|
||||
if (!this->m_demangledName.empty())
|
||||
View::doLater([]{ ImGui::OpenPopup("Demangled Name"); });
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ViewStrings::createView() {
|
||||
if (this->m_shouldInvalidate) {
|
||||
this->m_shouldInvalidate = false;
|
||||
|
||||
this->m_foundStrings.clear();
|
||||
|
||||
|
||||
std::vector<u8> buffer(1024, 0x00);
|
||||
u32 foundCharacters = 0;
|
||||
for (u64 offset = 0; offset < this->m_dataProvider->getSize(); offset += buffer.size()) {
|
||||
this->m_dataProvider->read(offset, buffer.data(), buffer.size());
|
||||
size_t readSize = std::min(buffer.size(), this->m_dataProvider->getSize() - offset);
|
||||
this->m_dataProvider->read(offset, buffer.data(), readSize);
|
||||
|
||||
for (u32 i = 0; i < buffer.size(); i++) {
|
||||
for (u32 i = 0; i < readSize; i++) {
|
||||
if (buffer[i] >= 0x20 && buffer[i] <= 0x7E)
|
||||
foundCharacters++;
|
||||
else {
|
||||
@@ -58,21 +81,22 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
if (ImGui::Begin("Strings", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Strings", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
if (this->m_dataProvider != nullptr && this->m_dataProvider->isReadable()) {
|
||||
if (ImGui::InputInt("Minimum length", &this->m_minimumLength, 1, 0))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::InputText("Filter", this->m_filter, 0xFFFF);
|
||||
if (ImGui::Button("Extract"))
|
||||
this->m_shouldInvalidate = true;
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::BeginChild("##scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
|
||||
|
||||
if (ImGui::BeginTable("##strings", 3,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable |
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg)) {
|
||||
ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("Offset", 0, -1, ImGui::GetID("offset"));
|
||||
ImGui::TableSetupColumn("Size", 0, -1, ImGui::GetID("size"));
|
||||
ImGui::TableSetupColumn("String", 0, -1, ImGui::GetID("string"));
|
||||
@@ -111,7 +135,6 @@ namespace hex {
|
||||
clipper.Begin(this->m_foundStrings.size());
|
||||
|
||||
while (clipper.Step()) {
|
||||
u32 rowCount = clipper.DisplayStart;
|
||||
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
|
||||
auto &foundString = this->m_foundStrings[i];
|
||||
|
||||
@@ -119,34 +142,47 @@ namespace hex {
|
||||
foundString.string.find(this->m_filter) == std::string::npos)
|
||||
continue;
|
||||
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##StringLine"s + std::to_string(i)).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
Region selectRegion = { foundString.offset, foundString.size };
|
||||
View::postEvent(Events::SelectionChangeRequest, &selectRegion);
|
||||
}
|
||||
ImGui::PushID(i + 1);
|
||||
createStringContextMenu(foundString);
|
||||
ImGui::PopID();
|
||||
ImGui::SameLine();
|
||||
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());
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0,
|
||||
((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
rowCount++;
|
||||
}
|
||||
}
|
||||
clipper.End();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void ViewStrings::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Strings View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
if (ImGui::BeginPopup("Demangled Name")) {
|
||||
if (ImGui::BeginChild("##scrolling", ImVec2(500, 150))) {
|
||||
ImGui::Text("Demangled Name");
|
||||
ImGui::Separator();
|
||||
ImGui::TextWrapped("%s", this->m_demangledName.c_str());
|
||||
ImGui::EndChild();
|
||||
ImGui::NewLine();
|
||||
if (ImGui::Button("Copy"))
|
||||
ImGui::SetClipboardText(this->m_demangledName.c_str());
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewStrings::createMenu() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
#include "views/view_tools.hpp"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
|
||||
#include "utils.hpp"
|
||||
|
||||
#include <llvm/Demangle/Demangle.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
ViewTools::ViewTools() {
|
||||
ViewTools::ViewTools() : View("Tools") {
|
||||
this->m_mangledBuffer = new char[0xF'FFFF];
|
||||
this->m_demangledName = static_cast<char*>(malloc(8));
|
||||
|
||||
std::memset(this->m_mangledBuffer, 0x00, 0xF'FFFF);
|
||||
strcpy(this->m_demangledName, "< ??? >");
|
||||
|
||||
this->m_regexInput = new char[0xF'FFFF];
|
||||
this->m_regexPattern = new char[0xF'FFFF];
|
||||
@@ -23,70 +24,19 @@ namespace hex {
|
||||
|
||||
ViewTools::~ViewTools() {
|
||||
delete[] this->m_mangledBuffer;
|
||||
free(this->m_demangledName);
|
||||
|
||||
delete[] this->m_regexInput;
|
||||
delete[] this->m_regexPattern;
|
||||
}
|
||||
|
||||
static std::string toASCIITableString(char c) {
|
||||
switch (c) {
|
||||
case 0: return "NUL";
|
||||
case 1: return "SOH";
|
||||
case 2: return "STX";
|
||||
case 3: return "ETX";
|
||||
case 4: return "EOT";
|
||||
case 5: return "ENQ";
|
||||
case 6: return "ACK";
|
||||
case 7: return "BEL";
|
||||
case 8: return "BS";
|
||||
case 9: return "TAB";
|
||||
case 10: return "LF";
|
||||
case 11: return "VT";
|
||||
case 12: return "FF";
|
||||
case 13: return "CR";
|
||||
case 14: return "SO";
|
||||
case 15: return "SI";
|
||||
case 16: return "DLE";
|
||||
case 17: return "DC1";
|
||||
case 18: return "DC2";
|
||||
case 19: return "DC3";
|
||||
case 20: return "DC4";
|
||||
case 21: return "NAK";
|
||||
case 22: return "SYN";
|
||||
case 23: return "ETB";
|
||||
case 24: return "CAN";
|
||||
case 25: return "EM";
|
||||
case 26: return "SUB";
|
||||
case 27: return "ESC";
|
||||
case 28: return "FS";
|
||||
case 29: return "GS";
|
||||
case 30: return "RS";
|
||||
case 31: return "US";
|
||||
case 32: return "Space";
|
||||
case 127: return "DEL";
|
||||
default: return std::string() + c;
|
||||
}
|
||||
delete[] this->m_replacePattern;
|
||||
}
|
||||
|
||||
void ViewTools::drawDemangler() {
|
||||
if (ImGui::CollapsingHeader("Itanium demangler")) {
|
||||
if (ImGui::CollapsingHeader("Itanium/MSVC demangler")) {
|
||||
if (ImGui::InputText("Mangled name", this->m_mangledBuffer, 0xF'FFFF)) {
|
||||
size_t length = 0;
|
||||
int status = 0;
|
||||
|
||||
if (this->m_demangledName != nullptr)
|
||||
free(this->m_demangledName);
|
||||
|
||||
this->m_demangledName = abi::__cxa_demangle(this->m_mangledBuffer, nullptr, &length, &status);
|
||||
|
||||
if (status != 0) {
|
||||
this->m_demangledName = static_cast<char*>(malloc(8));
|
||||
strcpy(this->m_demangledName, "< ??? >");
|
||||
}
|
||||
this->m_demangledName = llvm::demangle(this->m_mangledBuffer);
|
||||
}
|
||||
|
||||
ImGui::InputText("Demangled name", this->m_demangledName, strlen(this->m_demangledName), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::InputText("Demangled name", this->m_demangledName.data(), this->m_demangledName.size(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::NewLine();
|
||||
}
|
||||
}
|
||||
@@ -127,7 +77,7 @@ namespace hex {
|
||||
ImGui::Text("0x%02x", i + 32 * tablePart);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", toASCIITableString(i + 32 * tablePart).c_str());
|
||||
ImGui::Text("%s", makePrintable(i + 32 * tablePart).c_str());
|
||||
|
||||
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ((rowCount % 2) == 0) ? 0xFF101010 : 0xFF303030);
|
||||
|
||||
@@ -167,6 +117,7 @@ namespace hex {
|
||||
|
||||
void ViewTools::drawColorPicker() {
|
||||
if (ImGui::CollapsingHeader("Color picker")) {
|
||||
ImGui::SetNextItemWidth(300.0F);
|
||||
ImGui::ColorPicker4("Color Picker", this->m_pickedColor.data(),
|
||||
ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHex);
|
||||
ImGui::NewLine();
|
||||
@@ -174,10 +125,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewTools::createView() {
|
||||
if (!this->m_windowOpen)
|
||||
return;
|
||||
|
||||
if (ImGui::Begin("Tools", &this->m_windowOpen)) {
|
||||
if (ImGui::Begin("Tools", &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
|
||||
|
||||
this->drawDemangler();
|
||||
this->drawASCIITable();
|
||||
@@ -189,10 +137,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ViewTools::createMenu() {
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Tools View", "", &this->m_windowOpen);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,42 +12,51 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace {
|
||||
constexpr auto MenuBarItems = { "File", "Edit", "View", "Help" };
|
||||
|
||||
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
|
||||
return ctx; // Unused, but the return value has to be non-null
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
float scale;
|
||||
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = scale; }
|
||||
else if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
auto &style = ImGui::GetStyle();
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
if (window->m_globalScale != 0.0f)
|
||||
style.ScaleAllSizes(window->m_globalScale);
|
||||
if (window->m_fontScale != 0.0f)
|
||||
io.FontGlobalScale = window->m_fontScale;
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
buf->reserve(buf->size() + 0x20); // Ballpark reserve
|
||||
|
||||
buf->appendf("[%s][General]\n", handler->TypeName);
|
||||
buf->appendf("Scale=%.1f\n", window->m_globalScale);
|
||||
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
|
||||
buf->append("\n");
|
||||
void *ImHexSettingsHandler_ReadOpenFn(ImGuiContext *ctx, ImGuiSettingsHandler *, const char *) {
|
||||
return ctx; // Unused, but the return value has to be non-null
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler *handler, void *, const char* line) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
float scale;
|
||||
if (sscanf(line, "Scale=%f", &scale) == 1) { window->m_globalScale = scale; }
|
||||
else if (sscanf(line, "FontScale=%f", &scale) == 1) { window->m_fontScale = scale; }
|
||||
else {
|
||||
for (auto &view : window->m_views) {
|
||||
std::string format = view->getName() + "=%d";
|
||||
sscanf(line, format.c_str(), &view->getWindowOpenState());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_ApplyAll(ImGuiContext *ctx, ImGuiSettingsHandler *handler) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
auto &style = ImGui::GetStyle();
|
||||
auto &io = ImGui::GetIO();
|
||||
|
||||
if (window->m_globalScale != 0.0f)
|
||||
style.ScaleAllSizes(window->m_globalScale);
|
||||
if (window->m_fontScale != 0.0f)
|
||||
io.FontGlobalScale = window->m_fontScale;
|
||||
}
|
||||
|
||||
void ImHexSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler *handler, ImGuiTextBuffer *buf) {
|
||||
auto *window = reinterpret_cast<Window *>(handler->UserData);
|
||||
|
||||
buf->reserve(buf->size() + 0x20); // Ballpark reserve
|
||||
|
||||
buf->appendf("[%s][General]\n", handler->TypeName);
|
||||
buf->appendf("Scale=%.1f\n", window->m_globalScale);
|
||||
buf->appendf("FontScale=%.1f\n", window->m_fontScale);
|
||||
|
||||
for (auto &view : window->m_views) {
|
||||
buf->appendf("%s=%d\n", view->getName().c_str(), view->getWindowOpenState());
|
||||
}
|
||||
|
||||
buf->append("\n");
|
||||
}
|
||||
|
||||
Window::Window() {
|
||||
@@ -72,9 +81,20 @@ namespace hex {
|
||||
View::getDeferedCalls().clear();
|
||||
|
||||
for (auto &view : this->m_views) {
|
||||
if (!view->getWindowOpenState())
|
||||
continue;
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(480, 720), ImVec2(FLT_MAX, FLT_MAX));
|
||||
view->createView();
|
||||
}
|
||||
|
||||
View::drawCommonInterfaces();
|
||||
|
||||
#ifdef DEBUG
|
||||
if (this->m_demoWindowOpen)
|
||||
ImGui::ShowDemoWindow(&this->m_demoWindowOpen);
|
||||
#endif
|
||||
|
||||
this->frameEnd();
|
||||
}
|
||||
}
|
||||
@@ -97,54 +117,74 @@ namespace hex {
|
||||
windowFlags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
|
||||
windowFlags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
|
||||
|
||||
ImGui::Begin("DockSpace", nullptr, windowFlags);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
if (ImGui::Begin("DockSpace", nullptr, windowFlags)) {
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::DockSpace(ImGui::GetID("MainDock"), ImVec2(0.0f, 0.0f), ImGuiDockNodeFlags_None);
|
||||
|
||||
ImGui::BeginMenuBar();
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
|
||||
for (auto &view : this->m_views)
|
||||
view->createMenu();
|
||||
for (auto menu : MenuBarItems)
|
||||
if (ImGui::BeginMenu(menu)) ImGui::EndMenu();
|
||||
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
for (auto &view : this->m_views)
|
||||
ImGui::MenuItem((view->getName() + " View").c_str(), "", &view->getWindowOpenState());
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (this->m_fpsVisible) {
|
||||
char buffer[0x20];
|
||||
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
|
||||
for (auto &view : this->m_views) {
|
||||
view->createMenu();
|
||||
}
|
||||
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
|
||||
ImGui::TextUnformatted(buffer);
|
||||
}
|
||||
if (ImGui::BeginMenu("View")) {
|
||||
ImGui::Separator();
|
||||
ImGui::MenuItem("Display FPS", "", &this->m_fpsVisible);
|
||||
#ifdef DEBUG
|
||||
ImGui::MenuItem("Demo View", "", &this->m_demoWindowOpen);
|
||||
#endif
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (this->m_fpsVisible) {
|
||||
char buffer[0x20];
|
||||
snprintf(buffer, 0x20, "%.1f FPS", ImGui::GetIO().Framerate);
|
||||
|
||||
ImGui::EndMenuBar();
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - ImGui::GetFontSize() * strlen(buffer) + 20);
|
||||
ImGui::TextUnformatted(buffer);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
|
||||
for (auto &view : this->m_views) {
|
||||
if (view->handleShortcut(key, mods))
|
||||
break;
|
||||
ImGui::EndMenuBar();
|
||||
}
|
||||
|
||||
if (auto &[key, mods] = Window::s_currShortcut; key != -1) {
|
||||
for (auto &view : this->m_views) {
|
||||
if (view->handleShortcut(key, mods))
|
||||
break;
|
||||
}
|
||||
|
||||
Window::s_currShortcut = { -1, -1 };
|
||||
}
|
||||
|
||||
Window::s_currShortcut = { -1, -1 };
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
int displayWidth, displayHeight;
|
||||
glfwGetFramebufferSize(this->m_window, &displayWidth, &displayHeight);
|
||||
glViewport(0, 0, displayWidth, displayHeight);
|
||||
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
|
||||
GLFWwindow* backup_current_context = glfwGetCurrentContext();
|
||||
ImGui::UpdatePlatformWindows();
|
||||
ImGui::RenderPlatformWindowsDefault();
|
||||
glfwMakeContextCurrent(backup_current_context);
|
||||
|
||||
glfwSwapBuffers(this->m_window);
|
||||
}
|
||||
|
||||
@@ -156,9 +196,9 @@ namespace hex {
|
||||
if (!glfwInit())
|
||||
throw std::runtime_error("Failed to initialize GLFW!");
|
||||
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
#endif
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||
@@ -166,6 +206,7 @@ namespace hex {
|
||||
|
||||
this->m_window = glfwCreateWindow(1280, 720, "ImHex", nullptr, nullptr);
|
||||
|
||||
|
||||
if (this->m_window == nullptr)
|
||||
throw std::runtime_error("Failed to create window!");
|
||||
|
||||
@@ -194,7 +235,13 @@ namespace hex {
|
||||
IMGUI_CHECKVERSION();
|
||||
auto *ctx = ImGui::CreateContext();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable | ImGuiConfigFlags_ViewportsEnable;
|
||||
io.ConfigViewportsNoTaskBarIcon = true;
|
||||
|
||||
style.WindowMenuButtonPosition = ImGuiDir_None;
|
||||
style.IndentSpacing = 10.0F;
|
||||
|
||||
// Install custom settings handler
|
||||
ImGuiSettingsHandler handler;
|
||||
|
||||
Reference in New Issue
Block a user