Compare commits

...

52 Commits

Author SHA1 Message Date
WerWolv
1395c95831 Same treatment for the disassembly window 2020-11-28 16:15:12 +01:00
WerWolv
33b70a550f Improved look and feel of hash window. Added "Match selection" feature 2020-11-28 15:53:11 +01:00
WerWolv
985e924e9d Added simple bookmarks / comments feature. No saving yet though 2020-11-28 00:33:26 +01:00
WerWolv
3827919a32 Added error messages and error display to pattern language editor 2020-11-27 21:20:23 +01:00
WerWolv
d55bea7c46 Added character literals to pattern language 2020-11-27 14:18:28 +01:00
WerWolv
2efe326fdb Added Save, Save As and implemented error message for Base64 importing 2020-11-27 13:45:56 +01:00
WerWolv
d43bd23e1a Added default error message window 2020-11-27 13:45:27 +01:00
WerWolv
015ec12215 Improved byte write speed by a lot 2020-11-27 13:44:52 +01:00
WerWolv
fde9dc7961 Various small fixes 2020-11-27 09:09:59 +01:00
WerWolv
ed572ececf Added patching system and IPS/IPS32 patch exporting 2020-11-27 09:09:48 +01:00
WerWolv
fd2b79bea9 Removed cxxabi.h include 2020-11-24 18:12:48 +01:00
WerWolv
acc10930c2 Added MSVC symbol demangling, switched to LLVM libs for demangling 2020-11-24 18:12:08 +01:00
WerWolv
f17d6c2359 Added copy string and copy demangled string to strings window 2020-11-24 02:59:49 +01:00
WerWolv
e21211f3f6 Added back default debug/release specific flags 2020-11-24 02:00:48 +01:00
WerWolv
58deaa6b29 Added importing from base64 encoded file 2020-11-24 02:00:22 +01:00
WerWolv
fcd88b4b3b Shrink the color picker to a bit more reasonable size 2020-11-24 00:01:44 +01:00
WerWolv
3bd987ff2c Streamline view creation, save all view states when quitting 2020-11-23 23:57:19 +01:00
WerWolv
45bcdc8c46 Added toggle for ImGui demo window in Debug mode 2020-11-23 22:23:06 +01:00
WerWolv
0d0b2d6962 Indent variable name instead of color in pattern data view 2020-11-23 22:14:11 +01:00
WerWolv
fd6a41d219 Make selecting memory by clicking on a table item update the inspector 2020-11-23 16:26:01 +01:00
WerWolv
b052429a73 Improved ASCII and Wide char display in data inspector 2020-11-23 16:19:58 +01:00
WerWolv
1996f401e0 Fixed last block searched for strings to yield invalid results 2020-11-23 16:19:31 +01:00
WerWolv
3b3f2226f1 Remove collapse button from all windows 2020-11-23 15:51:58 +01:00
WerWolv
5c0f6a1e50 Enabled ImGui's viewports support 2020-11-23 15:51:40 +01:00
WerWolv
b7438f6ab8 Massively improved look and feel of pattern data, string and disassembly tables 2020-11-23 15:22:26 +01:00
WerWolv
84f80b3e06 Select region when clicking on string, disassembly or pattern data item 2020-11-23 13:10:14 +01:00
WerWolv
c281372b8d Improved table coloring 2020-11-23 13:08:24 +01:00
WerWolv
788a3cfc5e Merge pull request #3 from misson20000/bugfixes
a few bugfixes
2020-11-23 08:25:15 +01:00
misson20000
01eeb53af0 Fix out-of-bounds vector write on copy hex string 2020-11-22 18:00:46 -08:00
misson20000
7cf69128ea Actually fix time_t decoding crash on Linux 2020-11-22 18:00:17 -08:00
WerWolv
c977baecc4 Fixed crash when snprintf failed in formatter 2020-11-23 00:35:26 +01:00
WerWolv
2ab2f5e675 Improved block size text 2020-11-23 00:34:53 +01:00
WerWolv
6a38f1e9f3 Truncate file size to 2 digits after comma 2020-11-23 00:22:51 +01:00
WerWolv
f0eba69c4a Fixed time_t decoding crash on Linux 2020-11-23 00:20:29 +01:00
WerWolv
cea366e135 Fixed crash when scrolling disassembler options child off screen 2020-11-23 00:12:53 +01:00
WerWolv
d752c7434f Fixed crash when no patterns folder exists 2020-11-23 00:12:33 +01:00
WerWolv
4402120ffc Added the capstone disassembler and a disassembler window 2020-11-22 23:07:50 +01:00
WerWolv
b3fffdf530 Fixed automatic pattern loading 2020-11-22 23:06:17 +01:00
WerWolv
8d6d959e17 No need to manually set table colors 2020-11-22 20:41:54 +01:00
WerWolv
43f5cc622e Allow loading of huge files efficiently 2020-11-22 19:43:35 +01:00
WerWolv
5f025bcbcc Fixed UTF-8 decoding to not work 2020-11-22 16:22:23 +01:00
WerWolv
8297e22f10 Added global big and little endian support to the pattern parser 2020-11-22 16:22:02 +01:00
WerWolv
989eade5d7 Added big and little endian support to inspector 2020-11-22 15:32:37 +01:00
WerWolv
cd4de2ff96 Fixed data inspector to only show unsigned values 2020-11-22 12:50:49 +01:00
WerWolv
73d66365e9 Updated cheat sheet page 2020-11-22 11:20:01 +01:00
WerWolv
fbd4e593d2 Make array and pointer pattern data display more consistent with other types 2020-11-22 02:25:25 +01:00
WerWolv
033ef3889a Fixed enums failing to validate 2020-11-22 02:25:03 +01:00
WerWolv
0ce1b5d40b Added simple pointer type inside structs 2020-11-21 23:00:09 +01:00
WerWolv
ed4ed6b433 Added array sizes based on other local variables 2020-11-21 20:19:33 +01:00
WerWolv
4cd18b8358 Added auto loading patterns based on MIME types 2020-11-21 14:39:16 +01:00
WerWolv
fb85f272a1 Added pragmas to pattern language 2020-11-21 14:39:01 +01:00
WerWolv
28bb28b79c Also rename data inspector window 2020-11-21 14:37:09 +01:00
51 changed files with 2511 additions and 805 deletions

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -6,10 +6,15 @@
namespace hex {
enum class Events {
FileLoaded,
DataChanged,
PatternChanged,
FileDropped,
ByteSelected
RegionSelected,
SelectionChangeRequest,
AddBookmark
};
struct EventHandler {

View File

@@ -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

View File

@@ -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; };

View File

@@ -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);

View File

@@ -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;
};
}

View File

@@ -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);
};
}

View File

@@ -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)

View File

@@ -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;
};
}

View File

@@ -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;
};
}

View File

@@ -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
View 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);
}

View File

@@ -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;

View File

@@ -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;
};
}

View File

@@ -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;
};
}

View File

@@ -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;
};
}

View 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;
};
}

View File

@@ -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;

View 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;
};
}

View File

@@ -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" };
};

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);
};

View File

@@ -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;
};
}

View File

@@ -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);
};
}

View File

@@ -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;

View File

@@ -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 };
};

View File

@@ -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)
{

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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 };
}

View File

@@ -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) {

View File

@@ -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";
});
}
}

View File

@@ -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;
}
}

View File

@@ -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
View 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;
}
}

View File

@@ -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));

View File

@@ -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;
}
}
}

View 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, &region);
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() {
}
}

View File

@@ -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();
}
}
}

View 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() {
}
}

View File

@@ -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();
}
}
}

View File

@@ -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");

View File

@@ -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 &region = *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, &region);
});
}
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;

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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();
}
}
}

View File

@@ -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() {
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;