Compare commits

..

30 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
50 changed files with 1551 additions and 639 deletions

View File

@@ -6,21 +6,30 @@ 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} ${CAPSTONE_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
@@ -28,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
@@ -40,6 +49,7 @@ add_executable(ImHex
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
@@ -56,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 libcrypto.a libwinpthread.a libcapstone.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 libcapstone.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

@@ -20,19 +20,21 @@ 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, std::optional<std::string> arraySizeVariable = { }, std::optional<u8> pointerSize = { })
: ASTNode(Type::VariableDecl), m_type(type), m_name(name), m_customTypeName(customTypeName), m_offset(offset), m_arraySize(arraySize), m_arraySizeVariable(arraySizeVariable), m_pointerSize(pointerSize) { }
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; }
@@ -53,7 +55,7 @@ namespace hex::lang {
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:
@@ -62,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; }
@@ -74,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; }
@@ -86,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; }
@@ -98,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; };
@@ -112,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

@@ -19,11 +19,14 @@ namespace hex::lang {
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) {
@@ -127,17 +129,22 @@ namespace hex::lang {
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());
}
@@ -181,17 +188,17 @@ namespace hex::lang {
provider->read(this->getOffset(), &data, this->getSize());
data = hex::changeEndianess(data, this->getSize(), PatternData::s_endianess);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip);
ImGui::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->getTypeName().c_str());
ImGui::TableNextColumn();
ImGui::Text("*(0x%0*llx)", this->getSize() * 2, data);
@@ -340,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 | ImGuiColorEditFlags_AlphaPreview);
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", "{ ... }");
@@ -385,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", "{ ... }");
@@ -444,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", "{ ... }");
@@ -521,7 +535,25 @@ namespace hex::lang {
if (!foundValue)
valueString += "???";
this->createDefaultEntry(hex::format("%s (0x%0*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 {
@@ -543,17 +575,16 @@ namespace hex::lang {
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);
@@ -563,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

@@ -19,13 +19,17 @@ namespace hex::lang {
std::pair<Result, std::string> preprocess(const std::string& code, bool initialRun = true);
void addPragmaHandler(std::string pragmaType, std::function<bool(std::string)> function);
void addDefaultPragramHandlers();
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

@@ -63,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,7 +18,10 @@ 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;
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

@@ -3,6 +3,9 @@
#include <hex.hpp>
#include <cmath>
#include <concepts>
#include <map>
#include <optional>
#include <string>
#include <vector>
@@ -12,17 +15,29 @@ namespace hex::prv {
public:
constexpr static size_t PageSize = 0x1000'0000;
Provider() = default;
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 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; }
@@ -35,10 +50,21 @@ namespace hex::prv {
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

@@ -11,6 +11,7 @@
#ifdef __MINGW32__
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
@@ -32,59 +33,34 @@ namespace hex {
return std::string(buffer.data(), buffer.data() + size);
}
[[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);
}
constexpr inline bool isUnsigned(const lang::Token::TypeToken::Type type) {
[[nodiscard]] 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) {
[[nodiscard]] 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) {
[[nodiscard]] 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) {
[[nodiscard]] 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 = 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;
}
[[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;
}
std::string toByteString(u64 bytes);
std::string makePrintable(char c);
template<typename T>
struct always_false : std::false_type {};
@@ -123,7 +99,6 @@ namespace hex {
throw std::invalid_argument("Invalid value size!");
}
class ScopeExit {
public:
ScopeExit(std::function<void()> func) : m_func(func) {}
@@ -136,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

@@ -51,7 +51,6 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = true;
std::endian m_endianess = std::endian::native;

View File

@@ -15,8 +15,10 @@ namespace hex {
struct Disassembly {
u64 address;
u64 offset;
size_t size;
std::string bytes;
std::string opcodeString;
std::string mnemonic;
std::string operators;
};
class ViewDisassembler : public View {
@@ -29,13 +31,11 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_shouldInvalidate = false;
u64 m_baseAddress = 0;
u64 m_codeOffset = 0;
u64 m_codeSize = 0;
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);

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,7 +21,6 @@ namespace hex {
private:
prv::Provider* &m_dataProvider;
bool m_windowOpen = true;
bool m_dataValid = false;
u32 m_blockSize = 0;

View File

@@ -28,7 +28,6 @@ namespace hex {
std::vector<lang::PatternData*> &m_patternData;
prv::Provider* &m_dataProvider;
std::filesystem::path m_possiblePatternFile;
bool m_windowOpen = true;
TextEditor m_textEditor;
imgui_addons::ImGuiFileBrowser m_fileBrowser;

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

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

@@ -14,8 +14,10 @@ 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()) {
@@ -62,10 +64,12 @@ namespace hex::lang {
}
}
if (!arraySize.has_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 };
}
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
}
@@ -97,8 +101,10 @@ 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()) {
@@ -148,10 +154,12 @@ namespace hex::lang {
}
}
if (!arraySize.has_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 };
}
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
ASTNodeVariableDecl *processedMember = new ASTNodeVariableDecl(member->getLineNumber(), member->getVariableType(), member->getVariableName(), member->getCustomVariableTypeName(), member->getOffset(), arraySize.value());
std::tie(pattern, memberSize) = this->createArrayPattern(processedMember, memberOffset);
}
@@ -179,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());
@@ -195,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())
@@ -215,7 +225,7 @@ namespace hex::lang {
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) {
@@ -251,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) {
@@ -263,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:
@@ -286,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,85 +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 } });
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)) {
@@ -187,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

@@ -10,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) {
@@ -29,64 +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* parseBuiltinPointerVariableDecl(TokenIter &curr) {
ASTNode* Parser::parseBuiltinPointerVariableDecl(TokenIter &curr) {
auto pointerType = curr[-2].typeToken.type;
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
if (!isUnsigned(pointerType)) {
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
return nullptr;
}
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-4].identifierToken.identifier, "", { }, 1, { }, getTypeSize(pointerType));
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* parseCustomTypePointerVariableDecl(TokenIter &curr) {
ASTNode* Parser::parseCustomTypePointerVariableDecl(TokenIter &curr) {
auto pointerType = curr[-2].typeToken.type;
if (!isUnsigned(pointerType) || curr[-5].operatorToken.op != Token::OperatorToken::Operator::Star || curr[-3].operatorToken.op != Token::OperatorToken::Operator::Inherit)
if (!isUnsigned(pointerType)) {
this->m_error = { curr->lineNumber, "Pointer size needs to be a unsigned type" };
return nullptr;
}
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-4].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 1, { }, getTypeSize(pointerType));
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* parseBuiltinArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].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* parseCustomTypeArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, curr[-3].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* parseBuiltinVariableArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-6].typeToken.type, curr[-5].identifierToken.identifier, "", { }, 0, curr[-3].identifierToken.identifier);
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* parseCustomTypeVariableArrayDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(Token::TypeToken::Type::CustomType, curr[-5].identifierToken.identifier, curr[-6].identifierToken.identifier, { }, 0, curr[-3].identifierToken.identifier);
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* parsePaddingDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
ASTNode* Parser::parsePaddingDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, "", "", { }, curr[-3].integerToken.integer);
}
ASTNode* parseFreeBuiltinVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].integerToken.integer);
ASTNode* Parser::parseFreeBuiltinVariableDecl(TokenIter &curr) {
return new ASTNodeVariableDecl(curr[-5].lineNumber, curr[-5].typeToken.type, curr[-4].identifierToken.identifier, "", curr[-2].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::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* parseStruct(TokenIter &curr) {
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));
@@ -103,6 +125,8 @@ namespace hex::lang {
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));
@@ -110,21 +134,28 @@ namespace hex::lang {
nodes.push_back(parseBuiltinPointerVariableDecl(curr));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr));
else break;
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})) {
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));
@@ -138,28 +169,39 @@ namespace hex::lang {
nodes.push_back(parseBuiltinPointerVariableDecl(curr));
else if (tryConsume(curr, {Token::Type::Identifier, Token::Type::Operator, Token::Type::Identifier, Token::Type::Operator, Token::Type::Type, Token::Type::EndOfExpression}))
nodes.push_back(parseCustomTypePointerVariableDecl(curr));
else break;
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 })) {
@@ -183,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
@@ -324,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);
@@ -342,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

