Compare commits

..

136 Commits

Author SHA1 Message Date
WerWolv
7866e3fc2a build: Bumped version to 1.16.2 2022-03-03 14:32:30 +01:00
WerWolv
2abf89cd16 tests: Fixed build 2022-03-03 13:35:12 +01:00
WerWolv
8b2dcf976f patterns: Fixed auto parameter crash 2022-03-03 13:34:05 +01:00
WerWolv
559b86efe1 patterns: Display actual type name of types declared with using 2022-03-03 13:33:45 +01:00
WerWolv
949a26ca0e patterns: Fixed memory leak when using format attribute 2022-03-03 12:11:47 +01:00
WerWolv
2880ca00da patterns: Fixed crash when using attributes 2022-03-03 11:19:46 +01:00
WerWolv
39da62532b fix: Trailing zero at end of string input buffers 2022-03-03 09:27:27 +01:00
WerWolv
483ba95d80 fix: Some text boxes not being writable 2022-03-03 09:24:09 +01:00
WerWolv
2300b0c692 fix: Searching not working at all 2022-03-03 09:06:10 +01:00
WerWolv
cc59b36c54 patterns: Properly reset back current control flow type in arrays
Fixes issue mentioned in #460
2022-03-01 20:57:21 +01:00
WerWolv
61d9918dae patterns: Evaluate return value before setting control flow type
Fixes another issue mentioned in #460
2022-03-01 20:37:27 +01:00
WerWolv
2c361f9b0a build: Don't bundle yara rules anymore because Microsoft Defender is a little cry baby 2022-03-01 20:27:19 +01:00
WerWolv
775b3e8c52 patterns: Fixed crash when using control flow statements without value
Fixes #460
2022-03-01 20:17:03 +01:00
WerWolv
2a989c6cc1 build: Bumped version to 1.16.1 2022-03-01 18:18:56 +01:00
WerWolv
3d7adf6483 patterns: Make std::mem::find_sequence_in_range return -1 when no sequence is found 2022-03-01 16:55:45 +01:00
WerWolv
56079f70c7 ui: Added alpha preview to color picker tool 2022-03-01 16:36:06 +01:00
WerWolv
6c9e969099 ui: Improved base address setter popup 2022-03-01 16:23:53 +01:00
WerWolv
76f8e6d6ef fix: Crash when searching for empty strings 2022-03-01 16:18:39 +01:00
WerWolv
174cf3ed95 fix: Crash when entering too much text in various text boxes 2022-03-01 16:12:00 +01:00
WerWolv
540f468e8a patterns: Fixed offset being incorrectly incremented when calling functions 2022-03-01 09:40:08 +01:00
WerWolv
6b2423cdce build: Bumped version to 1.16.0 2022-03-01 08:49:48 +01:00
WerWolv
e4a3181e1d fix: Crash when saving projects 2022-03-01 00:03:39 +01:00
WerWolv
b57730c28b sys: Disable buffering on log files 2022-03-01 00:03:28 +01:00
WerWolv
5a02c38fcd store: Fixed more download issues when some folders don't have write perms 2022-02-28 23:10:04 +01:00
Lukas Cone
2847098020 patterns: Added bitfield_order pragma (#457) 2022-02-28 00:07:04 +01:00
WerWolv
0cc6d90e3d patterns: Fixed off-by-one error when calculating unsized array sizes
Closes #453, thanks to @Dyddye
2022-02-27 23:28:34 +01:00
WerWolv
66d1b3fd2f patterns: Huge refactor of Pattern Language runtime to use smart pointers (#458)
* patterns: Initial work to refactor pattern language to use smart pointers

* patterns: Fixed remaining issues, moved patterns to unique files

* sys: Added missing includes for macOS
2022-02-27 23:25:39 +01:00
WerWolv
b28eaf2dbf sys: Catch error produced in file size querying 2022-02-26 17:15:35 +01:00
WerWolv
191a99f91b fix: Crash when trying to read from an empty file or a directory 2022-02-26 16:43:38 +01:00
WerWolv
f3f1ac939a fix: Executable directory not getting added to paths correctly on Linux 2022-02-26 16:25:15 +01:00
Lukas Cone
9737b9cd62 patterns: Added find_sequence_in_range function (#448) 2022-02-26 15:53:18 +01:00
WerWolv
e3fbb490df fix: ImHex not properly loading plugins on first launch 2022-02-26 00:10:23 +01:00
WerWolv
73d74f6cde fix: Crash when changing font size 2022-02-23 10:16:27 +01:00
WerWolv
1487f760b0 build: Bundle files from the ImHex-Patterns repo with the installation 2022-02-21 22:47:56 +01:00
WerWolv
bdb2ac3a0b fix: imgui.ini file being created in the working directory sometimes
Fixes #450
2022-02-21 21:55:04 +01:00
WerWolv
75bd7805c9 ux: Added custom font and font size setting to settings menu, improve rebooting behaviour 2022-02-21 21:46:25 +01:00
WerWolv
ef8e9a83bb patterns: Properly treat arrays and custom types as references 2022-02-21 20:00:54 +01:00
WerWolv
7d9c24ff51 net: Increase store load timeout to 30 seconds 2022-02-21 18:57:53 +01:00
WerWolv
a8e83154f0 fix: Remove button of content store entries not updating correctly 2022-02-21 18:40:51 +01:00
WerWolv
27c2c4dc33 fix: Compile error because of prematurely committed change 2022-02-21 13:52:13 +01:00
WerWolv
a9a538cec8 build: Fixed circular dependency on the imhex target 2022-02-21 13:46:35 +01:00
jam1garner
57e1f7ee10 Rework libimhex-rs to use autocxx (#451)
* Rework libimhex-rs to use autocxx

* Remove Bookmarks::add overload

* Remove manual usage of cxx-rs
2022-02-20 19:14:11 -05:00
WerWolv
754eb89f04 patterns: Fixed jumping to and displaying tooltips of static array entries 2022-02-20 23:54:31 +01:00
WerWolv
2e95184d30 patterns: Allow variables to be named the same as types 2022-02-20 20:50:02 +01:00
WerWolv
9deab9c497 pattern: Allow many variable attributes to be applied directly to custom types 2022-02-19 18:03:07 +01:00
WerWolv
5ae6c8a627 fix: ImGui::TextFormattedCentered didn't properly pass format arguments forward 2022-02-19 18:02:44 +01:00
WerWolv
05104aef6c build: Properly define DEBUG macro again in debug mode 2022-02-19 18:02:10 +01:00
WerWolv
08da408471 ui: Added help text to data processor 2022-02-19 16:54:43 +01:00
WerWolv
4a4d5ac694 lang: Fixed broken localization in the Slice node 2022-02-19 16:31:04 +01:00
Lukas Cone
3b4d6d465b fix: Welcome screen corrupted banner when settings are open (#447) 2022-02-19 00:35:07 +01:00
Lukas Cone
26f998ecb6 feature: User now can add custom directories (#444)
* feat: user directories

* ux: show setting categories in order they were created

* feat: add descriptable setting categories
2022-02-18 22:34:54 +01:00
WerWolv
60a717365c ui: Remove unifont glyphs from the ASCII range 2022-02-18 16:19:12 +01:00
Polshakov Dmitry
07ae00aa20 fix: misuse of clipper (#445)
clipper.Step() should be called in a loop until it returns false.
You don't need to call End() after it.

Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-18 13:32:36 +01:00
Polshakov Dmitry
39cc845df3 Fix crash on typing "#include " (#446)
* fix: scan include name until end of line

* fix: correctly check file existance

Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-18 13:32:05 +01:00
Lukas Cone
6c8b75a05f pattern: add single_color attribute, fixed static array color override (#443) 2022-02-18 13:31:44 +01:00
WerWolv
4c8efed256 fix: Crashing when scrolling through the hex editor too quickly 2022-02-17 21:49:40 +01:00
WerWolv
faaa90fa0d fix: Borderless window mode getting enabled when it shouldn't 2022-02-17 15:22:29 +01:00
WerWolv
7e075e5ebb sys: Added editing support for strings and chars in the data inspector 2022-02-17 14:43:04 +01:00
WerWolv
b9508d853e build: Fixed building and loading of Rust plugins 2022-02-17 11:42:56 +01:00
WerWolv
716d52f3e3 sys: Register languages before registering any other things 2022-02-16 21:32:33 +01:00
WerWolv
90753f4d42 sys: Fixed logging to file omitting all tags and new lines 2022-02-16 21:31:47 +01:00
WerWolv
7117592f38 sys: Add highlighting provider function support, move pattern highlighting code out of hex editor 2022-02-16 14:57:13 +01:00
WerWolv
b9030d7e47 fix: Issue with erasing characters when pasting then into ImHex 2022-02-16 10:54:17 +01:00
WerWolv
b79cfa213d fix: Opening recent files with unicode characters in their path 2022-02-16 10:53:48 +01:00
WerWolv
60af9970c1 fix: Opening files with unicode characters in their path 2022-02-16 10:04:05 +01:00
WerWolv
33a1e7f055 sys: Added setting to override borderless window mode even on Intel 2022-02-15 23:07:48 +01:00
WerWolv
f72e9700ab sys: Move Windows theme detection to Windows plugin 2022-02-15 22:50:04 +01:00
WerWolv
4357d68462 sys: Automatically disable borderless window mode if Intel CPU is used 2022-02-15 22:36:36 +01:00
WerWolv
adfaa95149 lang: Added translation to long double inspector line 2022-02-15 21:53:39 +01:00
WerWolv
d6b887b7db sys: Added basic editing support to data inspector 2022-02-15 21:50:27 +01:00
WerWolv
227040f82f fix: Undo points not being created correctly 2022-02-15 21:50:02 +01:00
WerWolv
f5440ee52c build: Bumped version to 1.15.0 2022-02-15 13:57:57 +01:00
twevs
de86aee6a2 ui: Fixed columns in diffing view. (#441) 2022-02-14 21:35:55 +01:00
Polshakov Dmitry
038b7961db pattern: Fix scopes not always getting popped again correctly (#440)
Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-14 20:44:43 +01:00
Polshakov Dmitry
8be39488ec fix: Hex editor search buttons not working (#439)
* fix: reset focus only once after window opening

* refactor: extract search functions

* fix: restore focus after search/goto

Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-13 20:13:59 +01:00
Polshakov Dmitry
24f3b8dd3d pattern: Make preprocessor not drop defines after include (#433)
Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-13 11:50:13 +01:00
Bigfoot
2f61a91459 Prevent crash when opening a file w/o read access (#437) 2022-02-13 01:05:19 +01:00
Polshakov Dmitry
9399cf873f pattern: Add attribute to format array entries (#435)
Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-12 15:40:42 +01:00
Polshakov Dmitry
63455ce2be fix: don't change list while iteration (#434)
Co-authored-by: Dmitry Polshakov <dmitry.polshakov@dsr-corporation.com>
2022-02-12 15:39:47 +01:00
WerWolv
585058b500 pattern: Fixed control flow statements not being applied correctly 2022-02-11 21:02:18 +01:00
WerWolv
57f31123e7 pattern: Cleanup log console a bit 2022-02-11 16:53:01 +01:00
WerWolv
b4a3eb240e pattern: Don't allow negative array sizes 2022-02-11 16:52:51 +01:00
WerWolv
0e40b8a81a pattern: Don't reset offset back after variable placements in global scope 2022-02-09 08:55:51 +01:00
WerWolv
2510f61a4e pattern: Fixed array pattern data columns being offset 2022-02-09 08:55:00 +01:00
WerWolv
28e5f62c60 fix: ImU64 is somehow not the same as u64 on Linux 2022-02-08 22:25:25 +01:00
WerWolv
586bca4bf6 ui: Make hexadecimal prefix slightly darker than rest of text 2022-02-08 22:19:54 +01:00
WerWolv
bd30411ba7 ui: Replaced all hex input textboxes with the new ones 2022-02-08 21:51:09 +01:00
WerWolv
1b95722757 ui: Added hexadecimal input text box 2022-02-08 21:10:48 +01:00
WerWolv
1738c3f50a build: Use correct include directory for capstone 2022-02-08 19:22:52 +01:00
WerWolv
18169b461a pattern: Fixed selecting first member of nested patterns 2022-02-08 18:39:47 +01:00
WerWolv
42c0b6145b ux: Disable bookmark toolbar button if nothing is selected 2022-02-08 18:39:15 +01:00
WerWolv
abd3fe6ed1 sys/ux: Improved selection requests 2022-02-08 18:38:54 +01:00
WerWolv
e918a594f3 pattern: Improved attribute handling 2022-02-08 17:50:42 +01:00
WerWolv
3539b42c77 build: Make ImHex buildable again from plugin projects 2022-02-08 09:32:38 +01:00
dianwoshishi
56277ae6da build: fix errors when compiling example_cpp (#426) 2022-02-08 08:32:38 +01:00
Mark Nokalt
a52fee2248 nodes: Validate some buffer operations (#425)
* fix: NodeBufferSlice bounds validation

* fix: Make sure buffer is within u64 bounds in NodeCastBufferToInteger

* nodes: Use specific output methods in number constants
2022-02-08 08:32:10 +01:00
WerWolv
37ea9c6656 pattern: Allow pattern with nested children to be selectable, make pointers inlinable 2022-02-07 23:21:04 +01:00
WerWolv
a8e49c5a85 nodes: Added Selected Region node 2022-02-07 22:46:22 +01:00
WerWolv
137bfe48ca pattern: Fixed precedence of comparison operators 2022-02-07 21:37:10 +01:00
WerWolv
94506848e0 pattern: Added left_to_right and right_to_left attribute for bitfields 2022-02-07 20:08:19 +01:00
WerWolv
97db5e9698 external: Updated STB Image library
Fixes #421
2022-02-07 18:34:16 +01:00
gnuhead-chieb
d17cd64fea lang: Fixed Japanese mistranslation (#419) 2022-02-07 17:17:27 +01:00
gnuhead-chieb
949d036a81 lang: Add Japanese Translation (#418)
* Add Japanese Translation

* Add files via upload

* Update plugin_builtin.cpp

* Added Japanese translation to build script

Co-authored-by: WerWolv <werwolv98@gmail.com>
2022-02-07 15:42:38 +01:00
WerWolv
f1c7dea0ab build: Fix Icon on macOS (#417)
* build: Try to fix macOS icon

* add debugging

* Copy icon file manually

Fixes #414
2022-02-06 22:29:16 +01:00
WerWolv
2577a2f637 ux: Fixed docking to main window 2022-02-06 21:39:10 +01:00
WerWolv
b3728ae658 ui/ux: hexeditor -> hex_editor, Improved performance and flickering when highlighting bytes 2022-02-06 21:02:31 +01:00
WerWolv
decfad5c99 pattern: Properly treat calculated pointers as not part of the struct 2022-02-06 19:46:39 +01:00
WerWolv
334939324c pattern: Fixed use-after-free when accessing arrays through an r-value 2022-02-06 18:04:33 +01:00
WerWolv
99f8efac9a pattern: Fixed pragma once memory corruption 2022-02-06 15:28:20 +01:00
WerWolv
c1b4c4e42a nodes: Added layered distribution visualizer 2022-02-06 14:44:03 +01:00
WerWolv
a48a1ef272 nodes: Added byte distribution visualizer node 2022-02-06 13:55:19 +01:00
Robin Lambertz
6bdd114b99 build: Fix DEBUG without GIT_* variables defined (#416)
When building ImHex in debug mode outside of a git repo, the build would fail due to missing GIT_ variables.
2022-02-06 13:52:51 +01:00
WerWolv
69c48edfdf nodes: Added Image visualizer
Supports JPG, PNG, TGA, BMP, PSD, GIF, HDR and PIC via stb_image
2022-02-06 01:32:15 +01:00
WerWolv
16a9d0c0c6 sys: Streamline logger functions, reorder tags for better readability 2022-02-06 00:42:38 +01:00
WerWolv
545ff9de56 git: Disable Flatpak bundling as it's broken on GitHub Actions 2022-02-06 00:19:39 +01:00
WerWolv
d42d87280d pattern: Allow pattern function code to be executed separately 2022-02-06 00:18:04 +01:00
WerWolv
b8026398e0 pattern: Allow variable placements inside of structs
Fixes #364
2022-02-05 23:34:52 +01:00
WerWolv
cff8bab3d3 pattern: Allow assigning to global variables inside of structs 2022-02-05 23:20:44 +01:00
WerWolv
5c3bfa690b pattern: Improved error messages when including std files or calling unknown std functions 2022-02-05 23:02:38 +01:00
WerWolv
8ab4d25e33 ux: Only show file chooser popup when there's files in the the relevant folder 2022-02-05 22:35:42 +01:00
WerWolv
eade95dff7 fix: Pattern source code being deleted when closing a provider 2022-02-05 22:26:00 +01:00
WerWolv
ca57f91bfa nodes: Added Digram visualizer node 2022-02-05 22:19:32 +01:00
WerWolv
f9668f4ba6 fix: Crash on exit 2022-02-04 00:47:39 +01:00
WerWolv
618eead341 sys: Drastically improve highlighting performance 2022-02-04 00:29:47 +01:00
WerWolv
ba68f463e5 ui: Added to matched yara rules 2022-02-02 21:08:46 +01:00
WerWolv
df1d302bcb sys: Added time and source to logger output 2022-02-02 17:19:50 +01:00
WerWolv
c2bcbfb1e0 pattern: std::from_chars is not widely supported and doesn't support u128... 2022-02-02 17:12:19 +01:00
WerWolv
4c51efc5e0 pattern: Improved parsing of integer literals, added support for hex floats 2022-02-02 16:03:18 +01:00
WerWolv
f19944f54d lang: Added German translation for Windows plugin 2022-02-02 00:36:25 +01:00
WerWolv
876dbe8179 sys: Final cleanup to get rid of everything builtin in the main application 2022-02-02 00:36:09 +01:00
WerWolv
f76e65a58d ux: Added better checks and help messages for missing or duplicate plugins 2022-02-01 23:57:48 +01:00
WerWolv
6977061227 ux: Fixed tips popup and properly apply default layout on first start 2022-02-01 23:33:42 +01:00
WerWolv
ce59226909 sys: Reformat all 2022-02-01 22:09:44 +01:00
WerWolv
1991afb87b sys: Get rid of SharedData struct and cleanup code structure (#411)
* sys: Initial refactoring of the SharedData class

* sys/pattern: More refactoring, make every provider have its own patterns

* sys: Finished up refactoring. No more SharedData!

* sys: Fixed compile on Unix

* tests: Fixed unit tests

* sys: Moved view and lang files

* pattern: Added assignment operator support to for loops

* tests: Fixed compile issue
2022-02-01 18:09:40 +01:00
WerWolv
61fc479c79 pattern: Fixed many code inconsistencies and bugs 2022-01-31 14:37:12 +01:00
WerWolv
8f8f3c5415 pattern: Actually fix strings being displayed wrong 2022-01-31 12:53:04 +01:00
240 changed files with 21482 additions and 18689 deletions

View File

@@ -2,10 +2,10 @@
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: false
AlignArrayOfStructures: Left
AlignConsecutiveMacros: Consecutive
AlignConsecutiveAssignments: None
AlignConsecutiveAssignments: Consecutive
AlignConsecutiveBitFields: AcrossEmptyLinesAndComments
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Left
@@ -13,7 +13,7 @@ AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
@@ -95,7 +95,7 @@ IncludeCategories:
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseLabels: true
IndentCaseBlocks: true
IndentGotoLabels: true
IndentPPDirectives: BeforeHash

View File

@@ -203,14 +203,14 @@ jobs:
..
make -j 4 install
- name: 📦 Bundle Flatpak
run: |
sudo apt install flatpak flatpak-builder
flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak --user install -y flathub org.freedesktop.Platform//20.08
flatpak --user install -y flathub org.freedesktop.Sdk//20.08
flatpak-builder --jobs=4 --repo=imhex _flatpak dist/net.werwolv.ImHex.yaml --ccache --keep-build-dirs
flatpak build-bundle imhex imhex.flatpak net.werwolv.ImHex stable
#- name: 📦 Bundle Flatpak
# run: |
# sudo apt install flatpak flatpak-builder
# flatpak --user remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# flatpak --user install -y flathub org.freedesktop.Platform//20.08
# flatpak --user install -y flathub org.freedesktop.Sdk//20.08
# flatpak-builder --jobs=4 --repo=imhex _flatpak dist/net.werwolv.ImHex.yaml --ccache --keep-build-dirs
# flatpak build-bundle imhex imhex.flatpak net.werwolv.ImHex stable
- name: 📦 Bundle DEB
run: |

2
.gitignore vendored
View File

@@ -12,3 +12,5 @@ imgui.ini
plugins/.rustc_info.json
**/target
plugins/example_rust/Cargo.lock

2
.gitmodules vendored
View File

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

View File

@@ -1,13 +1,13 @@
cmake_minimum_required(VERSION 3.16)
# Updating the version here will update it throughout ImHex as well
set(IMHEX_VERSION "1.14.0")
set(IMHEX_VERSION "1.16.2")
project(imhex VERSION ${IMHEX_VERSION})
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
include("${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake")
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake")
# Make sure project is configured correctly
setDefaultBuiltTypeIfUnset()
@@ -28,7 +28,7 @@ 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()
@@ -36,3 +36,6 @@ add_subdirectory(tests EXCLUDE_FROM_ALL)
# Configure packaging
createPackage()
# Download and install all current files from the ImHex-Patterns repo
downloadImHexPatternsFiles()

View File

@@ -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="${PROJECT_VERSION}">
$<$<CONFIG:Debug>:IMHEX_VERSION="${PROJECT_VERSION}-Debug">
$<$<CONFIG:RelWithDebInfo>:IMHEX_VERSION="${PROJECT_VERSION}-ReleaseWithDebugInfo">
$<$<CONFIG:MinSizeRel>:IMHEX_VERSION="${PROJECT_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()
@@ -105,8 +106,8 @@ macro(configurePackingResources)
endif()
if (WIN32)
set(application_type)
set(IMHEX_ICON "${CMAKE_SOURCE_DIR}/resources/resource.rc")
set(APPLICATION_TYPE)
set(IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/resource.rc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--allow-multiple-definition")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-subsystem,windows")
@@ -123,11 +124,11 @@ macro(configurePackingResources)
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/resources/LICENSE.rtf")
endif()
elseif (APPLE)
set (IMHEX_ICON "${CMAKE_SOURCE_DIR}/resources/AppIcon.icns")
set (IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/AppIcon.icns")
if (CREATE_BUNDLE)
set(application_type MACOSX_BUNDLE)
set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
set(APPLICATION_TYPE MACOSX_BUNDLE)
set_source_files_properties(${IMHEX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
set(MACOSX_BUNDLE_INFO_STRING "WerWolv")
set(MACOSX_BUNDLE_BUNDLE_NAME "ImHex")
@@ -176,7 +177,7 @@ macro(createPackage)
endif ()
endif ()
add_dependencies(imhex ${plugin})
add_dependencies(imhex_all ${plugin})
endif ()
endforeach()
@@ -227,13 +228,16 @@ 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")
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${bundle_path}/Contents/Resources")
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")
@@ -268,7 +272,7 @@ macro(setDefaultBuiltTypeIfUnset)
endmacro()
macro(detectBadClone)
file (GLOB EXTERNAL_DIRS "external/*")
file (GLOB EXTERNAL_DIRS "lib/external/*")
foreach (EXTERNAL_DIR ${EXTERNAL_DIRS})
file(GLOB RESULT "${EXTERNAL_DIR}/*")
list(LENGTH RESULT ENTRY_COUNT)
@@ -276,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 magic)
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "./")
endforeach()
endfunction()

View File

@@ -47,6 +47,7 @@
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
#define IMGUI_DEFINE_MATH_OPERATORS
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H

View File

@@ -49,6 +49,7 @@
#include <hex.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/logger.hpp>
#include <string>
@@ -232,7 +233,7 @@ struct MemoryEditor
if (ImGui::Begin(title, p_open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoNavInputs))
{
if (DataPreviewAddr != DataPreviewAddrOld || DataPreviewAddrEnd != DataPreviewAddrEndOld) {
hex::Region selectionRegion = { std::min(DataPreviewAddr, DataPreviewAddrEnd) + base_display_addr, std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd) };
hex::Region selectionRegion = { std::min(DataPreviewAddr, DataPreviewAddrEnd) + base_display_addr, (std::max(DataPreviewAddr, DataPreviewAddrEnd) - std::min(DataPreviewAddr, DataPreviewAddrEnd)) + 1 };
hex::EventManager::post<hex::EventRegionSelected>(selectionRegion);
}
@@ -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;
@@ -335,7 +335,7 @@ struct MemoryEditor
else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)) && DataPreviewAddr < mem_size - 1) { DataPreviewAddr = data_preview_addr_next = mem_size - 1; if (!ImGui::GetIO().KeyShift) DataPreviewAddrEnd = DataPreviewAddr; }
}
} else if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape))) {
DataPreviewAddr = data_preview_addr_next = DataPreviewAddrEnd = (size_t)-1;
DataPreviewAddr = DataPreviewAddrOld = DataPreviewAddrEnd = DataPreviewAddrEndOld = data_preview_addr_next = (size_t)-1;
HighlightMin = HighlightMax = (size_t)-1;
hex::EventManager::post<hex::EventRegionSelected>(hex::Region{ (size_t)-1, 0 });
@@ -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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -25,7 +25,7 @@ if (WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--allow-multiple-definition -fvisibility=hidden")
endif()
add_compile_definitions(IMHEX_PLUGIN_NAME=${PROJECT_NAME})
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
if (NOT TARGET libimhex)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libimhex ${CMAKE_CURRENT_BINARY_DIR}/plugins/libimhex)

View File

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

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

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

View File

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

View File

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

View File

@@ -73,7 +73,7 @@ if (NOT USE_SYSTEM_CAPSTONE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/capstone ${CMAKE_CURRENT_BINARY_DIR}/external/capstone EXCLUDE_FROM_ALL)
set_target_properties(capstone-static PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(CAPSTONE_LIBRARIES "capstone-static")
set(CAPSTONE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../external/capstone/include/capstone)
set(CAPSTONE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../external/capstone/include)
else()
find_package(PkgConfig REQUIRED)
pkg_search_module(CAPSTONE 4.0.2 REQUIRED capstone)
@@ -105,6 +105,8 @@ set(LIBIMHEX_SOURCES
source/api/content_registry.cpp
source/api/task.cpp
source/api/keybinding.cpp
source/api/plugin_manager.cpp
source/api/localization.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
@@ -113,9 +115,7 @@ set(LIBIMHEX_SOURCES
source/helpers/utils.cpp
source/helpers/paths.cpp
source/helpers/magic.cpp
source/helpers/shared_data.cpp
source/helpers/crypto.cpp
source/helpers/lang.cpp
source/helpers/net.cpp
source/helpers/file.cpp
source/helpers/socket.cpp
@@ -136,8 +136,7 @@ set(LIBIMHEX_SOURCES
source/providers/provider.cpp
source/ui/imgui_imhex_extensions.cpp
source/views/view.cpp
source/ui/view.cpp
)
if (APPLE)
@@ -153,6 +152,8 @@ if (APPLE)
set(LIBIMHEX_SOURCES ${LIBIMHEX_SOURCES} source/helpers/paths_mac.mm)
endif ()
add_compile_definitions(IMHEX_PROJECT_NAME="${PROJECT_NAME}")
add_library(libimhex SHARED ${LIBIMHEX_SOURCES})
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
@@ -164,4 +165,4 @@ if (APPLE)
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
endif ()
target_link_libraries(libimhex PUBLIC imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs)
target_link_libraries(libimhex PUBLIC dl imgui nfd magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs)

View File

@@ -3,21 +3,23 @@
#include <cstdint>
#include <cstddef>
constexpr static const auto ImHexApiURL = "https://api.werwolv.net/imhex";
constexpr static const auto ImHexApiURL = "https://api.werwolv.net/imhex";
constexpr static const auto GitHubApiURL = "https://api.github.com/repos/WerWolv/ImHex";
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
using u128 = __uint128_t;
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i8 = std::int8_t;
using i16 = std::int16_t;
using i32 = std::int32_t;
using i64 = std::int64_t;
using i128 = __int128_t;
using color_t = u32;
namespace hex {
struct Region {

View File

@@ -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
};
@@ -85,21 +104,34 @@ namespace hex {
};
void add(
Type type, const std::string &command, const std::string &unlocalizedDescription, const DisplayCallback &displayCallback, const ExecuteCallback &executeCallback = [](auto) {});
Type type,
const std::string &command,
const std::string &unlocalizedDescription,
const DisplayCallback &displayCallback,
const ExecuteCallback &executeCallback = [](auto) {});
std::vector<Entry> &getEntries();
}
/* Pattern Language Function Registry. Allows adding of new functions that may be used inside the pattern language */
namespace PatternLanguage {
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
constexpr static u32 MoreParametersThan = 0x8000'0000;
constexpr static u32 LessParametersThan = 0x4000'0000;
constexpr static u32 ExactlyOrMoreParametersThan = 0x2000'0000;
constexpr static u32 NoParameters = 0x0000'0000;
namespace impl {
struct ColorPalette {
std::string name;
std::vector<u32> colors;
};
}
constexpr static u32 UnlimitedParameters = 0xFFFF'FFFF;
constexpr static u32 MoreParametersThan = 0x8000'0000;
constexpr static u32 LessParametersThan = 0x4000'0000;
constexpr static u32 ExactlyOrMoreParametersThan = 0x2000'0000;
constexpr static u32 NoParameters = 0x0000'0000;
using Namespace = std::vector<std::string>;
using Callback = std::function<std::optional<hex::pl::Token::Literal>(hex::pl::Evaluator *, const std::vector<hex::pl::Token::Literal> &)>;
using Callback = std::function<std::optional<hex::pl::Token::Literal>(hex::pl::Evaluator *, const std::vector<hex::pl::Token::Literal> &)>;
struct Function {
u32 parameterCount;
@@ -110,6 +142,12 @@ namespace hex {
void addFunction(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
void addDangerousFunction(const Namespace &ns, const std::string &name, u32 parameterCount, const Callback &func);
std::map<std::string, ContentRegistry::PatternLanguage::Function> &getFunctions();
std::vector<impl::ColorPalette> &getPalettes();
void addColorPalette(const std::string &unlocalizedName, const std::vector<u32> &colors);
void setSelectedPalette(u32 index);
u32 getNextColor();
void resetPalette();
}
/* View Registry. Allows adding of new windows */
@@ -154,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
@@ -162,18 +201,20 @@ namespace hex {
namespace impl {
using DisplayFunction = std::function<std::string()>;
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();
}
@@ -225,8 +266,9 @@ namespace hex {
namespace impl {
using DrawCallback = std::function<void()>;
using DrawCallback = std::function<void()>;
using LayoutFunction = std::function<void(u32)>;
using ClickCallback = std::function<void()>;
struct Layout {
std::string unlocalizedName;
@@ -247,9 +289,13 @@ namespace hex {
DrawCallback callback;
};
}
struct TitleBarButton {
std::string icon;
std::string unlocalizedTooltip;
ClickCallback callback;
};
u32 getDockSpaceId();
}
void registerMainMenuItem(const std::string &unlocalizedName, u32 priority);
void addMenuItem(const std::string &unlocalizedMainMenuName, u32 priority, const impl::DrawCallback &function);
@@ -258,6 +304,7 @@ namespace hex {
void addFooterItem(const impl::DrawCallback &function);
void addToolbarItem(const impl::DrawCallback &function);
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function);
void addTitleBarButton(const std::string &icon, const std::string &unlocalizedTooltip, const impl::ClickCallback &function);
void addLayout(const std::string &unlocalizedName, const impl::LayoutFunction &function);
@@ -268,6 +315,7 @@ namespace hex {
std::vector<impl::DrawCallback> &getFooterItems();
std::vector<impl::DrawCallback> &getToolbarItems();
std::vector<impl::SidebarItem> &getSidebarItems();
std::vector<impl::TitleBarButton> &getTitleBarButtons();
std::vector<impl::Layout> &getLayouts();
}
@@ -298,7 +346,7 @@ namespace hex {
impl::addProviderName(unlocalizedName);
}
const std::vector<std::string> &getEntries();
std::vector<std::string> &getEntries();
}
@@ -339,4 +387,4 @@ namespace hex {
}
};
}
}

View File

@@ -59,7 +59,7 @@ namespace hex {
using EventList = std::list<std::pair<EventId, EventBase *>>;
template<typename E>
[[nodiscard]] static EventList::iterator subscribe(typename E::Callback function) {
static EventList::iterator subscribe(typename E::Callback function) {
return s_events.insert(s_events.end(), std::make_pair(E::id, new E(function)));
}
@@ -97,14 +97,14 @@ namespace hex {
};
namespace pl {
class PatternData;
class Pattern;
}
/* Default Events */
EVENT_DEF(EventFileLoaded, fs::path);
EVENT_DEF(EventFileUnloaded);
EVENT_DEF(EventDataChanged);
EVENT_DEF(EventPatternChanged, std::vector<pl::PatternData *> &);
EVENT_DEF(EventHighlightingChanged);
EVENT_DEF(EventWindowClosing, GLFWwindow *);
EVENT_DEF(EventRegionSelected, Region);
EVENT_DEF(EventProjectFileStore);
@@ -113,12 +113,14 @@ namespace hex {
EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventOSThemeChanged);
EVENT_DEF(EventProviderCreated, prv::Provider *);
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);
EVENT_DEF(RequestAddBookmark, ImHexApi::Bookmarks::Entry);
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t);
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
EVENT_DEF(RequestChangeWindowTitle, std::string);
EVENT_DEF(RequestCloseImHex, bool);

View File

@@ -3,13 +3,17 @@
#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>
using ImGuiID = unsigned int;
struct ImVec2;
namespace hex {
namespace prv {
@@ -17,34 +21,76 @@ namespace hex {
}
namespace ImHexApi {
namespace Common {
void closeImHex(bool noQuestions = false);
void restartImHex();
};
}
namespace HexEditor {
class Highlighting {
public:
Highlighting() = default;
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 = 0x00;
std::string m_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 &region, color_t color, const std::string &tooltip = "");
void removeHighlight(u32 id);
u32 addHighlightingProvider(const impl::HighlightingFunction &function);
void removeHighlightingProvider(u32 id);
Region getSelection();
void setSelection(const Region &region);
void setSelection(u64 address, size_t size);
}
namespace Bookmarks {
struct Entry {
Region region;
std::vector<char> name;
std::vector<char> comment;
std::string name;
std::string comment;
u32 color;
bool locked;
u32 highlightId;
};
void add(Region region, const std::string &name, const std::string &comment, u32 color = 0x00000000);
void add(u64 addr, size_t size, const std::string &name, const std::string &comment, u32 color = 0x00000000);
void add(u64 address, size_t size, const std::string &name, const std::string &comment, color_t color = 0x00000000);
std::list<Entry> &getEntries();
};
}
namespace Provider {
prv::Provider *get();
const std::vector<prv::Provider *> &getProviders();
void setCurrentProvider(u32 index);
bool isValid();
void add(prv::Provider *provider);
@@ -56,14 +102,55 @@ namespace hex {
void remove(prv::Provider *provider);
};
}
namespace Tasks {
Task createTask(const std::string &unlocalizedName, u64 maxValue);
void doLater(const std::function<void()> &function);
std::vector<std::function<void()>> &getDeferredCalls();
}
};
namespace System {
namespace impl {
void setMainWindowPosition(u32 x, u32 y);
void setMainWindowSize(u32 width, u32 height);
void setMainDockSpaceId(ImGuiID id);
void setGlobalScale(float scale);
void setProgramArguments(int argc, char **argv, char **envp);
void setBorderlessWindowMode(bool enabled);
}
struct ProgramArguments {
int argc;
char **argv;
char **envp;
};
const ProgramArguments &getProgramArguments();
float getTargetFPS();
void setTargetFPS(float fps);
float getGlobalScale();
ImVec2 getMainWindowPosition();
ImVec2 getMainWindowSize();
ImGuiID getMainDockSpaceId();
bool isBorderlessWindowModeEnabled();
std::map<std::string, std::string> &getInitArguments();
}
}
}

View File

@@ -13,119 +13,120 @@ namespace hex {
struct View;
enum class Keys {
Space = GLFW_KEY_SPACE,
Apostrophe = GLFW_KEY_APOSTROPHE,
Comma = GLFW_KEY_COMMA,
Minus = GLFW_KEY_MINUS,
Period = GLFW_KEY_PERIOD,
Slash = GLFW_KEY_SLASH,
Num0 = GLFW_KEY_0,
Num1 = GLFW_KEY_1,
Num2 = GLFW_KEY_2,
Num3 = GLFW_KEY_3,
Num4 = GLFW_KEY_4,
Num5 = GLFW_KEY_5,
Num6 = GLFW_KEY_6,
Num7 = GLFW_KEY_7,
Num8 = GLFW_KEY_8,
Num9 = GLFW_KEY_9,
Semicolon = GLFW_KEY_SEMICOLON,
Equals = GLFW_KEY_EQUAL,
A = GLFW_KEY_A,
B = GLFW_KEY_B,
C = GLFW_KEY_C,
D = GLFW_KEY_D,
E = GLFW_KEY_E,
F = GLFW_KEY_F,
G = GLFW_KEY_G,
H = GLFW_KEY_H,
I = GLFW_KEY_I,
J = GLFW_KEY_J,
K = GLFW_KEY_K,
L = GLFW_KEY_L,
M = GLFW_KEY_M,
N = GLFW_KEY_N,
O = GLFW_KEY_O,
P = GLFW_KEY_P,
Q = GLFW_KEY_Q,
R = GLFW_KEY_R,
S = GLFW_KEY_S,
T = GLFW_KEY_T,
U = GLFW_KEY_U,
V = GLFW_KEY_V,
W = GLFW_KEY_W,
X = GLFW_KEY_X,
Y = GLFW_KEY_Y,
Z = GLFW_KEY_Z,
LeftBracket = GLFW_KEY_LEFT_BRACKET,
Backslash = GLFW_KEY_BACKSLASH,
RightBracket = GLFW_KEY_RIGHT_BRACKET,
GraveAccent = GLFW_KEY_GRAVE_ACCENT,
World1 = GLFW_KEY_WORLD_1,
World2 = GLFW_KEY_WORLD_2,
Escape = GLFW_KEY_ESCAPE,
Enter = GLFW_KEY_ENTER,
Tab = GLFW_KEY_TAB,
Backspace = GLFW_KEY_BACKSPACE,
Insert = GLFW_KEY_INSERT,
Delete = GLFW_KEY_DELETE,
Right = GLFW_KEY_RIGHT,
Left = GLFW_KEY_LEFT,
Down = GLFW_KEY_DOWN,
Up = GLFW_KEY_UP,
PageUp = GLFW_KEY_PAGE_UP,
PageDown = GLFW_KEY_PAGE_DOWN,
Home = GLFW_KEY_HOME,
End = GLFW_KEY_END,
CapsLock = GLFW_KEY_CAPS_LOCK,
ScrollLock = GLFW_KEY_SCROLL_LOCK,
NumLock = GLFW_KEY_NUM_LOCK,
PrintScreen = GLFW_KEY_PRINT_SCREEN,
Pause = GLFW_KEY_PAUSE,
F1 = GLFW_KEY_F1,
F2 = GLFW_KEY_F2,
F3 = GLFW_KEY_F3,
F4 = GLFW_KEY_F4,
F5 = GLFW_KEY_F5,
F6 = GLFW_KEY_F6,
F7 = GLFW_KEY_F7,
F8 = GLFW_KEY_F8,
F9 = GLFW_KEY_F9,
F10 = GLFW_KEY_F10,
F11 = GLFW_KEY_F11,
F12 = GLFW_KEY_F12,
F13 = GLFW_KEY_F13,
F14 = GLFW_KEY_F14,
F15 = GLFW_KEY_F15,
F16 = GLFW_KEY_F16,
F17 = GLFW_KEY_F17,
F18 = GLFW_KEY_F18,
F19 = GLFW_KEY_F19,
F20 = GLFW_KEY_F20,
F21 = GLFW_KEY_F21,
F22 = GLFW_KEY_F22,
F23 = GLFW_KEY_F23,
F24 = GLFW_KEY_F24,
F25 = GLFW_KEY_F25,
KeyPad0 = GLFW_KEY_KP_0,
KeyPad1 = GLFW_KEY_KP_1,
KeyPad2 = GLFW_KEY_KP_2,
KeyPad3 = GLFW_KEY_KP_3,
KeyPad4 = GLFW_KEY_KP_4,
KeyPad5 = GLFW_KEY_KP_5,
KeyPad6 = GLFW_KEY_KP_6,
KeyPad7 = GLFW_KEY_KP_7,
KeyPad8 = GLFW_KEY_KP_8,
KeyPad9 = GLFW_KEY_KP_9,
KeyPadDecimal = GLFW_KEY_KP_DECIMAL,
KeyPadDivide = GLFW_KEY_KP_DIVIDE,
enum class Keys
{
Space = GLFW_KEY_SPACE,
Apostrophe = GLFW_KEY_APOSTROPHE,
Comma = GLFW_KEY_COMMA,
Minus = GLFW_KEY_MINUS,
Period = GLFW_KEY_PERIOD,
Slash = GLFW_KEY_SLASH,
Num0 = GLFW_KEY_0,
Num1 = GLFW_KEY_1,
Num2 = GLFW_KEY_2,
Num3 = GLFW_KEY_3,
Num4 = GLFW_KEY_4,
Num5 = GLFW_KEY_5,
Num6 = GLFW_KEY_6,
Num7 = GLFW_KEY_7,
Num8 = GLFW_KEY_8,
Num9 = GLFW_KEY_9,
Semicolon = GLFW_KEY_SEMICOLON,
Equals = GLFW_KEY_EQUAL,
A = GLFW_KEY_A,
B = GLFW_KEY_B,
C = GLFW_KEY_C,
D = GLFW_KEY_D,
E = GLFW_KEY_E,
F = GLFW_KEY_F,
G = GLFW_KEY_G,
H = GLFW_KEY_H,
I = GLFW_KEY_I,
J = GLFW_KEY_J,
K = GLFW_KEY_K,
L = GLFW_KEY_L,
M = GLFW_KEY_M,
N = GLFW_KEY_N,
O = GLFW_KEY_O,
P = GLFW_KEY_P,
Q = GLFW_KEY_Q,
R = GLFW_KEY_R,
S = GLFW_KEY_S,
T = GLFW_KEY_T,
U = GLFW_KEY_U,
V = GLFW_KEY_V,
W = GLFW_KEY_W,
X = GLFW_KEY_X,
Y = GLFW_KEY_Y,
Z = GLFW_KEY_Z,
LeftBracket = GLFW_KEY_LEFT_BRACKET,
Backslash = GLFW_KEY_BACKSLASH,
RightBracket = GLFW_KEY_RIGHT_BRACKET,
GraveAccent = GLFW_KEY_GRAVE_ACCENT,
World1 = GLFW_KEY_WORLD_1,
World2 = GLFW_KEY_WORLD_2,
Escape = GLFW_KEY_ESCAPE,
Enter = GLFW_KEY_ENTER,
Tab = GLFW_KEY_TAB,
Backspace = GLFW_KEY_BACKSPACE,
Insert = GLFW_KEY_INSERT,
Delete = GLFW_KEY_DELETE,
Right = GLFW_KEY_RIGHT,
Left = GLFW_KEY_LEFT,
Down = GLFW_KEY_DOWN,
Up = GLFW_KEY_UP,
PageUp = GLFW_KEY_PAGE_UP,
PageDown = GLFW_KEY_PAGE_DOWN,
Home = GLFW_KEY_HOME,
End = GLFW_KEY_END,
CapsLock = GLFW_KEY_CAPS_LOCK,
ScrollLock = GLFW_KEY_SCROLL_LOCK,
NumLock = GLFW_KEY_NUM_LOCK,
PrintScreen = GLFW_KEY_PRINT_SCREEN,
Pause = GLFW_KEY_PAUSE,
F1 = GLFW_KEY_F1,
F2 = GLFW_KEY_F2,
F3 = GLFW_KEY_F3,
F4 = GLFW_KEY_F4,
F5 = GLFW_KEY_F5,
F6 = GLFW_KEY_F6,
F7 = GLFW_KEY_F7,
F8 = GLFW_KEY_F8,
F9 = GLFW_KEY_F9,
F10 = GLFW_KEY_F10,
F11 = GLFW_KEY_F11,
F12 = GLFW_KEY_F12,
F13 = GLFW_KEY_F13,
F14 = GLFW_KEY_F14,
F15 = GLFW_KEY_F15,
F16 = GLFW_KEY_F16,
F17 = GLFW_KEY_F17,
F18 = GLFW_KEY_F18,
F19 = GLFW_KEY_F19,
F20 = GLFW_KEY_F20,
F21 = GLFW_KEY_F21,
F22 = GLFW_KEY_F22,
F23 = GLFW_KEY_F23,
F24 = GLFW_KEY_F24,
F25 = GLFW_KEY_F25,
KeyPad0 = GLFW_KEY_KP_0,
KeyPad1 = GLFW_KEY_KP_1,
KeyPad2 = GLFW_KEY_KP_2,
KeyPad3 = GLFW_KEY_KP_3,
KeyPad4 = GLFW_KEY_KP_4,
KeyPad5 = GLFW_KEY_KP_5,
KeyPad6 = GLFW_KEY_KP_6,
KeyPad7 = GLFW_KEY_KP_7,
KeyPad8 = GLFW_KEY_KP_8,
KeyPad9 = GLFW_KEY_KP_9,
KeyPadDecimal = GLFW_KEY_KP_DECIMAL,
KeyPadDivide = GLFW_KEY_KP_DIVIDE,
KeyPadMultiply = GLFW_KEY_KP_MULTIPLY,
KeyPadSubtract = GLFW_KEY_KP_SUBTRACT,
KeyPadAdd = GLFW_KEY_KP_ADD,
KeyPadEnter = GLFW_KEY_KP_ENTER,
KeyPadEqual = GLFW_KEY_KP_EQUAL,
Menu = GLFW_KEY_MENU,
KeyPadAdd = GLFW_KEY_KP_ADD,
KeyPadEnter = GLFW_KEY_KP_ENTER,
KeyPadEqual = GLFW_KEY_KP_EQUAL,
Menu = GLFW_KEY_MENU,
};
@@ -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);
@@ -175,8 +179,8 @@ namespace hex {
return result;
}
static constexpr auto CTRL = Key(static_cast<Keys>(0x1000'0000));
static constexpr auto ALT = Key(static_cast<Keys>(0x2000'0000));
static constexpr auto CTRL = Key(static_cast<Keys>(0x1000'0000));
static constexpr auto ALT = Key(static_cast<Keys>(0x2000'0000));
static constexpr auto SHIFT = Key(static_cast<Keys>(0x3000'0000));
static constexpr auto SUPER = Key(static_cast<Keys>(0x4000'0000));
@@ -185,6 +189,11 @@ namespace hex {
static void addGlobalShortcut(const Shortcut &shortcut, const std::function<void()> &callback);
static void addShortcut(View *view, const Shortcut &shortcut, const std::function<void()> &callback);
static void process(View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
static void clearShortcuts();
private:
static std::map<Shortcut, std::function<void()>> s_globalShortcuts;
};
}

View File

@@ -35,10 +35,13 @@ namespace hex {
static void setFallbackLanguage(const std::string &language);
static const std::string &getFallbackLanguage();
static void resetLanguageStrings();
private:
std::string m_unlocalizedString;
static std::string s_fallbackLanguage;
static std::map<std::string, std::string> s_currStrings;
};
std::string operator+(const std::string &&left, const LangEntry &&right);
@@ -49,13 +52,8 @@ namespace hex {
std::string operator+(const LangEntry &&left, const char *right);
std::string operator+(const LangEntry &&left, const LangEntry &&right);
namespace lang_literals {
inline LangEntry operator""_lang(const char *string, size_t) {
return LangEntry(string);
}
inline LangEntry operator""_lang(const char *string, size_t) {
return LangEntry(string);
}
}

View File

@@ -24,30 +24,33 @@ namespace hex {
[[nodiscard]] std::string getPluginDescription() const;
[[nodiscard]] std::string getCompatibleVersion() const;
void setImGuiContext(ImGuiContext *ctx) const;
[[nodiscard]] bool isBuiltinPlugin() const;
[[nodiscard]] const fs::path &getPath() const;
[[nodiscard]] bool isLoaded() const;
private:
using InitializePluginFunc = void (*)();
using GetPluginNameFunc = const char *(*)();
using GetPluginAuthorFunc = const char *(*)();
using InitializePluginFunc = void (*)();
using GetPluginNameFunc = const char *(*)();
using GetPluginAuthorFunc = const char *(*)();
using GetPluginDescriptionFunc = const char *(*)();
using GetCompatibleVersionFunc = const char *(*)();
using SetImGuiContextFunc = void (*)(ImGuiContext *);
using SetImGuiContextFunc = void (*)(ImGuiContext *);
using IsBuiltinPluginFunc = bool (*)();
void *m_handle = nullptr;
fs::path m_path;
mutable bool m_initialized = false;
InitializePluginFunc m_initializePluginFunction = nullptr;
GetPluginNameFunc m_getPluginNameFunction = nullptr;
GetPluginAuthorFunc m_getPluginAuthorFunction = nullptr;
InitializePluginFunc m_initializePluginFunction = nullptr;
GetPluginNameFunc m_getPluginNameFunction = nullptr;
GetPluginAuthorFunc m_getPluginAuthorFunction = nullptr;
GetPluginDescriptionFunc m_getPluginDescriptionFunction = nullptr;
GetCompatibleVersionFunc m_getCompatibleVersionFunction = nullptr;
SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
SetImGuiContextFunc m_setImGuiContextFunction = nullptr;
IsBuiltinPluginFunc m_isBuiltinPluginFunction = nullptr;
template<typename T>
[[nodiscard]] auto getPluginFunction(const std::string &symbol) {
@@ -71,8 +74,8 @@ namespace hex {
}
private:
static inline fs::path s_pluginFolder;
static inline std::vector<Plugin> s_plugins;
static fs::path s_pluginFolder;
static std::vector<Plugin> s_plugins;
};
}

View File

@@ -2,6 +2,8 @@
#include <hex.hpp>
#include <list>
#include <mutex>
#include <string>
namespace hex {
@@ -21,9 +23,16 @@ namespace hex {
[[nodiscard]] bool isPending() const;
static size_t getRunningTaskCount();
static std::list<Task *> &getRunningTasks() { return Task::s_runningTasks; }
static std::mutex &getTaskMutex() { return Task::s_taskMutex; }
private:
std::string m_name;
u64 m_maxValue, m_currValue;
static std::list<Task *> s_runningTasks;
static std::mutex s_taskMutex;
};
}

View File

@@ -27,8 +27,8 @@ namespace hex::dp {
Attribute(IOType ioType, Type type, std::string unlocalizedName);
~Attribute();
[[nodiscard]] u32 getID() const { return this->m_id; }
void setID(u32 id) { this->m_id = id; }
[[nodiscard]] u32 getId() const { return this->m_id; }
void setId(u32 id) { this->m_id = id; }
[[nodiscard]] IOType getIOType() const { return this->m_ioType; }
[[nodiscard]] Type getType() const { return this->m_type; }
@@ -42,6 +42,11 @@ namespace hex::dp {
[[nodiscard]] std::optional<std::vector<u8>> &getOutputData() { return this->m_outputData; }
static void setIdCounter(u32 id) {
if (id > Attribute::s_idCounter)
Attribute::s_idCounter = id;
}
private:
u32 m_id;
IOType m_ioType;
@@ -54,6 +59,8 @@ namespace hex::dp {
friend class Node;
void setParentNode(Node *node) { this->m_parentNode = node; }
static u32 s_idCounter;
};
}

View File

@@ -8,15 +8,22 @@ namespace hex::dp {
public:
Link(u32 from, u32 to);
[[nodiscard]] u32 getID() const { return this->m_id; }
[[nodiscard]] u32 getId() const { return this->m_id; }
void setID(u32 id) { this->m_id = id; }
[[nodiscard]] u32 getFromID() const { return this->m_from; }
[[nodiscard]] u32 getToID() const { return this->m_to; }
[[nodiscard]] u32 getFromId() const { return this->m_from; }
[[nodiscard]] u32 getToId() const { return this->m_to; }
static void setIdCounter(u32 id) {
if (id > Link::s_idCounter)
Link::s_idCounter = id;
}
private:
u32 m_id;
u32 m_from, m_to;
static u32 s_idCounter;
};
}

View File

@@ -22,8 +22,8 @@ namespace hex::dp {
virtual ~Node() = default;
[[nodiscard]] u32 getID() const { return this->m_id; }
void setID(u32 id) { this->m_id = id; }
[[nodiscard]] u32 getId() const { return this->m_id; }
void setId(u32 id) { this->m_id = id; }
[[nodiscard]] const std::string &getUnlocalizedName() const { return this->m_unlocalizedName; }
void setUnlocalizedName(const std::string &unlocalizedName) { this->m_unlocalizedName = unlocalizedName; }
@@ -52,6 +52,11 @@ namespace hex::dp {
this->m_processedInputs.clear();
}
static void setIdCounter(u32 id) {
if (id > Node::s_idCounter)
Node::s_idCounter = id;
}
private:
u32 m_id;
std::string m_unlocalizedTitle, m_unlocalizedName;
@@ -59,6 +64,8 @@ namespace hex::dp {
std::set<u32> m_processedInputs;
prv::Overlay *m_overlay = nullptr;
static u32 s_idCounter;
Attribute *getConnectedInputAttribute(u32 index) {
if (index >= this->getAttributes().size())
throw std::runtime_error("Attribute index out of bounds!");

View File

@@ -3,6 +3,7 @@
#include <hex.hpp>
#include <type_traits>
#include <memory>
namespace hex {
@@ -159,3 +160,14 @@ namespace hex {
concept has_size = sizeof(T) == Size;
}
namespace hex {
template<typename T>
class Cloneable {
public:
[[nodiscard]] virtual std::unique_ptr<T> clone() const = 0;
};
}

View File

@@ -40,14 +40,14 @@ namespace hex::crypt {
std::string encode16(const std::vector<u8> &input);
enum class AESMode : u8 {
ECB = 0,
CBC = 1,
ECB = 0,
CBC = 1,
CFB128 = 2,
CTR = 3,
GCM = 4,
CCM = 5,
OFB = 6,
XTS = 7
CTR = 3,
GCM = 4,
CCM = 5,
OFB = 6,
XTS = 7
};
enum class KeyLength : u8 {

View File

@@ -1,15 +1,13 @@
#pragma once
#if __has_include(<capstone/capstone.h>)
#include <capstone/capstone.h>
#else
#include <capstone.h>
#endif
#include <hex.hpp>
#include <capstone/capstone.h>
namespace hex {
enum class Architecture : i32 {
enum class Architecture : i32
{
ARM,
ARM64,
MIPS,

View File

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

View File

@@ -1,56 +1,62 @@
#pragma once
#include <chrono>
#include <fmt/core.h>
#include <fmt/color.h>
#include <fmt/chrono.h>
namespace hex::log {
FILE *getDestination();
bool isRedirected();
template<typename... T>
void print(fmt::format_string<T...> fmt, T &&...args) {
fmt::print(getDestination(), fmt, args...);
namespace {
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(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) {
auto dest = getDestination();
printPrefix(dest, ts, level);
fmt::print(dest, fmt::runtime(fmt), args...);
fmt::print(dest, "\n");
}
}
template<typename S, typename... Args>
void print(const fmt::text_style &ts, const S &fmt, const Args &...args) {
if (isRedirected())
fmt::print(getDestination(), fmt::runtime(fmt), args...);
else
fmt::print(getDestination(), ts, fmt, args...);
}
void debug(const std::string &fmt, auto... args) {
void debug(const std::string &fmt, auto &&...args) {
#if defined(DEBUG)
log::print(fg(fmt::color::green_yellow) | fmt::emphasis::bold, "[DEBUG] ");
log::print(fmt::runtime(fmt), args...);
log::print("\n");
log::print(fg(fmt::color::green_yellow) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
#endif
}
void info(const std::string &fmt, auto... args) {
log::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ");
log::print(fmt::runtime(fmt), args...);
log::print("\n");
void info(const std::string &fmt, auto &&...args) {
log::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
}
void warn(const std::string &fmt, auto... args) {
log::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ");
log::print(fmt::runtime(fmt), args...);
log::print("\n");
void warn(const std::string &fmt, auto &&...args) {
log::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
}
void error(const std::string &fmt, auto... args) {
log::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR] ");
log::print(fmt::runtime(fmt), args...);
log::print("\n");
void error(const std::string &fmt, auto &&...args) {
log::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
}
void fatal(const std::string &fmt, auto... args) {
log::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL] ");
log::print(fmt::runtime(fmt), args...);
log::print("\n");
void fatal(const std::string &fmt, auto &&...args) {
log::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
}
void redirectToFile();

View File

@@ -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);
@@ -58,7 +60,7 @@ namespace hex {
curl_slist *m_headers = nullptr;
std::mutex m_transmissionActive;
float m_progress = 0.0F;
float m_progress = 0.0F;
bool m_shouldCancel = false;
};

View File

@@ -1,135 +0,0 @@
#pragma once
#include <any>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <vector>
#include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/api/task.hpp>
#include <hex/views/view.hpp>
#include <imgui.h>
#include <nlohmann/json_fwd.hpp>
namespace hex {
class SharedData;
}
namespace hex::plugin::internal {
void initializePlugin(SharedData &sharedData);
}
namespace hex {
namespace prv {
class Provider;
}
namespace dp {
class Node;
}
namespace pl {
class PatternData;
}
class View;
class SharedData {
SharedData() = default;
public:
SharedData(const SharedData &) = delete;
SharedData(SharedData &&) = delete;
friend class Window;
template<typename T>
static T &getVariable(std::string variableName) {
return std::any_cast<T &>(SharedData::sharedVariables[variableName]);
}
template<typename T>
static void setVariable(std::string variableName, T value) {
SharedData::sharedVariables[variableName] = value;
}
static void clearVariables() {
SharedData::sharedVariables.clear();
}
public:
static std::vector<std::function<void()>> deferredCalls;
static std::vector<prv::Provider *> providers;
static u32 currentProvider;
static std::map<std::string, std::vector<ContentRegistry::Settings::Entry>> settingsEntries;
static nlohmann::json settingsJson;
static std::vector<ContentRegistry::CommandPaletteCommands::Entry> commandPaletteCommands;
static std::map<std::string, ContentRegistry::PatternLanguage::Function> patternLanguageFunctions;
static std::map<std::string, View *> views;
static std::vector<ContentRegistry::Tools::impl::Entry> toolsEntries;
static std::vector<ContentRegistry::DataInspector::impl::Entry> dataInspectorEntries;
static u32 patternPaletteOffset;
static std::string popupMessage;
static std::list<ImHexApi::Bookmarks::Entry> bookmarkEntries;
static std::vector<pl::PatternData *> patternData;
static u32 selectableFileIndex;
static std::vector<fs::path> selectableFiles;
static std::function<void(fs::path)> selectableFileOpenCallback;
static std::vector<nfdfilteritem_t> selectableFilesValidExtensions;
static std::map<std::string, std::string> languageNames;
static std::map<std::string, std::vector<LanguageDefinition>> languageDefinitions;
static std::map<std::string, std::string> loadedLanguageStrings;
static ImGuiID dockSpaceId;
static std::multimap<u32, ContentRegistry::Interface::impl::MainMenuItem> mainMenuItems;
static std::multimap<u32, ContentRegistry::Interface::impl::MenuItem> menuItems;
static std::vector<ContentRegistry::Interface::impl::DrawCallback> welcomeScreenEntries;
static std::vector<ContentRegistry::Interface::impl::DrawCallback> footerItems;
static std::vector<ContentRegistry::Interface::impl::DrawCallback> toolbarItems;
static std::vector<ContentRegistry::Interface::impl::SidebarItem> sidebarItems;
static std::vector<ContentRegistry::Interface::impl::Layout> layouts;
static std::map<Shortcut, std::function<void()>> globalShortcuts;
static std::mutex tasksMutex;
static std::list<Task *> runningTasks;
static std::vector<std::string> providerNames;
static std::vector<ContentRegistry::DataProcessorNode::impl::Entry> dataProcessorNodes;
static u32 dataProcessorNodeIdCounter;
static u32 dataProcessorLinkIdCounter;
static u32 dataProcessorAttrIdCounter;
static std::vector<ContentRegistry::DataFormatter::impl::Entry> dataFormatters;
static std::vector<ContentRegistry::FileHandler::impl::Entry> fileHandlers;
static std::list<fs::path> recentFilePaths;
static int mainArgc;
static char **mainArgv;
static char **mainEnvp;
static ImFontAtlas *fontAtlas;
static ImFontConfig fontConfig;
static ImVec2 windowPos;
static ImVec2 windowSize;
static float globalScale;
static float fontScale;
private:
static std::map<std::string, std::any> sharedVariables;
};
}

View File

@@ -24,7 +24,7 @@ namespace hex {
class Socket {
public:
Socket() = default;
Socket() = default;
Socket(const Socket &) = delete;
Socket(Socket &&other);

View File

@@ -41,11 +41,16 @@ 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);
using ValueType = std::remove_cvref_t<decltype(value)>;
ValueType mask = (std::numeric_limits<ValueType>::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to;
ValueType mask = (std::numeric_limits<ValueType>::max() >> (((sizeof(value) * 8) - 1) - (from - to))) << to;
return (value & mask) >> to;
}
@@ -111,23 +116,23 @@ namespace hex {
static_assert(always_false<T>::value, "Invalid type provided!");
switch (Size) {
case 1:
swapped = unswapped;
break;
case 2:
swapped = __builtin_bswap16(unswapped);
break;
case 4:
swapped = __builtin_bswap32(unswapped);
break;
case 8:
swapped = __builtin_bswap64(unswapped);
break;
case 16:
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
break;
default:
__builtin_unreachable();
case 1:
swapped = unswapped;
break;
case 2:
swapped = __builtin_bswap16(unswapped);
break;
case 4:
swapped = __builtin_bswap32(unswapped);
break;
case 8:
swapped = __builtin_bswap64(unswapped);
break;
case 16:
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
break;
default:
__builtin_unreachable();
}
T result;
@@ -151,23 +156,23 @@ namespace hex {
u128 swapped;
switch (size) {
case 1:
swapped = unswapped;
break;
case 2:
swapped = __builtin_bswap16(unswapped);
break;
case 4:
swapped = __builtin_bswap32(unswapped);
break;
case 8:
swapped = __builtin_bswap64(unswapped);
break;
case 16:
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
break;
default:
__builtin_unreachable();
case 1:
swapped = unswapped;
break;
case 2:
swapped = __builtin_bswap16(unswapped);
break;
case 4:
swapped = __builtin_bswap32(unswapped);
break;
case 8:
swapped = __builtin_bswap64(unswapped);
break;
case 16:
swapped = (u128(__builtin_bswap64(unswapped & 0xFFFF'FFFF'FFFF'FFFF)) << 64) | __builtin_bswap64(u128(unswapped) >> 64);
break;
default:
__builtin_unreachable();
}
T result = 0;
@@ -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 = "");
@@ -231,15 +252,15 @@ namespace hex {
inline void trimLeft(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
return !std::isspace(ch) && ch >= 0x20;
}));
return !std::isspace(ch) && ch >= 0x20;
}));
}
inline void trimRight(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
return !std::isspace(ch) && ch >= 0x20;
}).base(),
s.end());
return !std::isspace(ch) && ch >= 0x20;
}).base(),
s.end());
}
inline void trim(std::string &s) {
@@ -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) {

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

View File

@@ -0,0 +1,312 @@
#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](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](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](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();
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
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();
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
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();
evaluator->setCurrentControlFlowStatement(ControlFlowStatement::None);
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);
}
};
}

View File

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

View File

@@ -0,0 +1,218 @@
#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));
(void)copy.release();
}
}
}
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();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,58 @@
#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 {
if (this->m_rvalue == nullptr) {
evaluator->setCurrentControlFlowStatement(this->m_type);
return std::nullopt;
} else {
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;
};
}

View File

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

View File

@@ -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>> &&params)
: 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 &param : 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 &param : 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;
};
}

View File

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

View File

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

View File

@@ -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, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](i128 left, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](double left, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](char left, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](bool left, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](const std::string &left, Pattern *const &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, u128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, i128 right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, double right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, char right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, bool right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, const std::string &right) -> ASTNode * { LogConsole::abortEvaluation("invalid operand used in mathematical expression", this); },
[this](Pattern *const &left, Pattern *const &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
}

View File

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

View File

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

View File

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

View File

@@ -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.get();
}
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](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();
}
Pattern *indexPattern;
if (currPattern->isLocal()) {
auto stackLiteral = evaluator->getStack()[currPattern->getOffset()];
if (auto stackPattern = std::get_if<Pattern *>(&stackLiteral); stackPattern != nullptr)
indexPattern = *stackPattern;
else
return hex::moveToVector<std::unique_ptr<Pattern>>(std::move(currPattern));
} else
indexPattern = currPattern.get();
if (auto structPattern = dynamic_cast<PatternStruct *>(indexPattern))
searchScope = structPattern->getMembers();
else if (auto unionPattern = dynamic_cast<PatternUnion *>(indexPattern))
searchScope = unionPattern->getMembers();
else if (auto bitfieldPattern = dynamic_cast<PatternBitfield *>(indexPattern))
searchScope = bitfieldPattern->getFields();
else if (auto dynamicArrayPattern = dynamic_cast<PatternArrayDynamic *>(indexPattern))
searchScope = dynamicArrayPattern->getEntries();
else if (auto staticArrayPattern = dynamic_cast<PatternArrayStatic *>(indexPattern))
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;
},
[&](Pattern *assignmentValue) { readVariable(evaluator, value, assignmentValue); },
[&](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());
}
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,27 @@
#pragma once
#include <hex.hpp>
#include <stdexcept>
#include <string>
namespace hex::pl {
class PatternLanguageError : public std::exception {
public:
PatternLanguageError(u32 lineNumber, std::string message) : m_lineNumber(lineNumber), m_message(std::move(message)) { }
[[nodiscard]] const char *what() const noexcept override {
return this->m_message.c_str();
}
[[nodiscard]] u32 getLineNumber() const {
return this->m_lineNumber;
}
private:
u32 m_lineNumber;
std::string m_message;
};
}

View File

@@ -19,20 +19,28 @@ namespace hex::prv {
namespace hex::pl {
enum class DangerousFunctionPermission {
enum class DangerousFunctionPermission
{
Ask,
Deny,
Allow
};
enum class ControlFlowStatement {
enum class ControlFlowStatement
{
None,
Continue,
Break,
Return
};
class PatternData;
enum class BitfieldOrder
{
RightToLeft,
LeftToRight
};
class Pattern;
class PatternCreationLimiter;
class ASTNode;
@@ -40,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;
@@ -52,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()));
@@ -159,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) {
@@ -195,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;
}
@@ -230,6 +247,10 @@ namespace hex::pl {
return this->m_currControlFlowStatement;
}
[[nodiscard]] const std::optional<Token::Literal> &getMainResult() {
return this->m_mainResult;
}
private:
void patternCreated();
void patternDestroyed();
@@ -251,16 +272,19 @@ 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;
std::map<std::string, Token::Literal> m_envVariables;
std::map<std::string, Token::Literal> m_inVariables;
std::map<std::string, size_t> m_outVariables;
std::atomic<bool> m_dangerousFunctionCalled = false;
std::atomic<bool> m_dangerousFunctionCalled = false;
std::atomic<DangerousFunctionPermission> m_allowDangerousFunctions = DangerousFunctionPermission::Ask;
ControlFlowStatement m_currControlFlowStatement;
BitfieldOrder m_bitfieldOrder = BitfieldOrder::RightToLeft;
friend class PatternCreationLimiter;
};

View File

@@ -12,18 +12,16 @@ namespace hex::pl {
class Lexer {
public:
using LexerError = std::pair<u32, std::string>;
Lexer() = default;
std::optional<std::vector<Token>> lex(const std::string &code);
const std::optional<LexerError> &getError() { return this->m_error; }
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
private:
std::optional<LexerError> m_error;
std::optional<PatternLanguageError> m_error;
[[noreturn]] void throwLexerError(const std::string &error, u32 lineNumber) const {
throw LexerError(lineNumber, "Lexer: " + error);
[[noreturn]] static void throwLexerError(const std::string &error, u32 lineNumber) {
throw PatternLanguageError(lineNumber, "Lexer: " + error);
}
};

View File

@@ -8,13 +8,18 @@
#include <utility>
#include <vector>
#include <hex/helpers/concepts.hpp>
#include <hex/pattern_language/error.hpp>
namespace hex::pl {
class ASTNode;
class LogConsole {
public:
enum Level {
enum Level
{
Debug,
Info,
Warning,
@@ -23,23 +28,33 @@ namespace hex::pl {
[[nodiscard]] const auto &getLog() const { return this->m_consoleLog; }
using EvaluateError = std::pair<u32, std::string>;
void log(Level level, const std::string &message) {
this->m_consoleLog.emplace_back(level, message);
}
void log(Level level, const std::string &message);
[[noreturn]] static void abortEvaluation(const std::string &message) {
abortEvaluation(message, nullptr);
}
[[noreturn]] static void abortEvaluation(const std::string &message);
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();
void clear() {
this->m_consoleLog.clear();
this->m_lastHardError.reset();
}
void setHardError(const EvaluateError &error) { this->m_lastHardError = error; }
void setHardError(const PatternLanguageError &error) { this->m_lastHardError = error; }
[[nodiscard]] const std::optional<EvaluateError> &getLastHardError() { return this->m_lastHardError; };
[[nodiscard]] const std::optional<PatternLanguageError> &getLastHardError() { return this->m_lastHardError; };
private:
std::vector<std::pair<Level, std::string>> m_consoleLog;
std::optional<EvaluateError> m_lastHardError;
std::optional<PatternLanguageError> m_lastHardError;
};
}

View File

@@ -3,8 +3,13 @@
#include <hex.hpp>
#include <hex/helpers/utils.hpp>
#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>
@@ -16,20 +21,19 @@ namespace hex::pl {
class Parser {
public:
using TokenIter = std::vector<Token>::const_iterator;
using ParseError = std::pair<u32, std::string>;
Parser() = default;
Parser() = default;
~Parser() = default;
std::optional<std::vector<ASTNode *>> parse(const std::vector<Token> &tokens);
const std::optional<ParseError> &getError() { return this->m_error; }
std::optional<std::vector<std::shared_ptr<ASTNode>>> parse(const std::vector<Token> &tokens);
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
private:
std::optional<ParseError> m_error;
std::optional<PatternLanguageError> m_error;
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>
@@ -47,7 +52,7 @@ namespace hex::pl {
auto value = std::get_if<T>(&this->m_curr[index].value);
if (value == nullptr)
throwParseError("failed to decode token. Invalid type.", getLineNumber(index));
throwParserError("failed to decode token. Invalid type.", getLineNumber(index));
return *value;
}
@@ -67,91 +72,87 @@ 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 *parseRelationExpression();
ASTNode *parseEqualityExpression();
ASTNode *parseBinaryAndExpression();
ASTNode *parseBinaryXorExpression();
ASTNode *parseBinaryOrExpression();
ASTNode *parseBooleanAnd();
ASTNode *parseBooleanXor();
ASTNode *parseBooleanOr();
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;
}
[[noreturn]] void throwParseError(const std::string &error, i32 token = -1) const {
throw ParseError(this->m_curr[token].lineNumber, "Parser: " + error);
[[noreturn]] void throwParserError(const std::string &error, i32 token = -1) const {
throw PatternLanguageError(this->m_curr[token].lineNumber, "Parser: " + error);
}
/* Token consuming */
enum class Setting { };
enum class Setting
{
};
constexpr static auto Normal = static_cast<Setting>(0);
constexpr static auto Not = static_cast<Setting>(1);
constexpr static auto Not = static_cast<Setting>(1);
bool begin() {
this->m_originalPosition = this->m_curr;

File diff suppressed because it is too large Load Diff

View File

@@ -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,15 +33,16 @@ namespace hex::pl {
PatternLanguage();
~PatternLanguage();
[[nodiscard]] std::optional<std::vector<ASTNode *>> parseString(const std::string &code);
[[nodiscard]] std::optional<std::vector<PatternData *>> executeString(prv::Provider *provider, const std::string &string, const std::map<std::string, Token::Literal> &envVars = {}, const std::map<std::string, Token::Literal> &inVariables = {});
[[nodiscard]] std::optional<std::vector<PatternData *>> 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]] const std::vector<ASTNode *> &getCurrentAST() const;
[[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<std::shared_ptr<ASTNode>> &getCurrentAST() const;
void abort();
[[nodiscard]] const std::vector<std::pair<LogConsole::Level, std::string>> &getConsoleLog();
[[nodiscard]] const std::optional<std::pair<u32, std::string>> &getError();
[[nodiscard]] const std::optional<PatternLanguageError> &getError();
[[nodiscard]] std::map<std::string, Token::Literal> getOutVariables() const;
[[nodiscard]] u32 getCreatedPatternCount();
@@ -49,6 +51,16 @@ namespace hex::pl {
[[nodiscard]] bool hasDangerousFunctionBeenCalled() const;
void allowDangerousFunctions(bool allow);
[[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;
}
void reset();
[[nodiscard]] bool isRunning() const { return this->m_running; }
private:
Preprocessor *m_preprocessor;
Lexer *m_lexer;
@@ -56,9 +68,13 @@ namespace hex::pl {
Validator *m_validator;
Evaluator *m_evaluator;
std::vector<ASTNode *> m_currAST;
std::vector<std::shared_ptr<ASTNode>> m_currAST;
std::optional<std::pair<u32, std::string>> m_currError;
std::optional<PatternLanguageError> m_currError;
std::vector<std::shared_ptr<Pattern>> m_patterns;
bool m_running = false;
};
}

View 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, 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->getTypeName().empty() ? this->getFormattedName() : this->getTypeName());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", formatDisplayValue(value, std::move(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;
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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->getTypeName().empty() ? this->getFormattedName() : this->getTypeName() + "* : ";
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;
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,24 +11,29 @@
#include <hex/helpers/paths.hpp>
#include <hex/pattern_language/error.hpp>
namespace hex::pl {
class Preprocessor {
public:
Preprocessor();
Preprocessor() = default;
std::optional<std::string> preprocess(const std::string &code, bool initialRun = true);
void addPragmaHandler(const std::string &pragmaType, const std::function<bool(const std::string &)> &function);
void removePragmaHandler(const std::string &pragmaType);
void addDefaultPragmaHandlers();
const std::pair<u32, std::string> &getError() { return this->m_error; }
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
bool shouldOnlyIncludeOnce() {
return this->m_onlyIncludeOnce;
}
private:
using PreprocessorError = std::pair<u32, std::string>;
[[noreturn]] void throwPreprocessorError(const std::string &error, u32 lineNumber) const {
throw PreprocessorError(lineNumber, "Preprocessor: " + error);
[[noreturn]] static void throwPreprocessorError(const std::string &error, u32 lineNumber) {
throw PatternLanguageError(lineNumber, "Preprocessor: " + error);
}
std::unordered_map<std::string, std::function<bool(std::string)>> m_pragmaHandlers;
@@ -38,7 +43,9 @@ namespace hex::pl {
std::set<fs::path> m_onceIncludedFiles;
std::pair<u32, std::string> m_error;
std::optional<PatternLanguageError> m_error;
bool m_onlyIncludeOnce = false;
};
}

View File

@@ -7,14 +7,17 @@
#include <variant>
#include <hex/helpers/utils.hpp>
#include <hex/pattern_language/log_console.hpp>
namespace hex::pl {
class PatternData;
class ASTNode;
class Pattern;
class Token {
public:
enum class Type : u64 {
enum class Type : u64
{
Keyword,
ValueType,
Operator,
@@ -24,7 +27,8 @@ namespace hex::pl {
Separator
};
enum class Keyword {
enum class Keyword
{
Struct,
Union,
Using,
@@ -47,7 +51,8 @@ namespace hex::pl {
Continue
};
enum class Operator {
enum class Operator
{
AtDeclaration,
Assignment,
Inherit,
@@ -79,35 +84,37 @@ namespace hex::pl {
ScopeResolution
};
enum class ValueType {
Unsigned8Bit = 0x10,
Signed8Bit = 0x11,
Unsigned16Bit = 0x20,
Signed16Bit = 0x21,
Unsigned32Bit = 0x40,
Signed32Bit = 0x41,
Unsigned64Bit = 0x80,
Signed64Bit = 0x81,
enum class ValueType
{
Unsigned8Bit = 0x10,
Signed8Bit = 0x11,
Unsigned16Bit = 0x20,
Signed16Bit = 0x21,
Unsigned32Bit = 0x40,
Signed32Bit = 0x41,
Unsigned64Bit = 0x80,
Signed64Bit = 0x81,
Unsigned128Bit = 0x100,
Signed128Bit = 0x101,
Character = 0x13,
Character16 = 0x23,
Boolean = 0x14,
Float = 0x42,
Double = 0x82,
String = 0x15,
Auto = 0x16,
CustomType = 0x00,
Padding = 0x1F,
Signed128Bit = 0x101,
Character = 0x13,
Character16 = 0x23,
Boolean = 0x14,
Float = 0x42,
Double = 0x82,
String = 0x15,
Auto = 0x16,
CustomType = 0x00,
Padding = 0x1F,
Unsigned = 0xFF00,
Signed = 0xFF01,
Unsigned = 0xFF00,
Signed = 0xFF01,
FloatingPoint = 0xFF02,
Integer = 0xFF03,
Any = 0xFFFF
Integer = 0xFF03,
Any = 0xFFFF
};
enum class Separator {
enum class Separator
{
RoundBracketOpen,
RoundBracketClose,
CurlyBracketOpen,
@@ -126,13 +133,13 @@ namespace hex::pl {
[[nodiscard]] const std::string &get() const { return this->m_identifier; }
auto operator<=>(const Identifier &) const = default;
bool operator==(const Identifier &) const = default;
bool operator==(const Identifier &) const = default;
private:
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, Pattern *>;
using ValueTypes = std::variant<Keyword, Identifier, Operator, Literal, ValueType, Separator>;
Token(Type type, auto value, u32 lineNumber) : type(type), value(value), lineNumber(lineNumber) {
@@ -156,87 +163,87 @@ namespace hex::pl {
static u128 literalToUnsigned(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](std::string) -> u128 { throw std::string("expected integral type, got string"); },
[](PatternData *) -> u128 { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> u128 { return value; } },
literal);
[](const std::string &) -> u128 { LogConsole::abortEvaluation("expected integral type, got string"); },
[](Pattern *) -> u128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> u128 { return result; } },
literal);
}
static i128 literalToSigned(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](std::string) -> i128 { throw std::string("expected integral type, got string"); },
[](PatternData *) -> i128 { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> i128 { return value; } },
literal);
[](const std::string &) -> i128 { LogConsole::abortEvaluation("expected integral type, got string"); },
[](Pattern *) -> i128 { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> i128 { return result; } },
literal);
}
static double literalToFloatingPoint(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](std::string) -> double { throw std::string("expected integral type, got string"); },
[](PatternData *) -> double { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> double { return value; } },
literal);
[](const std::string &) -> double { LogConsole::abortEvaluation("expected integral type, got string"); },
[](Pattern *) -> double { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> double { return result; } },
literal);
}
static bool literalToBoolean(const pl::Token::Literal &literal) {
return std::visit(overloaded {
[](std::string) -> bool { throw std::string("expected integral type, got string"); },
[](PatternData *) -> bool { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> bool { return value != 0; } },
literal);
[](const std::string &) -> bool { LogConsole::abortEvaluation("expected integral type, got string"); },
[](Pattern *) -> bool { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> bool { return result != 0; } },
literal);
}
static std::string literalToString(const pl::Token::Literal &literal, bool cast) {
if (!cast && std::get_if<std::string>(&literal) == nullptr)
throw std::string("expected string type, got integral");
LogConsole::abortEvaluation("expected string type, got integral");
return std::visit(overloaded {
[](std::string value) -> std::string { return value; },
[](u128 value) -> std::string { return std::to_string(u64(value)); },
[](i128 value) -> std::string { return std::to_string(i64(value)); },
[](bool value) -> std::string { return value ? "true" : "false"; },
[](char value) -> std::string { return std::string() + value; },
[](PatternData *) -> std::string { throw std::string("expected integral type, got custom type"); },
[](auto &&value) -> std::string { return std::to_string(value); } },
literal);
[](std::string result) -> std::string { return result; },
[](u128 result) -> std::string { return hex::to_string(result); },
[](i128 result) -> std::string { return hex::to_string(result); },
[](bool result) -> std::string { return result ? "true" : "false"; },
[](char result) -> std::string { return { 1, result }; },
[](Pattern *) -> std::string { LogConsole::abortEvaluation("expected integral type, got custom type"); },
[](auto &&result) -> std::string { return std::to_string(result); } },
literal);
}
[[nodiscard]] constexpr static auto getTypeName(const pl::Token::ValueType type) {
switch (type) {
case ValueType::Signed8Bit:
return "s8";
case ValueType::Signed16Bit:
return "s16";
case ValueType::Signed32Bit:
return "s32";
case ValueType::Signed64Bit:
return "s64";
case ValueType::Signed128Bit:
return "s128";
case ValueType::Unsigned8Bit:
return "u8";
case ValueType::Unsigned16Bit:
return "u16";
case ValueType::Unsigned32Bit:
return "u32";
case ValueType::Unsigned64Bit:
return "u64";
case ValueType::Unsigned128Bit:
return "u128";
case ValueType::Float:
return "float";
case ValueType::Double:
return "double";
case ValueType::Character:
return "char";
case ValueType::Character16:
return "char16";
case ValueType::Padding:
return "padding";
case ValueType::String:
return "str";
default:
return "< ??? >";
case ValueType::Signed8Bit:
return "s8";
case ValueType::Signed16Bit:
return "s16";
case ValueType::Signed32Bit:
return "s32";
case ValueType::Signed64Bit:
return "s64";
case ValueType::Signed128Bit:
return "s128";
case ValueType::Unsigned8Bit:
return "u8";
case ValueType::Unsigned16Bit:
return "u16";
case ValueType::Unsigned32Bit:
return "u32";
case ValueType::Unsigned64Bit:
return "u64";
case ValueType::Unsigned128Bit:
return "u128";
case ValueType::Float:
return "float";
case ValueType::Double:
return "double";
case ValueType::Character:
return "char";
case ValueType::Character16:
return "char16";
case ValueType::Padding:
return "padding";
case ValueType::String:
return "str";
default:
return "< ??? >";
}
}
@@ -245,7 +252,7 @@ namespace hex::pl {
return true;
else if (this->type == Type::ValueType) {
auto otherValueType = std::get_if<ValueType>(&other);
auto valueType = std::get_if<ValueType>(&this->value);
auto valueType = std::get_if<ValueType>(&this->value);
if (otherValueType == nullptr) return false;
if (valueType == nullptr) return false;

View File

@@ -2,28 +2,30 @@
#include <hex.hpp>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <hex/pattern_language/error.hpp>
namespace hex::pl {
class ASTNode;
class Validator {
public:
Validator();
Validator() = default;
bool validate(const std::vector<ASTNode *> &ast);
bool validate(const std::vector<std::shared_ptr<ASTNode>> &ast);
const std::pair<u32, std::string> &getError() { return this->m_error; }
const std::optional<PatternLanguageError> &getError() { return this->m_error; }
private:
std::pair<u32, std::string> m_error;
std::optional<PatternLanguageError> m_error;
using ValidatorError = std::pair<u32, std::string>;
[[noreturn]] void throwValidateError(std::string_view error, u32 lineNumber) const {
throw ValidatorError(lineNumber, error);
[[noreturn]] static void throwValidatorError(const std::string &error, u32 lineNumber) {
throw PatternLanguageError(lineNumber, "Validator: " + error);
}
};

View File

@@ -6,9 +6,9 @@
#include <hex.hpp>
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(IMHEX_PLUGIN_NAME, name, author, description)
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
#define IMHEX_PLUGIN_SETUP_IMPL(namespaceName, name, author, description) \
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
extern "C" [[gnu::visibility("default")]] const char *getPluginName() { return name; } \
extern "C" [[gnu::visibility("default")]] const char *getPluginAuthor() { return author; } \
extern "C" [[gnu::visibility("default")]] const char *getPluginDescription() { return description; } \

View File

@@ -2,13 +2,18 @@
#include <hex.hpp>
#include <list>
#include <map>
#include <optional>
#include <string>
#include <vector>
#include <hex/helpers/shared_data.hpp>
#include <hex/providers/overlay.hpp>
#include <hex/helpers/paths.hpp>
namespace hex::pl {
class PatternLanguage;
}
namespace hex::prv {
@@ -20,10 +25,10 @@ namespace hex::prv {
virtual ~Provider();
[[nodiscard]] virtual bool isAvailable() const = 0;
[[nodiscard]] virtual bool isReadable() const = 0;
[[nodiscard]] virtual bool isWritable() const = 0;
[[nodiscard]] virtual bool isReadable() const = 0;
[[nodiscard]] virtual bool isWritable() const = 0;
[[nodiscard]] virtual bool isResizable() const = 0;
[[nodiscard]] virtual bool isSavable() const = 0;
[[nodiscard]] virtual bool isSavable() const = 0;
virtual void read(u64 offset, void *buffer, size_t size, bool overlays = true);
virtual void write(u64 offset, const void *buffer, size_t size);
@@ -34,9 +39,9 @@ namespace hex::prv {
virtual void save();
virtual void saveAs(const fs::path &path);
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
virtual void readRaw(u64 offset, void *buffer, size_t size) = 0;
virtual void writeRaw(u64 offset, const void *buffer, size_t size) = 0;
[[nodiscard]] virtual size_t getActualSize() const = 0;
[[nodiscard]] virtual size_t getActualSize() const = 0;
void applyOverlays(u64 offset, void *buffer, size_t size);
@@ -58,11 +63,11 @@ namespace hex::prv {
[[nodiscard]] virtual size_t getSize() const;
[[nodiscard]] virtual std::optional<u32> getPageOfAddress(u64 address) const;
[[nodiscard]] virtual std::string getName() const = 0;
[[nodiscard]] virtual std::string getName() const = 0;
[[nodiscard]] virtual std::vector<std::pair<std::string, std::string>> getDataInformation() const = 0;
[[nodiscard]] virtual bool open() = 0;
virtual void close() = 0;
virtual void close() = 0;
void addPatch(u64 offset, const void *buffer, size_t size, bool createUndo = false);
void createUndoPoint();
@@ -78,13 +83,19 @@ namespace hex::prv {
virtual void drawLoadInterface();
virtual void drawInterface();
pl::PatternLanguage &getPatternLanguageRuntime() { return *this->m_patternLanguageRuntime; }
std::string &getPatternLanguageSourceCode() { return this->m_patternLanguageSourceCode; }
protected:
u32 m_currPage = 0;
u32 m_currPage = 0;
u64 m_baseAddress = 0;
u32 m_patchTreeOffset = 0;
std::list<std::map<u64, u8>> m_patches;
std::list<Overlay *> m_overlays;
std::unique_ptr<pl::PatternLanguage> m_patternLanguageRuntime;
std::string m_patternLanguageSourceCode;
};
}

View File

@@ -1,6 +1,9 @@
#pragma once
#include <hex.hpp>
#include <functional>
#include <string>
#define IMGUI_DEFINE_MATH_OPERATORS
#include <imgui.h>
@@ -29,8 +32,8 @@ enum ImGuiCustomCol {
namespace ImGui {
struct Texture {
ImTextureID textureId;
int width, height;
ImTextureID textureId = nullptr;
int width = 0, height = 0;
[[nodiscard]] constexpr bool valid() const noexcept {
return this->textureId != nullptr;
@@ -43,6 +46,12 @@ namespace ImGui {
[[nodiscard]] auto size() const noexcept {
return ImVec2(this->width, this->height);
}
[[nodiscard]] constexpr auto aspectRatio() const noexcept {
if (this->height == 0) return 1.0F;
return float(this->width) / float(this->height);
}
};
int UpdateStringSizeCallback(ImGuiInputTextCallbackData *data);
@@ -66,6 +75,9 @@ namespace ImGui {
bool ToolBarButton(const char *symbol, ImVec4 color);
bool IconButton(const char *symbol, ImVec4 color, ImVec2 size_arg = ImVec2(0, 0));
bool InputIntegerPrefix(const char* label, const char *prefix, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
bool InputHexadecimal(const char* label, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
inline bool HasSecondPassed() {
return static_cast<ImU32>(ImGui::GetTime() * 100) % 100 <= static_cast<ImU32>(ImGui::GetIO().DeltaTime * 100);
}
@@ -106,7 +118,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);
@@ -116,4 +128,7 @@ namespace ImGui {
ImGui::TextFormattedWrapped("{}", text);
ImGui::PopTextWrapPos();
}
bool InputText(const char* label, std::string &buffer, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
bool InputTextMultiline(const char* label, std::string &buffer, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
}

View File

@@ -13,8 +13,9 @@
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/lang.hpp>
#include <hex/api/localization.hpp>
#include <functional>
#include <string>
@@ -23,8 +24,6 @@
namespace hex {
using namespace hex::lang_literals;
class View {
public:
explicit View(std::string unlocalizedViewName);
@@ -32,26 +31,24 @@ namespace hex {
virtual void drawContent() = 0;
virtual void drawAlwaysVisible() { }
virtual bool isAvailable() const;
virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); }
static void doLater(std::function<void()> &&function);
static std::vector<std::function<void()>> &getDeferedCalls();
[[nodiscard]] virtual bool isAvailable() const;
[[nodiscard]] virtual bool shouldProcess() const { return this->isAvailable() && this->getWindowOpenState(); }
static void drawCommonInterfaces();
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);
virtual bool hasViewMenuItemEntry() const;
virtual ImVec2 getMinSize() const;
virtual ImVec2 getMaxSize() const;
[[nodiscard]] virtual bool hasViewMenuItemEntry() const;
[[nodiscard]] virtual ImVec2 getMinSize() const;
[[nodiscard]] virtual ImVec2 getMaxSize() const;
bool &getWindowOpenState();
const bool &getWindowOpenState() const;
[[nodiscard]] bool &getWindowOpenState();
[[nodiscard]] const bool &getWindowOpenState() const;
[[nodiscard]] const std::string &getUnlocalizedName() const;
[[nodiscard]] std::string getName() const;
@@ -63,11 +60,29 @@ namespace hex {
return LangEntry(unlocalizedName) + "###" + unlocalizedName;
}
static ImFontAtlas *getFontAtlas() { return View::s_fontAtlas; }
static void setFontAtlas(ImFontAtlas *atlas) { View::s_fontAtlas = atlas; }
static ImFontConfig getFontConfig() { return View::s_fontConfig; }
static void setFontConfig(ImFontConfig config) { View::s_fontConfig = config; }
private:
std::string m_unlocalizedViewName;
bool m_windowOpen = false;
std::map<Shortcut, std::function<void()>> m_shortcuts;
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;
static std::vector<nfdfilteritem_t> s_selectableFilesValidExtensions;
static ImFontAtlas *s_fontAtlas;
static ImFontConfig s_fontConfig;
friend class ShortcutManager;
};

Some files were not shown because too many files have changed in this diff Show More