mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-31 13:26:02 -05:00
Compare commits
58 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a989c6cc1 | ||
|
|
3d7adf6483 | ||
|
|
56079f70c7 | ||
|
|
6c9e969099 | ||
|
|
76f8e6d6ef | ||
|
|
174cf3ed95 | ||
|
|
540f468e8a | ||
|
|
6b2423cdce | ||
|
|
e4a3181e1d | ||
|
|
b57730c28b | ||
|
|
5a02c38fcd | ||
|
|
2847098020 | ||
|
|
0cc6d90e3d | ||
|
|
66d1b3fd2f | ||
|
|
b28eaf2dbf | ||
|
|
191a99f91b | ||
|
|
f3f1ac939a | ||
|
|
9737b9cd62 | ||
|
|
e3fbb490df | ||
|
|
73d74f6cde | ||
|
|
1487f760b0 | ||
|
|
bdb2ac3a0b | ||
|
|
75bd7805c9 | ||
|
|
ef8e9a83bb | ||
|
|
7d9c24ff51 | ||
|
|
a8e83154f0 | ||
|
|
27c2c4dc33 | ||
|
|
a9a538cec8 | ||
|
|
57e1f7ee10 | ||
|
|
754eb89f04 | ||
|
|
2e95184d30 | ||
|
|
9deab9c497 | ||
|
|
5ae6c8a627 | ||
|
|
05104aef6c | ||
|
|
08da408471 | ||
|
|
4a4d5ac694 | ||
|
|
3b4d6d465b | ||
|
|
26f998ecb6 | ||
|
|
60a717365c | ||
|
|
07ae00aa20 | ||
|
|
39cc845df3 | ||
|
|
6c8b75a05f | ||
|
|
4c8efed256 | ||
|
|
faaa90fa0d | ||
|
|
7e075e5ebb | ||
|
|
b9508d853e | ||
|
|
716d52f3e3 | ||
|
|
90753f4d42 | ||
|
|
7117592f38 | ||
|
|
b9030d7e47 | ||
|
|
b79cfa213d | ||
|
|
60af9970c1 | ||
|
|
33a1e7f055 | ||
|
|
f72e9700ab | ||
|
|
4357d68462 | ||
|
|
adfaa95149 | ||
|
|
d6b887b7db | ||
|
|
227040f82f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,3 +12,5 @@ imgui.ini
|
||||
plugins/.rustc_info.json
|
||||
|
||||
**/target
|
||||
|
||||
plugins/example_rust/Cargo.lock
|
||||
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -21,6 +21,8 @@
|
||||
[submodule "lib/external/capstone"]
|
||||
path = lib/external/capstone
|
||||
url = https://github.com/capstone-engine/capstone
|
||||
ignore = dirty
|
||||
[submodule "lib/external/libromfs"]
|
||||
path = lib/external/libromfs
|
||||
url = https://github.com/WerWolv/libromfs
|
||||
ignore = dirty
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Updating the version here will update it throughout ImHex as well
|
||||
set(IMHEX_VERSION "1.15.0")
|
||||
set(IMHEX_VERSION "1.16.1")
|
||||
project(imhex VERSION ${IMHEX_VERSION})
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
@@ -28,10 +28,14 @@ configurePackingResources()
|
||||
# Add ImHex sources
|
||||
add_subdirectory(lib/libimhex)
|
||||
add_subdirectory(main)
|
||||
add_custom_target(imhex ALL DEPENDS main)
|
||||
add_custom_target(imhex_all ALL DEPENDS main)
|
||||
|
||||
# Add unit tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
|
||||
# Configure packaging
|
||||
createPackage()
|
||||
|
||||
# Download and install all current files from the ImHex-Patterns repo
|
||||
downloadImHexPatternsFiles()
|
||||
@@ -1,3 +1,5 @@
|
||||
include(FetchContent)
|
||||
|
||||
macro(addVersionDefines)
|
||||
if (IS_DIRECTORY "${CMAKE_SOURCE_DIR}/.git")
|
||||
# Get the current working branch
|
||||
@@ -21,19 +23,18 @@ macro(addVersionDefines)
|
||||
|
||||
set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DPROJECT_VERSION_MAJOR=${PROJECT_VERSION_MAJOR} -DPROJECT_VERSION_MINOR=${PROJECT_VERSION_MINOR} -DPROJECT_VERSION_PATCH=${PROJECT_VERSION_PATCH} ")
|
||||
|
||||
add_compile_definitions(
|
||||
$<$<CONFIG:Release>:IMHEX_VERSION="${IMHEX_VERSION}">
|
||||
$<$<CONFIG:Debug>:IMHEX_VERSION="${IMHEX_VERSION}-Debug">
|
||||
$<$<CONFIG:RelWithDebInfo>:IMHEX_VERSION="${IMHEX_VERSION}-ReleaseWithDebugInfo">
|
||||
$<$<CONFIG:MinSizeRel>:IMHEX_VERSION="${IMHEX_VERSION}-ReleaseMinimumSize">
|
||||
)
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION})
|
||||
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-Debug)
|
||||
add_compile_definitions(DEBUG)
|
||||
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-RelWithDebInfo)
|
||||
elseif (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-MinSizeRel)
|
||||
endif ()
|
||||
|
||||
add_compile_definitions(
|
||||
$<$<CONFIG:Release>:RELEASE>
|
||||
$<$<CONFIG:Debug>:DEBUG>
|
||||
$<$<CONFIG:RelWithDebInfo>:RELEASE>
|
||||
$<$<CONFIG:MinSizeRel>:RELEASE>
|
||||
)
|
||||
add_compile_definitions(IMHEX_VERSION="${IMHEX_VERSION_STRING}")
|
||||
|
||||
endmacro()
|
||||
|
||||
@@ -176,7 +177,7 @@ macro(createPackage)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_dependencies(imhex ${plugin})
|
||||
add_dependencies(imhex_all ${plugin})
|
||||
endif ()
|
||||
endforeach()
|
||||
|
||||
@@ -227,7 +228,7 @@ macro(createPackage)
|
||||
include(PostprocessBundle)
|
||||
|
||||
# Fix rpath
|
||||
add_custom_command(TARGET imhex POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
|
||||
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
|
||||
|
||||
# FIXME: Remove this once we move/integrate the plugins directory.
|
||||
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${bundle_path}/Contents/MacOS/plugins")
|
||||
@@ -236,7 +237,7 @@ macro(createPackage)
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${bundle_path}/Contents/Resources")
|
||||
|
||||
# Update library references to make the bundle portable
|
||||
postprocess_bundle(imhex main)
|
||||
postprocess_bundle(imhex_all main)
|
||||
|
||||
# Enforce DragNDrop packaging.
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
@@ -279,4 +280,21 @@ macro(detectBadClone)
|
||||
message(FATAL_ERROR "External dependency ${EXTERNAL_DIR} is empty!\nMake sure to correctly clone ImHex using the --recurse-submodules git option or initialize the submodules manually.")
|
||||
endif()
|
||||
endforeach ()
|
||||
endmacro()
|
||||
endmacro()
|
||||
|
||||
|
||||
function(downloadImHexPatternsFiles)
|
||||
FetchContent_Declare(
|
||||
imhex_patterns
|
||||
GIT_REPOSITORY https://github.com/WerWolv/ImHex-Patterns.git
|
||||
GIT_TAG master
|
||||
)
|
||||
|
||||
FetchContent_Populate(imhex_patterns)
|
||||
|
||||
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns yara magic)
|
||||
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "./")
|
||||
endforeach()
|
||||
|
||||
endfunction()
|
||||
540
lib/external/imgui/include/imgui_memory_editor.h
vendored
540
lib/external/imgui/include/imgui_memory_editor.h
vendored
@@ -49,6 +49,7 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -294,7 +295,6 @@ struct MemoryEditor
|
||||
|
||||
const int line_total_count = (int)((mem_size + Cols - 1) / Cols);
|
||||
clipper.Begin(line_total_count, s.LineHeight);
|
||||
clipper.Step();
|
||||
const size_t visible_start_addr = clipper.DisplayStart * Cols;
|
||||
const size_t visible_end_addr = clipper.DisplayEnd * Cols;
|
||||
const size_t visible_count = visible_end_addr - visible_start_addr;
|
||||
@@ -361,7 +361,7 @@ struct MemoryEditor
|
||||
// Draw vertical separator
|
||||
ImVec2 window_pos = ImGui::GetWindowPos();
|
||||
float scrollX = ImGui::GetScrollX();
|
||||
|
||||
|
||||
if (OptShowAscii)
|
||||
draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth - scrollX, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));
|
||||
if (OptShowAdvancedDecoding)
|
||||
@@ -376,197 +376,20 @@ struct MemoryEditor
|
||||
const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x ";
|
||||
|
||||
bool tooltipShown = false;
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||
while (clipper.Step())
|
||||
{
|
||||
size_t addr = (size_t)(line_i * Cols);
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines
|
||||
{
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos() - ImVec2(ImGui::GetStyle().CellPadding.x / 2, 0);
|
||||
float highlight_width = s.GlyphWidth * 2 + ImGui::GetStyle().CellPadding.x / 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) &&
|
||||
((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
|
||||
(HighlightFn && HighlightFn(mem_data, addr + 1, true)) ||
|
||||
((addr + 1) >= DataPreviewAddr && (addr + 1) <= DataPreviewAddrEnd) || ((addr + 1) >= DataPreviewAddrEnd && (addr + 1) <= DataPreviewAddr));
|
||||
if (is_next_byte_highlighted)
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color);
|
||||
|
||||
if (is_highlight_from_preview) {
|
||||
size_t min = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
size_t max = std::max(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
// Draw vertical line at the left of first byte and the start of the line
|
||||
if (n == 0 || addr == min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(0, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw vertical line at the right of the last byte and the end of the line
|
||||
if (n == Cols - 1 || addr == max) {
|
||||
draw_list->AddRectFilled(pos + ImVec2(highlight_width, 0), pos + ImVec2(highlight_width + 1, s.LineHeight), color);
|
||||
draw_list->AddLine(pos + ImVec2(highlight_width + 1, -1), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
|
||||
// Draw horizontal line at the top of the bytes
|
||||
if ((addr - Cols) < min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(highlight_width + 1, 0), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw horizontal line at the bottom of the bytes
|
||||
if ((addr + Cols) == (max + 1) && OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 1)
|
||||
draw_list->AddLine(pos + ImVec2(-s.SpacingBetweenMidCols, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
else if ((addr + Cols) > max)
|
||||
draw_list->AddLine(pos + ImVec2(0, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
{
|
||||
// Display text input on current byte
|
||||
bool data_write = false;
|
||||
ImGui::PushID((void*)addr);
|
||||
if (DataEditingTakeFocus)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
ImGui::CaptureKeyboardFromApp(true);
|
||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
}
|
||||
ImGui::PushItemWidth(s.GlyphWidth * 2);
|
||||
struct UserData
|
||||
{
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
UserData* user_data = (UserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||
{
|
||||
// When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
|
||||
data->DeleteChars(0, data->BufTextLen);
|
||||
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
||||
data->SelectionStart = 0;
|
||||
data->SelectionEnd = 2;
|
||||
data->CursorPos = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
UserData user_data;
|
||||
user_data.CursorPos = -1;
|
||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysInsertMode | ImGuiInputTextFlags_CallbackAlways;
|
||||
if (ImGui::InputText("##data", DataInputBuf, 32, flags, UserData::Callback, &user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
ImGui::PopItemWidth();
|
||||
if (user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
{
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
|
||||
if (OptShowHexII)
|
||||
{
|
||||
if ((b >= 32 && b < 128))
|
||||
ImGui::Text(".%c ", b);
|
||||
else if (b == 0xFF && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("## ");
|
||||
else if (b == 0x00)
|
||||
ImGui::Text(" ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0 && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("00 ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !tooltipShown) {
|
||||
if (HoverFn) {
|
||||
HoverFn(mem_data, addr);
|
||||
tooltipShown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OptShowAscii)
|
||||
{
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
size_t addr = (size_t)(line_i * Cols);
|
||||
ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);
|
||||
|
||||
// Draw Hexadecimal
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;
|
||||
if (OptMidColsCount > 0)
|
||||
byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;
|
||||
ImGui::SameLine(byte_pos_x);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
@@ -574,127 +397,306 @@ struct MemoryEditor
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos() - ImVec2(ImGui::GetStyle().CellPadding.x / 2, 0);
|
||||
float highlight_width = s.GlyphWidth * 2 + ImGui::GetStyle().CellPadding.x / 2;
|
||||
bool is_next_byte_highlighted = (addr + 1 < mem_size) &&
|
||||
((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) ||
|
||||
(HighlightFn && HighlightFn(mem_data, addr + 1, true)) ||
|
||||
((addr + 1) >= DataPreviewAddr && (addr + 1) <= DataPreviewAddrEnd) || ((addr + 1) >= DataPreviewAddrEnd && (addr + 1) <= DataPreviewAddr));
|
||||
if (is_next_byte_highlighted)
|
||||
{
|
||||
highlight_width = s.HexCellWidth;
|
||||
if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)
|
||||
highlight_width += s.SpacingBetweenMidCols;
|
||||
}
|
||||
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color);
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + highlight_width, pos.y + s.LineHeight), color);
|
||||
|
||||
if (is_highlight_from_preview) {
|
||||
size_t min = std::min(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
size_t max = std::max(DataPreviewAddr, DataPreviewAddrEnd);
|
||||
|
||||
// Draw vertical line at the left of first byte and the start of the line
|
||||
if (n == 0 || addr == min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(0, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw vertical line at the right of the last byte and the end of the line
|
||||
if (n == Cols - 1 || addr == max) {
|
||||
draw_list->AddRectFilled(pos + ImVec2(highlight_width, 0), pos + ImVec2(highlight_width + 1, s.LineHeight), color);
|
||||
draw_list->AddLine(pos + ImVec2(highlight_width + 1, -1), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
|
||||
// Draw horizontal line at the top of the bytes
|
||||
if ((addr - Cols) < min)
|
||||
draw_list->AddLine(pos, pos + ImVec2(highlight_width + 1, 0), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
|
||||
// Draw horizontal line at the bottom of the bytes
|
||||
if ((addr + Cols) == (max + 1) && OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 1)
|
||||
draw_list->AddLine(pos + ImVec2(-s.SpacingBetweenMidCols, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
else if ((addr + Cols) > max)
|
||||
draw_list->AddLine(pos + ImVec2(0, s.LineHeight), pos + ImVec2(highlight_width + 1, s.LineHeight), ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Text)), 1.0F);
|
||||
}
|
||||
}
|
||||
|
||||
if (DataEditingAddr == addr)
|
||||
{
|
||||
// Display text input on current byte
|
||||
bool data_write = false;
|
||||
ImGui::PushID((void*)addr);
|
||||
if (DataEditingTakeFocus)
|
||||
{
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
ImGui::CaptureKeyboardFromApp(true);
|
||||
sprintf(AddrInputBuf, format_data, s.AddrDigitsCount, base_display_addr + addr);
|
||||
sprintf(DataInputBuf, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
}
|
||||
ImGui::PushItemWidth(s.GlyphWidth * 2);
|
||||
struct UserData
|
||||
{
|
||||
// FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.
|
||||
static int Callback(ImGuiInputTextCallbackData* data)
|
||||
{
|
||||
UserData* user_data = (UserData*)data->UserData;
|
||||
if (!data->HasSelection())
|
||||
user_data->CursorPos = data->CursorPos;
|
||||
if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)
|
||||
{
|
||||
// When not editing a byte, always rewrite its content (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there)
|
||||
data->DeleteChars(0, data->BufTextLen);
|
||||
data->InsertChars(0, user_data->CurrentBufOverwrite);
|
||||
data->SelectionStart = 0;
|
||||
data->SelectionEnd = 2;
|
||||
data->CursorPos = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
char CurrentBufOverwrite[3]; // Input
|
||||
int CursorPos; // Output
|
||||
};
|
||||
UserData user_data;
|
||||
user_data.CursorPos = -1;
|
||||
sprintf(user_data.CurrentBufOverwrite, format_byte, ReadFn ? ReadFn(mem_data, addr) : mem_data[addr]);
|
||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_AlwaysInsertMode | ImGuiInputTextFlags_CallbackAlways;
|
||||
if (ImGui::InputText("##data", DataInputBuf, 32, flags, UserData::Callback, &user_data))
|
||||
data_write = data_next = true;
|
||||
else if (!DataEditingTakeFocus && !ImGui::IsItemActive())
|
||||
DataEditingAddr = data_editing_addr_next = (size_t)-1;
|
||||
DataEditingTakeFocus = false;
|
||||
ImGui::PopItemWidth();
|
||||
if (user_data.CursorPos >= 2)
|
||||
data_write = data_next = true;
|
||||
if (data_editing_addr_next != (size_t)-1)
|
||||
data_write = data_next = false;
|
||||
unsigned int data_input_value = 0;
|
||||
if (data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1)
|
||||
{
|
||||
if (WriteFn)
|
||||
WriteFn(mem_data, addr, (ImU8)data_input_value);
|
||||
else
|
||||
mem_data[addr] = (ImU8)data_input_value;
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.
|
||||
ImU8 b = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
if (OptShowHexII)
|
||||
{
|
||||
if ((b >= 32 && b < 128))
|
||||
ImGui::Text(".%c ", b);
|
||||
else if (b == 0xFF && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("## ");
|
||||
else if (b == 0x00)
|
||||
ImGui::Text(" ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b == 0 && OptGreyOutZeroes)
|
||||
ImGui::TextDisabled("00 ");
|
||||
else
|
||||
ImGui::Text(format_byte_space, b);
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !tooltipShown) {
|
||||
if (HoverFn) {
|
||||
HoverFn(mem_data, addr);
|
||||
tooltipShown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (OptShowAscii)
|
||||
{
|
||||
// Draw ASCII values
|
||||
ImGui::SameLine(s.PosAsciiStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
for (int n = 0; n < Cols && addr < mem_size; n++, addr++)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
unsigned char c = ReadFn ? ReadFn(mem_data, addr) : mem_data[addr];
|
||||
char display_c = (c < 32 || c >= 128) ? '.' : c;
|
||||
draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += s.GlyphWidth;
|
||||
}
|
||||
|
||||
pos.x += s.GlyphWidth;
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
if (OptShowAdvancedDecoding && DecodeFn) {
|
||||
// Draw decoded bytes
|
||||
ImGui::SameLine(s.PosDecodingStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (OptShowAdvancedDecoding && DecodeFn) {
|
||||
// Draw decoded bytes
|
||||
ImGui::SameLine(s.PosDecodingStart);
|
||||
ImVec2 pos = ImGui::GetCursorScreenPos();
|
||||
addr = line_i * Cols;
|
||||
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
for (int n = 0; n < Cols && addr < mem_size;)
|
||||
{
|
||||
auto decodedData = DecodeFn(mem_data, addr);
|
||||
|
||||
auto displayData = decodedData.data;
|
||||
auto glyphWidth = ImGui::CalcTextSize(displayData.c_str()).x + 1;
|
||||
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
|
||||
draw_list->AddText(pos, decodedData.color, displayData.c_str(), displayData.c_str() + displayData.length());
|
||||
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::PushID(-1);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(glyphWidth, s.LineHeight));
|
||||
ImGui::Dummy(ImVec2(s.GlyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
for (int n = 0; n < Cols && addr < mem_size;)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
auto decodedData = DecodeFn(mem_data, addr);
|
||||
|
||||
auto displayData = decodedData.data;
|
||||
auto glyphWidth = ImGui::CalcTextSize(displayData.c_str()).x + 1;
|
||||
|
||||
if (addr == DataEditingAddr)
|
||||
{
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
draw_list->AddText(pos, decodedData.color, displayData.c_str(), displayData.c_str() + displayData.length());
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
// Draw highlight
|
||||
bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);
|
||||
bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, false));
|
||||
bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr <= DataPreviewAddrEnd) || (addr >= DataPreviewAddrEnd && addr <= DataPreviewAddr);
|
||||
if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)
|
||||
{
|
||||
ImU32 color = HighlightColor;
|
||||
if ((is_highlight_from_user_range + is_highlight_from_user_func + is_highlight_from_preview) > 1)
|
||||
color = (ImAlphaBlendColors(HighlightColor, 0x60C08080) & 0x00FFFFFF) | 0x90000000;
|
||||
|
||||
pos.x += glyphWidth;
|
||||
draw_list->AddRectFilled(pos, ImVec2(pos.x + glyphWidth, pos.y + s.LineHeight), color);
|
||||
}
|
||||
|
||||
if (addr <= 1) {
|
||||
n++;
|
||||
addr++;
|
||||
} else {
|
||||
n += decodedData.advance;
|
||||
addr += decodedData.advance;
|
||||
|
||||
ImGui::PushID(line_i * Cols + n);
|
||||
ImGui::SameLine();
|
||||
ImGui::Dummy(ImVec2(glyphWidth, s.LineHeight));
|
||||
|
||||
ImGui::PopID();
|
||||
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(0) && !ImGui::GetIO().KeyShift)
|
||||
{
|
||||
if (!ReadOnly && ImGui::IsMouseDoubleClicked(0)) {
|
||||
DataEditingTakeFocus = true;
|
||||
data_editing_addr_next = addr;
|
||||
}
|
||||
|
||||
DataPreviewAddr = addr;
|
||||
DataPreviewAddrEnd = addr;
|
||||
|
||||
}
|
||||
if (ImGui::IsItemHovered() && ((ImGui::IsMouseClicked(0) && ImGui::GetIO().KeyShift) || ImGui::IsMouseDragging(0))) {
|
||||
DataPreviewAddrEnd = addr;
|
||||
}
|
||||
|
||||
pos.x += glyphWidth;
|
||||
|
||||
if (addr <= 1) {
|
||||
n++;
|
||||
addr++;
|
||||
} else {
|
||||
n += decodedData.advance;
|
||||
addr += decodedData.advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
IM_ASSERT(clipper.Step() == false);
|
||||
clipper.End();
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::EndChild();
|
||||
|
||||
if (data_next && DataEditingAddr < mem_size)
|
||||
{
|
||||
DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;
|
||||
DataEditingAddr = DataPreviewAddr = DataPreviewAddrEnd = DataEditingAddr + 1;
|
||||
DataEditingTakeFocus = true;
|
||||
}
|
||||
else if (data_editing_addr_next != (size_t)-1)
|
||||
|
||||
558
lib/libimhex-rs/Cargo.lock
generated
558
lib/libimhex-rs/Cargo.lock
generated
@@ -2,6 +2,149 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aquamarine"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96e14cb2a51c8b45d26a4219981985c7350fc05eacb7b5b2939bceb2ffefdf3e"
|
||||
dependencies = [
|
||||
"itertools 0.9.0",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e6384cb95b48be8c5b83764ef800858322436f57aa17974915d23dadb6a7d5"
|
||||
dependencies = [
|
||||
"aquamarine",
|
||||
"autocxx-engine",
|
||||
"autocxx-macro",
|
||||
"cxx",
|
||||
"moveit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx-bindgen"
|
||||
version = "0.59.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e603c1eb79e21068072ef990e5463f613e0cedddd6712ff11afeae2a90b2510"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"itertools 0.10.3",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx-build"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f5b45a4fe71d3ac68d8b4fd11abe54c791046ec4def7effe27961269b6ab3"
|
||||
dependencies = [
|
||||
"autocxx-engine",
|
||||
"env_logger",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx-engine"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02323905bec49fde96ff028fcff1c478d0eba14fa34ea5eb5e4d17439748e42a"
|
||||
dependencies = [
|
||||
"aquamarine",
|
||||
"autocxx-bindgen",
|
||||
"autocxx-parser",
|
||||
"cc",
|
||||
"cxx",
|
||||
"cxx-gen",
|
||||
"indoc",
|
||||
"itertools 0.10.3",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_json",
|
||||
"strum_macros",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"unzip-n",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx-macro"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8106ca477cbe6edf188311f2e05606b81bf463c41748ce7120c31d1b11875515"
|
||||
dependencies = [
|
||||
"autocxx-parser",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocxx-parser"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad0ad08260adcecc119b08f460b0633b6e306ea2f6fda4f27e4dd28e20b9a2f4"
|
||||
dependencies = [
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -14,6 +157,15 @@ version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -26,6 +178,32 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd650552110e39b7c5058986cf177decd3365841836578ac50a286094eac0be6"
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
@@ -49,17 +227,14 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.55"
|
||||
name = "cxx-gen"
|
||||
version = "0.7.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83363b96cfd226eb820e37a21088c30c55e47f9fc8299c2d08a6090d50414ccc"
|
||||
checksum = "836e95ae34fc21fb39c206444879afda2c6e704424c9c621662764f1b459e83a"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn",
|
||||
]
|
||||
|
||||
@@ -81,15 +256,63 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libimhex-rs"
|
||||
version = "0.1.0"
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"imgui",
|
||||
"macros",
|
||||
"atty",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "imgui"
|
||||
version = "0.8.0"
|
||||
@@ -107,6 +330,23 @@ dependencies = [
|
||||
"chlorine",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imhex-macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7906a9fababaeacb774f72410e497a1d18de916322e33797bb2cd29baa23c9e"
|
||||
dependencies = [
|
||||
"unindent",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instant"
|
||||
version = "0.1.11"
|
||||
@@ -116,18 +356,69 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "libimhex-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"autocxx",
|
||||
"autocxx-build",
|
||||
"cxx",
|
||||
"imgui",
|
||||
"imhex-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.5"
|
||||
@@ -147,13 +438,52 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macros"
|
||||
version = "0.1.0"
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "moveit"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd833d6adefa6bcfc56948d061c1d697dfa3ab63711963c7ef4aa23eda945676"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
@@ -179,6 +509,36 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.29"
|
||||
@@ -206,6 +566,50 @@ dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@@ -213,10 +617,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.0"
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e114536316b51a5aa7a0e59fc49661fd263c5507dd08bd28de052e57626ce69"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
@@ -224,6 +656,25 @@ version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.77"
|
||||
@@ -235,6 +686,20 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.2"
|
||||
@@ -244,6 +709,21 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
@@ -256,6 +736,46 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514672a55d7380da379785a4d70ca8386c8883ff7eaae877be4d2081cebe73d8"
|
||||
|
||||
[[package]]
|
||||
name = "unzip-n"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a5a7e487e921cf220206864a94a89b6c6905bfc19f1057fa26a4cb360e5c1d2"
|
||||
dependencies = [
|
||||
"either",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -11,6 +11,8 @@ imhex-macros = { path = "proc_macros" }
|
||||
imgui = { path = "imgui-rs" }
|
||||
|
||||
cxx = "1.0.55"
|
||||
autocxx = "0.16"
|
||||
|
||||
[build-dependencies]
|
||||
cxx-build = "1.0.55"
|
||||
autocxx-build = "0.16"
|
||||
#cxx-build = "1.0.55"
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
fn main() {
|
||||
println!("cargo:rustc-link-lib=dylib=imhex");
|
||||
println!("cargo:rustc-link-search=all={}", env!("LIBIMHEX_OUTPUT_DIRECTORY"));
|
||||
println!(
|
||||
"cargo:rustc-link-search=all={}",
|
||||
env!("LIBIMHEX_OUTPUT_DIRECTORY")
|
||||
);
|
||||
|
||||
println!("cargo:rerun-if-changed=src/lib.rs");
|
||||
println!("cargo:rerun-if-changed=src/imhex_api.rs");
|
||||
|
||||
cxx_build::bridge("src/imhex_api.rs")
|
||||
let include = format!("-I{}/include", env!("LIBIMHEX_SOURCE_DIRECTORY"));
|
||||
|
||||
let path = std::path::PathBuf::from("src");
|
||||
let mut build = autocxx_build::Builder::new("src/lib.rs", &[&path])
|
||||
.extra_clang_args(&[&include, "-x", "c++", "-std=gnu++20"])
|
||||
.auto_allowlist(true)
|
||||
.expect_build();
|
||||
|
||||
build
|
||||
.include(format!("{}/include", env!("LIBIMHEX_SOURCE_DIRECTORY")))
|
||||
.flag_if_supported("-std=gnu++20")
|
||||
.flag_if_supported("-std=gnu++2a")
|
||||
.flag_if_supported("-fconcepts")
|
||||
.compiler(env!("CXX_COMPILER"))
|
||||
.compile("libimhex-bridge");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,6 @@ impl Parse for AttrList {
|
||||
}
|
||||
}
|
||||
|
||||
fn symbol(name: &str) -> String {
|
||||
let pkg_name = std::env::var("CARGO_PKG_NAME").unwrap();
|
||||
format!(
|
||||
"_ZN3hex6plugin{}{}8internal{}{}Ev",
|
||||
pkg_name.len(),
|
||||
pkg_name,
|
||||
name.len(),
|
||||
name,
|
||||
)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn plugin_setup(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let args = syn::parse_macro_input!(attr as AttrList)
|
||||
@@ -36,11 +25,14 @@ pub fn plugin_setup(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
||||
let function = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
let plugin_name_export = symbol("getPluginName");
|
||||
let plugin_author_export = symbol("getPluginAuthor");
|
||||
let plugin_desc_export = symbol("getPluginDescription");
|
||||
let plugin_init_export = symbol("initializePlugin");
|
||||
let plugin_set_imgui_ctxt_export = symbol("setImGuiContext");
|
||||
let plugin_name_export = "getPluginName";
|
||||
let plugin_author_export = "getPluginAuthor";
|
||||
let plugin_desc_export = "getPluginDescription";
|
||||
let plugin_version_export = "getCompatibleVersion";
|
||||
let plugin_init_export = "initializePlugin";
|
||||
let plugin_set_imgui_ctx_export = "setImGuiContext";
|
||||
|
||||
let imhex_version = std::env::var("IMHEX_VERSION").unwrap();
|
||||
|
||||
quote!(
|
||||
#[export_name = #plugin_name_export]
|
||||
@@ -58,11 +50,16 @@ pub fn plugin_setup(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
concat!(#description, "\0").as_ptr()
|
||||
}
|
||||
|
||||
#[export_name = #plugin_set_imgui_ctxt_export]
|
||||
#[export_name = #plugin_set_imgui_ctx_export]
|
||||
pub unsafe extern "C" fn set_imgui_context(context: *mut ::hex::imgui::sys::ImGuiContext) {
|
||||
::hex::imgui::sys::igSetCurrentContext(context);
|
||||
}
|
||||
|
||||
#[export_name = #plugin_version_export]
|
||||
pub unsafe extern "C" fn plugin_version() -> *const u8 {
|
||||
concat!(#imhex_version, "\0").as_ptr()
|
||||
}
|
||||
|
||||
#[export_name = #plugin_init_export]
|
||||
pub extern "C" #function
|
||||
)
|
||||
|
||||
27
lib/libimhex-rs/src/bookmarks.rs
Normal file
27
lib/libimhex-rs/src/bookmarks.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use autocxx::c_ulong;
|
||||
|
||||
use crate::Color;
|
||||
use std::ops::Range;
|
||||
|
||||
/// Add a bookmark to a region of the current imhex view with an optionally provided color
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// use hex::bookmarks;
|
||||
///
|
||||
/// bookmarks::add(0..0x10, "header", "this is the header of the file", None);
|
||||
///
|
||||
/// let red = Color::new(0xFF, 0, 0, 0xFF);
|
||||
/// bookmarks::add(0x10..0x30, "table", "this is the table of the file", red);
|
||||
/// ```
|
||||
pub fn add(region: Range<u64>, name: &str, comment: &str, color: impl Into<Option<Color>>) {
|
||||
cxx::let_cxx_string!(cpp_name = name);
|
||||
cxx::let_cxx_string!(cpp_comment = comment);
|
||||
|
||||
crate::ffi::hex::ImHexApi::Bookmarks::add(
|
||||
region.start,
|
||||
c_ulong::from(region.end.saturating_sub(region.start)),
|
||||
&cpp_name,
|
||||
&cpp_comment,
|
||||
color.into().unwrap_or(crate::Color::new(0, 0, 0, 0)).rgba(),
|
||||
);
|
||||
}
|
||||
11
lib/libimhex-rs/src/imhex.rs
Normal file
11
lib/libimhex-rs/src/imhex.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use crate::ffi::hex::ImHexApi::Common;
|
||||
|
||||
/// Close ImHex, optionally prompting the user if they'd like to quit
|
||||
pub fn close_imhex(without_question: bool) {
|
||||
Common::closeImHex(without_question)
|
||||
}
|
||||
|
||||
/// Close and reopen ImHex
|
||||
pub fn restart_imhex() {
|
||||
Common::restartImHex()
|
||||
}
|
||||
@@ -1,99 +1,17 @@
|
||||
pub mod ffi {
|
||||
|
||||
pub mod ImHexApi {
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod Common {
|
||||
|
||||
#[namespace = "hex::ImHexApi::Common"]
|
||||
extern "C++" {
|
||||
include!("hex/api/imhex_api.hpp");
|
||||
|
||||
pub unsafe fn closeImHex(no_questions: bool);
|
||||
pub unsafe fn restartImHex();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cxx::bridge]
|
||||
pub mod Bookmarks {
|
||||
|
||||
#[namespace = "hex::ImHexApi::Bookmarks"]
|
||||
extern "C++" {
|
||||
include!("hex/api/imhex_api.hpp");
|
||||
|
||||
pub unsafe fn add(addr : u64, size : usize, name : &CxxString, comment : &CxxString, color : u32);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Region {
|
||||
pub address : u64,
|
||||
pub size : usize
|
||||
}
|
||||
|
||||
/// A highlight color for use with the bookmarks API
|
||||
pub struct Color {
|
||||
pub a : u8,
|
||||
pub g : u8,
|
||||
pub b : u8,
|
||||
pub r : u8
|
||||
}
|
||||
|
||||
impl Region {
|
||||
|
||||
pub fn new(address : u64, size : usize) -> Self {
|
||||
Region { address, size }
|
||||
}
|
||||
|
||||
pub a: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub r: u8,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
|
||||
pub fn new(r : u8, g : u8, b : u8, a : u8) -> Self {
|
||||
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
|
||||
Color { a, g, b, r }
|
||||
}
|
||||
|
||||
pub fn rgba(self) -> u32 {
|
||||
pub const fn rgba(self) -> u32 {
|
||||
(self.a as u32) << 24 | (self.b as u32) << 16 | (self.g as u32) << 8 | (self.r as u32) << 0
|
||||
}
|
||||
}
|
||||
|
||||
pub mod ImHexApi {
|
||||
|
||||
pub mod Common {
|
||||
|
||||
pub fn closeImHex() {
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Common::closeImHex(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn restartImmHex() {
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Common::restartImHex();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub mod Bookmarks {
|
||||
|
||||
pub fn add(region : crate::Region, name : &str, comment : &str, color : Option<crate::Color>) {
|
||||
cxx::let_cxx_string!(cpp_name = name);
|
||||
cxx::let_cxx_string!(cpp_comment = comment);
|
||||
|
||||
unsafe {
|
||||
crate::imhex_api::ffi::ImHexApi::Bookmarks::add(region.address, region.size, &cpp_name, &cpp_comment, color.unwrap_or(crate::Color::new(0, 0, 0, 0)).rgba());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,42 @@
|
||||
#![allow(non_snake_case)]
|
||||
use autocxx::prelude::*;
|
||||
|
||||
include_cpp! {
|
||||
#include "hex/api/imhex_api.hpp"
|
||||
|
||||
safety!(unsafe)
|
||||
|
||||
generate!("hex::ImHexApi::Common::closeImHex")
|
||||
generate!("hex::ImHexApi::Common::restartImHex")
|
||||
generate!("hex::ImHexApi::Bookmarks::add")
|
||||
}
|
||||
|
||||
//pub use crate::ffi::*;
|
||||
|
||||
/// API for working with imhex bookmarks/highlights
|
||||
pub mod bookmarks;
|
||||
|
||||
/// API for working with imhex itself
|
||||
pub mod imhex;
|
||||
|
||||
mod imhex_api;
|
||||
|
||||
pub use imhex_macros::plugin_setup;
|
||||
pub use imhex_api::ImHexApi;
|
||||
pub use imhex_api::Region;
|
||||
pub use imgui;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use imhex_api::Color;
|
||||
pub use imgui;
|
||||
|
||||
/// A macro for declaring the init function for your plugin
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// #[hex::plugin_setup(
|
||||
/// /* Display name*/ "Example Rust",
|
||||
/// /* Author */ "WerWolv",
|
||||
/// /* Description */ "Example Rust plugin used as template for plugin devs"
|
||||
/// )]
|
||||
/// fn init() {
|
||||
/// // plugin initialization logic here
|
||||
/// }
|
||||
/// ```
|
||||
pub use imhex_macros::plugin_setup;
|
||||
|
||||
@@ -43,14 +43,31 @@ namespace hex {
|
||||
|
||||
struct Entry {
|
||||
std::string name;
|
||||
bool requiresRestart;
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
struct Category {
|
||||
std::string name;
|
||||
size_t slot = 0;
|
||||
|
||||
bool operator<(const Category &other) const {
|
||||
return name < other.name;
|
||||
}
|
||||
|
||||
operator const std::string &() const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
void load();
|
||||
void store();
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback, bool requiresRestart = false);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback, bool requiresRestart = false);
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback, bool requiresRestart = false);
|
||||
|
||||
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription);
|
||||
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value);
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &value);
|
||||
@@ -60,7 +77,8 @@ namespace hex {
|
||||
std::string read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue);
|
||||
std::vector<std::string> read(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue = {});
|
||||
|
||||
std::map<std::string, std::vector<Entry>> &getEntries();
|
||||
std::map<Category, std::vector<Entry>> &getEntries();
|
||||
std::map<std::string, std::string> &getCategoryDescriptions();
|
||||
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName);
|
||||
nlohmann::json &getSettingsData();
|
||||
}
|
||||
@@ -68,7 +86,8 @@ namespace hex {
|
||||
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
|
||||
namespace CommandPaletteCommands {
|
||||
|
||||
enum class Type : u32 {
|
||||
enum class Type : u32
|
||||
{
|
||||
SymbolCommand,
|
||||
KeywordCommand
|
||||
};
|
||||
@@ -173,7 +192,8 @@ namespace hex {
|
||||
/* Data Inspector Registry. Allows adding of new types to the data inspector */
|
||||
namespace DataInspector {
|
||||
|
||||
enum class NumberDisplayStyle {
|
||||
enum class NumberDisplayStyle
|
||||
{
|
||||
Decimal,
|
||||
Hexadecimal,
|
||||
Octal
|
||||
@@ -182,17 +202,19 @@ namespace hex {
|
||||
namespace impl {
|
||||
|
||||
using DisplayFunction = std::function<std::string()>;
|
||||
using EditingFunction = std::function<std::vector<u8>(std::string, std::endian)>;
|
||||
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
|
||||
|
||||
struct Entry {
|
||||
std::string unlocalizedName;
|
||||
size_t requiredSize;
|
||||
impl::GeneratorFunction generatorFunction;
|
||||
std::optional<impl::EditingFunction> editingFunction;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction function);
|
||||
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt);
|
||||
|
||||
std::vector<impl::Entry> &getEntries();
|
||||
}
|
||||
@@ -365,4 +387,4 @@ namespace hex {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace hex {
|
||||
};
|
||||
|
||||
namespace pl {
|
||||
class PatternData;
|
||||
class Pattern;
|
||||
}
|
||||
|
||||
/* Default Events */
|
||||
@@ -116,6 +116,7 @@ namespace hex {
|
||||
EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *);
|
||||
EVENT_DEF(EventFrameBegin);
|
||||
EVENT_DEF(EventFrameEnd);
|
||||
EVENT_DEF(EventWindowInitialized);
|
||||
|
||||
EVENT_DEF(RequestOpenWindow, std::string);
|
||||
EVENT_DEF(RequestSelectionChange, Region);
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
#include <hex/api/task.hpp>
|
||||
#include <hex/api/keybinding.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
using ImGuiID = unsigned int;
|
||||
struct ImVec2;
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -32,21 +34,32 @@ namespace hex {
|
||||
class Highlighting {
|
||||
public:
|
||||
Highlighting() = default;
|
||||
Highlighting(Region region, color_t color, const std::string &tooltip = "");
|
||||
Highlighting(Region region, color_t color, std::string tooltip = "");
|
||||
|
||||
[[nodiscard]] const Region &getRegion() const { return this->m_region; }
|
||||
[[nodiscard]] const color_t &getColor() const { return this->m_color; }
|
||||
[[nodiscard]] const std::string &getTooltip() const { return this->m_tooltip; }
|
||||
|
||||
private:
|
||||
Region m_region;
|
||||
color_t m_color;
|
||||
Region m_region = {};
|
||||
color_t m_color = 0x00;
|
||||
std::string m_tooltip;
|
||||
};
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, std::string tooltip = "");
|
||||
namespace impl {
|
||||
|
||||
using HighlightingFunction = std::function<std::optional<Highlighting>(u64)>;
|
||||
|
||||
std::map<u32, Highlighting> &getHighlights();
|
||||
std::map<u32, HighlightingFunction> &getHighlightingFunctions();
|
||||
|
||||
}
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip = "");
|
||||
void removeHighlight(u32 id);
|
||||
std::map<u32, Highlighting> &getHighlights();
|
||||
|
||||
u32 addHighlightingProvider(const impl::HighlightingFunction &function);
|
||||
void removeHighlightingProvider(u32 id);
|
||||
|
||||
Region getSelection();
|
||||
void setSelection(const Region ®ion);
|
||||
@@ -67,7 +80,6 @@ namespace hex {
|
||||
u32 highlightId;
|
||||
};
|
||||
|
||||
void add(Region region, const std::string &name, const std::string &comment, color_t color = 0x00000000);
|
||||
void add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000);
|
||||
|
||||
}
|
||||
@@ -112,6 +124,8 @@ namespace hex {
|
||||
void setGlobalScale(float scale);
|
||||
|
||||
void setProgramArguments(int argc, char **argv, char **envp);
|
||||
|
||||
void setBorderlessWindowMode(bool enabled);
|
||||
}
|
||||
|
||||
struct ProgramArguments {
|
||||
@@ -131,6 +145,8 @@ namespace hex {
|
||||
ImVec2 getMainWindowSize();
|
||||
ImGuiID getMainDockSpaceId();
|
||||
|
||||
bool isBorderlessWindowModeEnabled();
|
||||
|
||||
std::map<std::string, std::string> &getInitArguments();
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ namespace hex {
|
||||
|
||||
struct View;
|
||||
|
||||
enum class Keys {
|
||||
enum class Keys
|
||||
{
|
||||
Space = GLFW_KEY_SPACE,
|
||||
Apostrophe = GLFW_KEY_APOSTROPHE,
|
||||
Comma = GLFW_KEY_COMMA,
|
||||
@@ -141,6 +142,9 @@ namespace hex {
|
||||
|
||||
class Shortcut {
|
||||
public:
|
||||
Shortcut() = default;
|
||||
Shortcut(Keys key) : m_keys({ key }) { }
|
||||
|
||||
Shortcut operator+(const Key &other) const {
|
||||
Shortcut result = *this;
|
||||
result.m_keys.insert(other);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -166,7 +167,7 @@ namespace hex {
|
||||
template<typename T>
|
||||
class Cloneable {
|
||||
public:
|
||||
[[nodiscard]] virtual T *clone() const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<T> clone() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -20,7 +20,8 @@ namespace hex {
|
||||
|
||||
class File {
|
||||
public:
|
||||
enum class Mode {
|
||||
enum class Mode
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
Create
|
||||
@@ -36,7 +37,9 @@ namespace hex {
|
||||
File &operator=(File &&other) noexcept;
|
||||
|
||||
|
||||
[[nodiscard]] bool isValid() const { return this->m_file != nullptr; }
|
||||
[[nodiscard]] bool isValid() const {
|
||||
return this->m_file != nullptr && fs::exists(this->m_path) && !fs::is_directory(this->m_path);
|
||||
}
|
||||
|
||||
void seek(u64 offset);
|
||||
void close();
|
||||
@@ -53,11 +56,13 @@ namespace hex {
|
||||
void setSize(u64 size);
|
||||
|
||||
void flush();
|
||||
void remove();
|
||||
bool remove();
|
||||
|
||||
auto getHandle() { return this->m_file; }
|
||||
const fs::path &getPath() { return this->m_path; }
|
||||
|
||||
void disableBuffering();
|
||||
|
||||
private:
|
||||
FILE *m_file;
|
||||
fs::path m_path;
|
||||
|
||||
@@ -13,18 +13,26 @@ namespace hex::log {
|
||||
|
||||
namespace {
|
||||
|
||||
void printPrefix(const fmt::text_style &ts, const std::string &level) {
|
||||
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level) {
|
||||
const auto now = fmt::localtime(std::chrono::system_clock::now());
|
||||
fmt::print("[{0:%H:%M:%S}] ", now);
|
||||
fmt::print(ts, "{0} ", level);
|
||||
fmt::print("[{0}] ", IMHEX_PROJECT_NAME);
|
||||
|
||||
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
|
||||
|
||||
if (isRedirected())
|
||||
fmt::print(dest, "{0} ", level);
|
||||
else
|
||||
fmt::print(dest, ts, "{0} ", level);
|
||||
|
||||
fmt::print(dest, "[{0}] ", IMHEX_PROJECT_NAME);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto... args) {
|
||||
printPrefix(ts, level);
|
||||
fmt::print(getDestination(), fmt::runtime(fmt), args...);
|
||||
fmt::print("\n");
|
||||
auto dest = getDestination();
|
||||
|
||||
printPrefix(dest, ts, level);
|
||||
fmt::print(dest, fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,11 +35,13 @@ namespace hex {
|
||||
Net();
|
||||
~Net();
|
||||
|
||||
std::future<Response<std::string>> getString(const std::string &url, u32 timeout = 2000);
|
||||
std::future<Response<nlohmann::json>> getJson(const std::string &url, u32 timeout = 2000);
|
||||
static constexpr u32 DefaultTimeout = 2'000;
|
||||
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const fs::path &filePath, u32 timeout = 2000);
|
||||
std::future<Response<void>> downloadFile(const std::string &url, const fs::path &filePath, u32 timeout = 2000);
|
||||
std::future<Response<std::string>> getString(const std::string &url, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<nlohmann::json>> getJson(const std::string &url, u32 timeout = DefaultTimeout);
|
||||
|
||||
std::future<Response<std::string>> uploadFile(const std::string &url, const fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
std::future<Response<void>> downloadFile(const std::string &url, const fs::path &filePath, u32 timeout = DefaultTimeout);
|
||||
|
||||
[[nodiscard]] std::string encode(const std::string &input);
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ namespace hex {
|
||||
void runCommand(const std::string &command);
|
||||
void openWebpage(std::string url);
|
||||
|
||||
std::string encodeByteString(const std::vector<u8> &bytes);
|
||||
std::vector<u8> decodeByteString(const std::string &string);
|
||||
|
||||
bool isPathWritable(fs::path path);
|
||||
|
||||
[[nodiscard]] constexpr inline u64 extract(u8 from, u8 to, const hex::unsigned_integral auto &value) {
|
||||
if (from < to) std::swap(from, to);
|
||||
|
||||
@@ -189,6 +194,22 @@ namespace hex {
|
||||
return T(1) << bit_width(T(x - 1));
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
void moveToVector(std::vector<T> &buffer, T &&first, Args &&...rest) {
|
||||
buffer.push_back(std::move(first));
|
||||
|
||||
if constexpr (sizeof...(rest) > 0)
|
||||
moveToVector(buffer, std::move(rest)...);
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
std::vector<T> moveToVector(T &&first, Args &&...rest) {
|
||||
std::vector<T> result;
|
||||
moveToVector(result, T(std::move(first)), std::move(rest)...);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> splitString(const std::string &string, const std::string &delimiter);
|
||||
std::string combineStrings(const std::vector<std::string> &strings, const std::string &delimiter = "");
|
||||
|
||||
@@ -247,13 +268,14 @@ namespace hex {
|
||||
trimRight(s);
|
||||
}
|
||||
|
||||
enum class DialogMode {
|
||||
enum class DialogMode
|
||||
{
|
||||
Open,
|
||||
Save,
|
||||
Folder
|
||||
};
|
||||
|
||||
void openFileBrowser(const std::string &title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback, const std::string &defaultPath = {});
|
||||
bool openFileBrowser(const std::string &title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback, const std::string &defaultPath = {});
|
||||
|
||||
float float16ToFloat32(u16 float16);
|
||||
|
||||
@@ -307,7 +329,9 @@ namespace hex {
|
||||
ScopeGuard &operator=(ScopeGuard &&) = delete;
|
||||
};
|
||||
|
||||
enum class ScopeGuardOnExit { };
|
||||
enum class ScopeGuardOnExit
|
||||
{
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
constexpr ScopeGuard<F> operator+(ScopeGuardOnExit, F &&f) {
|
||||
@@ -328,7 +352,9 @@ namespace hex {
|
||||
FirstTimeExecute &operator=(FirstTimeExecute &&) = delete;
|
||||
};
|
||||
|
||||
enum class FirstTimeExecutor { };
|
||||
enum class FirstTimeExecutor
|
||||
{
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
constexpr FirstTimeExecute<F> operator+(FirstTimeExecutor, F &&f) {
|
||||
@@ -352,7 +378,9 @@ namespace hex {
|
||||
FinalCleanupExecute &operator=(FinalCleanupExecute &&) = delete;
|
||||
};
|
||||
|
||||
enum class FinalCleanupExecutor { };
|
||||
enum class FinalCleanupExecutor
|
||||
{
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
constexpr FinalCleanupExecute<F> operator+(FinalCleanupExecutor, F &&f) {
|
||||
|
||||
36
lib/libimhex/include/hex/pattern_language/ast/ast_node.hpp
Normal file
36
lib/libimhex/include/hex/pattern_language/ast/ast_node.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class Pattern;
|
||||
class Evaluator;
|
||||
|
||||
class ASTNode : public Cloneable<ASTNode> {
|
||||
public:
|
||||
constexpr ASTNode() = default;
|
||||
|
||||
constexpr virtual ~ASTNode() = default;
|
||||
|
||||
constexpr ASTNode(const ASTNode &) = default;
|
||||
|
||||
[[nodiscard]] constexpr u32 getLineNumber() const { return this->m_lineNumber; }
|
||||
|
||||
[[maybe_unused]] constexpr void setLineNumber(u32 lineNumber) { this->m_lineNumber = lineNumber; }
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const { return this->clone(); }
|
||||
|
||||
[[nodiscard]] virtual std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const { return {}; }
|
||||
|
||||
using FunctionResult = std::optional<Token::Literal>;
|
||||
virtual FunctionResult execute(Evaluator *evaluator) const { LogConsole::abortEvaluation("cannot execute non-function statement", this); }
|
||||
|
||||
private:
|
||||
u32 m_lineNumber = 1;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_builtin_type.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_while_statement.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeArrayVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeArrayVariableDecl(std::string name, std::unique_ptr<ASTNode> &&type, std::unique_ptr<ASTNode> &&size, std::unique_ptr<ASTNode> &&placementOffset = {})
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_size(std::move(size)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodeArrayVariableDecl(const ASTNodeArrayVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
if (other.m_size != nullptr)
|
||||
this->m_size = other.m_size->clone();
|
||||
else
|
||||
this->m_size = nullptr;
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeArrayVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
auto evaluatedPlacement = this->m_placementOffset->evaluate(evaluator);
|
||||
auto offset = dynamic_cast<ASTNodeLiteral *>(evaluatedPlacement.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (dynamic_cast<ASTNodeBuiltinType *>(type.get()))
|
||||
pattern = createStaticArray(evaluator);
|
||||
else if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
bool isStaticType = attributable->hasAttribute("static", false);
|
||||
|
||||
if (isStaticType)
|
||||
pattern = createStaticArray(evaluator);
|
||||
else
|
||||
pattern = createDynamicArray(evaluator);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid type used in array", this);
|
||||
}
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::unique_ptr<ASTNode> m_size;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
std::unique_ptr<Pattern> createStaticArray(Evaluator *evaluator) const {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
auto templatePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &templatePattern = templatePatterns.front();
|
||||
|
||||
evaluator->dataOffset() = startOffset;
|
||||
|
||||
i128 entryCount = 0;
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> i128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> i128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> i128 { return size; } },
|
||||
literal->getValue());
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
entryCount++;
|
||||
evaluator->dataOffset() += templatePattern->getSize();
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
if (entryCount < 0)
|
||||
LogConsole::abortEvaluation("array cannot have a negative size", this);
|
||||
} else {
|
||||
std::vector<u8> buffer(templatePattern->getSize());
|
||||
while (true) {
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size())
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset(), buffer.data(), buffer.size());
|
||||
evaluator->dataOffset() += buffer.size();
|
||||
|
||||
entryCount++;
|
||||
|
||||
bool reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> outputPattern;
|
||||
if (dynamic_cast<PatternPadding *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternString(evaluator, startOffset, 0));
|
||||
} else if (dynamic_cast<PatternWideCharacter *>(templatePattern.get())) {
|
||||
outputPattern = std::unique_ptr<Pattern>(new PatternWideString(evaluator, startOffset, 0));
|
||||
} else {
|
||||
auto arrayPattern = std::make_unique<PatternArrayStatic>(evaluator, startOffset, 0);
|
||||
arrayPattern->setEntries(templatePattern->clone(), entryCount);
|
||||
outputPattern = std::move(arrayPattern);
|
||||
}
|
||||
|
||||
outputPattern->setVariableName(this->m_name);
|
||||
outputPattern->setEndian(templatePattern->getEndian());
|
||||
outputPattern->setTypeName(templatePattern->getTypeName());
|
||||
outputPattern->setSize(templatePattern->getSize() * entryCount);
|
||||
|
||||
evaluator->dataOffset() = startOffset + outputPattern->getSize();
|
||||
|
||||
return outputPattern;
|
||||
}
|
||||
|
||||
std::unique_ptr<Pattern> createDynamicArray(Evaluator *evaluator) const {
|
||||
auto arrayPattern = std::make_unique<PatternArrayDynamic>(evaluator, evaluator->dataOffset(), 0);
|
||||
arrayPattern->setVariableName(this->m_name);
|
||||
|
||||
std::vector<std::shared_ptr<Pattern>> entries;
|
||||
|
||||
size_t size = 0;
|
||||
u64 entryIndex = 0;
|
||||
|
||||
auto addEntries = [&](std::vector<std::unique_ptr<Pattern>> &&patterns) {
|
||||
for (auto &pattern : patterns) {
|
||||
pattern->setVariableName(hex::format("[{}]", entryIndex));
|
||||
pattern->setEndian(arrayPattern->getEndian());
|
||||
|
||||
size += pattern->getSize();
|
||||
entryIndex++;
|
||||
|
||||
entries.push_back(std::move(pattern));
|
||||
|
||||
evaluator->handleAbort();
|
||||
}
|
||||
};
|
||||
|
||||
auto discardEntries = [&](u32 count) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
entries.pop_back();
|
||||
entryIndex--;
|
||||
}
|
||||
};
|
||||
|
||||
if (this->m_size != nullptr) {
|
||||
auto sizeNode = this->m_size->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(sizeNode.get())) {
|
||||
auto entryCount = std::visit(overloaded {
|
||||
[this](const std::string &) -> u128 { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u128 { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[](auto &&size) -> u128 { return size; } },
|
||||
literal->getValue());
|
||||
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryCount > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
for (u64 i = 0; i < entryCount; i++) {
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if (auto whileStatement = dynamic_cast<ASTNodeWhileStatement *>(sizeNode.get())) {
|
||||
while (whileStatement->evaluateCondition(evaluator)) {
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
size_t patternCount = patterns.size();
|
||||
|
||||
if (!patterns.empty())
|
||||
addEntries(std::move(patterns));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(patternCount);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while (true) {
|
||||
bool reachedEnd = true;
|
||||
auto limit = evaluator->getArrayLimit();
|
||||
if (entryIndex > limit)
|
||||
LogConsole::abortEvaluation(hex::format("array grew past set limit of {}", limit), this);
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
std::vector<u8> buffer(pattern->getSize());
|
||||
|
||||
if (evaluator->dataOffset() > evaluator->getProvider()->getActualSize() - buffer.size()) {
|
||||
LogConsole::abortEvaluation("reached end of file before finding end of unsized array", this);
|
||||
}
|
||||
|
||||
const auto patternSize = pattern->getSize();
|
||||
addEntries(hex::moveToVector(std::move(pattern)));
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::None)
|
||||
break;
|
||||
|
||||
evaluator->getProvider()->read(evaluator->dataOffset() - patternSize, buffer.data(), buffer.size());
|
||||
reachedEnd = true;
|
||||
for (u8 &byte : buffer) {
|
||||
if (byte != 0x00) {
|
||||
reachedEnd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
|
||||
auto ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue) {
|
||||
discardEntries(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reachedEnd) break;
|
||||
}
|
||||
}
|
||||
|
||||
arrayPattern->setEntries(std::move(entries));
|
||||
|
||||
if (auto &arrayEntries = arrayPattern->getEntries(); !entries.empty())
|
||||
arrayPattern->setTypeName(arrayEntries.front()->getTypeName());
|
||||
|
||||
arrayPattern->setSize(size);
|
||||
|
||||
return std::move(arrayPattern);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAssignment : public ASTNode {
|
||||
public:
|
||||
ASTNodeAssignment(std::string lvalueName, std::unique_ptr<ASTNode> &&rvalue) : m_lvalueName(std::move(lvalueName)), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeAssignment(const ASTNodeAssignment &other) : ASTNode(other) {
|
||||
this->m_lvalueName = other.m_lvalueName;
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAssignment(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getLValueName() const {
|
||||
return this->m_lvalueName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRValue() const {
|
||||
return this->m_rvalue;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
const auto node = this->getRValue()->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
if (this->getLValueName() == "$")
|
||||
evaluator->dataOffset() = Token::literalToUnsigned(literal->getValue());
|
||||
else
|
||||
evaluator->setVariable(this->getLValueName(), literal->getValue());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_lvalueName;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeAttribute : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeAttribute(std::string attribute, std::optional<std::string> value = std::nullopt)
|
||||
: ASTNode(), m_attribute(std::move(attribute)), m_value(std::move(value)) { }
|
||||
|
||||
~ASTNodeAttribute() override = default;
|
||||
|
||||
ASTNodeAttribute(const ASTNodeAttribute &other) : ASTNode(other) {
|
||||
this->m_attribute = other.m_attribute;
|
||||
this->m_value = other.m_value;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeAttribute(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getAttribute() const {
|
||||
return this->m_attribute;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::optional<std::string> &getValue() const {
|
||||
return this->m_value;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_attribute;
|
||||
std::optional<std::string> m_value;
|
||||
};
|
||||
|
||||
|
||||
class Attributable {
|
||||
protected:
|
||||
Attributable() = default;
|
||||
|
||||
Attributable(const Attributable &other) {
|
||||
for (auto &attribute : other.m_attributes) {
|
||||
auto copy = attribute->clone();
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(copy.get()))
|
||||
this->m_attributes.push_back(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) {
|
||||
this->m_attributes.push_back(std::move(attribute));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getAttributes() const {
|
||||
return this->m_attributes;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasAttribute(const std::string &key, bool needsParameter) const {
|
||||
return std::any_of(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
if (attribute->getAttribute() == key) {
|
||||
if (needsParameter && !attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' expected a parameter"), attribute);
|
||||
else if (!needsParameter && attribute->getValue().has_value())
|
||||
LogConsole::abortEvaluation(hex::format("attribute '{}' did not expect a parameter "), attribute);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::string> getAttributeValue(const std::string &key) const {
|
||||
auto attribute = std::find_if(this->m_attributes.begin(), this->m_attributes.end(), [&](const std::unique_ptr<ASTNodeAttribute> &attribute) {
|
||||
return attribute->getAttribute() == key;
|
||||
});
|
||||
|
||||
if (attribute != this->m_attributes.end())
|
||||
return (*attribute)->getValue();
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNodeAttribute>> m_attributes;
|
||||
};
|
||||
|
||||
|
||||
inline void applyTypeAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
if (attributable->hasAttribute("inline", false)) {
|
||||
auto inlinable = dynamic_cast<Inlinable *>(pattern);
|
||||
|
||||
if (inlinable == nullptr)
|
||||
LogConsole::abortEvaluation("inline attribute can only be applied to nested types", node);
|
||||
else
|
||||
inlinable->setInlined(true);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
pattern->setFormatterFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("format_entries"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find formatter function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("formatter function needs exactly one parameter", node);
|
||||
|
||||
auto array = dynamic_cast<PatternArrayDynamic *>(pattern);
|
||||
if (array == nullptr)
|
||||
LogConsole::abortEvaluation("inline_array attribute can only be applied to array types", node);
|
||||
|
||||
for (const auto &entry : array->getEntries()) {
|
||||
entry->setFormatterFunction(function);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("transform"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find transform function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("transform function needs exactly one parameter", node);
|
||||
|
||||
pattern->setTransformFunction(function);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("pointer_base"); value) {
|
||||
auto functions = evaluator->getCustomFunctions();
|
||||
if (!functions.contains(*value))
|
||||
LogConsole::abortEvaluation(hex::format("cannot find pointer base function '{}'", *value), node);
|
||||
|
||||
const auto &function = functions[*value];
|
||||
if (function.parameterCount != 1)
|
||||
LogConsole::abortEvaluation("pointer base function needs exactly one parameter", node);
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(pattern)) {
|
||||
u128 pointerValue = pointerPattern->getPointedAtAddress();
|
||||
|
||||
auto result = function.func(evaluator, { pointerValue });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("pointer base function did not return a value", node);
|
||||
|
||||
pointerPattern->setPointedAtAddress(Token::literalToUnsigned(result.value()) + pointerValue);
|
||||
} else {
|
||||
LogConsole::abortEvaluation("pointer_base attribute may only be applied to a pointer");
|
||||
}
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("hidden", false)) {
|
||||
pattern->setHidden(true);
|
||||
}
|
||||
|
||||
if (!pattern->hasOverriddenColor()) {
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void applyVariableAttributes(Evaluator *evaluator, const ASTNode *node, Pattern *pattern) {
|
||||
auto attributable = dynamic_cast<const Attributable *>(node);
|
||||
if (attributable == nullptr)
|
||||
LogConsole::abortEvaluation("attribute cannot be applied here", node);
|
||||
|
||||
auto endOffset = evaluator->dataOffset();
|
||||
evaluator->dataOffset() = pattern->getOffset();
|
||||
ON_SCOPE_EXIT { evaluator->dataOffset() = endOffset; };
|
||||
|
||||
applyTypeAttributes(evaluator, node, pattern);
|
||||
|
||||
if (auto colorValue = attributable->getAttributeValue("color"); colorValue) {
|
||||
u32 color = strtoul(colorValue->c_str(), nullptr, 16);
|
||||
pattern->setColor(hex::changeEndianess(color, std::endian::big) >> 8);
|
||||
} else if (auto singleColor = attributable->hasAttribute("single_color", false); singleColor) {
|
||||
pattern->setColor(ContentRegistry::PatternLanguage::getNextColor());
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("name"); value) {
|
||||
pattern->setDisplayName(*value);
|
||||
}
|
||||
|
||||
if (auto value = attributable->getAttributeValue("comment"); value) {
|
||||
pattern->setComment(*value);
|
||||
}
|
||||
|
||||
if (attributable->hasAttribute("no_unique_address", false)) {
|
||||
endOffset -= pattern->getSize();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBitfield : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeBitfield() : ASTNode() { }
|
||||
|
||||
ASTNodeBitfield(const ASTNodeBitfield &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace_back(name, entry->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBitfield(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&size) { this->m_entries.emplace_back(name, std::move(size)); }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternBitfield>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t bitOffset = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> fields;
|
||||
|
||||
BitfieldOrder order = evaluator->getBitfieldOrder();
|
||||
if (this->hasAttribute("left_to_right", false))
|
||||
order = BitfieldOrder::LeftToRight;
|
||||
else if (this->hasAttribute("right_to_left", false))
|
||||
order = BitfieldOrder::RightToLeft;
|
||||
|
||||
std::vector<std::pair<std::string, ASTNode *>> entries;
|
||||
for (const auto &[name, entry] : this->m_entries)
|
||||
entries.push_back({ name, entry.get() });
|
||||
|
||||
if (order == BitfieldOrder::LeftToRight)
|
||||
std::reverse(entries.begin(), entries.end());
|
||||
|
||||
evaluator->pushScope(pattern.get(), fields);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &[name, bitSizeNode] : entries) {
|
||||
auto literal = bitSizeNode->evaluate(evaluator);
|
||||
|
||||
u8 bitSize = std::visit(overloaded {
|
||||
[this](const std::string &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u8 { LogConsole::abortEvaluation("bitfield field size cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u8 { return static_cast<u8>(offset); } },
|
||||
dynamic_cast<ASTNodeLiteral *>(literal.get())->getValue());
|
||||
|
||||
// If a field is named padding, it was created through a padding expression and only advances the bit position
|
||||
if (name != "padding") {
|
||||
auto field = std::make_unique<PatternBitfieldField>(evaluator, evaluator->dataOffset(), bitOffset, bitSize, pattern.get());
|
||||
field->setVariableName(name);
|
||||
|
||||
fields.push_back(std::move(field));
|
||||
}
|
||||
|
||||
bitOffset += bitSize;
|
||||
}
|
||||
|
||||
pattern->setSize((bitOffset + 7) / 8);
|
||||
pattern->setFields(std::move(fields));
|
||||
|
||||
evaluator->dataOffset() += pattern->getSize();
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_entries;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_padding.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_wide_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeBuiltinType : public ASTNode {
|
||||
public:
|
||||
constexpr explicit ASTNodeBuiltinType(Token::ValueType type)
|
||||
: ASTNode(), m_type(type) { }
|
||||
|
||||
[[nodiscard]] constexpr const auto &getType() const { return this->m_type; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeBuiltinType(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto offset = evaluator->dataOffset();
|
||||
auto size = Token::getTypeSize(this->m_type);
|
||||
|
||||
evaluator->dataOffset() += size;
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
if (Token::isUnsigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(evaluator, offset, size));
|
||||
else if (Token::isSigned(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternSigned(evaluator, offset, size));
|
||||
else if (Token::isFloatingPoint(this->m_type))
|
||||
pattern = std::unique_ptr<Pattern>(new PatternFloat(evaluator, offset, size));
|
||||
else if (this->m_type == Token::ValueType::Boolean)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternBoolean(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Character16)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternWideCharacter(evaluator, offset));
|
||||
else if (this->m_type == Token::ValueType::Padding)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternPadding(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::String)
|
||||
pattern = std::unique_ptr<Pattern>(new PatternString(evaluator, offset, 1));
|
||||
else if (this->m_type == Token::ValueType::Auto)
|
||||
return {};
|
||||
else
|
||||
LogConsole::abortEvaluation("invalid built-in type", this);
|
||||
|
||||
pattern->setTypeName(Token::getTypeName(this->m_type));
|
||||
|
||||
return hex::moveToVector(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
const Token::ValueType m_type;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCast : public ASTNode {
|
||||
public:
|
||||
ASTNodeCast(std::unique_ptr<ASTNode> &&value, std::unique_ptr<ASTNode> &&type) : m_value(std::move(value)), m_type(std::move(type)) { }
|
||||
|
||||
ASTNodeCast(const ASTNodeCast &other) : ASTNode(other) {
|
||||
this->m_value = other.m_value->clone();
|
||||
this->m_type = other.m_type->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCast(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto evaluatedValue = this->m_value->evaluate(evaluator);
|
||||
auto evaluatedType = this->m_type->evaluate(evaluator);
|
||||
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(evaluatedValue.get());
|
||||
auto type = dynamic_cast<ASTNodeBuiltinType *>(evaluatedType.get())->getType();
|
||||
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
auto typePatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &typePattern = typePatterns.front();
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
[&, this](const std::shared_ptr<Pattern> &value) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast custom type '{}' to '{}'", value->getTypeName(), Token::getTypeName(type)), this); },
|
||||
[&, this](const std::string &) -> ASTNode * { LogConsole::abortEvaluation(hex::format("cannot cast string to '{}'", Token::getTypeName(type)), this); },
|
||||
[&, this](auto &&value) -> ASTNode * {
|
||||
auto endianAdjustedValue = hex::changeEndianess(value, typePattern->getSize(), typePattern->getEndian());
|
||||
switch (type) {
|
||||
case Token::ValueType::Unsigned8Bit:
|
||||
return new ASTNodeLiteral(u128(u8(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned16Bit:
|
||||
return new ASTNodeLiteral(u128(u16(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned32Bit:
|
||||
return new ASTNodeLiteral(u128(u32(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned64Bit:
|
||||
return new ASTNodeLiteral(u128(u64(endianAdjustedValue)));
|
||||
case Token::ValueType::Unsigned128Bit:
|
||||
return new ASTNodeLiteral(u128(endianAdjustedValue));
|
||||
case Token::ValueType::Signed8Bit:
|
||||
return new ASTNodeLiteral(i128(i8(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed16Bit:
|
||||
return new ASTNodeLiteral(i128(i16(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed32Bit:
|
||||
return new ASTNodeLiteral(i128(i32(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed64Bit:
|
||||
return new ASTNodeLiteral(i128(i64(endianAdjustedValue)));
|
||||
case Token::ValueType::Signed128Bit:
|
||||
return new ASTNodeLiteral(i128(endianAdjustedValue));
|
||||
case Token::ValueType::Float:
|
||||
return new ASTNodeLiteral(double(float(endianAdjustedValue)));
|
||||
case Token::ValueType::Double:
|
||||
return new ASTNodeLiteral(double(endianAdjustedValue));
|
||||
case Token::ValueType::Character:
|
||||
return new ASTNodeLiteral(char(endianAdjustedValue));
|
||||
case Token::ValueType::Character16:
|
||||
return new ASTNodeLiteral(u128(char16_t(endianAdjustedValue)));
|
||||
case Token::ValueType::Boolean:
|
||||
return new ASTNodeLiteral(bool(endianAdjustedValue));
|
||||
case Token::ValueType::String:
|
||||
{
|
||||
std::string string(sizeof(value), '\x00');
|
||||
std::memcpy(string.data(), &value, string.size());
|
||||
hex::trim(string);
|
||||
|
||||
if (typePattern->getEndian() != std::endian::native)
|
||||
std::reverse(string.begin(), string.end());
|
||||
|
||||
return new ASTNodeLiteral(string);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast value to '{}'", Token::getTypeName(type)), this);
|
||||
}
|
||||
},
|
||||
},
|
||||
literal->getValue()));
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_value;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeCompoundStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeCompoundStatement(std::vector<std::unique_ptr<ASTNode>> &&statements, bool newScope = false) : m_statements(std::move(statements)), m_newScope(newScope) {
|
||||
}
|
||||
|
||||
ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other) : ASTNode(other) {
|
||||
for (const auto &statement : other.m_statements) {
|
||||
this->m_statements.push_back(statement->clone());
|
||||
}
|
||||
|
||||
this->m_newScope = other.m_newScope;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeCompoundStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
std::unique_ptr<ASTNode> result = nullptr;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->evaluate(evaluator);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> result;
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
auto patterns = statement->createPatterns(evaluator);
|
||||
std::move(patterns.begin(), patterns.end(), std::back_inserter(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
FunctionResult result;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
u32 startVariableCount = variables.size();
|
||||
|
||||
if (this->m_newScope) {
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
}
|
||||
|
||||
for (const auto &statement : this->m_statements) {
|
||||
result = statement->execute(evaluator);
|
||||
if (evaluator->getCurrentControlFlowStatement() != ControlFlowStatement::None)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (this->m_newScope) {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
|
||||
evaluator->popScope();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_statements;
|
||||
bool m_newScope = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeConditionalStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeConditionalStatement(std::unique_ptr<ASTNode> condition, std::vector<std::unique_ptr<ASTNode>> &&trueBody, std::vector<std::unique_ptr<ASTNode>> &&falseBody)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_trueBody(std::move(trueBody)), m_falseBody(std::move(falseBody)) { }
|
||||
|
||||
|
||||
ASTNodeConditionalStatement(const ASTNodeConditionalStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_trueBody)
|
||||
this->m_trueBody.push_back(statement->clone());
|
||||
for (auto &statement : other.m_falseBody)
|
||||
this->m_falseBody.push_back(statement->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeConditionalStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto &scope = *evaluator->getScope(0).scope;
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
for (auto &node : body) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
for (auto &pattern : newPatterns) {
|
||||
scope.push_back(std::move(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
auto &body = evaluateCondition(evaluator) ? this->m_trueBody : this->m_falseBody;
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
i64 stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &statement : body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
if (auto ctrlStatement = evaluator->getCurrentControlFlowStatement(); ctrlStatement != ControlFlowStatement::None) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_trueBody, m_falseBody;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeControlFlowStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeControlFlowStatement(ControlFlowStatement type, std::unique_ptr<ASTNode> &&rvalue) : m_type(type), m_rvalue(std::move(rvalue)) {
|
||||
}
|
||||
|
||||
ASTNodeControlFlowStatement(const ASTNodeControlFlowStatement &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type;
|
||||
|
||||
if (other.m_rvalue != nullptr)
|
||||
this->m_rvalue = other.m_rvalue->clone();
|
||||
else
|
||||
this->m_rvalue = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeControlFlowStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getReturnValue() const {
|
||||
return this->m_rvalue;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
auto returnValue = this->m_rvalue->evaluate(evaluator);
|
||||
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(returnValue.get());
|
||||
|
||||
evaluator->setCurrentControlFlowStatement(this->m_type);
|
||||
|
||||
if (literal == nullptr)
|
||||
return std::nullopt;
|
||||
else
|
||||
return literal->getValue();
|
||||
}
|
||||
|
||||
private:
|
||||
ControlFlowStatement m_type;
|
||||
std::unique_ptr<ASTNode> m_rvalue;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeEnum : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
explicit ASTNodeEnum(std::unique_ptr<ASTNode> &&underlyingType) : ASTNode(), m_underlyingType(std::move(underlyingType)) { }
|
||||
|
||||
ASTNodeEnum(const ASTNodeEnum &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &[name, entry] : other.getEntries())
|
||||
this->m_entries.emplace(name, entry->clone());
|
||||
this->m_underlyingType = other.m_underlyingType->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeEnum(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternEnum>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
std::vector<std::pair<Token::Literal, std::string>> enumEntries;
|
||||
for (const auto &[name, value] : this->m_entries) {
|
||||
const auto node = value->evaluate(evaluator);
|
||||
auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
enumEntries.emplace_back(literal->getValue(), name);
|
||||
}
|
||||
|
||||
pattern->setEnumValues(enumEntries);
|
||||
|
||||
const auto nodes = this->m_underlyingType->createPatterns(evaluator);
|
||||
auto &underlying = nodes.front();
|
||||
|
||||
pattern->setSize(underlying->getSize());
|
||||
pattern->setEndian(underlying->getEndian());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::map<std::string, std::unique_ptr<ASTNode>> &getEntries() const { return this->m_entries; }
|
||||
void addEntry(const std::string &name, std::unique_ptr<ASTNode> &&expression) { this->m_entries.insert({ name, std::move(expression) }); }
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getUnderlyingType() { return this->m_underlyingType; }
|
||||
|
||||
private:
|
||||
std::map<std::string, std::unique_ptr<ASTNode>> m_entries;
|
||||
std::unique_ptr<ASTNode> m_underlyingType;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_mathematical_expression.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionCall : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeFunctionCall(std::string functionName, std::vector<std::unique_ptr<ASTNode>> &¶ms)
|
||||
: ASTNode(), m_functionName(std::move(functionName)), m_params(std::move(params)) { }
|
||||
|
||||
ASTNodeFunctionCall(const ASTNodeFunctionCall &other) : ASTNode(other) {
|
||||
this->m_functionName = other.m_functionName;
|
||||
|
||||
for (auto ¶m : other.m_params)
|
||||
this->m_params.push_back(param->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionCall(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getFunctionName() {
|
||||
return this->m_functionName;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
|
||||
this->execute(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
ON_SCOPE_EXIT { evaluator->dataOffset() = startOffset; };
|
||||
|
||||
std::vector<Token::Literal> evaluatedParams;
|
||||
for (auto ¶m : this->m_params) {
|
||||
const auto expression = param->evaluate(evaluator)->evaluate(evaluator);
|
||||
|
||||
if (auto literal = dynamic_cast<ASTNodeLiteral *>(expression.get())) {
|
||||
evaluatedParams.push_back(literal->getValue());
|
||||
} else if (auto parameterPack = dynamic_cast<ASTNodeParameterPack *>(expression.get())) {
|
||||
for (auto &value : parameterPack->getValues()) {
|
||||
evaluatedParams.push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto &customFunctions = evaluator->getCustomFunctions();
|
||||
auto functions = ContentRegistry::PatternLanguage::getFunctions();
|
||||
|
||||
for (auto &func : customFunctions)
|
||||
functions.insert(func);
|
||||
|
||||
if (!functions.contains(this->m_functionName)) {
|
||||
if (this->m_functionName.starts_with("std::")) {
|
||||
evaluator->getConsole().log(LogConsole::Level::Warning, "This function might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store and then\ninclude the correct file.");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("call to unknown function '{}'", this->m_functionName), this);
|
||||
}
|
||||
|
||||
auto function = functions[this->m_functionName];
|
||||
if (function.parameterCount == ContentRegistry::PatternLanguage::UnlimitedParameters) {
|
||||
; // Don't check parameter count
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::LessParametersThan) {
|
||||
if (evaluatedParams.size() >= (function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too many parameters for function '{0}'. Expected less than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::LessParametersThan), this);
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::MoreParametersThan) {
|
||||
if (evaluatedParams.size() <= (function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, function.parameterCount & ~ContentRegistry::PatternLanguage::MoreParametersThan), this);
|
||||
} else if (function.parameterCount & ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan) {
|
||||
if (evaluatedParams.size() < (function.parameterCount & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan))
|
||||
LogConsole::abortEvaluation(hex::format("too few parameters for function '{0}'. Expected more than {1}", this->m_functionName, (function.parameterCount - 1) & ~ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan), this);
|
||||
} else if (function.parameterCount != evaluatedParams.size()) {
|
||||
LogConsole::abortEvaluation(hex::format("invalid number of parameters for function '{0}'. Expected {1}", this->m_functionName, function.parameterCount), this);
|
||||
}
|
||||
|
||||
try {
|
||||
if (function.dangerous && evaluator->getDangerousFunctionPermission() != DangerousFunctionPermission::Allow) {
|
||||
evaluator->dangerousFunctionCalled();
|
||||
|
||||
while (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Ask) {
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
|
||||
if (evaluator->getDangerousFunctionPermission() == DangerousFunctionPermission::Deny) {
|
||||
LogConsole::abortEvaluation(hex::format("calling of dangerous function '{}' is not allowed", this->m_functionName), this);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = function.func(evaluator, evaluatedParams);
|
||||
|
||||
if (result.has_value())
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(result.value())));
|
||||
else
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(nullptr, nullptr, Token::Operator::Plus));
|
||||
} catch (std::string &error) {
|
||||
LogConsole::abortEvaluation(error, this);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
(void)this->evaluate(evaluator);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_functionName;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_params;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeFunctionDefinition : public ASTNode {
|
||||
public:
|
||||
ASTNodeFunctionDefinition(std::string name, std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> &¶ms, std::vector<std::unique_ptr<ASTNode>> &&body, std::optional<std::string> parameterPack)
|
||||
: m_name(std::move(name)), m_params(std::move(params)), m_body(std::move(body)), m_parameterPack(std::move(parameterPack)) {
|
||||
}
|
||||
|
||||
ASTNodeFunctionDefinition(const ASTNodeFunctionDefinition &other) : ASTNode(other) {
|
||||
this->m_name = other.m_name;
|
||||
|
||||
for (const auto &[name, type] : other.m_params) {
|
||||
this->m_params.emplace_back(name, type->clone());
|
||||
}
|
||||
|
||||
for (auto &statement : other.m_body) {
|
||||
this->m_body.push_back(statement->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeFunctionDefinition(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const {
|
||||
return this->m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getParams() const {
|
||||
return this->m_params;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getBody() const {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
|
||||
size_t paramCount = this->m_params.size();
|
||||
|
||||
if (this->m_parameterPack.has_value())
|
||||
paramCount |= ContentRegistry::PatternLanguage::ExactlyOrMoreParametersThan;
|
||||
|
||||
evaluator->addCustomFunction(this->m_name, paramCount, [this](Evaluator *ctx, const std::vector<Token::Literal> ¶ms) -> std::optional<Token::Literal> {
|
||||
std::vector<std::shared_ptr<Pattern>> variables;
|
||||
|
||||
auto startOffset = ctx->dataOffset();
|
||||
ctx->pushScope(nullptr, variables);
|
||||
ON_SCOPE_EXIT {
|
||||
ctx->popScope();
|
||||
ctx->dataOffset() = startOffset;
|
||||
};
|
||||
|
||||
if (this->m_parameterPack.has_value()) {
|
||||
std::vector<Token::Literal> parameterPackContent;
|
||||
for (u32 paramIndex = this->m_params.size(); paramIndex < params.size(); paramIndex++)
|
||||
parameterPackContent.push_back(params[paramIndex]);
|
||||
|
||||
ctx->createParameterPack(this->m_parameterPack.value(), parameterPackContent);
|
||||
}
|
||||
|
||||
for (u32 paramIndex = 0; paramIndex < this->m_params.size(); paramIndex++) {
|
||||
const auto &[name, type] = this->m_params[paramIndex];
|
||||
|
||||
ctx->createVariable(name, type.get(), params[paramIndex]);
|
||||
ctx->setVariable(name, params[paramIndex]);
|
||||
}
|
||||
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(ctx);
|
||||
|
||||
if (ctx->getCurrentControlFlowStatement() != ControlFlowStatement::None) {
|
||||
switch (ctx->getCurrentControlFlowStatement()) {
|
||||
case ControlFlowStatement::Break:
|
||||
LogConsole::abortEvaluation("break statement not within a loop", statement);
|
||||
case ControlFlowStatement::Continue:
|
||||
LogConsole::abortEvaluation("continue statement not within a loop", statement);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ctx->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::pair<std::string, std::unique_ptr<ASTNode>>> m_params;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::optional<std::string> m_parameterPack;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeLiteral : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeLiteral(Token::Literal literal) : ASTNode(), m_literal(std::move(literal)) { }
|
||||
|
||||
ASTNodeLiteral(const ASTNodeLiteral &) = default;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getValue() const {
|
||||
return this->m_literal;
|
||||
}
|
||||
|
||||
private:
|
||||
Token::Literal m_literal;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
#define FLOAT_BIT_OPERATION(name) \
|
||||
auto name(hex::floating_point auto left, auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(auto left, hex::floating_point auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::floating_point auto left, hex::floating_point auto right) const { \
|
||||
LogConsole::abortEvaluation("invalid floating point operation", this); \
|
||||
return 0; \
|
||||
} \
|
||||
auto name(hex::integral auto left, hex::integral auto right) const
|
||||
|
||||
class ASTNodeMathematicalExpression : public ASTNode {
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftLeft) {
|
||||
return left << right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(shiftRight) {
|
||||
return left >> right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitAnd) {
|
||||
return left & right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitOr) {
|
||||
return left | right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitXor) {
|
||||
return left ^ right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(bitNot) {
|
||||
return ~right;
|
||||
}
|
||||
|
||||
FLOAT_BIT_OPERATION(modulus) {
|
||||
return left % right;
|
||||
}
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
public:
|
||||
ASTNodeMathematicalExpression(std::unique_ptr<ASTNode> &&left, std::unique_ptr<ASTNode> &&right, Token::Operator op)
|
||||
: ASTNode(), m_left(std::move(left)), m_right(std::move(right)), m_operator(op) { }
|
||||
|
||||
ASTNodeMathematicalExpression(const ASTNodeMathematicalExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_left = other.m_left->clone();
|
||||
this->m_right = other.m_right->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMathematicalExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getLeftOperand() == nullptr || this->getRightOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto leftValue = this->getLeftOperand()->evaluate(evaluator);
|
||||
auto rightValue = this->getRightOperand()->evaluate(evaluator);
|
||||
|
||||
auto *left = dynamic_cast<ASTNodeLiteral *>(leftValue.get());
|
||||
auto *right = dynamic_cast<ASTNodeLiteral *>(rightValue.get());
|
||||
|
||||
return std::unique_ptr<ASTNode>(std::visit(overloaded {
|
||||
// TODO: :notlikethis:
|
||||
[this](u128 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](i128 left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](double left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](char left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](bool left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::shared_ptr<Pattern> &left, const std::shared_ptr<Pattern> &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
|
||||
[this](auto &&left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
|
||||
[this](const std::string &left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Star:
|
||||
{
|
||||
std::string result;
|
||||
for (auto i = 0; i < right; i++)
|
||||
result += left;
|
||||
return new ASTNodeLiteral(result);
|
||||
}
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(left == right);
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(left != right);
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(left > right);
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(left < right);
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(left >= right);
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(left <= right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](const std::string &left, char right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](char left, const std::string &right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
},
|
||||
[this](auto &&left, auto &&right) -> ASTNode * {
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::Plus:
|
||||
return new ASTNodeLiteral(left + right);
|
||||
case Token::Operator::Minus:
|
||||
return new ASTNodeLiteral(left - right);
|
||||
case Token::Operator::Star:
|
||||
return new ASTNodeLiteral(left * right);
|
||||
case Token::Operator::Slash:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(left / right);
|
||||
case Token::Operator::Percent:
|
||||
if (right == 0) LogConsole::abortEvaluation("division by zero!", this);
|
||||
return new ASTNodeLiteral(modulus(left, right));
|
||||
case Token::Operator::ShiftLeft:
|
||||
return new ASTNodeLiteral(shiftLeft(left, right));
|
||||
case Token::Operator::ShiftRight:
|
||||
return new ASTNodeLiteral(shiftRight(left, right));
|
||||
case Token::Operator::BitAnd:
|
||||
return new ASTNodeLiteral(bitAnd(left, right));
|
||||
case Token::Operator::BitXor:
|
||||
return new ASTNodeLiteral(bitXor(left, right));
|
||||
case Token::Operator::BitOr:
|
||||
return new ASTNodeLiteral(bitOr(left, right));
|
||||
case Token::Operator::BitNot:
|
||||
return new ASTNodeLiteral(bitNot(left, right));
|
||||
case Token::Operator::BoolEquals:
|
||||
return new ASTNodeLiteral(bool(left == right));
|
||||
case Token::Operator::BoolNotEquals:
|
||||
return new ASTNodeLiteral(bool(left != right));
|
||||
case Token::Operator::BoolGreaterThan:
|
||||
return new ASTNodeLiteral(bool(left > right));
|
||||
case Token::Operator::BoolLessThan:
|
||||
return new ASTNodeLiteral(bool(left < right));
|
||||
case Token::Operator::BoolGreaterThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left >= right));
|
||||
case Token::Operator::BoolLessThanOrEquals:
|
||||
return new ASTNodeLiteral(bool(left <= right));
|
||||
case Token::Operator::BoolAnd:
|
||||
return new ASTNodeLiteral(bool(left && right));
|
||||
case Token::Operator::BoolXor:
|
||||
return new ASTNodeLiteral(bool(left && !right || !left && right));
|
||||
case Token::Operator::BoolOr:
|
||||
return new ASTNodeLiteral(bool(left || right));
|
||||
case Token::Operator::BoolNot:
|
||||
return new ASTNodeLiteral(bool(!right));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid operand used in mathematical expression", this);
|
||||
}
|
||||
} },
|
||||
left->getValue(),
|
||||
right->getValue()));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getLeftOperand() const { return this->m_left; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getRightOperand() const { return this->m_right; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_left, m_right;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
#undef FLOAT_BIT_OPERATION
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeMultiVariableDecl : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeMultiVariableDecl(std::vector<std::unique_ptr<ASTNode>> &&variables) : m_variables(std::move(variables)) { }
|
||||
|
||||
ASTNodeMultiVariableDecl(const ASTNodeMultiVariableDecl &other) : ASTNode(other) {
|
||||
for (auto &variable : other.m_variables)
|
||||
this->m_variables.push_back(variable->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeMultiVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getVariables() {
|
||||
return this->m_variables;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::unique_ptr<Pattern>> patterns;
|
||||
|
||||
for (auto &node : this->m_variables) {
|
||||
auto newPatterns = node->createPatterns(evaluator);
|
||||
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
for (auto &variable : this->m_variables) {
|
||||
auto variableDecl = dynamic_cast<ASTNodeVariableDecl *>(variable.get());
|
||||
auto variableType = variableDecl->getType()->evaluate(evaluator);
|
||||
|
||||
evaluator->createVariable(variableDecl->getName(), variableType.get());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<ASTNode>> m_variables;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeParameterPack : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeParameterPack(std::vector<Token::Literal> &&values) : m_values(std::move(values)) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<Token::Literal> &getValues() const {
|
||||
return this->m_values;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Token::Literal> m_values;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodePointerVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodePointerVariableDecl(std::string name, std::shared_ptr<ASTNode> &&type, std::shared_ptr<ASTNode> &&sizeType, std::unique_ptr<ASTNode> &&placementOffset = nullptr)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_sizeType(std::move(sizeType)), m_placementOffset(std::move(placementOffset)) { }
|
||||
|
||||
ASTNodePointerVariableDecl(const ASTNodePointerVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_sizeType = other.m_sizeType->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodePointerVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::shared_ptr<ASTNode> &getSizeType() const { return this->m_sizeType; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return u64(offset); } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto pointerStartOffset = evaluator->dataOffset();
|
||||
|
||||
const auto sizePatterns = this->m_sizeType->createPatterns(evaluator);
|
||||
const auto &sizePattern = sizePatterns.front();
|
||||
|
||||
auto pattern = std::make_unique<PatternPointer>(evaluator, pointerStartOffset, sizePattern->getSize());
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
auto pointerEndOffset = evaluator->dataOffset();
|
||||
|
||||
{
|
||||
u128 pointerAddress = 0;
|
||||
evaluator->getProvider()->read(pattern->getOffset(), &pointerAddress, pattern->getSize());
|
||||
pointerAddress = hex::changeEndianess(pointerAddress, sizePattern->getSize(), sizePattern->getEndian());
|
||||
|
||||
evaluator->dataOffset() = pointerStartOffset;
|
||||
|
||||
pattern->setPointedAtAddress(pointerAddress);
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
evaluator->dataOffset() = pattern->getPointedAtAddress();
|
||||
|
||||
auto pointedAtPatterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pointedAtPattern = pointedAtPatterns.front();
|
||||
|
||||
pattern->setPointedAtPattern(std::move(pointedAtPattern));
|
||||
pattern->setEndian(sizePattern->getEndian());
|
||||
}
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
} else {
|
||||
evaluator->dataOffset() = pointerEndOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNode> m_type;
|
||||
std::shared_ptr<ASTNode> m_sizeType;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_parameter_pack.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_pointer.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_dynamic.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_array_static.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_bitfield.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeRValue : public ASTNode {
|
||||
public:
|
||||
using PathSegment = std::variant<std::string, std::unique_ptr<ASTNode>>;
|
||||
using Path = std::vector<PathSegment>;
|
||||
|
||||
explicit ASTNodeRValue(Path &&path) : ASTNode(), m_path(std::move(path)) { }
|
||||
|
||||
ASTNodeRValue(const ASTNodeRValue &other) : ASTNode(other) {
|
||||
for (auto &part : other.m_path) {
|
||||
if (auto stringPart = std::get_if<std::string>(&part); stringPart != nullptr)
|
||||
this->m_path.push_back(*stringPart);
|
||||
else if (auto nodePart = std::get_if<std::unique_ptr<ASTNode>>(&part); nodePart != nullptr)
|
||||
this->m_path.push_back((*nodePart)->clone());
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeRValue(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const Path &getPath() const {
|
||||
return this->m_path;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getPath().size() == 1) {
|
||||
if (auto name = std::get_if<std::string>(&this->getPath().front()); name != nullptr) {
|
||||
if (*name == "$") return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(evaluator->dataOffset())));
|
||||
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
if (parameterPack && *name == parameterPack->name)
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeParameterPack(std::move(parameterPack->values)));
|
||||
}
|
||||
}
|
||||
|
||||
auto patterns = this->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
Token::Literal literal;
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get())) {
|
||||
u128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternSigned *>(pattern.get())) {
|
||||
i128 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
value = hex::signExtend(pattern->getSize() * 8, value);
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternFloat *>(pattern.get())) {
|
||||
if (pattern->getSize() == sizeof(u16)) {
|
||||
u16 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(float16ToFloat32(value));
|
||||
} else if (pattern->getSize() == sizeof(float)) {
|
||||
float value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = double(value);
|
||||
} else if (pattern->getSize() == sizeof(double)) {
|
||||
double value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else LogConsole::abortEvaluation("invalid floating point type access", this);
|
||||
} else if (dynamic_cast<PatternCharacter *>(pattern.get())) {
|
||||
char value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternBoolean *>(pattern.get())) {
|
||||
bool value = false;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = value;
|
||||
} else if (dynamic_cast<PatternString *>(pattern.get())) {
|
||||
std::string value;
|
||||
|
||||
if (pattern->isLocal()) {
|
||||
auto &variableValue = evaluator->getStack()[pattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](char assignmentValue) { if (assignmentValue != 0x00) value = std::string({ assignmentValue }); },
|
||||
[&](std::string &assignmentValue) { value = assignmentValue; },
|
||||
[&, this](Pattern *const &assignmentValue) {
|
||||
if (!dynamic_cast<PatternString *>(assignmentValue) && !dynamic_cast<PatternCharacter *>(assignmentValue))
|
||||
LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this);
|
||||
|
||||
readVariable(evaluator, value, assignmentValue);
|
||||
},
|
||||
[&, this](auto &&assignmentValue) { LogConsole::abortEvaluation(hex::format("cannot assign '{}' to string", pattern->getTypeName()), this); } },
|
||||
variableValue);
|
||||
} else {
|
||||
value.resize(pattern->getSize());
|
||||
evaluator->getProvider()->read(pattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
}
|
||||
|
||||
literal = value;
|
||||
} else if (auto bitfieldFieldPattern = dynamic_cast<PatternBitfieldField *>(pattern.get())) {
|
||||
u64 value = 0;
|
||||
readVariable(evaluator, value, pattern.get());
|
||||
literal = u128(hex::extract(bitfieldFieldPattern->getBitOffset() + (bitfieldFieldPattern->getBitSize() - 1), bitfieldFieldPattern->getBitOffset(), value));
|
||||
} else {
|
||||
literal = pattern->clone();
|
||||
}
|
||||
|
||||
if (auto transformFunc = pattern->getTransformFunction(); transformFunc.has_value()) {
|
||||
auto result = transformFunc->func(evaluator, { std::move(literal) });
|
||||
|
||||
if (!result.has_value())
|
||||
LogConsole::abortEvaluation("transform function did not return a value", this);
|
||||
literal = std::move(result.value());
|
||||
}
|
||||
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(std::move(literal)));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
std::vector<std::shared_ptr<Pattern>> searchScope;
|
||||
std::unique_ptr<Pattern> currPattern;
|
||||
i32 scopeIndex = 0;
|
||||
|
||||
|
||||
if (!evaluator->isGlobalScope()) {
|
||||
auto globalScope = evaluator->getGlobalScope().scope;
|
||||
std::copy(globalScope->begin(), globalScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
{
|
||||
auto currScope = evaluator->getScope(scopeIndex).scope;
|
||||
std::copy(currScope->begin(), currScope->end(), std::back_inserter(searchScope));
|
||||
}
|
||||
|
||||
for (const auto &part : this->getPath()) {
|
||||
|
||||
if (part.index() == 0) {
|
||||
// Variable access
|
||||
auto name = std::get<std::string>(part);
|
||||
|
||||
if (name == "parent") {
|
||||
scopeIndex--;
|
||||
|
||||
if (-scopeIndex >= evaluator->getScopeCount())
|
||||
LogConsole::abortEvaluation("cannot access parent of global scope", this);
|
||||
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
auto currParent = evaluator->getScope(scopeIndex).parent;
|
||||
|
||||
if (currParent == nullptr) {
|
||||
currPattern = nullptr;
|
||||
} else {
|
||||
currPattern = currParent->clone();
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (name == "this") {
|
||||
searchScope = *evaluator->getScope(scopeIndex).scope;
|
||||
|
||||
auto currParent = evaluator->getScope(0).parent;
|
||||
|
||||
if (currParent == nullptr)
|
||||
LogConsole::abortEvaluation("invalid use of 'this' outside of struct-like type", this);
|
||||
|
||||
currPattern = currParent->clone();
|
||||
continue;
|
||||
} else {
|
||||
bool found = false;
|
||||
for (auto iter = searchScope.crbegin(); iter != searchScope.crend(); ++iter) {
|
||||
if ((*iter)->getVariableName() == name) {
|
||||
currPattern = (*iter)->clone();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "$")
|
||||
LogConsole::abortEvaluation("invalid use of placeholder operator in rvalue");
|
||||
|
||||
if (!found) {
|
||||
LogConsole::abortEvaluation(hex::format("no variable named '{}' found", name), this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Array indexing
|
||||
auto node = std::get<std::unique_ptr<ASTNode>>(part)->evaluate(evaluator);
|
||||
auto index = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
std::visit(overloaded {
|
||||
[this](const std::string &) { LogConsole::abortEvaluation("cannot use string to index array", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) { LogConsole::abortEvaluation("cannot use custom type to index array", this); },
|
||||
[&, this](auto &&index) {
|
||||
if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(currPattern.get())) {
|
||||
if (index >= searchScope.size() || index < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
currPattern = searchScope[index]->clone();
|
||||
} else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(currPattern.get())) {
|
||||
if (index >= staticArrayPattern->getEntryCount() || index < 0)
|
||||
LogConsole::abortEvaluation("array index out of bounds", this);
|
||||
|
||||
auto newPattern = searchScope.front()->clone();
|
||||
newPattern->setOffset(staticArrayPattern->getOffset() + index * staticArrayPattern->getTemplate()->getSize());
|
||||
currPattern = std::move(newPattern);
|
||||
}
|
||||
} },
|
||||
index->getValue());
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
break;
|
||||
|
||||
if (auto pointerPattern = dynamic_cast<PatternPointer *>(currPattern.get())) {
|
||||
currPattern = pointerPattern->getPointedAtPattern()->clone();
|
||||
}
|
||||
|
||||
std::shared_ptr<Pattern> indexPattern;
|
||||
if (currPattern->isLocal()) {
|
||||
auto stackLiteral = evaluator->getStack()[currPattern->getOffset()];
|
||||
if (auto stackPattern = std::get_if<std::shared_ptr<Pattern>>(&stackLiteral); stackPattern != nullptr)
|
||||
indexPattern = *stackPattern;
|
||||
else
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
} else
|
||||
indexPattern = currPattern->clone();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(indexPattern.get()))
|
||||
searchScope = structPattern->getMembers();
|
||||
else if (auto unionPattern = dynamic_cast<PatternUnion *>(indexPattern.get()))
|
||||
searchScope = unionPattern->getMembers();
|
||||
else if (auto bitfieldPattern = dynamic_cast<PatternBitfield *>(indexPattern.get()))
|
||||
searchScope = bitfieldPattern->getFields();
|
||||
else if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(indexPattern.get()))
|
||||
searchScope = dynamicArrayPattern->getEntries();
|
||||
else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(indexPattern.get()))
|
||||
searchScope = { staticArrayPattern->getTemplate() };
|
||||
}
|
||||
|
||||
if (currPattern == nullptr)
|
||||
LogConsole::abortEvaluation("cannot reference global scope", this);
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
|
||||
}
|
||||
|
||||
private:
|
||||
Path m_path;
|
||||
|
||||
void readVariable(Evaluator *evaluator, auto &value, Pattern *variablePattern) const {
|
||||
constexpr bool isString = std::same_as<std::remove_cvref_t<decltype(value)>, std::string>;
|
||||
|
||||
if (variablePattern->isLocal()) {
|
||||
auto &literal = evaluator->getStack()[variablePattern->getOffset()];
|
||||
|
||||
std::visit(overloaded {
|
||||
[&](std::string &assignmentValue) {
|
||||
if constexpr (isString) value = assignmentValue;
|
||||
},
|
||||
[&](std::shared_ptr<Pattern> &assignmentValue) { readVariable(evaluator, value, assignmentValue.get()); },
|
||||
[&](auto &&assignmentValue) { value = assignmentValue; } },
|
||||
literal);
|
||||
} else {
|
||||
if constexpr (isString) {
|
||||
value.resize(variablePattern->getSize());
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), value.data(), value.size());
|
||||
value.erase(std::find(value.begin(), value.end(), '\0'), value.end());
|
||||
} else {
|
||||
evaluator->getProvider()->read(variablePattern->getOffset(), &value, variablePattern->getSize());
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (!isString)
|
||||
value = hex::changeEndianess(value, variablePattern->getSize(), variablePattern->getEndian());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeScopeResolution : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeScopeResolution(std::unique_ptr<ASTNode> &&type, std::string name) : ASTNode(), m_type(std::move(type)), m_name(std::move(name)) { }
|
||||
|
||||
ASTNodeScopeResolution(const ASTNodeScopeResolution &other) : ASTNode(other) {
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_name = other.m_name;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeScopeResolution(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
if (auto enumType = dynamic_cast<ASTNodeEnum *>(type.get())) {
|
||||
for (auto &[name, value] : enumType->getEntries()) {
|
||||
if (name == this->m_name)
|
||||
return value->evaluate(evaluator);
|
||||
}
|
||||
} else {
|
||||
LogConsole::abortEvaluation("invalid scope resolution. Cannot access this type");
|
||||
}
|
||||
|
||||
LogConsole::abortEvaluation(hex::format("could not find constant '{}'", this->m_name), this);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_struct.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeStruct : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeStruct() : ASTNode() { }
|
||||
|
||||
ASTNodeStruct(const ASTNodeStruct &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
for (const auto &otherInheritance : other.getInheritance())
|
||||
this->m_inheritance.push_back(otherInheritance->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeStruct(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternStruct>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &inheritance : this->m_inheritance) {
|
||||
auto inheritancePatterns = inheritance->createPatterns(evaluator);
|
||||
auto &inheritancePattern = inheritancePatterns.front();
|
||||
|
||||
if (auto structPattern = dynamic_cast<PatternStruct *>(inheritancePattern.get())) {
|
||||
for (auto &member : structPattern->getMembers()) {
|
||||
memberPatterns.push_back(member->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(evaluator->dataOffset() - startOffset);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getInheritance() const { return this->m_inheritance; }
|
||||
void addInheritance(std::shared_ptr<ASTNode> &&node) { this->m_inheritance.push_back(std::move(node)); }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
std::vector<std::shared_ptr<ASTNode>> m_inheritance;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTernaryExpression : public ASTNode {
|
||||
public:
|
||||
ASTNodeTernaryExpression(std::unique_ptr<ASTNode> &&first, std::unique_ptr<ASTNode> &&second, std::unique_ptr<ASTNode> &&third, Token::Operator op)
|
||||
: ASTNode(), m_first(std::move(first)), m_second(std::move(second)), m_third(std::move(third)), m_operator(op) { }
|
||||
|
||||
ASTNodeTernaryExpression(const ASTNodeTernaryExpression &other) : ASTNode(other) {
|
||||
this->m_operator = other.m_operator;
|
||||
this->m_first = other.m_first->clone();
|
||||
this->m_second = other.m_second->clone();
|
||||
this->m_third = other.m_third->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTernaryExpression(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
if (this->getFirstOperand() == nullptr || this->getSecondOperand() == nullptr || this->getThirdOperand() == nullptr)
|
||||
LogConsole::abortEvaluation("attempted to use void expression in mathematical expression", this);
|
||||
|
||||
auto firstNode = this->getFirstOperand()->evaluate(evaluator);
|
||||
auto secondNode = this->getSecondOperand()->evaluate(evaluator);
|
||||
auto thirdNode = this->getThirdOperand()->evaluate(evaluator);
|
||||
|
||||
auto *first = dynamic_cast<ASTNodeLiteral *>(firstNode.get());
|
||||
auto *second = dynamic_cast<ASTNodeLiteral *>(secondNode.get());
|
||||
auto *third = dynamic_cast<ASTNodeLiteral *>(thirdNode.get());
|
||||
|
||||
auto condition = std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return bool(value); } },
|
||||
first->getValue());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[condition]<typename T>(const T &second, const T &third) -> std::unique_ptr<ASTNode> { return std::unique_ptr<ASTNode>(new ASTNodeLiteral(condition ? second : third)); },
|
||||
[this](auto &&second, auto &&third) -> std::unique_ptr<ASTNode> { LogConsole::abortEvaluation("operands to ternary expression have different types", this); } },
|
||||
second->getValue(),
|
||||
third->getValue());
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getFirstOperand() const { return this->m_first; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getSecondOperand() const { return this->m_second; }
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getThirdOperand() const { return this->m_third; }
|
||||
[[nodiscard]] Token::Operator getOperator() const { return this->m_operator; }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_first, m_second, m_third;
|
||||
Token::Operator m_operator;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeTypeDecl(std::string name, std::shared_ptr<ASTNode> &&type, std::optional<std::endian> endian = std::nullopt)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_endian(endian) { }
|
||||
|
||||
ASTNodeTypeDecl(const ASTNodeTypeDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
this->m_endian = other.m_endian;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeDecl(*this));
|
||||
}
|
||||
|
||||
void setName(const std::string &name) { this->m_name = name; }
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] const std::shared_ptr<ASTNode> &getType() { return this->m_type; }
|
||||
[[nodiscard]] std::optional<std::endian> getEndian() const { return this->m_endian; }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto type = this->m_type->evaluate(evaluator);
|
||||
|
||||
if (auto attributable = dynamic_cast<Attributable *>(type.get())) {
|
||||
for (auto &attribute : this->getAttributes()) {
|
||||
if (auto node = dynamic_cast<ASTNodeAttribute *>(attribute.get()))
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(node));
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
|
||||
for (auto &pattern : patterns) {
|
||||
if (pattern == nullptr)
|
||||
continue;
|
||||
|
||||
if (!this->m_name.empty())
|
||||
pattern->setTypeName(this->m_name);
|
||||
|
||||
if (this->m_endian.has_value())
|
||||
pattern->setEndian(this->m_endian.value());
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
}
|
||||
|
||||
return patterns;
|
||||
}
|
||||
|
||||
void addAttribute(std::unique_ptr<ASTNodeAttribute> &&attribute) override {
|
||||
if (auto attributable = dynamic_cast<Attributable *>(this->m_type.get()); attributable != nullptr) {
|
||||
attributable->addAttribute(std::unique_ptr<ASTNodeAttribute>(static_cast<ASTNodeAttribute *>(attribute->clone().release())));
|
||||
}
|
||||
|
||||
Attributable::addAttribute(std::move(attribute));
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::shared_ptr<ASTNode> m_type;
|
||||
std::optional<std::endian> m_endian;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeTypeOperator : public ASTNode {
|
||||
public:
|
||||
ASTNodeTypeOperator(Token::Operator op, std::unique_ptr<ASTNode> &&expression) : m_op(op), m_expression(std::move(expression)) {
|
||||
}
|
||||
|
||||
ASTNodeTypeOperator(const ASTNodeTypeOperator &other) : ASTNode(other) {
|
||||
this->m_op = other.m_op;
|
||||
this->m_expression = other.m_expression->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeTypeOperator(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] Token::Operator getOperator() const {
|
||||
return this->m_op;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getExpression() const {
|
||||
return this->m_expression;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> evaluate(Evaluator *evaluator) const override {
|
||||
auto patterns = this->m_expression->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
|
||||
switch (this->getOperator()) {
|
||||
case Token::Operator::AddressOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getOffset())));
|
||||
case Token::Operator::SizeOf:
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeLiteral(u128(pattern->getSize())));
|
||||
default:
|
||||
LogConsole::abortEvaluation("invalid type operator", this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Token::Operator m_op;
|
||||
std::unique_ptr<ASTNode> m_expression;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_union.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeUnion : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeUnion() : ASTNode() { }
|
||||
|
||||
ASTNodeUnion(const ASTNodeUnion &other) : ASTNode(other), Attributable(other) {
|
||||
for (const auto &otherMember : other.getMembers())
|
||||
this->m_members.push_back(otherMember->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeUnion(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
auto pattern = std::make_unique<PatternUnion>(evaluator, evaluator->dataOffset(), 0);
|
||||
|
||||
size_t size = 0;
|
||||
std::vector<std::shared_ptr<Pattern>> memberPatterns;
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
evaluator->pushScope(pattern.get(), memberPatterns);
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
for (auto &member : this->m_members) {
|
||||
for (auto &memberPattern : member->createPatterns(evaluator)) {
|
||||
memberPattern->setOffset(startOffset);
|
||||
size = std::max(memberPattern->getSize(), size);
|
||||
|
||||
memberPatterns.push_back(std::move(memberPattern));
|
||||
}
|
||||
}
|
||||
|
||||
evaluator->dataOffset() = startOffset + size;
|
||||
pattern->setMembers(std::move(memberPatterns));
|
||||
pattern->setSize(size);
|
||||
|
||||
applyTypeAttributes(evaluator, this, pattern.get());
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getMembers() const { return this->m_members; }
|
||||
void addMember(std::shared_ptr<ASTNode> &&node) { this->m_members.push_back(std::move(node)); }
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<ASTNode>> m_members;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_literal.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeVariableDecl : public ASTNode,
|
||||
public Attributable {
|
||||
public:
|
||||
ASTNodeVariableDecl(std::string name, std::unique_ptr<ASTNode> &&type, std::unique_ptr<ASTNode> &&placementOffset = nullptr, bool inVariable = false, bool outVariable = false)
|
||||
: ASTNode(), m_name(std::move(name)), m_type(std::move(type)), m_placementOffset(std::move(placementOffset)), m_inVariable(inVariable), m_outVariable(outVariable) { }
|
||||
|
||||
ASTNodeVariableDecl(const ASTNodeVariableDecl &other) : ASTNode(other), Attributable(other) {
|
||||
this->m_name = other.m_name;
|
||||
this->m_type = other.m_type->clone();
|
||||
|
||||
if (other.m_placementOffset != nullptr)
|
||||
this->m_placementOffset = other.m_placementOffset->clone();
|
||||
else
|
||||
this->m_placementOffset = nullptr;
|
||||
|
||||
this->m_inVariable = other.m_inVariable;
|
||||
this->m_outVariable = other.m_outVariable;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeVariableDecl(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string &getName() const { return this->m_name; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getType() const { return this->m_type; }
|
||||
[[nodiscard]] constexpr const std::unique_ptr<ASTNode> &getPlacementOffset() const { return this->m_placementOffset; }
|
||||
|
||||
[[nodiscard]] constexpr bool isInVariable() const { return this->m_inVariable; }
|
||||
[[nodiscard]] constexpr bool isOutVariable() const { return this->m_outVariable; }
|
||||
|
||||
[[nodiscard]] std::vector<std::unique_ptr<Pattern>> createPatterns(Evaluator *evaluator) const override {
|
||||
u64 startOffset = evaluator->dataOffset();
|
||||
|
||||
if (this->m_placementOffset != nullptr) {
|
||||
const auto node = this->m_placementOffset->evaluate(evaluator);
|
||||
const auto offset = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
evaluator->dataOffset() = std::visit(overloaded {
|
||||
[this](const std::string &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a string", this); },
|
||||
[this](const std::shared_ptr<Pattern> &) -> u64 { LogConsole::abortEvaluation("placement offset cannot be a custom type", this); },
|
||||
[](auto &&offset) -> u64 { return offset; } },
|
||||
offset->getValue());
|
||||
}
|
||||
|
||||
auto patterns = this->m_type->createPatterns(evaluator);
|
||||
auto &pattern = patterns.front();
|
||||
pattern->setVariableName(this->m_name);
|
||||
|
||||
applyVariableAttributes(evaluator, this, pattern.get());
|
||||
|
||||
if (this->m_placementOffset != nullptr && !evaluator->isGlobalScope()) {
|
||||
evaluator->dataOffset() = startOffset;
|
||||
}
|
||||
|
||||
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(pattern));
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
evaluator->createVariable(this->getName(), this->getType().get());
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::unique_ptr<ASTNode> m_type;
|
||||
std::unique_ptr<ASTNode> m_placementOffset;
|
||||
|
||||
bool m_inVariable = false, m_outVariable = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class ASTNodeWhileStatement : public ASTNode {
|
||||
public:
|
||||
explicit ASTNodeWhileStatement(std::unique_ptr<ASTNode> &&condition, std::vector<std::unique_ptr<ASTNode>> &&body, std::unique_ptr<ASTNode> &&postExpression = nullptr)
|
||||
: ASTNode(), m_condition(std::move(condition)), m_body(std::move(body)), m_postExpression(std::move(postExpression)) { }
|
||||
|
||||
ASTNodeWhileStatement(const ASTNodeWhileStatement &other) : ASTNode(other) {
|
||||
this->m_condition = other.m_condition->clone();
|
||||
|
||||
for (auto &statement : other.m_body)
|
||||
this->m_body.push_back(statement->clone());
|
||||
|
||||
if (other.m_postExpression != nullptr)
|
||||
this->m_postExpression = other.m_postExpression->clone();
|
||||
else
|
||||
this->m_postExpression = nullptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ASTNode> clone() const override {
|
||||
return std::unique_ptr<ASTNode>(new ASTNodeWhileStatement(*this));
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<ASTNode> &getCondition() {
|
||||
return this->m_condition;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<std::unique_ptr<ASTNode>> &getBody() {
|
||||
return this->m_body;
|
||||
}
|
||||
|
||||
FunctionResult execute(Evaluator *evaluator) const override {
|
||||
|
||||
u64 loopIterations = 0;
|
||||
while (evaluateCondition(evaluator)) {
|
||||
evaluator->handleAbort();
|
||||
|
||||
auto variables = *evaluator->getScope(0).scope;
|
||||
auto parameterPack = evaluator->getScope(0).parameterPack;
|
||||
u32 startVariableCount = variables.size();
|
||||
ON_SCOPE_EXIT {
|
||||
ssize_t stackSize = evaluator->getStack().size();
|
||||
for (u32 i = startVariableCount; i < variables.size(); i++) {
|
||||
stackSize--;
|
||||
}
|
||||
if (stackSize < 0) LogConsole::abortEvaluation("stack pointer underflow!", this);
|
||||
evaluator->getStack().resize(stackSize);
|
||||
};
|
||||
|
||||
evaluator->pushScope(nullptr, variables);
|
||||
evaluator->getScope(0).parameterPack = parameterPack;
|
||||
ON_SCOPE_EXIT {
|
||||
evaluator->popScope();
|
||||
};
|
||||
|
||||
auto ctrlFlow = ControlFlowStatement::None;
|
||||
for (auto &statement : this->m_body) {
|
||||
auto result = statement->execute(evaluator);
|
||||
|
||||
ctrlFlow = evaluator->getCurrentControlFlowStatement();
|
||||
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
if (ctrlFlow == ControlFlowStatement::Return)
|
||||
return result;
|
||||
else if (ctrlFlow != ControlFlowStatement::None)
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->m_postExpression != nullptr)
|
||||
this->m_postExpression->execute(evaluator);
|
||||
|
||||
loopIterations++;
|
||||
if (loopIterations >= evaluator->getLoopLimit())
|
||||
LogConsole::abortEvaluation(hex::format("loop iterations exceeded limit of {}", evaluator->getLoopLimit()), this);
|
||||
|
||||
evaluator->handleAbort();
|
||||
|
||||
if (ctrlFlow == ControlFlowStatement::Break)
|
||||
break;
|
||||
else if (ctrlFlow == ControlFlowStatement::Continue)
|
||||
continue;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool evaluateCondition(Evaluator *evaluator) const {
|
||||
const auto node = this->m_condition->evaluate(evaluator);
|
||||
const auto literal = dynamic_cast<ASTNodeLiteral *>(node.get());
|
||||
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &value) -> bool { return !value.empty(); },
|
||||
[this](Pattern *const &) -> bool { LogConsole::abortEvaluation("cannot cast custom type to bool", this); },
|
||||
[](auto &&value) -> bool { return value != 0; } },
|
||||
literal->getValue());
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ASTNode> m_condition;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_body;
|
||||
std::unique_ptr<ASTNode> m_postExpression;
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,13 @@ namespace hex::pl {
|
||||
Return
|
||||
};
|
||||
|
||||
class PatternData;
|
||||
enum class BitfieldOrder
|
||||
{
|
||||
RightToLeft,
|
||||
LeftToRight
|
||||
};
|
||||
|
||||
class Pattern;
|
||||
class PatternCreationLimiter;
|
||||
class ASTNode;
|
||||
|
||||
@@ -42,7 +48,7 @@ namespace hex::pl {
|
||||
public:
|
||||
Evaluator() = default;
|
||||
|
||||
std::optional<std::vector<PatternData *>> evaluate(const std::vector<ASTNode *> &ast);
|
||||
std::optional<std::vector<std::shared_ptr<Pattern>>> evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast);
|
||||
|
||||
[[nodiscard]] LogConsole &getConsole() {
|
||||
return this->m_console;
|
||||
@@ -54,11 +60,11 @@ namespace hex::pl {
|
||||
};
|
||||
|
||||
struct Scope {
|
||||
PatternData *parent;
|
||||
std::vector<PatternData *> *scope;
|
||||
Pattern *parent;
|
||||
std::vector<std::shared_ptr<Pattern>> *scope;
|
||||
std::optional<ParameterPack> parameterPack;
|
||||
};
|
||||
void pushScope(PatternData *parent, std::vector<PatternData *> &scope) {
|
||||
void pushScope(Pattern *parent, std::vector<std::shared_ptr<Pattern>> &scope) {
|
||||
if (this->m_scopes.size() > this->getEvaluationDepth())
|
||||
LogConsole::abortEvaluation(hex::format("evaluation depth exceeded set limit of {}", this->getEvaluationDepth()));
|
||||
|
||||
@@ -161,6 +167,14 @@ namespace hex::pl {
|
||||
return this->m_loopLimit;
|
||||
}
|
||||
|
||||
void setBitfieldOrder(BitfieldOrder order) {
|
||||
this->m_bitfieldOrder = order;
|
||||
}
|
||||
|
||||
[[nodiscard]] BitfieldOrder getBitfieldOrder() {
|
||||
return this->m_bitfieldOrder;
|
||||
}
|
||||
|
||||
u64 &dataOffset() { return this->m_currOffset; }
|
||||
|
||||
bool addCustomFunction(const std::string &name, u32 numParams, const ContentRegistry::PatternLanguage::Callback &function) {
|
||||
@@ -197,9 +211,10 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Token::Literal> getEnvVariable(const std::string &name) const {
|
||||
if (this->m_envVariables.contains(name))
|
||||
if (this->m_envVariables.contains(name)) {
|
||||
auto value = this->m_envVariables.at(name);
|
||||
return this->m_envVariables.at(name);
|
||||
else
|
||||
} else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -232,7 +247,7 @@ namespace hex::pl {
|
||||
return this->m_currControlFlowStatement;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Token::Literal> getMainResult() {
|
||||
[[nodiscard]] const std::optional<Token::Literal> &getMainResult() {
|
||||
return this->m_mainResult;
|
||||
}
|
||||
|
||||
@@ -257,7 +272,7 @@ namespace hex::pl {
|
||||
|
||||
std::vector<Scope> m_scopes;
|
||||
std::map<std::string, ContentRegistry::PatternLanguage::Function> m_customFunctions;
|
||||
std::vector<ASTNode *> m_customFunctionDefinitions;
|
||||
std::vector<std::unique_ptr<ASTNode>> m_customFunctionDefinitions;
|
||||
std::vector<Token::Literal> m_stack;
|
||||
|
||||
std::optional<Token::Literal> m_mainResult;
|
||||
@@ -269,6 +284,7 @@ namespace hex::pl {
|
||||
std::atomic<bool> m_dangerousFunctionCalled = false;
|
||||
std::atomic<DangerousFunctionPermission> m_allowDangerousFunctions = DangerousFunctionPermission::Ask;
|
||||
ControlFlowStatement m_currControlFlowStatement;
|
||||
BitfieldOrder m_bitfieldOrder = BitfieldOrder::RightToLeft;
|
||||
|
||||
friend class PatternCreationLimiter;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <hex/helpers/concepts.hpp>
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
@@ -26,11 +28,25 @@ namespace hex::pl {
|
||||
|
||||
[[nodiscard]] const auto &getLog() const { return this->m_consoleLog; }
|
||||
|
||||
void log(Level level, const std::string &message);
|
||||
void log(Level level, const std::string &message) {
|
||||
this->m_consoleLog.emplace_back(level, message);
|
||||
}
|
||||
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message, const ASTNode *node = nullptr);
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message) {
|
||||
abortEvaluation(message, nullptr);
|
||||
}
|
||||
|
||||
void clear();
|
||||
template<typename T = ASTNode>
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message, const std::unique_ptr<T> &node) {
|
||||
abortEvaluation(message, node.get());
|
||||
}
|
||||
|
||||
[[noreturn]] static void abortEvaluation(const std::string &message, const ASTNode *node);
|
||||
|
||||
void clear() {
|
||||
this->m_consoleLog.clear();
|
||||
this->m_lastHardError.reset();
|
||||
}
|
||||
|
||||
void setHardError(const PatternLanguageError &error) { this->m_lastHardError = error; }
|
||||
|
||||
|
||||
@@ -5,7 +5,11 @@
|
||||
|
||||
#include <hex/pattern_language/error.hpp>
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/ast_node.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_rvalue.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_attribute.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
@@ -21,7 +25,7 @@ namespace hex::pl {
|
||||
Parser() = default;
|
||||
~Parser() = default;
|
||||
|
||||
std::optional<std::vector<ASTNode *>> parse(const std::vector<Token> &tokens);
|
||||
std::optional<std::vector<std::shared_ptr<ASTNode>>> parse(const std::vector<Token> &tokens);
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
private:
|
||||
@@ -29,7 +33,7 @@ namespace hex::pl {
|
||||
TokenIter m_curr;
|
||||
TokenIter m_originalPosition, m_partOriginalPosition;
|
||||
|
||||
std::unordered_map<std::string, ASTNode *> m_types;
|
||||
std::unordered_map<std::string, std::shared_ptr<ASTNode>> m_types;
|
||||
std::vector<TokenIter> m_matchedOptionals;
|
||||
std::vector<std::vector<std::string>> m_currNamespace;
|
||||
|
||||
@@ -37,9 +41,10 @@ namespace hex::pl {
|
||||
return this->m_curr[index].lineNumber;
|
||||
}
|
||||
|
||||
auto *create(auto *node) {
|
||||
template<typename T>
|
||||
std::unique_ptr<T> create(T *node) {
|
||||
node->setLineNumber(this->getLineNumber(-1));
|
||||
return node;
|
||||
return std::unique_ptr<T>(node);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -67,79 +72,73 @@ namespace hex::pl {
|
||||
return result;
|
||||
}
|
||||
|
||||
ASTNode *parseFunctionCall();
|
||||
ASTNode *parseStringLiteral();
|
||||
std::unique_ptr<ASTNode> parseFunctionCall();
|
||||
std::unique_ptr<ASTNode> parseStringLiteral();
|
||||
std::string parseNamespaceResolution();
|
||||
ASTNode *parseScopeResolution();
|
||||
ASTNode *parseRValue();
|
||||
ASTNode *parseRValue(ASTNodeRValue::Path &path);
|
||||
ASTNode *parseFactor();
|
||||
ASTNode *parseCastExpression();
|
||||
ASTNode *parseUnaryExpression();
|
||||
ASTNode *parseMultiplicativeExpression();
|
||||
ASTNode *parseAdditiveExpression();
|
||||
ASTNode *parseShiftExpression();
|
||||
ASTNode *parseBinaryAndExpression();
|
||||
ASTNode *parseBinaryXorExpression();
|
||||
ASTNode *parseBinaryOrExpression();
|
||||
ASTNode *parseBooleanAnd();
|
||||
ASTNode *parseBooleanXor();
|
||||
ASTNode *parseBooleanOr();
|
||||
ASTNode *parseRelationExpression();
|
||||
ASTNode *parseEqualityExpression();
|
||||
ASTNode *parseTernaryConditional();
|
||||
ASTNode *parseMathematicalExpression();
|
||||
std::unique_ptr<ASTNode> parseScopeResolution();
|
||||
std::unique_ptr<ASTNode> parseRValue();
|
||||
std::unique_ptr<ASTNode> parseRValue(ASTNodeRValue::Path &path);
|
||||
std::unique_ptr<ASTNode> parseFactor();
|
||||
std::unique_ptr<ASTNode> parseCastExpression();
|
||||
std::unique_ptr<ASTNode> parseUnaryExpression();
|
||||
std::unique_ptr<ASTNode> parseMultiplicativeExpression();
|
||||
std::unique_ptr<ASTNode> parseAdditiveExpression();
|
||||
std::unique_ptr<ASTNode> parseShiftExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryAndExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryXorExpression();
|
||||
std::unique_ptr<ASTNode> parseBinaryOrExpression();
|
||||
std::unique_ptr<ASTNode> parseBooleanAnd();
|
||||
std::unique_ptr<ASTNode> parseBooleanXor();
|
||||
std::unique_ptr<ASTNode> parseBooleanOr();
|
||||
std::unique_ptr<ASTNode> parseRelationExpression();
|
||||
std::unique_ptr<ASTNode> parseEqualityExpression();
|
||||
std::unique_ptr<ASTNode> parseTernaryConditional();
|
||||
std::unique_ptr<ASTNode> parseMathematicalExpression();
|
||||
|
||||
ASTNode *parseFunctionDefinition();
|
||||
ASTNode *parseFunctionVariableDecl();
|
||||
ASTNode *parseFunctionStatement();
|
||||
ASTNode *parseFunctionVariableAssignment(const std::string &lvalue);
|
||||
ASTNode *parseFunctionVariableCompoundAssignment(const std::string &lvalue);
|
||||
ASTNode *parseFunctionControlFlowStatement();
|
||||
std::vector<ASTNode *> parseStatementBody();
|
||||
ASTNode *parseFunctionConditional();
|
||||
ASTNode *parseFunctionWhileLoop();
|
||||
ASTNode *parseFunctionForLoop();
|
||||
std::unique_ptr<ASTNode> parseFunctionDefinition();
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableDecl();
|
||||
std::unique_ptr<ASTNode> parseFunctionStatement();
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableAssignment(const std::string &lvalue);
|
||||
std::unique_ptr<ASTNode> parseFunctionVariableCompoundAssignment(const std::string &lvalue);
|
||||
std::unique_ptr<ASTNode> parseFunctionControlFlowStatement();
|
||||
std::vector<std::unique_ptr<ASTNode>> parseStatementBody();
|
||||
std::unique_ptr<ASTNode> parseFunctionConditional();
|
||||
std::unique_ptr<ASTNode> parseFunctionWhileLoop();
|
||||
std::unique_ptr<ASTNode> parseFunctionForLoop();
|
||||
|
||||
void parseAttribute(Attributable *currNode);
|
||||
ASTNode *parseConditional();
|
||||
ASTNode *parseWhileStatement();
|
||||
ASTNodeTypeDecl *parseType(bool allowFunctionTypes = false);
|
||||
ASTNode *parseUsingDeclaration();
|
||||
ASTNode *parsePadding();
|
||||
ASTNode *parseMemberVariable(ASTNodeTypeDecl *type);
|
||||
ASTNode *parseMemberArrayVariable(ASTNodeTypeDecl *type);
|
||||
ASTNode *parseMemberPointerVariable(ASTNodeTypeDecl *type);
|
||||
ASTNode *parseMember();
|
||||
ASTNode *parseStruct();
|
||||
ASTNode *parseUnion();
|
||||
ASTNode *parseEnum();
|
||||
ASTNode *parseBitfield();
|
||||
ASTNode *parseVariablePlacement(ASTNodeTypeDecl *type);
|
||||
ASTNode *parseArrayVariablePlacement(ASTNodeTypeDecl *type);
|
||||
ASTNode *parsePointerVariablePlacement(ASTNodeTypeDecl *type);
|
||||
ASTNode *parsePlacement();
|
||||
std::vector<ASTNode *> parseNamespace();
|
||||
std::vector<ASTNode *> parseStatements();
|
||||
std::unique_ptr<ASTNode> parseConditional();
|
||||
std::unique_ptr<ASTNode> parseWhileStatement();
|
||||
std::unique_ptr<ASTNodeTypeDecl> parseType(bool allowFunctionTypes = false);
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseUsingDeclaration();
|
||||
std::unique_ptr<ASTNode> parsePadding();
|
||||
std::unique_ptr<ASTNode> parseMemberVariable(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parseMemberArrayVariable(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parseMemberPointerVariable(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parseMember();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseStruct();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseUnion();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseEnum();
|
||||
std::shared_ptr<ASTNodeTypeDecl> parseBitfield();
|
||||
std::unique_ptr<ASTNode> parseVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parseArrayVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parsePointerVariablePlacement(std::unique_ptr<ASTNodeTypeDecl> &&type);
|
||||
std::unique_ptr<ASTNode> parsePlacement();
|
||||
std::vector<std::shared_ptr<ASTNode>> parseNamespace();
|
||||
std::vector<std::shared_ptr<ASTNode>> parseStatements();
|
||||
|
||||
ASTNodeTypeDecl *addType(const std::string &name, ASTNode *node, std::optional<std::endian> endian = std::nullopt);
|
||||
std::shared_ptr<ASTNodeTypeDecl> addType(const std::string &name, std::unique_ptr<ASTNode> &&node, std::optional<std::endian> endian = std::nullopt);
|
||||
|
||||
std::vector<ASTNode *> parseTillToken(Token::Type endTokenType, const auto value) {
|
||||
std::vector<ASTNode *> program;
|
||||
auto guard = SCOPE_GUARD {
|
||||
for (auto &node : program)
|
||||
delete node;
|
||||
};
|
||||
std::vector<std::shared_ptr<ASTNode>> parseTillToken(Token::Type endTokenType, const auto value) {
|
||||
std::vector<std::shared_ptr<ASTNode>> program;
|
||||
|
||||
while (this->m_curr->type != endTokenType || (*this->m_curr) != value) {
|
||||
for (auto statement : parseStatements())
|
||||
program.push_back(statement);
|
||||
for (auto &statement : parseStatements())
|
||||
program.push_back(std::move(statement));
|
||||
}
|
||||
|
||||
this->m_curr++;
|
||||
|
||||
guard.release();
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
#include <hex/pattern_language/token.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::prv {
|
||||
class Provider;
|
||||
@@ -23,7 +24,7 @@ namespace hex::pl {
|
||||
class Parser;
|
||||
class Validator;
|
||||
class Evaluator;
|
||||
class PatternData;
|
||||
class Pattern;
|
||||
|
||||
class ASTNode;
|
||||
|
||||
@@ -32,11 +33,11 @@ namespace hex::pl {
|
||||
PatternLanguage();
|
||||
~PatternLanguage();
|
||||
|
||||
[[nodiscard]] std::optional<std::vector<ASTNode *>> parseString(const std::string &code);
|
||||
[[nodiscard]] std::optional<std::vector<std::shared_ptr<ASTNode>>> parseString(const std::string &code);
|
||||
[[nodiscard]] bool executeString(prv::Provider *provider, const std::string &string, const std::map<std::string, Token::Literal> &envVars = {}, const std::map<std::string, Token::Literal> &inVariables = {}, bool checkResult = true);
|
||||
[[nodiscard]] bool executeFile(prv::Provider *provider, const fs::path &path, const std::map<std::string, Token::Literal> &envVars = {}, const std::map<std::string, Token::Literal> &inVariables = {});
|
||||
[[nodiscard]] std::pair<bool, std::optional<Token::Literal>> executeFunction(prv::Provider *provider, const std::string &code);
|
||||
[[nodiscard]] const std::vector<ASTNode *> &getCurrentAST() const;
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<ASTNode>> &getCurrentAST() const;
|
||||
|
||||
void abort();
|
||||
|
||||
@@ -50,8 +51,8 @@ namespace hex::pl {
|
||||
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const;
|
||||
void allowDangerousFunctions(bool allow);
|
||||
|
||||
[[nodiscard]] const std::vector<PatternData *> &getPatterns() {
|
||||
const static std::vector<PatternData *> empty;
|
||||
[[nodiscard]] const std::vector<std::shared_ptr<Pattern>> &getPatterns() {
|
||||
const static std::vector<std::shared_ptr<Pattern>> empty;
|
||||
|
||||
if (isRunning()) return empty;
|
||||
else return this->m_patterns;
|
||||
@@ -67,11 +68,11 @@ namespace hex::pl {
|
||||
Validator *m_validator;
|
||||
Evaluator *m_evaluator;
|
||||
|
||||
std::vector<ASTNode *> m_currAST;
|
||||
std::vector<std::shared_ptr<ASTNode>> m_currAST;
|
||||
|
||||
std::optional<PatternLanguageError> m_currError;
|
||||
|
||||
std::vector<PatternData *> m_patterns;
|
||||
std::vector<std::shared_ptr<Pattern>> m_patterns;
|
||||
|
||||
bool m_running = false;
|
||||
};
|
||||
|
||||
293
lib/libimhex/include/hex/pattern_language/patterns/pattern.hpp
Normal file
293
lib/libimhex/include/hex/pattern_language/patterns/pattern.hpp
Normal file
@@ -0,0 +1,293 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
using namespace ::std::literals::string_literals;
|
||||
|
||||
class Inlinable {
|
||||
public:
|
||||
[[nodiscard]] bool isInlined() const { return this->m_inlined; }
|
||||
void setInlined(bool inlined) { this->m_inlined = inlined; }
|
||||
|
||||
private:
|
||||
bool m_inlined = false;
|
||||
};
|
||||
|
||||
class PatternCreationLimiter {
|
||||
public:
|
||||
explicit PatternCreationLimiter(Evaluator *evaluator) : m_evaluator(evaluator) {
|
||||
if (getEvaluator() == nullptr) return;
|
||||
|
||||
getEvaluator()->patternCreated();
|
||||
}
|
||||
|
||||
PatternCreationLimiter(const PatternCreationLimiter &other) : PatternCreationLimiter(other.m_evaluator) { }
|
||||
|
||||
virtual ~PatternCreationLimiter() {
|
||||
if (getEvaluator() == nullptr) return;
|
||||
|
||||
getEvaluator()->patternDestroyed();
|
||||
}
|
||||
|
||||
[[nodiscard]] Evaluator *getEvaluator() const {
|
||||
return this->m_evaluator;
|
||||
}
|
||||
|
||||
private:
|
||||
Evaluator *m_evaluator = nullptr;
|
||||
};
|
||||
|
||||
class Pattern : public PatternCreationLimiter,
|
||||
public Cloneable<Pattern> {
|
||||
public:
|
||||
Pattern(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: PatternCreationLimiter(evaluator), m_offset(offset), m_size(size), m_color(color) {
|
||||
|
||||
if (color != 0)
|
||||
return;
|
||||
|
||||
this->m_color = ContentRegistry::PatternLanguage::getNextColor();
|
||||
this->m_manualColor = false;
|
||||
}
|
||||
|
||||
Pattern(const Pattern &other) = default;
|
||||
|
||||
~Pattern() override = default;
|
||||
|
||||
[[nodiscard]] u64 getOffset() const { return this->m_offset; }
|
||||
virtual void setOffset(u64 offset) { this->m_offset = offset; }
|
||||
|
||||
[[nodiscard]] size_t getSize() const { return this->m_size; }
|
||||
void setSize(size_t size) { this->m_size = size; }
|
||||
|
||||
[[nodiscard]] const std::string &getVariableName() const { return this->m_variableName; }
|
||||
void setVariableName(std::string name) { this->m_variableName = std::move(name); }
|
||||
|
||||
[[nodiscard]] const std::optional<std::string> &getComment() const { return this->m_comment; }
|
||||
void setComment(std::string comment) { this->m_comment = std::move(comment); }
|
||||
|
||||
[[nodiscard]] const std::string &getTypeName() const { return this->m_typeName; }
|
||||
void setTypeName(std::string name) { this->m_typeName = std::move(name); }
|
||||
|
||||
[[nodiscard]] u32 getColor() const { return this->m_color; }
|
||||
virtual void setColor(u32 color) {
|
||||
this->m_color = color;
|
||||
this->m_manualColor = true;
|
||||
}
|
||||
[[nodiscard]] bool hasOverriddenColor() const { return this->m_manualColor; }
|
||||
|
||||
[[nodiscard]] std::endian getEndian() const {
|
||||
if (this->getEvaluator() == nullptr) return std::endian::native;
|
||||
else return this->m_endian.value_or(this->getEvaluator()->getDefaultEndian());
|
||||
}
|
||||
virtual void setEndian(std::endian endian) { this->m_endian = endian; }
|
||||
[[nodiscard]] bool hasOverriddenEndian() const { return this->m_endian.has_value(); }
|
||||
|
||||
[[nodiscard]] std::string getDisplayName() const { return this->m_displayName.value_or(this->m_variableName); }
|
||||
void setDisplayName(const std::string &name) { this->m_displayName = name; }
|
||||
|
||||
[[nodiscard]] const auto &getTransformFunction() const { return this->m_transformFunction; }
|
||||
void setTransformFunction(const ContentRegistry::PatternLanguage::Function &function) { this->m_transformFunction = function; }
|
||||
[[nodiscard]] const auto &getFormatterFunction() const { return this->m_formatterFunction; }
|
||||
void setFormatterFunction(const ContentRegistry::PatternLanguage::Function &function) { this->m_formatterFunction = function; }
|
||||
|
||||
virtual void createEntry(prv::Provider *&provider) = 0;
|
||||
[[nodiscard]] virtual std::string getFormattedName() const = 0;
|
||||
|
||||
[[nodiscard]] virtual const Pattern *getPattern(u64 offset) const {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()) && !this->isHidden())
|
||||
return this;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void getHighlightedAddresses(std::map<u64, u32> &highlight) const {
|
||||
if (this->isHidden()) return;
|
||||
|
||||
for (u64 i = 0; i < this->getSize(); i++)
|
||||
highlight.insert({ this->getOffset() + i, this->getColor() });
|
||||
|
||||
this->getEvaluator()->handleAbort();
|
||||
}
|
||||
|
||||
virtual void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) { }
|
||||
|
||||
[[nodiscard]] virtual std::string toString(prv::Provider *provider) const {
|
||||
return hex::format("{} {} @ 0x{:X}", this->getTypeName(), this->getVariableName(), this->getOffset());
|
||||
}
|
||||
|
||||
static bool sortPatternTable(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider, pl::Pattern *left, pl::Pattern *right) {
|
||||
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getDisplayName() > right->getDisplayName();
|
||||
else
|
||||
return left->getDisplayName() < right->getDisplayName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getOffset() > right->getOffset();
|
||||
else
|
||||
return left->getOffset() < right->getOffset();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getSize() > right->getSize();
|
||||
else
|
||||
return left->getSize() < right->getSize();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
|
||||
size_t biggerSize = std::max(left->getSize(), right->getSize());
|
||||
std::vector<u8> leftBuffer(biggerSize, 0x00), rightBuffer(biggerSize, 0x00);
|
||||
|
||||
provider->read(left->getOffset(), leftBuffer.data(), left->getSize());
|
||||
provider->read(right->getOffset(), rightBuffer.data(), right->getSize());
|
||||
|
||||
if (left->m_endian != std::endian::native)
|
||||
std::reverse(leftBuffer.begin(), leftBuffer.end());
|
||||
if (right->m_endian != std::endian::native)
|
||||
std::reverse(rightBuffer.begin(), rightBuffer.end());
|
||||
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return leftBuffer > rightBuffer;
|
||||
else
|
||||
return leftBuffer < rightBuffer;
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("type")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getTypeName() > right->getTypeName();
|
||||
else
|
||||
return left->getTypeName() < right->getTypeName();
|
||||
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("color")) {
|
||||
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
|
||||
return left->getColor() > right->getColor();
|
||||
else
|
||||
return left->getColor() < right->getColor();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void draw(prv::Provider *provider) {
|
||||
if (isHidden()) return;
|
||||
|
||||
this->createEntry(provider);
|
||||
}
|
||||
|
||||
void setHidden(bool hidden) {
|
||||
this->m_hidden = hidden;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isHidden() const {
|
||||
return this->m_hidden;
|
||||
}
|
||||
|
||||
void setLocal(bool local) {
|
||||
this->m_local = local;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isLocal() const {
|
||||
return this->m_local;
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool operator!=(const Pattern &other) const final { return !operator==(other); }
|
||||
[[nodiscard]] virtual bool operator==(const Pattern &other) const = 0;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] bool areCommonPropertiesEqual(const Pattern &other) const {
|
||||
return typeid(other) == typeid(std::remove_cvref_t<T>) &&
|
||||
this->m_offset == other.m_offset &&
|
||||
this->m_size == other.m_size &&
|
||||
this->m_hidden == other.m_hidden &&
|
||||
(this->m_endian == other.m_endian || (!this->m_endian.has_value() && other.m_endian == std::endian::native) || (!other.m_endian.has_value() && this->m_endian == std::endian::native)) &&
|
||||
this->m_variableName == other.m_variableName &&
|
||||
this->m_typeName == other.m_typeName &&
|
||||
this->m_comment == other.m_comment &&
|
||||
this->m_local == other.m_local;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string formatDisplayValue(const std::string &value, const Token::Literal &literal) const {
|
||||
if (!this->m_formatterFunction.has_value())
|
||||
return value;
|
||||
else {
|
||||
try {
|
||||
auto result = this->m_formatterFunction->func(this->getEvaluator(), { literal });
|
||||
|
||||
if (result.has_value()) {
|
||||
if (auto displayValue = std::get_if<std::string>(&result.value()); displayValue != nullptr)
|
||||
return *displayValue;
|
||||
else
|
||||
return "???";
|
||||
} else {
|
||||
return "???";
|
||||
}
|
||||
} catch (PatternLanguageError &error) {
|
||||
return "Error: "s + error.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void createDefaultEntry(const std::string &value, const Token::Literal &literal) const {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::PushID(this->getOffset());
|
||||
ImGui::PushID(this->getVariableName().c_str());
|
||||
if (ImGui::Selectable("##PatternLine", false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::PopID();
|
||||
|
||||
this->drawCommentTooltip();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", this->getFormattedName());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", formatDisplayValue(value, literal));
|
||||
}
|
||||
|
||||
void drawCommentTooltip() const {
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem) && this->getComment().has_value()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::TextUnformatted(this->getComment()->c_str());
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::optional<std::endian> m_endian;
|
||||
bool m_hidden = false;
|
||||
|
||||
private:
|
||||
u64 m_offset = 0x00;
|
||||
size_t m_size = 0x00;
|
||||
|
||||
u32 m_color = 0x00;
|
||||
std::optional<std::string> m_displayName;
|
||||
std::string m_variableName;
|
||||
std::optional<std::string> m_comment;
|
||||
std::string m_typeName;
|
||||
|
||||
std::optional<ContentRegistry::PatternLanguage::Function> m_formatterFunction;
|
||||
std::optional<ContentRegistry::PatternLanguage::Function> m_transformFunction;
|
||||
|
||||
bool m_local = false;
|
||||
bool m_manualColor = false;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternArrayDynamic : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternArrayDynamic(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternArrayDynamic(const PatternArrayDynamic &other) : Pattern(other) {
|
||||
std::vector<std::shared_ptr<Pattern>> entries;
|
||||
for (const auto &entry : other.m_entries)
|
||||
entries.push_back(entry->clone());
|
||||
|
||||
this->setEntries(std::move(entries));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternArrayDynamic(*this));
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &entry : this->m_entries)
|
||||
entry->setColor(color);
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
if (this->m_entries.empty())
|
||||
return;
|
||||
|
||||
bool open = true;
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", this->m_entries[0]->getTypeName());
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::TextUnformatted("[");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", this->m_entries.size());
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted("]");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
for (u64 i = 0; i < this->m_entries.size(); i++) {
|
||||
this->m_entries[i]->draw(provider);
|
||||
|
||||
if (i >= (this->m_displayEnd - 1)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns);
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
this->m_displayEnd += 50;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
} else {
|
||||
this->m_displayEnd = 50;
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return this->m_entries[0]->getTypeName() + "[" + std::to_string(this->m_entries.size()) + "]";
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &entry : this->m_entries)
|
||||
entry->setOffset(entry->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getEntries() {
|
||||
return this->m_entries;
|
||||
}
|
||||
|
||||
void setEntries(std::vector<std::shared_ptr<Pattern>> &&entries) {
|
||||
this->m_entries = std::move(entries);
|
||||
|
||||
if (this->hasOverriddenColor()) {
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->setColor(this->getColor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherArray = *static_cast<const PatternArrayDynamic *>(&other);
|
||||
if (this->m_entries.size() != otherArray.m_entries.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_entries.size(); i++) {
|
||||
if (*this->m_entries[i] != *otherArray.m_entries[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
for (auto &pattern : this->m_entries) {
|
||||
auto result = pattern->getPattern(offset);
|
||||
if (result != nullptr)
|
||||
return result;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &entry : this->m_entries) {
|
||||
entry->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_entries;
|
||||
u64 m_displayEnd = 50;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternArrayStatic : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternArrayStatic(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternArrayStatic(const PatternArrayStatic &other) : Pattern(other) {
|
||||
this->setEntries(other.getTemplate()->clone(), other.getEntryCount());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternArrayStatic(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
if (this->getEntryCount() == 0)
|
||||
return;
|
||||
|
||||
bool open = true;
|
||||
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{0}", this->m_template->getTypeName().c_str());
|
||||
ImGui::SameLine(0, 0);
|
||||
|
||||
ImGui::TextUnformatted("[");
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextFormattedColored(ImColor(0xFF00FF00), "{0}", this->m_entryCount);
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::TextUnformatted("]");
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
auto entry = this->m_template->clone();
|
||||
for (u64 index = 0; index < this->m_entryCount; index++) {
|
||||
entry->setVariableName(hex::format("[{0}]", index));
|
||||
entry->setOffset(this->getOffset() + index * this->m_template->getSize());
|
||||
entry->draw(provider);
|
||||
|
||||
if (index >= (this->m_displayEnd - 1)) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::Selectable("... (Double-click to see more items)", false, ImGuiSelectableFlags_SpanAllColumns);
|
||||
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
|
||||
this->m_displayEnd += 50;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
} else {
|
||||
this->m_displayEnd = 50;
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
auto entry = this->m_template->clone();
|
||||
|
||||
for (u64 address = this->getOffset(); address < this->getOffset() + this->getSize(); address += entry->getSize()) {
|
||||
entry->setOffset(address);
|
||||
entry->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
this->m_template->setOffset(this->m_template->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
this->m_template->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return this->m_template->getTypeName() + "[" + std::to_string(this->m_entryCount) + "]";
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::shared_ptr<Pattern> &getTemplate() const {
|
||||
return this->m_template;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t getEntryCount() const {
|
||||
return this->m_entryCount;
|
||||
}
|
||||
|
||||
void setEntryCount(size_t count) {
|
||||
this->m_entryCount = count;
|
||||
}
|
||||
|
||||
void setEntries(std::unique_ptr<Pattern> &&templatePattern, size_t count) {
|
||||
this->m_template = std::move(templatePattern);
|
||||
this->m_highlightTemplate = this->m_template->clone();
|
||||
this->m_entryCount = count;
|
||||
|
||||
if (this->hasOverriddenColor())
|
||||
this->setColor(this->m_template->getColor());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherArray = *static_cast<const PatternArrayStatic *>(&other);
|
||||
return *this->m_template == *otherArray.m_template && this->m_entryCount == otherArray.m_entryCount;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
this->m_highlightTemplate->setColor(this->getColor());
|
||||
this->m_highlightTemplate->setVariableName(this->getVariableName());
|
||||
this->m_highlightTemplate->setDisplayName(this->getDisplayName());
|
||||
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize())) {
|
||||
this->m_highlightTemplate->setOffset((offset / this->m_highlightTemplate->getSize()) * this->m_highlightTemplate->getSize());
|
||||
return this->m_highlightTemplate->getPattern(offset);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
this->m_template->setEndian(endian);
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<Pattern> m_template = nullptr;
|
||||
mutable std::unique_ptr<Pattern> m_highlightTemplate = nullptr;
|
||||
size_t m_entryCount = 0;
|
||||
u64 m_displayEnd = 50;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternBitfieldField : public Pattern {
|
||||
public:
|
||||
PatternBitfieldField(Evaluator *evaluator, u64 offset, u8 bitOffset, u8 bitSize, Pattern *bitField, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 0, color), m_bitOffset(bitOffset), m_bitSize(bitSize), m_bitField(bitField) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBitfieldField(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
std::vector<u8> value(this->m_bitField->getSize(), 0);
|
||||
provider->read(this->m_bitField->getOffset(), &value[0], value.size());
|
||||
|
||||
if (this->m_bitField->getEndian() != std::endian::native)
|
||||
std::reverse(value.begin(), value.end());
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextUnformatted(this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_bitSize == 1)
|
||||
ImGui::TextFormatted("0x{0:08X} bit {1}", this->getOffset() + this->m_bitOffset / 8, (this->m_bitOffset + (this->m_bitSize - 1)) % 8);
|
||||
else
|
||||
ImGui::TextFormatted("0x{0:08X} bits {1} - {2}", this->getOffset() + this->m_bitOffset / 8, this->m_bitOffset % 8, this->m_bitOffset % 8 + (this->m_bitSize - 1) % 8);
|
||||
ImGui::TableNextColumn();
|
||||
if (this->m_bitSize == 1)
|
||||
ImGui::TextFormatted("{0} bit", this->m_bitSize);
|
||||
else
|
||||
ImGui::TextFormatted("{0} bits", this->m_bitSize);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "bits");
|
||||
ImGui::TableNextColumn();
|
||||
{
|
||||
u8 numBytes = (this->m_bitSize / 8) + 1;
|
||||
|
||||
u64 extractedValue = hex::extract(this->m_bitOffset + (this->m_bitSize - 1), this->m_bitOffset, value);
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue(hex::format("{0} (0x{1:X})", extractedValue, extractedValue), this->clone()));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bits";
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 getBitOffset() const {
|
||||
return this->m_bitOffset;
|
||||
}
|
||||
|
||||
[[nodiscard]] u8 getBitSize() const {
|
||||
return this->m_bitSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherBitfieldField = *static_cast<const PatternBitfieldField *>(&other);
|
||||
return this->m_bitOffset == otherBitfieldField.m_bitOffset && this->m_bitSize == otherBitfieldField.m_bitSize;
|
||||
}
|
||||
|
||||
private:
|
||||
u8 m_bitOffset, m_bitSize;
|
||||
Pattern *m_bitField;
|
||||
};
|
||||
|
||||
class PatternBitfield : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternBitfield(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternBitfield(const PatternBitfield &other) : Pattern(other) {
|
||||
for (auto &field : other.m_fields)
|
||||
this->m_fields.push_back(field->clone());
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBitfield(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
std::vector<u8> value(this->getSize(), 0);
|
||||
provider->read(this->getOffset(), &value[0], value.size());
|
||||
|
||||
if (this->m_endian == std::endian::little)
|
||||
std::reverse(value.begin(), value.end());
|
||||
|
||||
bool open = true;
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "bitfield");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
std::string valueString = "{ ";
|
||||
for (auto i : value)
|
||||
valueString += hex::format("{0:02X} ", i);
|
||||
valueString += "}";
|
||||
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue(valueString, this->clone()));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
|
||||
for (auto &field : this->m_fields)
|
||||
field->draw(provider);
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &field : this->m_fields)
|
||||
field->setOffset(field->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bitfield " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getFields() const {
|
||||
return this->m_fields;
|
||||
}
|
||||
|
||||
void setFields(std::vector<std::shared_ptr<Pattern>> &&fields) {
|
||||
this->m_fields = std::move(fields);
|
||||
|
||||
for (auto &field : this->m_fields) {
|
||||
field->setSize(this->getSize());
|
||||
field->setColor(this->getColor());
|
||||
}
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &field : this->m_fields)
|
||||
field->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherBitfield = *static_cast<const PatternBitfield *>(&other);
|
||||
if (this->m_fields.size() != otherBitfield.m_fields.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_fields.size(); i++) {
|
||||
if (*this->m_fields[i] != *otherBitfield.m_fields[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_fields;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternBoolean : public Pattern {
|
||||
public:
|
||||
explicit PatternBoolean(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 1, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternBoolean(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
u8 boolean;
|
||||
provider->read(this->getOffset(), &boolean, 1);
|
||||
|
||||
if (boolean == 0)
|
||||
this->createDefaultEntry("false", false);
|
||||
else if (boolean == 1)
|
||||
this->createDefaultEntry("true", true);
|
||||
else
|
||||
this->createDefaultEntry("true*", true);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternCharacter : public Pattern {
|
||||
public:
|
||||
explicit PatternCharacter(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 1, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternCharacter(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
char character;
|
||||
provider->read(this->getOffset(), &character, 1);
|
||||
|
||||
this->createDefaultEntry(hex::format("'{0}'", character), character);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "char";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternEnum : public Pattern {
|
||||
public:
|
||||
PatternEnum(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternEnum(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
u64 value = 0;
|
||||
provider->read(this->getOffset(), &value, this->getSize());
|
||||
value = hex::changeEndianess(value, this->getSize(), this->getEndian());
|
||||
|
||||
std::string valueString = Pattern::getTypeName() + "::";
|
||||
|
||||
bool foundValue = false;
|
||||
for (auto &[entryValueLiteral, entryName] : this->m_enumValues) {
|
||||
bool matches = std::visit(overloaded {
|
||||
[&, name = entryName](auto &&entryValue) {
|
||||
if (value == entryValue) {
|
||||
valueString += name;
|
||||
foundValue = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
[](std::string &) { return false; },
|
||||
[](std::shared_ptr<Pattern> &) { return false; } },
|
||||
entryValueLiteral);
|
||||
if (matches)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundValue)
|
||||
valueString += "???";
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_AllowItemOverlap);
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(this->getDisplayName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "enum");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue(hex::format("{} (0x{:0{}X})", valueString.c_str(), value, this->getSize() * 2), this->clone()));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "enum " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getEnumValues() const {
|
||||
return this->m_enumValues;
|
||||
}
|
||||
|
||||
void setEnumValues(const std::vector<std::pair<Token::Literal, std::string>> &enumValues) {
|
||||
this->m_enumValues = enumValues;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherEnum = *static_cast<const PatternEnum *>(&other);
|
||||
if (this->m_enumValues.size() != otherEnum.m_enumValues.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_enumValues.size(); i++) {
|
||||
if (this->m_enumValues[i] != otherEnum.m_enumValues[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::pair<Token::Literal, std::string>> m_enumValues;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternFloat : public Pattern {
|
||||
public:
|
||||
PatternFloat(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternFloat(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
if (this->getSize() == 4) {
|
||||
u32 data = 0;
|
||||
provider->read(this->getOffset(), &data, 4);
|
||||
data = hex::changeEndianess(data, 4, this->getEndian());
|
||||
|
||||
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<float *>(&data), data, this->getSize() * 2), *reinterpret_cast<float *>(&data));
|
||||
} else if (this->getSize() == 8) {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, 8);
|
||||
data = hex::changeEndianess(data, 8, this->getEndian());
|
||||
|
||||
this->createDefaultEntry(hex::format("{:e} (0x{:0{}X})", *reinterpret_cast<double *>(&data), data, this->getSize() * 2), *reinterpret_cast<double *>(&data));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 4:
|
||||
return "float";
|
||||
case 8:
|
||||
return "double";
|
||||
default:
|
||||
return "Floating point data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternPadding : public Pattern {
|
||||
public:
|
||||
PatternPadding(Evaluator *evaluator, u64 offset, size_t size) : Pattern(evaluator, offset, size, 0xFF000000) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternPadding(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternPointer : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternPointer(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color), m_pointedAt(nullptr) {
|
||||
}
|
||||
|
||||
PatternPointer(const PatternPointer &other) : Pattern(other) {
|
||||
this->m_pointedAt = other.m_pointedAt->clone();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternPointer(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
u64 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
bool open = true;
|
||||
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::SameLine(0, 0);
|
||||
ImGui::ColorButton("color", ImColor(this->getColor()), ImGuiColorEditFlags_NoTooltip, ImVec2(ImGui::GetColumnWidth(), ImGui::GetTextLineHeight()));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFF9BC64D), "{}", this->getFormattedName());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", formatDisplayValue(hex::format("*(0x{0:X})", data), u128(data)));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
this->m_pointedAt->createEntry(provider);
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
Pattern::getHighlightedAddresses(highlight);
|
||||
this->m_pointedAt->getHighlightedAddresses(highlight);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
std::string result = this->m_pointedAt->getFormattedName() + "* : ";
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
result += "u8";
|
||||
break;
|
||||
case 2:
|
||||
result += "u16";
|
||||
break;
|
||||
case 4:
|
||||
result += "u32";
|
||||
break;
|
||||
case 8:
|
||||
result += "u64";
|
||||
break;
|
||||
case 16:
|
||||
result += "u128";
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setPointedAtPattern(std::unique_ptr<Pattern> &&pattern) {
|
||||
this->m_pointedAt = std::move(pattern);
|
||||
this->m_pointedAt->setVariableName(hex::format("*({})", this->getVariableName()));
|
||||
this->m_pointedAt->setOffset(this->m_pointedAtAddress);
|
||||
}
|
||||
|
||||
void setPointedAtAddress(u64 address) {
|
||||
this->m_pointedAtAddress = address;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 getPointedAtAddress() const {
|
||||
return this->m_pointedAtAddress;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::unique_ptr<Pattern> &getPointedAtPattern() {
|
||||
return this->m_pointedAt;
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
this->m_pointedAt->setColor(color);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
return areCommonPropertiesEqual<decltype(*this)>(other) &&
|
||||
*static_cast<const PatternPointer *>(&other)->m_pointedAt == *this->m_pointedAt;
|
||||
}
|
||||
|
||||
void rebase(u64 base) {
|
||||
if (this->m_pointedAt != nullptr) {
|
||||
this->m_pointedAtAddress = (this->m_pointedAt->getOffset() - this->m_pointerBase) + base;
|
||||
this->m_pointedAt->setOffset(this->m_pointedAtAddress);
|
||||
}
|
||||
|
||||
this->m_pointerBase = base;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (offset >= this->getOffset() && offset < (this->getOffset() + this->getSize()) && !this->isHidden())
|
||||
return this;
|
||||
else
|
||||
return this->m_pointedAt->getPattern(offset);
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
this->m_pointedAt->setEndian(endian);
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Pattern> m_pointedAt;
|
||||
u64 m_pointedAtAddress = 0;
|
||||
|
||||
u64 m_pointerBase = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternSigned : public Pattern {
|
||||
public:
|
||||
PatternSigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternSigned(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
i128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
data = hex::signExtend(this->getSize() * 8, data);
|
||||
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, 1 * 2), data);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
return "s8";
|
||||
case 2:
|
||||
return "s16";
|
||||
case 4:
|
||||
return "s32";
|
||||
case 8:
|
||||
return "s64";
|
||||
case 16:
|
||||
return "s128";
|
||||
default:
|
||||
return "Signed data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternString : public Pattern {
|
||||
public:
|
||||
PatternString(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternString(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
auto size = std::min<size_t>(this->getSize(), 0x7F);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
std::vector<u8> buffer(size, 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), size);
|
||||
|
||||
auto displayString = hex::encodeByteString(buffer);
|
||||
this->createDefaultEntry(hex::format("\"{0}\" {1}", displayString, size > this->getSize() ? "(truncated)" : ""), displayString);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "String";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
std::string buffer(this->getSize(), 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), buffer.size());
|
||||
|
||||
std::erase_if(buffer, [](auto c) {
|
||||
return c == 0x00;
|
||||
});
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternStruct : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternStruct(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternStruct(const PatternStruct &other) : Pattern(other) {
|
||||
for (const auto &member : other.m_members) {
|
||||
auto copy = member->clone();
|
||||
|
||||
this->m_sortedMembers.push_back(copy.get());
|
||||
this->m_members.push_back(std::move(copy));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternStruct(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
bool open = true;
|
||||
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), this->getOffset() + this->getSize() - (this->getSize() == 0 ? 0 : 1));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "struct");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(this->getTypeName().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->draw(provider);
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &member : this->m_members) {
|
||||
member->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &member : this->m_members)
|
||||
member->setOffset(member->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenColor())
|
||||
member->setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers.clear();
|
||||
for (auto &member : this->m_members)
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](Pattern *left, Pattern *right) {
|
||||
return Pattern::sortPatternTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "struct " + Pattern::getTypeName();
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
void setMembers(std::vector<std::shared_ptr<Pattern>> &&members) {
|
||||
this->m_members.clear();
|
||||
|
||||
for (auto &member : members) {
|
||||
if (member == nullptr) continue;
|
||||
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
this->m_members.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherStruct = *static_cast<const PatternStruct *>(&other);
|
||||
if (this->m_members.size() != otherStruct.m_members.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_members.size(); i++) {
|
||||
if (*this->m_members[i] != *otherStruct.m_members[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
auto iter = std::find_if(this->m_members.begin(), this->m_members.end(), [offset](const std::shared_ptr<Pattern> &pattern) {
|
||||
return offset >= pattern->getOffset() && offset < (pattern->getOffset() + pattern->getSize());
|
||||
});
|
||||
|
||||
if (iter == this->m_members.end())
|
||||
return nullptr;
|
||||
else
|
||||
return (*iter)->getPattern(offset);
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenEndian())
|
||||
member->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_members;
|
||||
std::vector<Pattern *> m_sortedMembers;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternUnion : public Pattern,
|
||||
public Inlinable {
|
||||
public:
|
||||
PatternUnion(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) {
|
||||
}
|
||||
|
||||
PatternUnion(const PatternUnion &other) : Pattern(other) {
|
||||
for (const auto &member : other.m_members) {
|
||||
auto copy = member->clone();
|
||||
|
||||
this->m_sortedMembers.push_back(copy.get());
|
||||
this->m_members.push_back(std::move(copy));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternUnion(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
bool open = true;
|
||||
|
||||
if (!this->isInlined()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
open = ImGui::TreeNodeEx(this->getDisplayName().c_str(), ImGuiTreeNodeFlags_SpanFullWidth);
|
||||
ImGui::TableNextColumn();
|
||||
if (ImGui::Selectable(("##PatternLine"s + std::to_string(u64(this))).c_str(), false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowItemOverlap)) {
|
||||
ImHexApi::HexEditor::setSelection(this->getOffset(), this->getSize());
|
||||
}
|
||||
this->drawCommentTooltip();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:08X} : 0x{1:08X}", this->getOffset(), std::max(this->getOffset() + this->getSize() - (this->getSize() == 0 ? 0 : 1), u64(0)));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("0x{0:04X}", this->getSize());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormattedColored(ImColor(0xFFD69C56), "union");
|
||||
ImGui::SameLine();
|
||||
ImGui::TextUnformatted(Pattern::getTypeName().c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextFormatted("{}", this->formatDisplayValue("{ ... }", this->clone()));
|
||||
}
|
||||
|
||||
if (open) {
|
||||
for (auto &member : this->m_sortedMembers)
|
||||
member->draw(provider);
|
||||
|
||||
if (!this->isInlined())
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void getHighlightedAddresses(std::map<u64, u32> &highlight) const override {
|
||||
for (auto &member : this->m_members) {
|
||||
member->getHighlightedAddresses(highlight);
|
||||
}
|
||||
}
|
||||
|
||||
void setOffset(u64 offset) override {
|
||||
for (auto &member : this->m_members)
|
||||
member->setOffset(member->getOffset() - this->getOffset() + offset);
|
||||
|
||||
Pattern::setOffset(offset);
|
||||
}
|
||||
|
||||
void setColor(u32 color) override {
|
||||
Pattern::setColor(color);
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenColor())
|
||||
member->setColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
void sort(ImGuiTableSortSpecs *sortSpecs, prv::Provider *provider) override {
|
||||
this->m_sortedMembers.clear();
|
||||
for (auto &member : this->m_members)
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
|
||||
std::sort(this->m_sortedMembers.begin(), this->m_sortedMembers.end(), [&sortSpecs, &provider](Pattern *left, Pattern *right) {
|
||||
return Pattern::sortPatternTable(sortSpecs, provider, left, right);
|
||||
});
|
||||
|
||||
for (auto &member : this->m_members)
|
||||
member->sort(sortSpecs, provider);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "union " + Pattern::getTypeName();
|
||||
;
|
||||
}
|
||||
|
||||
[[nodiscard]] const auto &getMembers() const {
|
||||
return this->m_members;
|
||||
}
|
||||
|
||||
void setMembers(std::vector<std::shared_ptr<Pattern>> &&members) {
|
||||
this->m_members.clear();
|
||||
for (auto &member : members) {
|
||||
if (member == nullptr) continue;
|
||||
|
||||
this->m_sortedMembers.push_back(member.get());
|
||||
this->m_members.push_back(std::move(member));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override {
|
||||
if (!areCommonPropertiesEqual<decltype(*this)>(other))
|
||||
return false;
|
||||
|
||||
auto &otherUnion = *static_cast<const PatternUnion *>(&other);
|
||||
if (this->m_members.size() != otherUnion.m_members.size())
|
||||
return false;
|
||||
|
||||
for (u64 i = 0; i < this->m_members.size(); i++) {
|
||||
if (*this->m_members[i] != *otherUnion.m_members[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] const Pattern *getPattern(u64 offset) const override {
|
||||
if (this->isHidden()) return nullptr;
|
||||
|
||||
auto largestMember = std::find_if(this->m_members.begin(), this->m_members.end(), [this](const std::shared_ptr<Pattern> &pattern) {
|
||||
return pattern->getSize() == this->getSize();
|
||||
});
|
||||
|
||||
if (largestMember == this->m_members.end())
|
||||
return nullptr;
|
||||
else
|
||||
return (*largestMember)->getPattern(offset);
|
||||
;
|
||||
}
|
||||
|
||||
void setEndian(std::endian endian) override {
|
||||
for (auto &member : this->m_members) {
|
||||
if (!member->hasOverriddenEndian())
|
||||
member->setEndian(endian);
|
||||
}
|
||||
|
||||
Pattern::setEndian(endian);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<Pattern>> m_members;
|
||||
std::vector<Pattern *> m_sortedMembers;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternUnsigned : public Pattern {
|
||||
public:
|
||||
PatternUnsigned(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternUnsigned(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
u128 data = 0;
|
||||
provider->read(this->getOffset(), &data, this->getSize());
|
||||
data = hex::changeEndianess(data, this->getSize(), this->getEndian());
|
||||
|
||||
this->createDefaultEntry(hex::format("{:d} (0x{:0{}X})", data, data, this->getSize() * 2), data);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
switch (this->getSize()) {
|
||||
case 1:
|
||||
return "u8";
|
||||
case 2:
|
||||
return "u16";
|
||||
case 4:
|
||||
return "u32";
|
||||
case 8:
|
||||
return "u64";
|
||||
case 16:
|
||||
return "u128";
|
||||
default:
|
||||
return "Unsigned data";
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <codecvt>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternWideCharacter : public Pattern {
|
||||
public:
|
||||
explicit PatternWideCharacter(Evaluator *evaluator, u64 offset, u32 color = 0)
|
||||
: Pattern(evaluator, offset, 2, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternWideCharacter(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
char16_t character;
|
||||
provider->read(this->getOffset(), &character, 2);
|
||||
character = hex::changeEndianess(character, this->getEndian());
|
||||
|
||||
u128 literal = character;
|
||||
this->createDefaultEntry(hex::format("'{0}'", std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(character)), literal);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "char16";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
char16_t character;
|
||||
provider->read(this->getOffset(), &character, 2);
|
||||
character = hex::changeEndianess(character, this->getEndian());
|
||||
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(character);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <codecvt>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternWideString : public Pattern {
|
||||
public:
|
||||
PatternWideString(Evaluator *evaluator, u64 offset, size_t size, u32 color = 0)
|
||||
: Pattern(evaluator, offset, size, color) { }
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Pattern> clone() const override {
|
||||
return std::unique_ptr<Pattern>(new PatternWideString(*this));
|
||||
}
|
||||
|
||||
void createEntry(prv::Provider *&provider) override {
|
||||
auto size = std::min<size_t>(this->getSize(), 0x100);
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
std::u16string buffer(this->getSize() / sizeof(char16_t), 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), size);
|
||||
|
||||
for (auto &c : buffer)
|
||||
c = hex::changeEndianess(c, 2, this->getEndian());
|
||||
|
||||
buffer.erase(std::remove_if(buffer.begin(), buffer.end(), [](auto c) {
|
||||
return c == 0x00;
|
||||
}),
|
||||
buffer.end());
|
||||
|
||||
auto utf8String = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(buffer);
|
||||
|
||||
this->createDefaultEntry(hex::format("\"{0}\" {1}", utf8String, size > this->getSize() ? "(truncated)" : ""), utf8String);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getFormattedName() const override {
|
||||
return "String16";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string toString(prv::Provider *provider) const override {
|
||||
std::u16string buffer(this->getSize() / sizeof(char16_t), 0x00);
|
||||
provider->read(this->getOffset(), buffer.data(), this->getSize());
|
||||
|
||||
for (auto &c : buffer)
|
||||
c = hex::changeEndianess(c, 2, this->getEndian());
|
||||
|
||||
std::erase_if(buffer, [](auto c) {
|
||||
return c == 0x00;
|
||||
});
|
||||
|
||||
return std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> {}.to_bytes(buffer);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool operator==(const Pattern &other) const override { return areCommonPropertiesEqual<decltype(*this)>(other); }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -11,11 +11,13 @@
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternData;
|
||||
class ASTNode;
|
||||
class Pattern;
|
||||
|
||||
class Token {
|
||||
public:
|
||||
enum class Type : u64 {
|
||||
enum class Type : u64
|
||||
{
|
||||
Keyword,
|
||||
ValueType,
|
||||
Operator,
|
||||
@@ -25,7 +27,8 @@ namespace hex::pl {
|
||||
Separator
|
||||
};
|
||||
|
||||
enum class Keyword {
|
||||
enum class Keyword
|
||||
{
|
||||
Struct,
|
||||
Union,
|
||||
Using,
|
||||
@@ -48,7 +51,8 @@ namespace hex::pl {
|
||||
Continue
|
||||
};
|
||||
|
||||
enum class Operator {
|
||||
enum class Operator
|
||||
{
|
||||
AtDeclaration,
|
||||
Assignment,
|
||||
Inherit,
|
||||
@@ -80,7 +84,8 @@ namespace hex::pl {
|
||||
ScopeResolution
|
||||
};
|
||||
|
||||
enum class ValueType {
|
||||
enum class ValueType
|
||||
{
|
||||
Unsigned8Bit = 0x10,
|
||||
Signed8Bit = 0x11,
|
||||
Unsigned16Bit = 0x20,
|
||||
@@ -108,7 +113,8 @@ namespace hex::pl {
|
||||
Any = 0xFFFF
|
||||
};
|
||||
|
||||
enum class Separator {
|
||||
enum class Separator
|
||||
{
|
||||
RoundBracketOpen,
|
||||
RoundBracketClose,
|
||||
CurlyBracketOpen,
|
||||
@@ -133,7 +139,7 @@ namespace hex::pl {
|
||||
std::string m_identifier;
|
||||
};
|
||||
|
||||
using Literal = std::variant<char, bool, u128, i128, double, std::string, PatternData *>;
|
||||
using Literal = std::variant<char, bool, u128, i128, double, std::string, std::shared_ptr<Pattern>>;
|
||||
using ValueTypes = std::variant<Keyword, Identifier, Operator, Literal, ValueType, Separator>;
|
||||
|
||||
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
|
||||
@@ -158,7 +164,7 @@ namespace hex::pl {
|
||||
static u128 literalToUnsigned(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> u128 { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](PatternData *) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](const std::shared_ptr<Pattern> &) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> u128 { return result; } },
|
||||
literal);
|
||||
}
|
||||
@@ -166,7 +172,7 @@ namespace hex::pl {
|
||||
static i128 literalToSigned(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> i128 { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](PatternData *) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](const std::shared_ptr<Pattern> &) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> i128 { return result; } },
|
||||
literal);
|
||||
}
|
||||
@@ -174,7 +180,7 @@ namespace hex::pl {
|
||||
static double literalToFloatingPoint(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> double { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](PatternData *) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](const std::shared_ptr<Pattern> &) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> double { return result; } },
|
||||
literal);
|
||||
}
|
||||
@@ -182,7 +188,7 @@ namespace hex::pl {
|
||||
static bool literalToBoolean(const pl::Token::Literal &literal) {
|
||||
return std::visit(overloaded {
|
||||
[](const std::string &) -> bool { LogConsole::abortEvaluation("expected integral type, got string"); },
|
||||
[](PatternData *) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](const std::unique_ptr<Pattern> &) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> bool { return result != 0; } },
|
||||
literal);
|
||||
}
|
||||
@@ -197,7 +203,7 @@ namespace hex::pl {
|
||||
[](i128 result) -> std::string { return hex::to_string(result); },
|
||||
[](bool result) -> std::string { return result ? "true" : "false"; },
|
||||
[](char result) -> std::string { return { 1, result }; },
|
||||
[](PatternData *) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](const std::shared_ptr<Pattern> &) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
|
||||
[](auto &&result) -> std::string { return std::to_string(result); } },
|
||||
literal);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -16,7 +17,7 @@ namespace hex::pl {
|
||||
public:
|
||||
Validator() = default;
|
||||
|
||||
bool validate(const std::vector<ASTNode *> &ast);
|
||||
bool validate(const std::vector<std::shared_ptr<ASTNode>> &ast);
|
||||
|
||||
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include <hex/providers/overlay.hpp>
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
class PatternLanguage;
|
||||
}
|
||||
|
||||
namespace hex::prv {
|
||||
|
||||
@@ -79,7 +83,7 @@ namespace hex::prv {
|
||||
virtual void drawLoadInterface();
|
||||
virtual void drawInterface();
|
||||
|
||||
pl::PatternLanguage &getPatternLanguageRuntime() { return this->m_patternLanguageRuntime; }
|
||||
pl::PatternLanguage &getPatternLanguageRuntime() { return *this->m_patternLanguageRuntime; }
|
||||
std::string &getPatternLanguageSourceCode() { return this->m_patternLanguageSourceCode; }
|
||||
|
||||
protected:
|
||||
@@ -90,7 +94,7 @@ namespace hex::prv {
|
||||
std::list<std::map<u64, u8>> m_patches;
|
||||
std::list<Overlay *> m_overlays;
|
||||
|
||||
pl::PatternLanguage m_patternLanguageRuntime;
|
||||
std::unique_ptr<pl::PatternLanguage> m_patternLanguageRuntime;
|
||||
std::string m_patternLanguageSourceCode;
|
||||
};
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace ImGui {
|
||||
}
|
||||
|
||||
void TextFormattedCentered(const std::string &fmt, auto &&...args) {
|
||||
auto text = hex::format(fmt);
|
||||
auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
|
||||
auto availableSpace = ImGui::GetContentRegionAvail();
|
||||
auto textSize = ImGui::CalcTextSize(text.c_str(), nullptr, false, availableSpace.x * 0.75F);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <hex/api/localization.hpp>
|
||||
|
||||
@@ -38,6 +39,7 @@ namespace hex {
|
||||
static void showMessagePopup(const std::string &message);
|
||||
static void showErrorPopup(const std::string &errorMessage);
|
||||
static void showFatalPopup(const std::string &errorMessage);
|
||||
static void showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback);
|
||||
|
||||
static void showFileChooserPopup(const std::vector<fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback);
|
||||
|
||||
@@ -71,6 +73,8 @@ namespace hex {
|
||||
|
||||
static std::string s_popupMessage;
|
||||
|
||||
static std::function<void()> s_yesCallback, s_noCallback;
|
||||
|
||||
static u32 s_selectableFileIndex;
|
||||
static std::vector<fs::path> s_selectableFiles;
|
||||
static std::function<void(fs::path)> s_selectableFileOpenCallback;
|
||||
|
||||
@@ -41,10 +41,23 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback) {
|
||||
static auto getCategoryEntry(const std::string &unlocalizedCategory) {
|
||||
auto &entries = getEntries();
|
||||
const size_t curSlot = entries.size();
|
||||
auto found = entries.find(Category { unlocalizedCategory });
|
||||
|
||||
if (found == entries.end()) {
|
||||
auto [iter, _] = entries.emplace(Category { unlocalizedCategory, curSlot }, std::vector<Entry> {});
|
||||
return iter;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 defaultValue, const Callback &callback, bool requiresRestart) {
|
||||
log::info("Registered new integer setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getEntries()[unlocalizedCategory.c_str()].emplace_back(Entry { unlocalizedName.c_str(), callback });
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName.c_str(), requiresRestart, callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
@@ -54,10 +67,10 @@ namespace hex {
|
||||
json[unlocalizedCategory][unlocalizedName] = int(defaultValue);
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback) {
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::string &defaultValue, const Callback &callback, bool requiresRestart) {
|
||||
log::info("Registered new string setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getEntries()[unlocalizedCategory].emplace_back(Entry { unlocalizedName, callback });
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, requiresRestart, callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
@@ -67,6 +80,23 @@ namespace hex {
|
||||
json[unlocalizedCategory][unlocalizedName] = std::string(defaultValue);
|
||||
}
|
||||
|
||||
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, const std::vector<std::string> &defaultValue, const Callback &callback, bool requiresRestart) {
|
||||
log::info("Registered new string array setting: [{}]: {}", unlocalizedCategory, unlocalizedName);
|
||||
|
||||
getCategoryEntry(unlocalizedCategory)->second.emplace_back(Entry { unlocalizedName, requiresRestart, callback });
|
||||
|
||||
auto &json = getSettingsData();
|
||||
|
||||
if (!json.contains(unlocalizedCategory))
|
||||
json[unlocalizedCategory] = nlohmann::json::object();
|
||||
if (!json[unlocalizedCategory].contains(unlocalizedName) || !json[unlocalizedCategory][unlocalizedName].is_array())
|
||||
json[unlocalizedCategory][unlocalizedName] = defaultValue;
|
||||
}
|
||||
|
||||
void addCategoryDescription(const std::string &unlocalizedCategory, const std::string &unlocalizedCategoryDescription) {
|
||||
getCategoryDescriptions()[unlocalizedCategory] = unlocalizedCategoryDescription;
|
||||
}
|
||||
|
||||
void write(const std::string &unlocalizedCategory, const std::string &unlocalizedName, i64 value) {
|
||||
auto &json = getSettingsData();
|
||||
|
||||
@@ -141,12 +171,18 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
std::map<std::string, std::vector<Entry>> &getEntries() {
|
||||
static std::map<std::string, std::vector<Entry>> entries;
|
||||
std::map<Category, std::vector<Entry>> &getEntries() {
|
||||
static std::map<Category, std::vector<Entry>> entries;
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> &getCategoryDescriptions() {
|
||||
static std::map<std::string, std::string> descriptions;
|
||||
|
||||
return descriptions;
|
||||
}
|
||||
|
||||
nlohmann::json getSetting(const std::string &unlocalizedCategory, const std::string &unlocalizedName) {
|
||||
auto &settings = getSettingsData();
|
||||
|
||||
@@ -297,10 +333,10 @@ namespace hex {
|
||||
|
||||
namespace ContentRegistry::DataInspector {
|
||||
|
||||
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction function) {
|
||||
void add(const std::string &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
|
||||
log::info("Registered new data inspector format: {}", unlocalizedName);
|
||||
|
||||
getEntries().push_back({ unlocalizedName, requiredSize, std::move(function) });
|
||||
getEntries().push_back({ unlocalizedName, requiredSize, std::move(displayGeneratorFunction), std::move(editingFunction) });
|
||||
}
|
||||
|
||||
std::vector<impl::Entry> &getEntries() {
|
||||
@@ -314,7 +350,7 @@ namespace hex {
|
||||
namespace ContentRegistry::DataProcessorNode {
|
||||
|
||||
void impl::add(const impl::Entry &entry) {
|
||||
log::info("Registered new data processor node type: [{}]: ", entry.category, entry.name);
|
||||
log::info("Registered new data processor node type: [{}]: {}", entry.category, entry.name);
|
||||
|
||||
getEntries().push_back(entry);
|
||||
}
|
||||
@@ -496,4 +532,4 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <utility>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace ImHexApi::Common {
|
||||
@@ -26,16 +29,29 @@ namespace hex {
|
||||
|
||||
namespace ImHexApi::HexEditor {
|
||||
|
||||
static std::map<u32, ImHexApi::HexEditor::Highlighting> s_highlights;
|
||||
|
||||
Highlighting::Highlighting(Region region, color_t color, const std::string &tooltip)
|
||||
: m_region(region), m_color(color), m_tooltip(tooltip) {
|
||||
Highlighting::Highlighting(Region region, color_t color, std::string tooltip)
|
||||
: m_region(region), m_color(color), m_tooltip(std::move(tooltip)) {
|
||||
}
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, std::string tooltip) {
|
||||
auto id = s_highlights.size();
|
||||
namespace impl {
|
||||
|
||||
s_highlights.insert({
|
||||
static std::map<u32, Highlighting> s_highlights;
|
||||
std::map<u32, Highlighting> &getHighlights() {
|
||||
return s_highlights;
|
||||
}
|
||||
|
||||
static std::map<u32, HighlightingFunction> s_highlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getHighlightingFunctions() {
|
||||
return s_highlightingFunctions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u32 addHighlight(const Region ®ion, color_t color, const std::string &tooltip) {
|
||||
auto &highlights = impl::getHighlights();
|
||||
auto id = highlights.size();
|
||||
|
||||
highlights.insert({
|
||||
id, Highlighting {region, color, tooltip}
|
||||
});
|
||||
|
||||
@@ -45,13 +61,27 @@ namespace hex {
|
||||
}
|
||||
|
||||
void removeHighlight(u32 id) {
|
||||
s_highlights.erase(id);
|
||||
impl::getHighlights().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
std::map<u32, Highlighting> &getHighlights() {
|
||||
return s_highlights;
|
||||
u32 addHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
auto &highlightFuncs = impl::getHighlightingFunctions();
|
||||
|
||||
auto id = highlightFuncs.size();
|
||||
|
||||
highlightFuncs.insert({ id, function });
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeHighlightingProvider(u32 id) {
|
||||
impl::getHighlightingFunctions().erase(id);
|
||||
|
||||
EventManager::post<EventHighlightingChanged>();
|
||||
}
|
||||
|
||||
Region getSelection() {
|
||||
@@ -189,6 +219,11 @@ namespace hex {
|
||||
s_programArguments.envp = envp;
|
||||
}
|
||||
|
||||
static bool s_borderlessWindowMode;
|
||||
void setBorderlessWindowMode(bool enabled) {
|
||||
s_borderlessWindowMode = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -225,6 +260,10 @@ namespace hex {
|
||||
return impl::s_mainDockSpaceId;
|
||||
}
|
||||
|
||||
bool isBorderlessWindowModeEnabled() {
|
||||
return impl::s_borderlessWindowMode;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> &getInitArguments() {
|
||||
static std::map<std::string, std::string> initArgs;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace hex {
|
||||
|
||||
void File::close() {
|
||||
if (isValid()) {
|
||||
fclose(this->m_file);
|
||||
std::fclose(this->m_file);
|
||||
this->m_file = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,8 @@ namespace hex {
|
||||
if (!isValid()) return {};
|
||||
|
||||
auto size = numBytes ?: getSize();
|
||||
if (size == 0) return {};
|
||||
|
||||
std::vector<u8> bytes(size);
|
||||
auto bytesRead = fread(bytes.data(), 1, bytes.size(), this->m_file);
|
||||
|
||||
@@ -72,25 +74,28 @@ namespace hex {
|
||||
|
||||
auto bytes = readBytes(numBytes);
|
||||
|
||||
if (bytes.empty())
|
||||
return "";
|
||||
|
||||
return { reinterpret_cast<char *>(bytes.data()), bytes.size() };
|
||||
}
|
||||
|
||||
void File::write(const u8 *buffer, size_t size) {
|
||||
if (!isValid()) return;
|
||||
|
||||
fwrite(buffer, size, 1, this->m_file);
|
||||
std::fwrite(buffer, size, 1, this->m_file);
|
||||
}
|
||||
|
||||
void File::write(const std::vector<u8> &bytes) {
|
||||
if (!isValid()) return;
|
||||
|
||||
fwrite(bytes.data(), 1, bytes.size(), this->m_file);
|
||||
std::fwrite(bytes.data(), 1, bytes.size(), this->m_file);
|
||||
}
|
||||
|
||||
void File::write(const std::string &string) {
|
||||
if (!isValid()) return;
|
||||
|
||||
fwrite(string.data(), string.size(), 1, this->m_file);
|
||||
std::fwrite(string.data(), string.size(), 1, this->m_file);
|
||||
}
|
||||
|
||||
size_t File::getSize() const {
|
||||
@@ -98,9 +103,12 @@ namespace hex {
|
||||
|
||||
auto startPos = ftello64(this->m_file);
|
||||
fseeko64(this->m_file, 0, SEEK_END);
|
||||
size_t size = ftello64(this->m_file);
|
||||
auto size = ftello64(this->m_file);
|
||||
fseeko64(this->m_file, startPos, SEEK_SET);
|
||||
|
||||
if (size < 0)
|
||||
return 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -111,12 +119,18 @@ namespace hex {
|
||||
}
|
||||
|
||||
void File::flush() {
|
||||
fflush(this->m_file);
|
||||
std::fflush(this->m_file);
|
||||
}
|
||||
|
||||
void File::remove() {
|
||||
bool File::remove() {
|
||||
this->close();
|
||||
std::remove(this->m_path.string().c_str());
|
||||
return std::remove(this->m_path.string().c_str()) == 0;
|
||||
}
|
||||
|
||||
void File::disableBuffering() {
|
||||
if (!isValid()) return;
|
||||
|
||||
std::setvbuf(this->m_file, nullptr, _IONBF, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,8 +23,10 @@ namespace hex::log {
|
||||
void redirectToFile() {
|
||||
if (g_loggerFile.isValid()) return;
|
||||
|
||||
for (const auto &path : hex::getPath(ImHexPath::Logs)) {
|
||||
for (const auto &path : hex::getPath(ImHexPath::Logs, true)) {
|
||||
fs::create_directories(path);
|
||||
g_loggerFile = File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::now())), File::Mode::Create);
|
||||
g_loggerFile.disableBuffering();
|
||||
|
||||
if (g_loggerFile.isValid()) break;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace hex {
|
||||
std::optional<i32> Net::execute() {
|
||||
CURLcode result = curl_easy_perform(this->m_ctx);
|
||||
if (result != CURLE_OK)
|
||||
log::error("Net request failed with error {}!", curl_easy_strerror(result));
|
||||
log::error("Net request failed with error {0}: '{1}'", result, curl_easy_strerror(result));
|
||||
|
||||
i32 responseCode = 0;
|
||||
curl_easy_getinfo(this->m_ctx, CURLINFO_RESPONSE_CODE, &responseCode);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/paths_mac.h>
|
||||
|
||||
#include <xdg.hpp>
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -38,9 +36,17 @@ namespace hex {
|
||||
|
||||
std::vector<fs::path> getPath(ImHexPath path, bool listNonExisting) {
|
||||
std::vector<fs::path> result;
|
||||
const auto exePath = getExecutablePath();
|
||||
const std::string settingName { "hex.builtin.setting.folders" };
|
||||
auto userDirs = ContentRegistry::Settings::read(settingName, settingName, std::vector<std::string> {});
|
||||
|
||||
auto addUserDirs = [&userDirs](auto &paths) {
|
||||
std::transform(userDirs.begin(), userDirs.end(), std::back_inserter(paths), [](auto &item) {
|
||||
return std::move(item);
|
||||
});
|
||||
};
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
const auto exePath = getExecutablePath();
|
||||
const auto parentDir = fs::path(exePath).parent_path();
|
||||
|
||||
fs::path appDataDir;
|
||||
@@ -53,25 +59,29 @@ namespace hex {
|
||||
CoTaskMemFree(wAppDataPath);
|
||||
}
|
||||
|
||||
std::vector<fs::path> paths = { parentDir, appDataDir / "imhex" };
|
||||
std::vector<fs::path> paths = { appDataDir / "imhex", parentDir };
|
||||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "patterns").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "includes").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "magic").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "python").string();
|
||||
});
|
||||
@@ -82,6 +92,7 @@ namespace hex {
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "yara").string();
|
||||
});
|
||||
@@ -94,11 +105,13 @@ namespace hex {
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "constants").string();
|
||||
});
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
addUserDirs(paths);
|
||||
std::transform(paths.begin(), paths.end(), std::back_inserter(result), [](auto &path) {
|
||||
return (path / "encodings").string();
|
||||
});
|
||||
@@ -113,10 +126,9 @@ namespace hex {
|
||||
}
|
||||
#elif defined(OS_MACOS)
|
||||
// Get path to special directories
|
||||
const auto exePath = getExecutablePath();
|
||||
const fs::path applicationSupportDir(getMacApplicationSupportDirectoryPath());
|
||||
|
||||
std::vector<fs::path> paths = { exePath, applicationSupportDir };
|
||||
std::vector<fs::path> paths = { applicationSupportDir, exePath };
|
||||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
@@ -161,34 +173,37 @@ namespace hex {
|
||||
std::vector<fs::path> configDirs = xdg::ConfigDirs();
|
||||
std::vector<fs::path> dataDirs = xdg::DataDirs();
|
||||
|
||||
configDirs.insert(configDirs.begin(), xdg::ConfigHomeDir());
|
||||
dataDirs.insert(dataDirs.begin(), xdg::DataHomeDir());
|
||||
configDirs.push_back(xdg::ConfigHomeDir());
|
||||
dataDirs.push_back(xdg::DataHomeDir());
|
||||
|
||||
for (auto &dir : dataDirs)
|
||||
dir = dir / "imhex";
|
||||
|
||||
const auto exePath = getExecutablePath();
|
||||
|
||||
if (!exePath.empty())
|
||||
dataDirs.emplace(dataDirs.begin(), fs::path(exePath.data()).parent_path());
|
||||
|
||||
switch (path) {
|
||||
case ImHexPath::Patterns:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "patterns").string(); });
|
||||
break;
|
||||
case ImHexPath::PatternsInclude:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "includes").string(); });
|
||||
break;
|
||||
case ImHexPath::Magic:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "magic").string(); });
|
||||
break;
|
||||
case ImHexPath::Python:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p).string(); });
|
||||
break;
|
||||
case ImHexPath::Plugins:
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "plugins").string(); });
|
||||
break;
|
||||
case ImHexPath::Yara:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "yara").string(); });
|
||||
break;
|
||||
case ImHexPath::Config:
|
||||
@@ -198,9 +213,11 @@ namespace hex {
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "resources").string(); });
|
||||
break;
|
||||
case ImHexPath::Constants:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "constants").string(); });
|
||||
break;
|
||||
case ImHexPath::Encodings:
|
||||
addUserDirs(dataDirs);
|
||||
std::transform(dataDirs.begin(), dataDirs.end(), std::back_inserter(result), [](auto p) { return (p / "encodings").string(); });
|
||||
break;
|
||||
case ImHexPath::Logs:
|
||||
@@ -209,6 +226,7 @@ namespace hex {
|
||||
default:
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (!listNonExisting) {
|
||||
@@ -221,4 +239,4 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace hex {
|
||||
filePath = ProjectFile::s_currProjectFilePath;
|
||||
|
||||
try {
|
||||
projectFileData["filePath"] = ProjectFile::s_filePath;
|
||||
projectFileData["filePath"] = ProjectFile::s_filePath.string();
|
||||
projectFileData["pattern"] = ProjectFile::s_pattern;
|
||||
projectFileData["patches"] = ProjectFile::s_patches;
|
||||
projectFileData["dataProcessor"] = ProjectFile::s_dataProcessorContent;
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#endif
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
long double operator""_scaled(long double value) {
|
||||
@@ -111,6 +114,18 @@ namespace hex {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string makeStringPrintable(const std::string &string) {
|
||||
std::string result;
|
||||
for (char c : string) {
|
||||
if (std::isprint(c))
|
||||
result += c;
|
||||
else
|
||||
result += hex::format("\\x{0:02X}", u8(c));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string makePrintable(u8 c) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
@@ -262,7 +277,142 @@ namespace hex {
|
||||
#endif
|
||||
}
|
||||
|
||||
void openFileBrowser(const std::string &title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback, const std::string &defaultPath) {
|
||||
std::string encodeByteString(const std::vector<u8> &bytes) {
|
||||
std::string result;
|
||||
|
||||
for (u8 byte : bytes) {
|
||||
if (std::isprint(byte) && byte != '\\')
|
||||
result += char(byte);
|
||||
else {
|
||||
switch (byte) {
|
||||
case '\\':
|
||||
result += "\\";
|
||||
break;
|
||||
case '\a':
|
||||
result += "\\a";
|
||||
break;
|
||||
case '\b':
|
||||
result += "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
result += "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
result += "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
result += "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
result += "\\t";
|
||||
break;
|
||||
case '\v':
|
||||
result += "\\v";
|
||||
break;
|
||||
default:
|
||||
result += hex::format("\\x{:02X}", byte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> decodeByteString(const std::string &string) {
|
||||
u32 offset = 0;
|
||||
std::vector<u8> result;
|
||||
|
||||
while (offset < string.length()) {
|
||||
auto c = [&] { return string[offset]; };
|
||||
|
||||
if (c() == '\\') {
|
||||
if ((offset + 2) >= string.length()) return {};
|
||||
|
||||
offset++;
|
||||
|
||||
char escapeChar = c();
|
||||
|
||||
offset++;
|
||||
|
||||
switch (escapeChar) {
|
||||
case 'a':
|
||||
result.push_back('\a');
|
||||
break;
|
||||
case 'b':
|
||||
result.push_back('\b');
|
||||
break;
|
||||
case 'f':
|
||||
result.push_back('\f');
|
||||
break;
|
||||
case 'n':
|
||||
result.push_back('\n');
|
||||
break;
|
||||
case 'r':
|
||||
result.push_back('\r');
|
||||
break;
|
||||
case 't':
|
||||
result.push_back('\t');
|
||||
break;
|
||||
case 'v':
|
||||
result.push_back('\v');
|
||||
break;
|
||||
case '\\':
|
||||
result.push_back('\\');
|
||||
break;
|
||||
case 'x':
|
||||
{
|
||||
u8 byte = 0x00;
|
||||
if ((offset + 1) >= string.length()) return {};
|
||||
|
||||
for (u8 i = 0; i < 2; i++) {
|
||||
byte <<= 4;
|
||||
if (c() >= '0' && c() <= '9')
|
||||
byte |= 0x00 + (c() - '0');
|
||||
else if (c() >= 'A' && c() <= 'F')
|
||||
byte |= 0x0A + (c() - 'A');
|
||||
else if (c() >= 'a' && c() <= 'f')
|
||||
byte |= 0x0A + (c() - 'a');
|
||||
else
|
||||
return {};
|
||||
|
||||
offset++;
|
||||
}
|
||||
|
||||
result.push_back(byte);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
result.push_back(c());
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isPathWritable(fs::path path) {
|
||||
constexpr static auto TestFileName = "__imhex__tmp__";
|
||||
{
|
||||
File file(path / TestFileName, File::Mode::Read);
|
||||
if (file.isValid()) {
|
||||
if (!file.remove())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
File file(path / TestFileName, File::Mode::Create);
|
||||
bool result = file.isValid();
|
||||
if (!file.remove())
|
||||
return false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool openFileBrowser(const std::string &title, DialogMode mode, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback, const std::string &defaultPath) {
|
||||
NFD::Init();
|
||||
|
||||
nfdchar_t *outPath;
|
||||
@@ -282,11 +432,13 @@ namespace hex {
|
||||
}
|
||||
|
||||
if (result == NFD_OKAY) {
|
||||
callback(outPath);
|
||||
callback(reinterpret_cast<const char8_t *>(outPath));
|
||||
NFD::FreePath(outPath);
|
||||
}
|
||||
|
||||
NFD::Quit();
|
||||
|
||||
return result == NFD_OKAY;
|
||||
}
|
||||
|
||||
float float16ToFloat32(u16 float16) {
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
#include <hex/pattern_language/evaluator.hpp>
|
||||
#include <hex/pattern_language/ast_node.hpp>
|
||||
#include <hex/pattern_language/pattern_data.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_function_call.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_function_definition.hpp>
|
||||
|
||||
#include <hex/pattern_language/patterns/pattern_unsigned.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_signed.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_float.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_boolean.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_character.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_string.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern_enum.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
@@ -19,52 +32,61 @@ namespace hex::pl {
|
||||
}
|
||||
}
|
||||
|
||||
auto startOffset = this->dataOffset();
|
||||
auto pattern = type == nullptr ? nullptr : type->createPatterns(this).front();
|
||||
auto startOffset = this->dataOffset();
|
||||
|
||||
std::unique_ptr<Pattern> pattern;
|
||||
this->dataOffset() = startOffset;
|
||||
|
||||
if (pattern == nullptr) {
|
||||
bool referenceType = false;
|
||||
|
||||
if (type == nullptr) {
|
||||
// Handle auto variables
|
||||
if (!value.has_value())
|
||||
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
|
||||
|
||||
if (std::get_if<u128>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataUnsigned(this, 0, sizeof(u128));
|
||||
pattern = std::unique_ptr<Pattern>(new PatternUnsigned(this, 0, sizeof(u128)));
|
||||
else if (std::get_if<i128>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataSigned(this, 0, sizeof(i128));
|
||||
pattern = std::unique_ptr<Pattern>(new PatternSigned(this, 0, sizeof(i128)));
|
||||
else if (std::get_if<double>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataFloat(this, 0, sizeof(double));
|
||||
pattern = std::unique_ptr<Pattern>(new PatternFloat(this, 0, sizeof(double)));
|
||||
else if (std::get_if<bool>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataBoolean(this, 0);
|
||||
pattern = std::unique_ptr<Pattern>(new PatternBoolean(this, 0));
|
||||
else if (std::get_if<char>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataCharacter(this, 0);
|
||||
else if (std::get_if<PatternData *>(&value.value()) != nullptr)
|
||||
pattern = std::get<PatternData *>(value.value())->clone();
|
||||
pattern = std::unique_ptr<Pattern>(new PatternCharacter(this, 0));
|
||||
else if (std::get_if<std::string>(&value.value()) != nullptr)
|
||||
pattern = new PatternDataString(this, 0, 1);
|
||||
else
|
||||
pattern = std::unique_ptr<Pattern>(new PatternString(this, 0, 1));
|
||||
else if (auto patternValue = std::get_if<std::shared_ptr<Pattern>>(&value.value()); patternValue != nullptr) {
|
||||
pattern = (*patternValue)->clone();
|
||||
referenceType = true;
|
||||
} else
|
||||
LogConsole::abortEvaluation("cannot determine type of auto variable", type);
|
||||
} else {
|
||||
pattern = std::move(type->createPatterns(this).front());
|
||||
}
|
||||
|
||||
pattern->setVariableName(name);
|
||||
pattern->setLocal(true);
|
||||
pattern->setOffset(this->getStack().size());
|
||||
|
||||
this->getStack().emplace_back();
|
||||
variables.push_back(pattern);
|
||||
if (!referenceType) {
|
||||
pattern->setOffset(this->getStack().size());
|
||||
pattern->setLocal(true);
|
||||
this->getStack().emplace_back();
|
||||
}
|
||||
|
||||
if (outVariable)
|
||||
this->m_outVariables[name] = pattern->getOffset();
|
||||
|
||||
variables.push_back(std::move(pattern));
|
||||
}
|
||||
|
||||
void Evaluator::setVariable(const std::string &name, const Token::Literal &value) {
|
||||
PatternData *pattern = nullptr;
|
||||
std::unique_ptr<Pattern> pattern = nullptr;
|
||||
|
||||
{
|
||||
auto &variables = *this->getScope(0).scope;
|
||||
for (auto &variable : variables) {
|
||||
if (variable->getVariableName() == name) {
|
||||
pattern = variable;
|
||||
pattern = variable->clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -77,7 +99,7 @@ namespace hex::pl {
|
||||
if (!variable->isLocal())
|
||||
LogConsole::abortEvaluation(hex::format("cannot modify global variable '{}' which has been placed in memory", name));
|
||||
|
||||
pattern = variable;
|
||||
pattern = variable->clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -86,39 +108,41 @@ namespace hex::pl {
|
||||
if (pattern == nullptr)
|
||||
LogConsole::abortEvaluation(hex::format("no variable with name '{}' found", name));
|
||||
|
||||
if (!pattern->isLocal()) return;
|
||||
|
||||
Token::Literal castedLiteral = std::visit(overloaded {
|
||||
[&](double &value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternDataUnsigned *>(pattern))
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()))
|
||||
return u128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternDataSigned *>(pattern))
|
||||
else if (dynamic_cast<PatternSigned *>(pattern.get()))
|
||||
return i128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternDataFloat *>(pattern))
|
||||
else if (dynamic_cast<PatternFloat *>(pattern.get()))
|
||||
return pattern->getSize() == sizeof(float) ? double(float(value)) : value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type 'double' to type '{}'", pattern->getTypeName()));
|
||||
},
|
||||
[&](const std::string &value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternDataString *>(pattern))
|
||||
if (dynamic_cast<PatternString *>(pattern.get()))
|
||||
return value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type 'string' to type '{}'", pattern->getTypeName()));
|
||||
},
|
||||
[&](PatternData *const &value) -> Token::Literal {
|
||||
[&](const std::shared_ptr<Pattern> &value) -> Token::Literal {
|
||||
if (value->getTypeName() == pattern->getTypeName())
|
||||
return value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast type '{}' to type '{}'", value->getTypeName(), pattern->getTypeName()));
|
||||
},
|
||||
[&](auto &&value) -> Token::Literal {
|
||||
if (dynamic_cast<PatternDataUnsigned *>(pattern) || dynamic_cast<PatternDataEnum *>(pattern))
|
||||
if (dynamic_cast<PatternUnsigned *>(pattern.get()) || dynamic_cast<PatternEnum *>(pattern.get()))
|
||||
return u128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternDataSigned *>(pattern))
|
||||
else if (dynamic_cast<PatternSigned *>(pattern.get()))
|
||||
return i128(value) & bitmask(pattern->getSize() * 8);
|
||||
else if (dynamic_cast<PatternDataCharacter *>(pattern))
|
||||
else if (dynamic_cast<PatternCharacter *>(pattern.get()))
|
||||
return char(value);
|
||||
else if (dynamic_cast<PatternDataBoolean *>(pattern))
|
||||
else if (dynamic_cast<PatternBoolean *>(pattern.get()))
|
||||
return bool(value);
|
||||
else if (dynamic_cast<PatternDataFloat *>(pattern))
|
||||
else if (dynamic_cast<PatternFloat *>(pattern.get()))
|
||||
return pattern->getSize() == sizeof(float) ? double(float(value)) : value;
|
||||
else
|
||||
LogConsole::abortEvaluation(hex::format("cannot cast integer literal to type '{}'", pattern->getTypeName()));
|
||||
@@ -128,7 +152,7 @@ namespace hex::pl {
|
||||
this->getStack()[pattern->getOffset()] = castedLiteral;
|
||||
}
|
||||
|
||||
std::optional<std::vector<PatternData *>> Evaluator::evaluate(const std::vector<ASTNode *> &ast) {
|
||||
std::optional<std::vector<std::shared_ptr<Pattern>>> Evaluator::evaluate(const std::vector<std::shared_ptr<ASTNode>> &ast) {
|
||||
this->m_stack.clear();
|
||||
this->m_customFunctions.clear();
|
||||
this->m_scopes.clear();
|
||||
@@ -147,11 +171,9 @@ namespace hex::pl {
|
||||
this->dataOffset() = 0x00;
|
||||
this->m_currPatternCount = 0;
|
||||
|
||||
for (auto &func : this->m_customFunctionDefinitions)
|
||||
delete func;
|
||||
this->m_customFunctionDefinitions.clear();
|
||||
|
||||
std::vector<PatternData *> patterns;
|
||||
std::vector<std::shared_ptr<Pattern>> patterns;
|
||||
|
||||
try {
|
||||
this->setCurrentControlFlowStatement(ControlFlowStatement::None);
|
||||
@@ -160,33 +182,30 @@ namespace hex::pl {
|
||||
popScope();
|
||||
};
|
||||
|
||||
for (auto node : ast) {
|
||||
if (dynamic_cast<ASTNodeTypeDecl *>(node)) {
|
||||
for (auto &node : ast) {
|
||||
if (dynamic_cast<ASTNodeTypeDecl *>(node.get())) {
|
||||
; // Don't create patterns from type declarations
|
||||
} else if (dynamic_cast<ASTNodeFunctionCall *>(node)) {
|
||||
delete node->evaluate(this);
|
||||
} else if (dynamic_cast<ASTNodeFunctionDefinition *>(node)) {
|
||||
} else if (dynamic_cast<ASTNodeFunctionCall *>(node.get())) {
|
||||
(void)node->evaluate(this);
|
||||
} else if (dynamic_cast<ASTNodeFunctionDefinition *>(node.get())) {
|
||||
this->m_customFunctionDefinitions.push_back(node->evaluate(this));
|
||||
} else if (auto varDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node)) {
|
||||
auto pattern = node->createPatterns(this).front();
|
||||
} else if (auto varDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node.get())) {
|
||||
for (auto &pattern : node->createPatterns(this)) {
|
||||
if (varDeclNode->getPlacementOffset() == nullptr) {
|
||||
auto type = varDeclNode->getType()->evaluate(this);
|
||||
|
||||
if (varDeclNode->getPlacementOffset() == nullptr) {
|
||||
auto type = varDeclNode->getType()->evaluate(this);
|
||||
ON_SCOPE_EXIT { delete type; };
|
||||
auto &name = pattern->getVariableName();
|
||||
this->createVariable(name, type.get(), std::nullopt, varDeclNode->isOutVariable());
|
||||
|
||||
auto &name = pattern->getVariableName();
|
||||
this->createVariable(name, type, std::nullopt, varDeclNode->isOutVariable());
|
||||
|
||||
if (varDeclNode->isInVariable() && this->m_inVariables.contains(name))
|
||||
this->setVariable(name, this->m_inVariables[name]);
|
||||
|
||||
delete pattern;
|
||||
} else {
|
||||
patterns.push_back(pattern);
|
||||
if (varDeclNode->isInVariable() && this->m_inVariables.contains(name))
|
||||
this->setVariable(name, this->m_inVariables[name]);
|
||||
} else {
|
||||
patterns.push_back(std::move(pattern));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto newPatterns = node->createPatterns(this);
|
||||
std::copy(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
std::move(newPatterns.begin(), newPatterns.end(), std::back_inserter(patterns));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,8 +223,6 @@ namespace hex::pl {
|
||||
if (error.getLineNumber() != 0)
|
||||
this->m_console.setHardError(error);
|
||||
|
||||
for (auto &pattern : patterns)
|
||||
delete pattern;
|
||||
patterns.clear();
|
||||
|
||||
this->m_currPatternCount = 0;
|
||||
@@ -214,7 +231,7 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
// Remove global local variables
|
||||
std::erase_if(patterns, [](PatternData *pattern) {
|
||||
std::erase_if(patterns, [](const std::shared_ptr<Pattern> &pattern) {
|
||||
return pattern->isLocal();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
#include <hex/pattern_language/log_console.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
void LogConsole::log(Level level, const std::string &message) {
|
||||
this->m_consoleLog.emplace_back(level, message);
|
||||
}
|
||||
|
||||
[[noreturn]] void LogConsole::abortEvaluation(const std::string &message, const ASTNode *node) {
|
||||
if (node == nullptr)
|
||||
throw PatternLanguageError(0, "Evaluator: " + message);
|
||||
@@ -15,9 +11,4 @@ namespace hex::pl {
|
||||
throw PatternLanguageError(node->getLineNumber(), "Evaluator: " + message);
|
||||
}
|
||||
|
||||
void LogConsole::clear() {
|
||||
this->m_consoleLog.clear();
|
||||
this->m_lastHardError = {};
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
class PatternData;
|
||||
class Pattern;
|
||||
|
||||
PatternLanguage::PatternLanguage() {
|
||||
this->m_preprocessor = new Preprocessor();
|
||||
@@ -83,6 +83,18 @@ namespace hex::pl {
|
||||
ImHexApi::Provider::get()->setBaseAddress(baseAddress);
|
||||
return true;
|
||||
});
|
||||
|
||||
this->m_preprocessor->addPragmaHandler("bitfield_order", [this](const std::string &value) {
|
||||
if (value == "left_to_right") {
|
||||
this->m_evaluator->setBitfieldOrder(BitfieldOrder::LeftToRight);
|
||||
return true;
|
||||
} else if (value == "right_to_left") {
|
||||
this->m_evaluator->setBitfieldOrder(BitfieldOrder::RightToLeft);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
PatternLanguage::~PatternLanguage() {
|
||||
@@ -92,7 +104,7 @@ namespace hex::pl {
|
||||
delete this->m_validator;
|
||||
}
|
||||
|
||||
std::optional<std::vector<ASTNode *>> PatternLanguage::parseString(const std::string &code) {
|
||||
std::optional<std::vector<std::shared_ptr<ASTNode>>> PatternLanguage::parseString(const std::string &code) {
|
||||
auto preprocessedCode = this->m_preprocessor->preprocess(code);
|
||||
if (!preprocessedCode.has_value()) {
|
||||
this->m_currError = this->m_preprocessor->getError();
|
||||
@@ -114,9 +126,6 @@ namespace hex::pl {
|
||||
if (!this->m_validator->validate(*ast)) {
|
||||
this->m_currError = this->m_validator->getError();
|
||||
|
||||
for (auto &node : *ast)
|
||||
delete node;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -140,17 +149,18 @@ namespace hex::pl {
|
||||
for (const auto &[name, value] : envVars)
|
||||
this->m_evaluator->setEnvVariable(name, value);
|
||||
|
||||
for (auto &node : this->m_currAST)
|
||||
delete node;
|
||||
this->m_currAST.clear();
|
||||
|
||||
auto ast = this->parseString(code);
|
||||
if (!ast)
|
||||
return false;
|
||||
{
|
||||
auto ast = this->parseString(code);
|
||||
if (!ast)
|
||||
return false;
|
||||
|
||||
this->m_currAST = ast.value();
|
||||
this->m_currAST = std::move(ast.value());
|
||||
}
|
||||
|
||||
auto patterns = this->m_evaluator->evaluate(ast.value());
|
||||
|
||||
auto patterns = this->m_evaluator->evaluate(this->m_currAST);
|
||||
if (!patterns.has_value()) {
|
||||
this->m_currError = this->m_evaluator->getConsole().getLastHardError();
|
||||
return false;
|
||||
@@ -187,14 +197,14 @@ namespace hex::pl {
|
||||
auto success = this->executeString(provider, functionContent, {}, {}, false);
|
||||
auto result = this->m_evaluator->getMainResult();
|
||||
|
||||
return { success, result };
|
||||
return { success, std::move(result) };
|
||||
}
|
||||
|
||||
void PatternLanguage::abort() {
|
||||
this->m_evaluator->abort();
|
||||
}
|
||||
|
||||
const std::vector<ASTNode *> &PatternLanguage::getCurrentAST() const {
|
||||
const std::vector<std::shared_ptr<ASTNode>> &PatternLanguage::getCurrentAST() const {
|
||||
return this->m_currAST;
|
||||
}
|
||||
|
||||
@@ -229,8 +239,6 @@ namespace hex::pl {
|
||||
}
|
||||
|
||||
void PatternLanguage::reset() {
|
||||
for (auto &pattern : this->m_patterns)
|
||||
delete pattern;
|
||||
this->m_patterns.clear();
|
||||
|
||||
this->m_currAST.clear();
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace hex::pl {
|
||||
offset += 1;
|
||||
|
||||
std::string includeFile;
|
||||
while (code[offset] != endChar) {
|
||||
while (code[offset] != endChar && code[offset] != '\n') {
|
||||
includeFile += code[offset];
|
||||
offset += 1;
|
||||
|
||||
@@ -63,20 +63,24 @@ namespace hex::pl {
|
||||
|
||||
if (includeFile[0] != '/') {
|
||||
for (const auto &dir : hex::getPath(ImHexPath::PatternsInclude)) {
|
||||
fs::path tempPath = dir / includePath;
|
||||
if (fs::exists(tempPath)) {
|
||||
fs::path tempPath = dir / includePath;
|
||||
if (fs::is_regular_file(tempPath)) {
|
||||
includePath = tempPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs::is_regular_file(includePath)) {
|
||||
if (includePath.parent_path().filename().string() == "std")
|
||||
throwPreprocessorError(hex::format("{0}: No such file.\n\nThis file might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store.", includeFile.c_str()), lineNumber);
|
||||
else
|
||||
throwPreprocessorError(hex::format("{0}: No such file", includeFile.c_str()), lineNumber);
|
||||
}
|
||||
|
||||
File file(includePath, File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
if (includePath.parent_path().filename().string() == "std")
|
||||
throwPreprocessorError(hex::format("{0}: No such file or directory.\n\nThis file might be part of the standard library.\nYou can install the standard library though\nthe Content Store found under Help -> Content Store.", includeFile.c_str()), lineNumber);
|
||||
else
|
||||
throwPreprocessorError(hex::format("{0}: No such file or directory", includeFile.c_str()), lineNumber);
|
||||
throwPreprocessorError(hex::format("{0}: Failed to open file", includeFile.c_str()), lineNumber);
|
||||
}
|
||||
|
||||
Preprocessor preprocessor;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#include <hex/pattern_language/validator.hpp>
|
||||
|
||||
#include <hex/pattern_language/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_variable_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_type_decl.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_struct.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_union.hpp>
|
||||
#include <hex/pattern_language/ast/ast_node_enum.hpp>
|
||||
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
@@ -9,8 +14,9 @@
|
||||
|
||||
namespace hex::pl {
|
||||
|
||||
bool Validator::validate(const std::vector<ASTNode *> &ast) {
|
||||
bool Validator::validate(const std::vector<std::shared_ptr<ASTNode>> &ast) {
|
||||
std::unordered_set<std::string> identifiers;
|
||||
std::unordered_set<std::string> types;
|
||||
|
||||
try {
|
||||
|
||||
@@ -18,21 +24,21 @@ namespace hex::pl {
|
||||
if (node == nullptr)
|
||||
throwValidatorError("nullptr in AST. This is a bug!", 1);
|
||||
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node); variableDeclNode != nullptr) {
|
||||
if (auto variableDeclNode = dynamic_cast<ASTNodeVariableDecl *>(node.get()); variableDeclNode != nullptr) {
|
||||
if (!identifiers.insert(variableDeclNode->getName().data()).second)
|
||||
throwValidatorError(hex::format("redefinition of identifier '{0}'", variableDeclNode->getName().data()), variableDeclNode->getLineNumber());
|
||||
|
||||
this->validate({ variableDeclNode->getType() });
|
||||
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl *>(node); typeDeclNode != nullptr) {
|
||||
if (!identifiers.insert(typeDeclNode->getName().data()).second)
|
||||
throwValidatorError(hex::format("redefinition of identifier '{0}'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber());
|
||||
this->validate(hex::moveToVector<std::shared_ptr<ASTNode>>(variableDeclNode->getType()->clone()));
|
||||
} else if (auto typeDeclNode = dynamic_cast<ASTNodeTypeDecl *>(node.get()); typeDeclNode != nullptr) {
|
||||
if (!types.insert(typeDeclNode->getName().data()).second)
|
||||
throwValidatorError(hex::format("redefinition of type '{0}'", typeDeclNode->getName().data()), typeDeclNode->getLineNumber());
|
||||
|
||||
this->validate({ typeDeclNode->getType() });
|
||||
} else if (auto structNode = dynamic_cast<ASTNodeStruct *>(node); structNode != nullptr) {
|
||||
this->validate(hex::moveToVector<std::shared_ptr<ASTNode>>(typeDeclNode->getType()->clone()));
|
||||
} else if (auto structNode = dynamic_cast<ASTNodeStruct *>(node.get()); structNode != nullptr) {
|
||||
this->validate(structNode->getMembers());
|
||||
} else if (auto unionNode = dynamic_cast<ASTNodeUnion *>(node); unionNode != nullptr) {
|
||||
} else if (auto unionNode = dynamic_cast<ASTNodeUnion *>(node.get()); unionNode != nullptr) {
|
||||
this->validate(unionNode->getMembers());
|
||||
} else if (auto enumNode = dynamic_cast<ASTNodeEnum *>(node); enumNode != nullptr) {
|
||||
} else if (auto enumNode = dynamic_cast<ASTNodeEnum *>(node.get()); enumNode != nullptr) {
|
||||
std::unordered_set<std::string> enumIdentifiers;
|
||||
for (auto &[name, value] : enumNode->getEntries()) {
|
||||
if (!enumIdentifiers.insert(name).second)
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/event.hpp>
|
||||
#include <hex/pattern_language/pattern_data.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/pattern_language/pattern_language.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
@@ -14,6 +17,7 @@ namespace hex::prv {
|
||||
|
||||
Provider::Provider() {
|
||||
this->m_patches.emplace_back();
|
||||
this->m_patternLanguageRuntime = std::make_unique<pl::PatternLanguage>();
|
||||
|
||||
if (this->hasLoadInterface())
|
||||
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
namespace ImGui {
|
||||
|
||||
int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data) {
|
||||
auto &mathInput = *static_cast<std::string *>(data->UserData);
|
||||
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize) {
|
||||
auto &string = *static_cast<std::string *>(data->UserData);
|
||||
|
||||
string.resize(data->BufSize);
|
||||
data->Buf = string.data();
|
||||
}
|
||||
|
||||
mathInput.resize(data->BufTextLen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace hex {
|
||||
|
||||
std::string View::s_popupMessage;
|
||||
|
||||
std::function<void()> View::s_yesCallback, View::s_noCallback;
|
||||
|
||||
u32 View::s_selectableFileIndex;
|
||||
std::vector<fs::path> View::s_selectableFiles;
|
||||
std::function<void(fs::path)> View::s_selectableFileOpenCallback;
|
||||
@@ -65,6 +67,23 @@ namespace hex {
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(scaled(ImVec2(400, 100)), scaled(ImVec2(600, 300)));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.question"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::TextFormattedWrapped("{}", s_popupMessage.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
|
||||
View::confirmButtons(
|
||||
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] {
|
||||
s_yesCallback();
|
||||
ImGui::CloseCurrentPopup(); }, [] {
|
||||
s_noCallback();
|
||||
ImGui::CloseCurrentPopup(); });
|
||||
|
||||
ImGui::SetWindowPos((windowSize - ImGui::GetWindowSize()) / 2, ImGuiCond_Appearing);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
bool opened = true;
|
||||
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
|
||||
if (ImGui::BeginPopupModal("hex.builtin.common.choose_file"_lang, &opened, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
@@ -117,6 +136,15 @@ namespace hex {
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.fatal"_lang); });
|
||||
}
|
||||
|
||||
void View::showYesNoQuestionPopup(const std::string &message, const std::function<void()> &yesCallback, const std::function<void()> &noCallback) {
|
||||
s_popupMessage = message;
|
||||
|
||||
s_yesCallback = yesCallback;
|
||||
s_noCallback = noCallback;
|
||||
|
||||
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.common.question"_lang); });
|
||||
}
|
||||
|
||||
void View::showFileChooserPopup(const std::vector<fs::path> &paths, const std::vector<nfdfilteritem_t> &validExtensions, const std::function<void(fs::path)> &callback) {
|
||||
if (paths.empty()) {
|
||||
hex::openFileBrowser("hex.builtin.common.open"_lang, DialogMode::Open, validExtensions, [callback](const auto &path) {
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace hex::init {
|
||||
this->m_tasks.emplace_back(taskName, task);
|
||||
}
|
||||
|
||||
const std::string &getGPUVendor() const { return this->m_gpuVendor; }
|
||||
|
||||
private:
|
||||
GLFWwindow *m_window;
|
||||
std::mutex m_progressMutex;
|
||||
@@ -38,6 +40,8 @@ namespace hex::init {
|
||||
std::future<bool> processTasksAsync();
|
||||
|
||||
std::vector<std::pair<std::string, TaskFunction>> m_tasks;
|
||||
|
||||
std::string m_gpuVendor;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -46,12 +46,14 @@ namespace hex {
|
||||
|
||||
std::string m_windowTitle;
|
||||
|
||||
double m_lastFrameTime;
|
||||
double m_lastFrameTime = 0;
|
||||
|
||||
ImGui::Texture m_logoTexture = { 0 };
|
||||
ImGui::Texture m_logoTexture = { nullptr };
|
||||
|
||||
std::list<std::string> m_popupsToOpen;
|
||||
std::vector<int> m_pressedKeys;
|
||||
|
||||
fs::path m_imguiSettingsPath;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -29,6 +29,8 @@ namespace hex::init {
|
||||
WindowSplash::WindowSplash() : m_window(nullptr) {
|
||||
this->initGLFW();
|
||||
this->initImGui();
|
||||
|
||||
this->m_gpuVendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
|
||||
}
|
||||
|
||||
WindowSplash::~WindowSplash() {
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <imgui.h>
|
||||
#include <imgui_freetype.h>
|
||||
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/pattern_language/pattern_data.hpp>
|
||||
#include <hex/pattern_language/patterns/pattern.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
#include <hex/helpers/net.hpp>
|
||||
#include <hex/helpers/paths.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <fontawesome_font.h>
|
||||
#include <codicons_font.h>
|
||||
@@ -92,14 +94,18 @@ namespace hex::init {
|
||||
auto fonts = IM_NEW(ImFontAtlas)();
|
||||
ImFontConfig cfg = {};
|
||||
|
||||
fs::path fontFile;
|
||||
for (const auto &dir : hex::getPath(ImHexPath::Resources)) {
|
||||
auto path = dir / "font.ttf";
|
||||
if (fs::exists(path)) {
|
||||
log::info("Loading custom front from {}", path.string());
|
||||
fs::path fontFile = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_path", "");
|
||||
|
||||
fontFile = path;
|
||||
break;
|
||||
// If no custom font has been specified, search for a file called "font.ttf" in one of the resource folders
|
||||
if (fontFile.empty()) {
|
||||
for (const auto &dir : hex::getPath(ImHexPath::Resources)) {
|
||||
auto path = dir / "font.ttf";
|
||||
if (fs::exists(path)) {
|
||||
log::info("Loading custom front from {}", path.string());
|
||||
|
||||
fontFile = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,12 +131,12 @@ namespace hex::init {
|
||||
};
|
||||
|
||||
ImWchar unifontRange[] = {
|
||||
0x0020, 0xFFF0, 0
|
||||
0x0100, 0xFFF0, 0
|
||||
};
|
||||
|
||||
float fontSize = 13.0F * ImHexApi::System::getGlobalScale();
|
||||
if (fontFile.empty()) {
|
||||
// Load default font
|
||||
// Load default font if no custom one has been specified
|
||||
|
||||
fonts->Clear();
|
||||
|
||||
@@ -140,7 +146,7 @@ namespace hex::init {
|
||||
} else {
|
||||
// Load custom font
|
||||
|
||||
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.interface", "hex.builtin.setting.interface.font_size", 14) * ImHexApi::System::getGlobalScale();
|
||||
fontSize = ContentRegistry::Settings::read("hex.builtin.setting.font", "hex.builtin.setting.font.font_size", 13) * ImHexApi::System::getGlobalScale();
|
||||
|
||||
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
|
||||
cfg.SizePixels = fontSize;
|
||||
@@ -165,7 +171,8 @@ namespace hex::init {
|
||||
bool deleteSharedData() {
|
||||
ImHexApi::System::getInitArguments().clear();
|
||||
ImHexApi::Tasks::getDeferredCalls().clear();
|
||||
ImHexApi::HexEditor::getHighlights().clear();
|
||||
ImHexApi::HexEditor::impl::getHighlights().clear();
|
||||
ImHexApi::HexEditor::impl::getHighlightingFunctions().clear();
|
||||
|
||||
while (ImHexApi::Provider::isValid())
|
||||
ImHexApi::Provider::remove(ImHexApi::Provider::get());
|
||||
|
||||
@@ -8,12 +8,15 @@
|
||||
#include "init/splash_window.hpp"
|
||||
#include "init/tasks.hpp"
|
||||
|
||||
#include <hex/helpers/file.hpp>
|
||||
|
||||
int main(int argc, char **argv, char **envp) {
|
||||
using namespace hex;
|
||||
ImHexApi::System::impl::setProgramArguments(argc, argv, envp);
|
||||
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(true);
|
||||
#endif
|
||||
|
||||
// Initialization
|
||||
{
|
||||
Window::initNative();
|
||||
@@ -22,6 +25,17 @@ int main(int argc, char **argv, char **envp) {
|
||||
|
||||
init::WindowSplash splashWindow;
|
||||
|
||||
// Intel's OpenGL driver has weird bugs that cause the drawn window to be offset to the bottom right.
|
||||
// This can be fixed by either using Mesa3D's OpenGL Software renderer or by simply disabling it.
|
||||
// If you want to try if it works anyways on your GPU, set the hex.builtin.setting.interface.force_borderless_window_mode setting to 1
|
||||
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
||||
bool isIntelGPU = hex::containsIgnoreCase(splashWindow.getGPUVendor(), "Intel");
|
||||
ImHexApi::System::impl::setBorderlessWindowMode(!isIntelGPU);
|
||||
|
||||
if (isIntelGPU)
|
||||
log::warn("Intel GPU detected! Intel's OpenGL driver has bugs that can cause issues when using ImHex. If you experience any rendering bugs, please try the Mesa3D Software Renderer");
|
||||
}
|
||||
|
||||
for (const auto &[name, task] : init::getInitTasks())
|
||||
splashWindow.addStartupTask(name, task);
|
||||
|
||||
|
||||
@@ -241,36 +241,18 @@ namespace hex {
|
||||
// Setup borderless window
|
||||
auto hwnd = glfwGetWin32Window(this->m_window);
|
||||
|
||||
g_oldWndProc = ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)windowProc);
|
||||
if (ImHexApi::System::isBorderlessWindowModeEnabled()) {
|
||||
g_oldWndProc = ::SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)windowProc);
|
||||
|
||||
MARGINS borderless = { 1, 1, 1, 1 };
|
||||
::DwmExtendFrameIntoClientArea(hwnd, &borderless);
|
||||
MARGINS borderless = { 1, 1, 1, 1 };
|
||||
::DwmExtendFrameIntoClientArea(hwnd, &borderless);
|
||||
|
||||
DWORD attribute = DWMNCRP_ENABLED;
|
||||
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &attribute, sizeof(attribute));
|
||||
DWORD attribute = DWMNCRP_ENABLED;
|
||||
::DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &attribute, sizeof(attribute));
|
||||
|
||||
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
|
||||
::SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
|
||||
|
||||
// Setup system theme change detector
|
||||
bool themeFollowSystem = ContentRegistry::Settings::getSetting("hex.builtin.setting.interface", "hex.builtin.setting.interface.color") == 0;
|
||||
EventManager::subscribe<EventOSThemeChanged>(this, [themeFollowSystem] {
|
||||
if (!themeFollowSystem) return;
|
||||
|
||||
HKEY hkey;
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, R"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)", &hkey) == ERROR_SUCCESS) {
|
||||
DWORD value = 0;
|
||||
DWORD size = sizeof(DWORD);
|
||||
|
||||
auto error = RegQueryValueEx(hkey, "AppsUseLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
|
||||
if (error == ERROR_SUCCESS) {
|
||||
EventManager::post<RequestChangeTheme>(value == 0 ? 1 : 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (themeFollowSystem)
|
||||
EventManager::post<EventOSThemeChanged>();
|
||||
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
|
||||
::SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::beginNativeWindowFrame() {
|
||||
@@ -318,6 +300,8 @@ namespace hex {
|
||||
}
|
||||
|
||||
void Window::drawTitleBar() {
|
||||
if (!ImHexApi::System::isBorderlessWindowModeEnabled()) return;
|
||||
|
||||
auto buttonSize = ImVec2(g_titleBarHeight * 1.5F, g_titleBarHeight - 1);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user