@@ -8,6 +8,7 @@ namespace hex::lang {
std::pair<Result, std::string> Preprocessor::preprocess(const std::string& code, bool initialRun) {
u32 offset = 0;
u32 lineNumber = 1;
if (initialRun) {
this->m_defines.clear();
@@ -136,6 +137,9 @@ namespace hex::lang {
return { ResultPreprocessingError, "" };
}
if (code[offset] == '\n')
lineNumber++;
output += code[offset];
offset += 1;
}
@@ -168,7 +172,7 @@ namespace hex::lang {
this->m_pragmaHandlers.emplace(pragmaType, function);
}
void Preprocessor::addDefaultPragramHandlers() {
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');
});

View File

@@ -3,6 +3,8 @@
#include <unordered_set>
#include <string>
#include "utils.hpp"
namespace hex::lang {
Validator::Validator() {
@@ -19,73 +21,96 @@ 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())
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;
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;
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;
}

View File

@@ -11,6 +11,7 @@
#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"
@@ -22,6 +23,7 @@ int main() {
// Shared Data
std::vector<hex::lang::PatternData*> patternData;
hex::prv::Provider *dataProvider = nullptr;
// Create views
window.addView<hex::ViewHexEditor>(dataProvider, patternData);
window.addView<hex::ViewPattern>(dataProvider, patternData);
@@ -31,6 +33,7 @@ int main() {
window.addView<hex::ViewInformation>(dataProvider);
window.addView<hex::ViewStrings>(dataProvider);
window.addView<hex::ViewDisassembler>(dataProvider);
window.addView<hex::ViewBookmarks>(dataProvider);
window.addView<hex::ViewTools>();
window.addView<hex::ViewHelp>();

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

@@ -48,17 +48,40 @@ namespace hex::prv {
return;
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fread(buffer, 1, size, this->m_file);
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, void *buffer, size_t size) {
void FileProvider::write(u64 offset, const void *buffer, size_t size) {
if (buffer == nullptr || size == 0)
return;
fseeko64(this->m_file, this->getCurrentPage() * PageSize + offset, SEEK_SET);
fwrite(buffer, 1, size, this->m_file);
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::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::getActualSize() {
fseeko64(this->m_file, 0, SEEK_END);
return ftello64(this->m_file);

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;
@@ -50,8 +47,8 @@ namespace hex {
this->m_cachedData.emplace_back("uint64_t", hex::format("%llu", hex::changeEndianess(this->m_previewData.unsigned64, this->m_endianess)));
this->m_cachedData.emplace_back("int64_t", hex::format("%lld", hex::changeEndianess(this->m_previewData.signed64, this->m_endianess)));
this->m_cachedData.emplace_back("ANSI Character / char8_t", hex::format("%c", hex::changeEndianess(this->m_previewData.ansiChar, this->m_endianess)));
this->m_cachedData.emplace_back("Wide Character / char16_t", hex::format("%lc", hex::changeEndianess(this->m_previewData.wideChar, this->m_endianess)));
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 };
@@ -92,7 +89,7 @@ namespace hex {
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");
@@ -108,7 +105,7 @@ namespace hex {
}
if (ImGui::Begin("Data Inspector", &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", ImVec2(0, ImGui::GetWindowHeight() - 60))) {
if (ImGui::BeginTable("##datainspector", 2, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
@@ -155,10 +152,7 @@ namespace hex {
}
void ViewDataInspector::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data Preview View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -9,20 +9,27 @@ using namespace std::literals::string_literals;
namespace hex {
ViewDisassembler::ViewDisassembler(prv::Provider* &dataProvider) : View(), m_dataProvider(dataProvider) {
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_windowOpen)
return;
if (this->m_shouldInvalidate) {
this->m_disassembly.clear();
@@ -45,11 +52,11 @@ namespace hex {
if (cs_open(this->m_architecture, mode, &capstoneHandle) == CS_ERR_OK) {
std::vector<u8> buffer(2048, 0x00);
for (u64 address = 0; address < this->m_codeSize; address += 2048) {
size_t bufferSize = std::min(u64(2048), this->m_codeSize - address);
this->m_dataProvider->read(this->m_codeOffset + address, buffer.data(), bufferSize);
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(), buffer.size(), this->m_baseAddress + address, 0, &instructions);
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions);
if (instructionCount == 0)
break;
@@ -58,8 +65,10 @@ namespace hex {
for (u32 instr = 0; instr < instructionCount; instr++) {
Disassembly disassembly = { 0 };
disassembly.address = instructions[instr].address;
disassembly.offset = this->m_codeOffset + address + usedBytes;
disassembly.opcodeString = instructions[instr].mnemonic + " "s + instructions[instr].op_str;
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]);
@@ -83,21 +92,21 @@ namespace hex {
}
if (ImGui::Begin("Disassembler", &this->m_windowOpen)) {
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::InputScalar("Base address", ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::NewLine();
ImGui::InputScalar("Code start offset", ImGuiDataType_U64, &this->m_codeOffset, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputScalar("Code size", ImGuiDataType_U64, &this->m_codeSize, nullptr, nullptr, "%llx", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::NewLine();
ImGui::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);
@@ -120,6 +129,9 @@ namespace hex {
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();
@@ -142,6 +154,9 @@ namespace hex {
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();
@@ -161,6 +176,9 @@ namespace hex {
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();
@@ -178,6 +196,9 @@ namespace hex {
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();
@@ -214,55 +235,57 @@ namespace hex {
}
ImGui::EndChild();
ImGui::NewLine();
if (ImGui::Button("Disassemble"))
ImGui::SetCursorPosX((ImGui::GetContentRegionAvailWidth() - 300) / 2);
if (ImGui::Button("Disassemble", ImVec2(300, 20)))
this->m_shouldInvalidate = true;
ImGui::NewLine();
ImGui::TextUnformatted("Disassembly");
ImGui::Separator();
ImGui::NewLine();
if (ImGui::BeginChild("##scrolling")) {
if (ImGui::BeginTable("##disassembly", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_RowBg | ImGuiTableFlags_NoBordersInBody)) {
ImGui::TableSetupColumn("Address");
ImGui::TableSetupColumn("Offset");
ImGui::TableSetupColumn("Bytes");
ImGui::TableSetupColumn("Disassembly");
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());
ImGuiListClipper clipper;
clipper.Begin(this->m_disassembly.size());
ImGui::TableHeadersRow();
while (clipper.Step()) {
for (u64 i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("0x%llx", this->m_disassembly[i].address);
ImGui::TableNextColumn();
ImGui::Text("0x%llx", this->m_disassembly[i].offset);
ImGui::TableNextColumn();
ImGui::TextUnformatted(this->m_disassembly[i].bytes.c_str());
ImGui::TableNextColumn();
ImGui::TextUnformatted(this->m_disassembly[i].opcodeString.c_str());
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::EndChild();
clipper.End();
ImGui::EndTable();
}
}
}
ImGui::End();
}
void ViewDisassembler::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Disassembler View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -10,14 +10,25 @@
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);
}
@@ -27,42 +38,38 @@ namespace hex {
}
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;
@@ -70,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;
@@ -94,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
@@ -113,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
@@ -125,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
@@ -137,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
@@ -149,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
@@ -161,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
@@ -173,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
@@ -185,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;
}
@@ -204,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() {

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,12 +85,9 @@ 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, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
this->m_memoryEditor.DrawWindow("Hex Editor", &this->getWindowOpenState(), this, dataSize, dataSize == 0 ? 0x00 : this->m_dataProvider->getBaseAddress());
if (dataSize != 0x00) {
ImGui::Begin("Hex Editor");
@@ -81,8 +98,8 @@ namespace hex {
if (ImGui::ArrowButton("prevPage", ImGuiDir_Left)) {
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() - 1);
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
View::postEvent(Events::ByteSelected, &dataPreviewStart);
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
View::postEvent(Events::RegionSelected, &dataPreview);
}
ImGui::SameLine();
@@ -90,8 +107,8 @@ namespace hex {
if (ImGui::ArrowButton("nextPage", ImGuiDir_Right)) {
this->m_dataProvider->setCurrentPage(this->m_dataProvider->getCurrentPage() + 1);
size_t dataPreviewStart = std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd);
View::postEvent(Events::ByteSelected, &dataPreviewStart);
Region dataPreview = { std::min(this->m_memoryEditor.DataPreviewAddr, this->m_memoryEditor.DataPreviewAddrEnd), 1 };
View::postEvent(Events::RegionSelected, &dataPreview);
}
ImGui::End();
@@ -100,20 +117,233 @@ namespace hex {
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);
@@ -137,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);
@@ -352,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;

View File

@@ -15,7 +15,7 @@
namespace hex {
ViewInformation::ViewInformation(prv::Provider* &dataProvider)
: View(), m_dataProvider(dataProvider) {
: View("Information"), m_dataProvider(dataProvider) {
View::subscribeEvent(Events::DataChanged, [this](const void*) {
this->m_dataValid = false;
this->m_highestBlockEntropy = 0;
@@ -47,10 +47,7 @@ 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()) {
@@ -192,10 +189,7 @@ namespace hex {
}
void ViewInformation::createMenu() {
if (ImGui::BeginMenu("View")) {
ImGui::MenuItem("Data Information View", "", &this->m_windowOpen);
ImGui::EndMenu();
}
}
}

View File

@@ -48,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;
};
@@ -69,12 +71,12 @@ 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::DataChanged, [this](const void* userData) {
View::subscribeEvent(Events::FileLoaded, [this](const void* userData) {
lang::Preprocessor preprocessor;
std::string magicFiles;
@@ -85,7 +87,7 @@ namespace hex {
magicFiles += entry.path().string() + MAGIC_PATH_SEPARATOR;
}
std::vector<u8> buffer(std::min(this->m_dataProvider->getSize(), size_t(0xFF'FFFF)), 0x00);
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;
@@ -104,7 +106,7 @@ namespace hex {
}
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
preprocessor.addDefaultPragramHandlers();
preprocessor.addDefaultPragmaHandlers();
std::error_code errorCode;
@@ -148,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");
@@ -240,6 +234,7 @@ namespace hex {
void ViewPattern::parsePattern(char *buffer) {
this->clearPatternData();
this->m_textEditor.SetErrorMarkers({ });
this->postEvent(Events::PatternChanged);
hex::lang::Preprocessor preprocessor;
@@ -258,21 +253,25 @@ namespace hex {
} else
return false;
});
preprocessor.addDefaultPragramHandlers();
preprocessor.addDefaultPragmaHandlers();
auto [preprocessingResult, preprocesedCode] = preprocessor.preprocess(buffer);
if (preprocessingResult.failed())
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;
}
@@ -281,12 +280,14 @@ namespace hex {
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 | ImGuiTableFlags_NoBordersInBody)) {
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,12 +49,7 @@ 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)) {
@@ -69,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,12 +1,17 @@
#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_foundStrings.clear();
});
@@ -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,7 +81,7 @@ 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;
@@ -70,11 +93,10 @@ namespace hex {
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_NoBordersInBody)) {
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"));
@@ -120,8 +142,16 @@ 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);
@@ -133,18 +163,26 @@ namespace hex {
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;