Compare commits

...

58 Commits

Author SHA1 Message Date
WerWolv
21b22e7667 build: Bumped version to 1.21.0 2022-08-14 14:54:20 +02:00
WerWolv
7d5b17d5c9 build: Added option to use NFD system library 2022-08-14 14:53:27 +02:00
WerWolv
49d47a0eed feat: Added bookmark import/export function 2022-08-14 14:45:32 +02:00
WerWolv
78785ddc3c fix: More provider display issues 2022-08-14 14:45:18 +02:00
WerWolv
dea6caccf1 fix: File opening in existing instance not being unicode aware 2022-08-14 13:42:22 +02:00
iTrooz_
b917bfca07 git: Fix Fedora spec name and removed caching from Fedora CI (#672)
* changed name to 'imhex' in imhex.spec

* removed CMakeCache for Fedora
2022-08-14 10:08:58 +02:00
WerWolv
85f0e04d0e feat: Allow recents to also display other providers 2022-08-14 10:07:45 +02:00
WerWolv
440ba3823e fix: Hex editor displaying ?? for every byte after editing 2022-08-14 09:38:38 +02:00
WerWolv
b580691871 feat: Added Intel Hex and Motorola SREC provider (#670)
* feat: Initial implementation of an Intel Hex provider

* fix: Reading of bytes from intel hex files

* lang: Added localization for new provider

* ui: Only show file name in intel hex provider name

* feat: Added Motorola SREC provider
2022-08-12 15:11:27 +02:00
eoineoineoin
72c4f50871 fix: Crash when any signal is being thrown (#669)
This avoids a crash on POSIX after running a command from the command
pallete; on completion, a SIGCHILD will be raised, which was incorrectly
calling the error handler and terminating ImHex.

Co-authored-by: Eoin Mcloughlin <helloworld@eoinrul.es>
2022-08-12 11:33:20 +02:00
WerWolv
7bc2c4a0d4 build: Cleanup build scripts 2022-08-11 10:50:25 +02:00
WerWolv
128551e193 build: Fix banner of Windows installer 2022-08-10 23:07:20 +02:00
WerWolv
72f7c72094 build: Improved look and feel of the Windows installer and macOS dmg 2022-08-10 22:47:21 +02:00
WerWolv
568b7f5139 build: Move plist property setting 2022-08-10 20:26:27 +02:00
WerWolv
164cb1285b build: Set MACOSX_BUNDLE_INFO_PLIST in a different way 2022-08-10 20:18:37 +02:00
WerWolv
e16be09f9a build: Allow ImHex to open any file types on macOS 2022-08-10 19:18:32 +02:00
WerWolv
d55c59c796 feat: Added close button for welcome screen 2022-08-10 10:28:40 +02:00
WerWolv
5c13cf9dbf sys: Added support for providers with unreadable regions 2022-08-10 09:26:48 +02:00
WerWolv
19a0dc80db fix: Window title not always updating correctly 2022-08-10 00:11:16 +02:00
WerWolv
683018a9d2 dist: Improve compile instructions 2022-08-09 22:50:02 +02:00
WerWolv
4c331341e5 git: Output unit test errors on failure 2022-08-09 16:31:10 +02:00
WerWolv
ceb26add15 sys: Added better support for Windows 7 2022-08-09 16:29:52 +02:00
WerWolv
4b720ee3a2 fix: More crashes with multi-byte visualizers 2022-08-09 15:00:31 +02:00
WerWolv
d4af07ed51 sys: Improved signal handling 2022-08-09 15:00:16 +02:00
WerWolv
a3132b7d13 build: Fixed system llvm linking errors 2022-08-09 14:59:58 +02:00
WerWolv
0192c791ce sys: Updated llvm demangler, now supports D-Lang and Rust symbols 2022-08-09 13:51:03 +02:00
WerWolv
f1ec2ef0c4 lang: Added some unlocalized values 2022-08-09 08:38:41 +02:00
WerWolv
b1e93fda4b sys: Format settings file more prettily 2022-08-09 09:39:04 +02:00
WerWolv
f349aafc37 fix: Crash when auto-load-patterns is being disabled 2022-08-09 09:38:43 +02:00
WerWolv
8ebbe6fb4e patterns: Fixed crash when preprocessor throws an error 2022-08-09 08:58:31 +02:00
WerWolv
e38b6ecd2c patterns: Fixed reading from strings passed to functions 2022-08-08 23:51:13 +02:00
WerWolv
966f3b8597 sys: Replace existing bad project system with a much better one (#663)
* sys: Initial effort to replace existing project files with a better system

* sys: Added back marking provider as dirty

* sys: Remove git commit information from project files

* sys: Format data processor save file nicely

* fix: Automatic pattern loading not working correctly

* ui: Added warning popup when closing a provider with modifications

Closes #604

* sys: Fixed build issues

* tests: Removed useless debug logs

* patterns: Updated pattern language

* sys: Added log message when crashing with a signal

* sys: Make sure abnormal termination handlers are being called more reliably
2022-08-08 21:23:52 +02:00
WerWolv
f0756bceb8 git: Updated runtime requirements 2022-08-08 21:12:19 +02:00
WerWolv
423e23e3c0 fix: Windows native theme detection not working correctly 2022-08-08 12:57:51 +02:00
WerWolv
2c740cab06 sys: Added select region command 2022-08-08 12:57:44 +02:00
WerWolv
519a9edb60 build: Make sure version string is always being set 2022-08-07 23:50:49 +02:00
WerWolv
e16216b39e patterns: Fixed for-loop parsing 2022-08-07 23:27:25 +02:00
WerWolv
f221d0f430 fix: Properly place config files in .../imhex/config on Linux 2022-08-07 23:27:15 +02:00
WerWolv
1aa497cb7b build: Added support for Clang 14.0.0+ 2022-08-07 23:27:09 +02:00
WerWolv
738a537723 patterns: Fixed display issue with static array entries 2022-08-07 22:03:32 +02:00
Bernard Teo
d8d3a315a4 build: Update nfd-extended to get correct feature test detection (#661) 2022-08-07 15:32:56 +02:00
WerWolv
f62ca307b0 ui: Streamline region selection in different views
Fixes #660
2022-08-07 12:20:40 +02:00
WerWolv
edfac4ef60 fix: Evaluating patterns not creating a Task 2022-08-07 12:13:08 +02:00
WerWolv
916962cf83 ui: Added hexadecimal size of file and selection to hex editor footer 2022-08-07 00:35:36 +02:00
WerWolv
189766ceb4 build: Set library and plugin permissions to 755 on install
Fixes #655
2022-08-07 00:30:03 +02:00
WerWolv
2200e11e85 fix: Pattern language runtime not being reset properly after checking MIME types 2022-08-07 00:11:54 +02:00
WerWolv
69d000488e api: Added api function to create pattern 2022-08-06 22:29:59 +02:00
WerWolv
88f8bb9848 sys: Updated to new pattern language library structure 2022-08-06 21:38:09 +02:00
WerWolv
4f37345324 fix: Crash when exiting hex editor editing mode
Fixes #658
2022-08-06 17:47:11 +02:00
WerWolv
31fcf86008 build: Make sure plugin building works fine on Linux 2022-08-06 16:03:47 +02:00
WerWolv
7d93c54444 build: Updated nfd-extended to prevent linking of macOS 11 framework 2022-08-06 14:33:23 +02:00
WerWolv
b2b753c2b3 ui: Fix displaying of advanced decoding row with multi-byte visualizers 2022-08-06 13:23:40 +02:00
WerWolv
a97f853110 fix: More search issues 2022-08-06 13:16:53 +02:00
WerWolv
3dc5f0e2be fix: Buffered Reader not reading last byte
Closes #653
2022-08-06 13:07:02 +02:00
WerWolv
d7accb6916 feat: Allow Find view to find overlapping occurrences
Closes #653
2022-08-06 12:57:47 +02:00
Berylskid
9c01f3efe3 lang: Updated Japanese translation (#654)
* Update ja_JP.cpp

- Corrected some mistranslations
- Updated some outdated class names
- Added some translations
- Replaced all exclamation marks (!) with Japanese reading marks (。)
- Replaced all 3-dot readers (...) with 2-byte character versions (…)

* Update ja_JP.cpp

Corrected mistranslation of "Contributors"
2022-08-05 20:31:20 +02:00
WerWolv
49cc85dd3b git: Fix deb compression argument 2022-08-05 19:45:01 +02:00
WerWolv
974b9c77e0 git: Use gzip to compress .deb files 2022-08-05 19:33:32 +02:00
121 changed files with 8218 additions and 4054 deletions

View File

@@ -257,7 +257,7 @@ jobs:
- name: 📦 Bundle DEB
run: |
cp -r build/DEBIAN build/AppDir
dpkg-deb --build build/AppDir
dpkg-deb -Zgzip --build build/AppDir
mv build/AppDir.deb imhex-${{env.IMHEX_VERSION}}.deb
rm -rf build/AppDir/DEBIAN
@@ -448,14 +448,7 @@ jobs:
~/.cache/ccache
key: fedora-${{ matrix.release }}-${{ secrets.CACHE_VERSION }}-build-${{ github.run_id }}
restore-keys: fedora-${{ matrix.release }}-${{ secrets.CACHE_VERSION }}-build
- name: 📜 Restore CMakeCache
uses: actions/cache@v3
with:
path: |
~/rpmbuild/BUILDROOT/CMakeCache.txt
key: fedora-${{ matrix.release }}-${{ secrets.CACHE_VERSION }}-build-${{ hashFiles('**/CMakeLists.txt') }}
- name: 📜 Set version variable
run: |
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV

View File

@@ -61,4 +61,4 @@ jobs:
- name: 🧪 Perform Unit Tests
run: |
cd build
ctest
ctest --output-on-failure

View File

@@ -46,4 +46,4 @@ enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
# Configure packaging
createPackage()
createPackage()

View File

@@ -116,7 +116,7 @@ For format patterns, libraries, magic and constant files, check out the [ImHex-P
To use ImHex, the following minimal system requirements need to be met:
- **OS**: Windows 10 or higher, macOS 11 (Big Sur) or higher, "Modern" Linux (Ubuntu 22.04+, Fedora and Arch Linux are officially supported)
- **OS**: Windows 7 or higher, macOS 10.10 (Yosemite) or higher, "Modern" Linux (Ubuntu 22.04+, Fedora Stable/Rawhide and Arch Linux are officially supported)
- **CPU**: x86_64 (64 Bit)
- **GPU**: OpenGL 3.0 or higher (preferable a dedicated GPU and not Intel HD Graphics)
- **RAM**: 512MB, more may be required for more complicated analysis
@@ -153,8 +153,9 @@ ImHex is available in various third party repositories.
## Compiling
To compile ImHex on any platform, GCC is required with a version that supports C++23 or higher.
On macOS, Clang is also required to compile some ObjC code.
To compile ImHex on any platform, GCC (or Clang) is required with a version that supports C++23 or higher.
On macOS, Clang is also required to compile some ObjC code.
All releases are being built using latest available GCC.
Many dependencies are bundled into the repository using submodules so make sure to clone it using the `--recurse-submodules` option.
All dependencies that aren't bundled, can be installed using the dependency installer scripts found in the `/dist` folder.

View File

@@ -1 +1 @@
1.20.0
1.21.0

View File

@@ -36,15 +36,16 @@ 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} ")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION})
if (CMAKE_BUILD_TYPE STREQUAL "Release")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION})
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-Debug)
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug)
add_compile_definitions(DEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-RelWithDebInfo)
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-RelWithDebInfo)
elseif (CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION}-MinSizeRel)
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-MinSizeRel)
endif ()
add_compile_definitions(IMHEX_VERSION="${IMHEX_VERSION_STRING}")
@@ -135,15 +136,18 @@ macro(configurePackingResources)
set(CPACK_PACKAGE_NAME "ImHex")
set(CPACK_PACKAGE_VENDOR "WerWolv")
set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285")
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/icon.ico")
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico")
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png")
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png")
set(CPACK_WIX_CULTURES "en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex")
set_property(INSTALL "$<TARGET_FILE_NAME:main>"
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
)
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/resources/LICENSE.rtf")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/resources/dist/windows/LICENSE.rtf")
endif()
elseif (APPLE)
set (IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/AppIcon.icns")
set (IMHEX_ICON "${IMHEX_BASE_FOLDER}/resources/dist/macos/AppIcon.icns")
if (CREATE_BUNDLE)
set(APPLICATION_TYPE MACOSX_BUNDLE)
@@ -151,21 +155,26 @@ macro(configurePackingResources)
set(MACOSX_BUNDLE_ICON_FILE "AppIcon.icns")
set(MACOSX_BUNDLE_INFO_STRING "WerWolv")
set(MACOSX_BUNDLE_BUNDLE_NAME "ImHex")
set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${GIT_COMMIT_HASH}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 WerWolv and Thog. All rights reserved." )
string(TIMESTAMP CURR_YEAR "%Y")
set(MACOSX_BUNDLE_COPYRIGHT "Copyright © 2020 - ${CURR_YEAR} WerWolv. All rights reserved." )
if ("${CMAKE_GENERATOR}" STREQUAL "Xcode")
set ( bundle_path "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/ImHex.app" )
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_BUILD_TYPE}/ImHex.app")
else ()
set ( bundle_path "${CMAKE_BINARY_DIR}/ImHex.app" )
set (IMHEX_BUNDLE_PATH "${CMAKE_BINARY_DIR}/ImHex.app")
endif()
endif()
endif()
endmacro()
macro(createPackage)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}")
@@ -180,7 +189,7 @@ macro(createPackage)
get_target_property(PLUGIN_LOCATION ${plugin} LOCATION)
install(FILES "${PLUGIN_LOCATION}/../${plugin}.hexplug" DESTINATION "${PLUGINS_INSTALL_LOCATION}")
install(FILES "${PLUGIN_LOCATION}/../${plugin}.hexplug" DESTINATION "${PLUGINS_INSTALL_LOCATION}" PERMISSIONS ${LIBRARY_PERMISSIONS})
else ()
if (WIN32)
install(TARGETS ${plugin} RUNTIME DESTINATION ${PLUGINS_INSTALL_LOCATION})
@@ -236,18 +245,18 @@ macro(createPackage)
endforeach()
]])
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./")
elseif(UNIX AND NOT APPLE)
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
configure_file(${CMAKE_SOURCE_DIR}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dist/DEBIAN/control.in ${CMAKE_BINARY_DIR}/DEBIAN/control)
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
install(FILES ${CMAKE_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./share/imhex")
endif()
@@ -255,20 +264,21 @@ macro(createPackage)
include(PostprocessBundle)
set_target_properties(libimhex PROPERTIES SOVERSION ${IMHEX_VERSION})
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath
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")
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
downloadImHexPatternsFiles("${bundle_path}/Contents/MacOS")
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
install(FILES ${IMHEX_ICON} DESTINATION "${bundle_path}/Contents/Resources")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".")
install(FILES $<TARGET_FILE:main> DESTINATION "${bundle_path}")
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
# Update library references to make the bundle portable
postprocess_bundle(imhex_all main)
@@ -280,7 +290,10 @@ macro(createPackage)
endif()
if (CREATE_PACKAGE)
include(apple)
set (CPACK_BUNDLE_NAME "ImHex")
set (CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns" )
set (CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/ImHex.app/Contents/Info.plist")
include(CPack)
endif()
endmacro()
@@ -322,15 +335,17 @@ function(detectBadClone)
endforeach ()
endfunction()
set(IMHEX_REQUIRED_COMPILER "GNU")
set(IMHEX_MIN_COMPILER_VERSION "12.0.0")
function(verifyCompiler)
if (IMHEX_IGNORE_BAD_COMPILER)
return()
endif()
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL ${IMHEX_REQUIRED_COMPILER} OR CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${IMHEX_MIN_COMPILER_VERSION})
message(FATAL_ERROR "ImHex requires GCC ${IMHEX_MIN_COMPILER_VERSION} or newer. Please use the latest GCC version.")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "12.0.0")
message(FATAL_ERROR "ImHex requires GCC 12.0.0 or newer. Please use the latest GCC version.")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "14.0.0")
message(FATAL_ERROR "ImHex requires Clang 14.0.0 or newer. Please use the latest Clang version.")
elseif (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR "ImHex can only be compiled with GCC or Clang. ${CMAKE_CXX_COMPILER_ID} is not supported.")
endif()
endfunction()

View File

@@ -1,3 +0,0 @@
set (CPACK_BUNDLE_NAME "ImHex")
set (CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/AppIcon.icns" )
set (CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/ImHex.app/Contents/Info.plist")

View File

@@ -1,9 +1,12 @@
### Compiling ImHex on Linux
Dependency installation scripts are available for many common Linux distributions in the [/dist](dist) folder.
After all the dependencies are installed, run the following commands to build ImHex:
On Linux, ImHex is built through regular GCC (or optionally Clang).
1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`
2. Install the dependencies using one of the `dist/get_deps_*.sh` scripts. Choose the one that matches your distro.
3. Build ImHex itself using the following commands:
```sh
cd ImHex
mkdir -p build
cd build
CC=gcc-12 CXX=g++-12 cmake \
@@ -15,27 +18,10 @@ CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DRUST_PATH="$HOME/.cargo/bin/" \
..
make -j 4 install
```
---
Put the ImHex executable into the `/usr/bin` folder.
Put libimhex.so into the `/usr/lib` folder.
Configuration files go to `/usr/etc/imhex` or `~/.config/imhex`.
All other files belong in `/usr/share/imhex` or `~/.local/share/imhex`:
```
Patterns: /usr/share/imhex/patterns
Pattern Includes: /usr/share/imhex/includes
Magic files: /usr/share/imhex/magic
Python: /usr/share/imhex/lib/pythonX.X
Plugins: /usr/share/imhex/plugins
Configuration: /etc/xdg/imhex/config
```
All paths follow the XDG Base Directories standard, and can thus be modified
with the environment variables `XDG_CONFIG_HOME`, `XDG_CONFIG_DIRS`,
`XDG_DATA_HOME` and `XDG_DATA_DIRS`.

View File

@@ -1,9 +1,12 @@
### Compiling ImHex on macOS
To build ImHex on macOS, run the following commands:
On macOS, ImHex is built through regular GCC and AppleClang.
1. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`
2. Install all the dependencies using `brew bundle --no-lock --file dist/Brewfile`
3. Build ImHex itself using the following commands:
```sh
brew bundle --no-lock --file dist/Brewfile
cd ImHex
mkdir -p build
cd build
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
@@ -24,18 +27,6 @@ cmake \
make -j4 package
```
Open the generated .dmg file and drag-n-drop the ImHex executable to the Applications folder
All other files belong in `~/Library/Application Support/imhex`:
```
Patterns: ~/Library/Application Support/imhex/patterns
Pattern Includes: ~/Library/Application Support/imhex/includes
Magic files: ~/Library/Application Support/imhex/magic
Python: ~/Library/Application Support/imhex/lib/pythonX.X
Plugins: ~/Library/Application Support/imhex/plugins
Configuration: ~/Library/Application Support/imhex/config
```
If the build fails while trying to find the macOS libraries, make sure you have
XCode installed with `xcode-select --install`. Homebrew will also help get the
most recent SDK installed and configured with `brew doctor`.

View File

@@ -1,9 +1,14 @@
### Compiling ImHex on Windows
On Windows, ImHex is built through msys2 / mingw. To install all dependencies, open a msys2 window and run the PKGCONFIG script in the [dist/msys2](dist/msys2) folder.
After all the dependencies are installed, run the following commands to build ImHex:
On Windows, ImHex is built through [msys2 / mingw](https://www.msys2.org/)'s gcc.
1. Download and install msys2 from their [website](https://www.msys2.org/).
2. Open the `MSYS2 MinGW x64` shell
3. Clone the repo using `git clone https://github.com/WerWolv/ImHex --recurse-submodules`
4. Install all the dependencies using `./ImHex/dist/get_deps_msys2.sh`
5. Build ImHex itself using the following commands:
```sh
cd ImHex
mkdir build
cd build
cmake -G "MinGW Makefiles" \

2
dist/rpm/imhex.spec vendored
View File

@@ -1,7 +1,7 @@
# ftbfs without this
%global _lto_cflags %{nil}
Name: ImHex
Name: imhex
Version: %{_version}
Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers

View File

@@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.16)
project(intervaltree)
set(CMAKE_CXX_STANDARD20)
add_library(intervaltree INTERFACE)
target_include_directories(intervaltree INTERFACE include)
target_compile_options(intervaltree INTERFACE "-DUSE_INTERVAL_TREE_NAMESPACE")

19
lib/external/intervaltree/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2011 Erik Garrison
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

37
lib/external/intervaltree/README.md vendored Normal file
View File

@@ -0,0 +1,37 @@
# intervaltree
## Overview
An interval tree can be used to efficiently find a set of numeric intervals overlapping or containing another interval.
This library provides a basic implementation of an interval tree using C++ templates, allowing the insertion of arbitrary types into the tree.
## Usage
Add `#include "IntervalTree.h"` to the source files in which you will use the interval tree.
To make an IntervalTree to contain objects of class T, use:
```c++
vector<Interval<T> > intervals;
T a, b, c;
intervals.push_back(Interval<T>(2, 10, a));
intervals.push_back(Interval<T>(3, 4, b));
intervals.push_back(Interval<T>(20, 100, c));
IntervalTree<T> tree;
tree = IntervalTree<T>(intervals);
```
Now, it's possible to query the tree and obtain a set of intervals which are contained within the start and stop coordinates.
```c++
vector<Interval<T> > results;
tree.findContained(start, stop, results);
cout << "found " << results.size() << " overlapping intervals" << endl;
```
The function IntervalTree::findOverlapping provides a method to find all those intervals which are contained or partially overlap the interval (start, stop).
### Author: Erik Garrison <erik.garrison@gmail.com>
### License: MIT

View File

@@ -0,0 +1,325 @@
#ifndef __INTERVAL_TREE_H
#define __INTERVAL_TREE_H
#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>
#include <cassert>
#include <limits>
#ifdef USE_INTERVAL_TREE_NAMESPACE
namespace interval_tree {
#endif
template <class Scalar, typename Value>
class Interval {
public:
Scalar start;
Scalar stop;
Value value;
Interval(const Scalar& s, const Scalar& e, const Value& v)
: start(std::min(s, e))
, stop(std::max(s, e))
, value(v)
{}
};
template <class Scalar, typename Value>
Value intervalStart(const Interval<Scalar,Value>& i) {
return i.start;
}
template <class Scalar, typename Value>
Value intervalStop(const Interval<Scalar, Value>& i) {
return i.stop;
}
template <class Scalar, typename Value>
std::ostream& operator<<(std::ostream& out, const Interval<Scalar, Value>& i) {
out << "Interval(" << i.start << ", " << i.stop << "): " << i.value;
return out;
}
template <class Scalar, class Value>
class IntervalTree {
public:
typedef Interval<Scalar, Value> interval;
typedef std::vector<interval> interval_vector;
struct IntervalStartCmp {
bool operator()(const interval& a, const interval& b) {
return a.start < b.start;
}
};
struct IntervalStopCmp {
bool operator()(const interval& a, const interval& b) {
return a.stop < b.stop;
}
};
IntervalTree()
: left(nullptr)
, right(nullptr)
, center(0)
{}
~IntervalTree() = default;
std::unique_ptr<IntervalTree> clone() const {
return std::unique_ptr<IntervalTree>(new IntervalTree(*this));
}
IntervalTree(const IntervalTree& other)
: intervals(other.intervals),
left(other.left ? other.left->clone() : nullptr),
right(other.right ? other.right->clone() : nullptr),
center(other.center)
{}
IntervalTree& operator=(IntervalTree&&) = default;
IntervalTree(IntervalTree&&) = default;
IntervalTree& operator=(const IntervalTree& other) {
center = other.center;
intervals = other.intervals;
left = other.left ? other.left->clone() : nullptr;
right = other.right ? other.right->clone() : nullptr;
return *this;
}
IntervalTree(
interval_vector&& ivals,
std::size_t depth = 16,
std::size_t minbucket = 64,
std::size_t maxbucket = 512,
Scalar leftextent = 0,
Scalar rightextent = 0)
: left(nullptr)
, right(nullptr)
{
--depth;
const auto minmaxStop = std::minmax_element(ivals.begin(), ivals.end(),
IntervalStopCmp());
const auto minmaxStart = std::minmax_element(ivals.begin(), ivals.end(),
IntervalStartCmp());
if (!ivals.empty()) {
center = (minmaxStart.first->start + minmaxStop.second->stop) / 2;
}
if (leftextent == 0 && rightextent == 0) {
// sort intervals by start
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
} else {
assert(std::is_sorted(ivals.begin(), ivals.end(), IntervalStartCmp()));
}
if (depth == 0 || (ivals.size() < minbucket && ivals.size() < maxbucket)) {
std::sort(ivals.begin(), ivals.end(), IntervalStartCmp());
intervals = std::move(ivals);
assert(is_valid().first);
return;
} else {
Scalar leftp = 0;
Scalar rightp = 0;
if (leftextent || rightextent) {
leftp = leftextent;
rightp = rightextent;
} else {
leftp = ivals.front().start;
rightp = std::max_element(ivals.begin(), ivals.end(),
IntervalStopCmp())->stop;
}
interval_vector lefts;
interval_vector rights;
for (typename interval_vector::const_iterator i = ivals.begin();
i != ivals.end(); ++i) {
const interval& interval = *i;
if (interval.stop < center) {
lefts.push_back(interval);
} else if (interval.start > center) {
rights.push_back(interval);
} else {
assert(interval.start <= center);
assert(center <= interval.stop);
intervals.push_back(interval);
}
}
if (!lefts.empty()) {
left.reset(new IntervalTree(std::move(lefts),
depth, minbucket, maxbucket,
leftp, center));
}
if (!rights.empty()) {
right.reset(new IntervalTree(std::move(rights),
depth, minbucket, maxbucket,
center, rightp));
}
}
assert(is_valid().first);
}
// Call f on all intervals near the range [start, stop]:
template <class UnaryFunction>
void visit_near(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
if (!intervals.empty() && ! (stop < intervals.front().start)) {
for (auto & i : intervals) {
f(i);
}
}
if (left && start <= center) {
left->visit_near(start, stop, f);
}
if (right && stop >= center) {
right->visit_near(start, stop, f);
}
}
// Call f on all intervals crossing pos
template <class UnaryFunction>
void visit_overlapping(const Scalar& pos, UnaryFunction f) const {
visit_overlapping(pos, pos, f);
}
// Call f on all intervals overlapping [start, stop]
template <class UnaryFunction>
void visit_overlapping(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
auto filterF = [&](const interval& interval) {
if (interval.stop >= start && interval.start <= stop) {
// Only apply f if overlapping
f(interval);
}
};
visit_near(start, stop, filterF);
}
// Call f on all intervals contained within [start, stop]
template <class UnaryFunction>
void visit_contained(const Scalar& start, const Scalar& stop, UnaryFunction f) const {
auto filterF = [&](const interval& interval) {
if (start <= interval.start && interval.stop <= stop) {
f(interval);
}
};
visit_near(start, stop, filterF);
}
interval_vector findOverlapping(const Scalar& start, const Scalar& stop) const {
interval_vector result;
visit_overlapping(start, stop,
[&](const interval& interval) {
result.emplace_back(interval);
});
return result;
}
interval_vector findContained(const Scalar& start, const Scalar& stop) const {
interval_vector result;
visit_contained(start, stop,
[&](const interval& interval) {
result.push_back(interval);
});
return result;
}
bool empty() const {
if (left && !left->empty()) {
return false;
}
if (!intervals.empty()) {
return false;
}
if (right && !right->empty()) {
return false;
}
return true;
}
template <class UnaryFunction>
void visit_all(UnaryFunction f) const {
if (left) {
left->visit_all(f);
}
std::for_each(intervals.begin(), intervals.end(), f);
if (right) {
right->visit_all(f);
}
}
std::pair<Scalar, Scalar> extentBruitForce() const {
struct Extent {
std::pair<Scalar, Scalar> x = {std::numeric_limits<Scalar>::max(),
std::numeric_limits<Scalar>::min() };
void operator()(const interval & interval) {
x.first = std::min(x.first, interval.start);
x.second = std::max(x.second, interval.stop);
}
};
Extent extent;
visit_all([&](const interval & interval) { extent(interval); });
return extent.x;
}
// Check all constraints.
// If first is false, second is invalid.
std::pair<bool, std::pair<Scalar, Scalar>> is_valid() const {
const auto minmaxStop = std::minmax_element(intervals.begin(), intervals.end(),
IntervalStopCmp());
const auto minmaxStart = std::minmax_element(intervals.begin(), intervals.end(),
IntervalStartCmp());
std::pair<bool, std::pair<Scalar, Scalar>> result = {true, { std::numeric_limits<Scalar>::max(),
std::numeric_limits<Scalar>::min() }};
if (!intervals.empty()) {
result.second.first = std::min(result.second.first, minmaxStart.first->start);
result.second.second = std::min(result.second.second, minmaxStop.second->stop);
}
if (left) {
auto valid = left->is_valid();
result.first &= valid.first;
result.second.first = std::min(result.second.first, valid.second.first);
result.second.second = std::min(result.second.second, valid.second.second);
if (!result.first) { return result; }
if (valid.second.second >= center) {
result.first = false;
return result;
}
}
if (right) {
auto valid = right->is_valid();
result.first &= valid.first;
result.second.first = std::min(result.second.first, valid.second.first);
result.second.second = std::min(result.second.second, valid.second.second);
if (!result.first) { return result; }
if (valid.second.first <= center) {
result.first = false;
return result;
}
}
if (!std::is_sorted(intervals.begin(), intervals.end(), IntervalStartCmp())) {
result.first = false;
}
return result;
}
void clear() {
left.reset();
right.reset();
intervals.clear();
center = 0;
}
private:
interval_vector intervals;
std::unique_ptr<IntervalTree> left;
std::unique_ptr<IntervalTree> right;
Scalar center;
};
#ifdef USE_INTERVAL_TREE_NAMESPACE
}
#endif
#endif

View File

@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.16)
project(LLVMDemangle)
set(CMAKE_CXX_STANDARD 17)
add_library(LLVMDemangle STATIC
source/Demangle.cpp
source/DLangDemangle.cpp
source/ItaniumDemangle.cpp
source/MicrosoftDemangle.cpp
source/MicrosoftDemangleNodes.cpp
source/RustDemangle.cpp
)
target_include_directories(LLVMDemangle PUBLIC include)

279
lib/external/llvm-demangle/LICENSE.TXT vendored Normal file
View File

@@ -0,0 +1,279 @@
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

View File

@@ -31,7 +31,6 @@ enum : int {
char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n,
int *status);
enum MSDemangleFlags {
MSDF_None = 0,
MSDF_DumpBackrefs = 1 << 0,
@@ -39,6 +38,7 @@ enum MSDemangleFlags {
MSDF_NoCallingConvention = 1 << 2,
MSDF_NoReturnType = 1 << 3,
MSDF_NoMemberType = 1 << 4,
MSDF_NoVariableType = 1 << 5,
};
/// Demangles the Microsoft symbol pointed at by mangled_name and returns it.
@@ -53,9 +53,15 @@ enum MSDemangleFlags {
/// receives the size of the demangled string on output if n_buf is not nullptr.
/// status receives one of the demangle_ enum entries above if it's not nullptr.
/// Flags controls various details of the demangled representation.
char *microsoftDemangle(const char *mangled_name, size_t *n_read,
char *buf, size_t *n_buf,
int *status, MSDemangleFlags Flags = MSDF_None);
char *microsoftDemangle(const char *mangled_name, size_t *n_read, char *buf,
size_t *n_buf, int *status,
MSDemangleFlags Flags = MSDF_None);
// Demangles a Rust v0 mangled symbol.
char *rustDemangle(const char *MangledName);
// Demangles a D mangled symbol.
char *dlangDemangle(const char *MangledName);
/// Attempt to demangle a string using different demangling schemes.
/// The function uses heuristics to determine which demangling scheme to use.
@@ -64,6 +70,8 @@ char *microsoftDemangle(const char *mangled_name, size_t *n_read,
/// demangling occurred.
std::string demangle(const std::string &MangledName);
bool nonMicrosoftDemangle(const char *MangledName, std::string &Result);
/// "Partial" demangler. This supports demangling a string into an AST
/// (typically an intermediate stage in itaniumDemangle) and querying certain
/// properties or partially printing the demangled name.
@@ -115,6 +123,7 @@ struct ItaniumPartialDemangler {
bool isSpecialName() const;
~ItaniumPartialDemangler();
private:
void *RootNode;
void *Context;

View File

@@ -12,8 +12,8 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEMANGLE_COMPILER_H
#define LLVM_DEMANGLE_COMPILER_H
#ifndef LLVM_DEMANGLE_DEMANGLECONFIG_H
#define LLVM_DEMANGLE_DEMANGLECONFIG_H
#ifndef __has_feature
#define __has_feature(x) 0

View File

@@ -0,0 +1,95 @@
//===--- ItaniumNodes.def ------------*- mode:c++;eval:(read-only-mode) -*-===//
// Do not edit! See README.txt.
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Define the demangler's node names
#ifndef NODE
#error Define NODE to handle nodes
#endif
NODE(NodeArrayNode)
NODE(DotSuffix)
NODE(VendorExtQualType)
NODE(QualType)
NODE(ConversionOperatorType)
NODE(PostfixQualifiedType)
NODE(ElaboratedTypeSpefType)
NODE(NameType)
NODE(AbiTagAttr)
NODE(EnableIfAttr)
NODE(ObjCProtoName)
NODE(PointerType)
NODE(ReferenceType)
NODE(PointerToMemberType)
NODE(ArrayType)
NODE(FunctionType)
NODE(NoexceptSpec)
NODE(DynamicExceptionSpec)
NODE(FunctionEncoding)
NODE(LiteralOperator)
NODE(SpecialName)
NODE(CtorVtableSpecialName)
NODE(QualifiedName)
NODE(NestedName)
NODE(LocalName)
NODE(ModuleName)
NODE(ModuleEntity)
NODE(VectorType)
NODE(PixelVectorType)
NODE(BinaryFPType)
NODE(BitIntType)
NODE(SyntheticTemplateParamName)
NODE(TypeTemplateParamDecl)
NODE(NonTypeTemplateParamDecl)
NODE(TemplateTemplateParamDecl)
NODE(TemplateParamPackDecl)
NODE(ParameterPack)
NODE(TemplateArgumentPack)
NODE(ParameterPackExpansion)
NODE(TemplateArgs)
NODE(ForwardTemplateReference)
NODE(NameWithTemplateArgs)
NODE(GlobalQualifiedName)
NODE(ExpandedSpecialSubstitution)
NODE(SpecialSubstitution)
NODE(CtorDtorName)
NODE(DtorName)
NODE(UnnamedTypeName)
NODE(ClosureTypeName)
NODE(StructuredBindingName)
NODE(BinaryExpr)
NODE(ArraySubscriptExpr)
NODE(PostfixExpr)
NODE(ConditionalExpr)
NODE(MemberExpr)
NODE(SubobjectExpr)
NODE(EnclosingExpr)
NODE(CastExpr)
NODE(SizeofParamPackExpr)
NODE(CallExpr)
NODE(NewExpr)
NODE(DeleteExpr)
NODE(PrefixExpr)
NODE(FunctionParam)
NODE(ConversionExpr)
NODE(PointerToMemberConversionExpr)
NODE(InitListExpr)
NODE(FoldExpr)
NODE(ThrowExpr)
NODE(BoolExpr)
NODE(StringLiteral)
NODE(LambdaExpr)
NODE(EnumLiteral)
NODE(IntegerLiteral)
NODE(FloatLiteral)
NODE(DoubleLiteral)
NODE(LongDoubleLiteral)
NODE(BracedExpr)
NODE(BracedRangeExpr)
#undef NODE

View File

@@ -6,13 +6,11 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEMANGLE_MICROSOFT_DEMANGLE_H
#define LLVM_DEMANGLE_MICROSOFT_DEMANGLE_H
#ifndef LLVM_DEMANGLE_MICROSOFTDEMANGLE_H
#define LLVM_DEMANGLE_MICROSOFTDEMANGLE_H
#include "llvm/Demangle/DemangleConfig.h"
#include "llvm/Demangle/MicrosoftDemangleNodes.h"
#include "llvm/Demangle/StringView.h"
#include "llvm/Demangle/Utility.h"
#include <utility>
@@ -275,4 +273,4 @@ private:
} // namespace ms_demangle
} // namespace llvm
#endif // LLVM_DEMANGLE_MICROSOFT_DEMANGLE_H
#endif // LLVM_DEMANGLE_MICROSOFTDEMANGLE_H

View File

@@ -10,10 +10,9 @@
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_MICROSOFTDEMANGLENODES_H
#define LLVM_SUPPORT_MICROSOFTDEMANGLENODES_H
#ifndef LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H
#define LLVM_DEMANGLE_MICROSOFTDEMANGLENODES_H
#include "llvm/Demangle/DemangleConfig.h"
#include "llvm/Demangle/StringView.h"
#include <array>
#include <cstdint>
@@ -21,11 +20,11 @@
namespace llvm {
namespace itanium_demangle {
class OutputStream;
class OutputBuffer;
}
}
using llvm::itanium_demangle::OutputStream;
using llvm::itanium_demangle::OutputBuffer;
using llvm::itanium_demangle::StringView;
namespace llvm {
@@ -67,6 +66,8 @@ enum class CallingConv : uint8_t {
Eabi,
Vectorcall,
Regcall,
Swift, // Clang-only
SwiftAsync, // Clang-only
};
enum class ReferenceKind : uint8_t { None, LValueRef, RValueRef };
@@ -78,6 +79,7 @@ enum OutputFlags {
OF_NoAccessSpecifier = 4,
OF_NoMemberType = 8,
OF_NoReturnType = 16,
OF_NoVariableType = 32,
};
// Types
@@ -259,7 +261,7 @@ struct Node {
NodeKind kind() const { return Kind; }
virtual void output(OutputStream &OS, OutputFlags Flags) const = 0;
virtual void output(OutputBuffer &OB, OutputFlags Flags) const = 0;
std::string toString(OutputFlags Flags = OF_Default) const;
@@ -280,9 +282,7 @@ struct StructorIdentifierNode;
struct ThunkSignatureNode;
struct PointerTypeNode;
struct ArrayTypeNode;
struct CustomNode;
struct TagTypeNode;
struct IntrinsicTypeNode;
struct NodeArrayNode;
struct QualifiedNameNode;
struct TemplateParameterReferenceNode;
@@ -298,12 +298,12 @@ struct SpecialTableSymbolNode;
struct TypeNode : public Node {
explicit TypeNode(NodeKind K) : Node(K) {}
virtual void outputPre(OutputStream &OS, OutputFlags Flags) const = 0;
virtual void outputPost(OutputStream &OS, OutputFlags Flags) const = 0;
virtual void outputPre(OutputBuffer &OB, OutputFlags Flags) const = 0;
virtual void outputPost(OutputBuffer &OB, OutputFlags Flags) const = 0;
void output(OutputStream &OS, OutputFlags Flags) const override {
outputPre(OS, Flags);
outputPost(OS, Flags);
void output(OutputBuffer &OB, OutputFlags Flags) const override {
outputPre(OB, Flags);
outputPost(OB, Flags);
}
Qualifiers Quals = Q_None;
@@ -313,8 +313,8 @@ struct PrimitiveTypeNode : public TypeNode {
explicit PrimitiveTypeNode(PrimitiveKind K)
: TypeNode(NodeKind::PrimitiveType), PrimKind(K) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override {}
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override {}
PrimitiveKind PrimKind;
};
@@ -323,8 +323,8 @@ struct FunctionSignatureNode : public TypeNode {
explicit FunctionSignatureNode(NodeKind K) : TypeNode(K) {}
FunctionSignatureNode() : TypeNode(NodeKind::FunctionSignature) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
// Valid if this FunctionTypeNode is the Pointee of a PointerType or
// MemberPointerType.
@@ -357,13 +357,13 @@ struct IdentifierNode : public Node {
NodeArrayNode *TemplateParams = nullptr;
protected:
void outputTemplateParameters(OutputStream &OS, OutputFlags Flags) const;
void outputTemplateParameters(OutputBuffer &OB, OutputFlags Flags) const;
};
struct VcallThunkIdentifierNode : public IdentifierNode {
VcallThunkIdentifierNode() : IdentifierNode(NodeKind::VcallThunkIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
uint64_t OffsetInVTable = 0;
};
@@ -372,7 +372,7 @@ struct DynamicStructorIdentifierNode : public IdentifierNode {
DynamicStructorIdentifierNode()
: IdentifierNode(NodeKind::DynamicStructorIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
VariableSymbolNode *Variable = nullptr;
QualifiedNameNode *Name = nullptr;
@@ -382,7 +382,7 @@ struct DynamicStructorIdentifierNode : public IdentifierNode {
struct NamedIdentifierNode : public IdentifierNode {
NamedIdentifierNode() : IdentifierNode(NodeKind::NamedIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
StringView Name;
};
@@ -392,7 +392,7 @@ struct IntrinsicFunctionIdentifierNode : public IdentifierNode {
: IdentifierNode(NodeKind::IntrinsicFunctionIdentifier),
Operator(Operator) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
IntrinsicFunctionKind Operator;
};
@@ -401,7 +401,7 @@ struct LiteralOperatorIdentifierNode : public IdentifierNode {
LiteralOperatorIdentifierNode()
: IdentifierNode(NodeKind::LiteralOperatorIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
StringView Name;
};
@@ -410,7 +410,7 @@ struct LocalStaticGuardIdentifierNode : public IdentifierNode {
LocalStaticGuardIdentifierNode()
: IdentifierNode(NodeKind::LocalStaticGuardIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
bool IsThread = false;
uint32_t ScopeIndex = 0;
@@ -420,7 +420,7 @@ struct ConversionOperatorIdentifierNode : public IdentifierNode {
ConversionOperatorIdentifierNode()
: IdentifierNode(NodeKind::ConversionOperatorIdentifier) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
// The type that this operator converts too.
TypeNode *TargetType = nullptr;
@@ -432,7 +432,7 @@ struct StructorIdentifierNode : public IdentifierNode {
: IdentifierNode(NodeKind::StructorIdentifier),
IsDestructor(IsDestructor) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
// The name of the class that this is a structor of.
IdentifierNode *Class = nullptr;
@@ -442,8 +442,8 @@ struct StructorIdentifierNode : public IdentifierNode {
struct ThunkSignatureNode : public FunctionSignatureNode {
ThunkSignatureNode() : FunctionSignatureNode(NodeKind::ThunkSignature) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
struct ThisAdjustor {
uint32_t StaticOffset = 0;
@@ -457,8 +457,8 @@ struct ThunkSignatureNode : public FunctionSignatureNode {
struct PointerTypeNode : public TypeNode {
PointerTypeNode() : TypeNode(NodeKind::PointerType) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
// Is this a pointer, reference, or rvalue-reference?
PointerAffinity Affinity = PointerAffinity::None;
@@ -474,8 +474,8 @@ struct PointerTypeNode : public TypeNode {
struct TagTypeNode : public TypeNode {
explicit TagTypeNode(TagKind Tag) : TypeNode(NodeKind::TagType), Tag(Tag) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
QualifiedNameNode *QualifiedName = nullptr;
TagKind Tag;
@@ -484,11 +484,11 @@ struct TagTypeNode : public TypeNode {
struct ArrayTypeNode : public TypeNode {
ArrayTypeNode() : TypeNode(NodeKind::ArrayType) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
void outputDimensionsImpl(OutputStream &OS, OutputFlags Flags) const;
void outputOneDimension(OutputStream &OS, OutputFlags Flags, Node *N) const;
void outputDimensionsImpl(OutputBuffer &OB, OutputFlags Flags) const;
void outputOneDimension(OutputBuffer &OB, OutputFlags Flags, Node *N) const;
// A list of array dimensions. e.g. [3,4,5] in `int Foo[3][4][5]`
NodeArrayNode *Dimensions = nullptr;
@@ -499,14 +499,14 @@ struct ArrayTypeNode : public TypeNode {
struct IntrinsicNode : public TypeNode {
IntrinsicNode() : TypeNode(NodeKind::IntrinsicType) {}
void output(OutputStream &OS, OutputFlags Flags) const override {}
void output(OutputBuffer &OB, OutputFlags Flags) const override {}
};
struct CustomTypeNode : public TypeNode {
CustomTypeNode() : TypeNode(NodeKind::Custom) {}
void outputPre(OutputStream &OS, OutputFlags Flags) const override;
void outputPost(OutputStream &OS, OutputFlags Flags) const override;
void outputPre(OutputBuffer &OB, OutputFlags Flags) const override;
void outputPost(OutputBuffer &OB, OutputFlags Flags) const override;
IdentifierNode *Identifier = nullptr;
};
@@ -514,9 +514,9 @@ struct CustomTypeNode : public TypeNode {
struct NodeArrayNode : public Node {
NodeArrayNode() : Node(NodeKind::NodeArray) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
void output(OutputStream &OS, OutputFlags Flags, StringView Separator) const;
void output(OutputBuffer &OB, OutputFlags Flags, StringView Separator) const;
Node **Nodes = nullptr;
size_t Count = 0;
@@ -525,7 +525,7 @@ struct NodeArrayNode : public Node {
struct QualifiedNameNode : public Node {
QualifiedNameNode() : Node(NodeKind::QualifiedName) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
NodeArrayNode *Components = nullptr;
@@ -539,7 +539,7 @@ struct TemplateParameterReferenceNode : public Node {
TemplateParameterReferenceNode()
: Node(NodeKind::TemplateParameterReference) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
SymbolNode *Symbol = nullptr;
@@ -554,7 +554,7 @@ struct IntegerLiteralNode : public Node {
IntegerLiteralNode(uint64_t Value, bool IsNegative)
: Node(NodeKind::IntegerLiteral), Value(Value), IsNegative(IsNegative) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
uint64_t Value = 0;
bool IsNegative = false;
@@ -564,7 +564,7 @@ struct RttiBaseClassDescriptorNode : public IdentifierNode {
RttiBaseClassDescriptorNode()
: IdentifierNode(NodeKind::RttiBaseClassDescriptor) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
uint32_t NVOffset = 0;
int32_t VBPtrOffset = 0;
@@ -574,7 +574,7 @@ struct RttiBaseClassDescriptorNode : public IdentifierNode {
struct SymbolNode : public Node {
explicit SymbolNode(NodeKind K) : Node(K) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
QualifiedNameNode *Name = nullptr;
};
@@ -582,7 +582,7 @@ struct SpecialTableSymbolNode : public SymbolNode {
explicit SpecialTableSymbolNode()
: SymbolNode(NodeKind::SpecialTableSymbol) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
QualifiedNameNode *TargetName = nullptr;
Qualifiers Quals = Qualifiers::Q_None;
};
@@ -591,7 +591,7 @@ struct LocalStaticGuardVariableNode : public SymbolNode {
LocalStaticGuardVariableNode()
: SymbolNode(NodeKind::LocalStaticGuardVariable) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
bool IsVisible = false;
};
@@ -599,7 +599,7 @@ struct LocalStaticGuardVariableNode : public SymbolNode {
struct EncodedStringLiteralNode : public SymbolNode {
EncodedStringLiteralNode() : SymbolNode(NodeKind::EncodedStringLiteral) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
StringView DecodedString;
bool IsTruncated = false;
@@ -609,7 +609,7 @@ struct EncodedStringLiteralNode : public SymbolNode {
struct VariableSymbolNode : public SymbolNode {
VariableSymbolNode() : SymbolNode(NodeKind::VariableSymbol) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
StorageClass SC = StorageClass::None;
TypeNode *Type = nullptr;
@@ -618,7 +618,7 @@ struct VariableSymbolNode : public SymbolNode {
struct FunctionSymbolNode : public SymbolNode {
FunctionSymbolNode() : SymbolNode(NodeKind::FunctionSymbol) {}
void output(OutputStream &OS, OutputFlags Flags) const override;
void output(OutputBuffer &OB, OutputFlags Flags) const override;
FunctionSignatureNode *Signature = nullptr;
};

View File

@@ -0,0 +1,61 @@
Itanium Name Demangler Library
==============================
Introduction
------------
This directory contains the generic itanium name demangler
library. The main purpose of the library is to demangle C++ symbols,
i.e. convert the string "_Z1fv" into "f()". You can also use the CRTP
base ManglingParser to perform some simple analysis on the mangled
name, or (in LLVM) use the opaque ItaniumPartialDemangler to query the
demangled AST.
Why are there multiple copies of the this library in the source tree?
---------------------------------------------------------------------
The canonical sources are in libcxxabi/src/demangle and some of the
files are copied to llvm/include/llvm/Demangle. The simple reason for
this comes from before the monorepo, and both [sub]projects need to
demangle symbols, but neither can depend on each other.
* libcxxabi needs the demangler to implement __cxa_demangle, which is
part of the itanium ABI spec.
* LLVM needs a copy for a bunch of places, and cannot rely on the
system's __cxa_demangle because it a) might not be available (i.e.,
on Windows), and b) may not be up-to-date on the latest language
features.
The copy of the demangler in LLVM has some extra stuff that aren't
needed in libcxxabi (ie, the MSVC demangler, ItaniumPartialDemangler),
which depend on the shared generic components. Despite these
differences, we want to keep the "core" generic demangling library
identical between both copies to simplify development and testing.
If you're working on the generic library, then do the work first in
libcxxabi, then run the cp-to-llvm.sh script in src/demangle. This
script takes as an optional argument the path to llvm, and copies the
changes you made to libcxxabi over. Note that this script just
blindly overwrites all changes to the generic library in llvm, so be
careful.
Because the core demangler needs to work in libcxxabi, everything
needs to be declared in an anonymous namespace (see
DEMANGLE_NAMESPACE_BEGIN), and you can't introduce any code that
depends on the libcxx dylib.
FIXME: Now that LLVM is a monorepo, it should be possible to
de-duplicate this code, and have both LLVM and libcxxabi depend on a
shared demangler library.
Testing
-------
The tests are split up between libcxxabi/test/{unit,}test_demangle.cpp, and
llvm/unittest/Demangle. The llvm directory should only get tests for stuff not
included in the core library. In the future though, we should probably move all
the tests to LLVM.
It is also a really good idea to run libFuzzer after non-trivial changes, see
libcxxabi/fuzz/cxa_demangle_fuzzer.cpp and https://llvm.org/docs/LibFuzzer.html.

View File

@@ -1,5 +1,5 @@
//===--- StringView.h -------------------------------------------*- C++ -*-===//
//
//===--- StringView.h ----------------*- mode:c++;eval:(read-only-mode) -*-===//
// Do not edit! See README.txt.
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
@@ -7,14 +7,16 @@
//===----------------------------------------------------------------------===//
//
// FIXME: Use std::string_view instead when we support C++17.
// There are two copies of this file in the source tree. The one under
// libcxxabi is the original and the one under llvm is the copy. Use
// cp-to-llvm.sh to update the copy. See README.txt for more details.
//
//===----------------------------------------------------------------------===//
#ifndef DEMANGLE_STRINGVIEW_H
#define DEMANGLE_STRINGVIEW_H
#ifndef LLVM_DEMANGLE_STRINGVIEW_H
#define LLVM_DEMANGLE_STRINGVIEW_H
#include "DemangleConfig.h"
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -36,29 +38,23 @@ public:
StringView(const char *Str) : First(Str), Last(Str + std::strlen(Str)) {}
StringView() : First(nullptr), Last(nullptr) {}
StringView substr(size_t From) const {
return StringView(begin() + From, size() - From);
StringView substr(size_t Pos, size_t Len = npos) const {
assert(Pos <= size());
if (Len > size() - Pos)
Len = size() - Pos;
return StringView(begin() + Pos, Len);
}
size_t find(char C, size_t From = 0) const {
size_t FindBegin = std::min(From, size());
// Avoid calling memchr with nullptr.
if (FindBegin < size()) {
if (From < size()) {
// Just forward to memchr, which is faster than a hand-rolled loop.
if (const void *P = ::memchr(First + FindBegin, C, size() - FindBegin))
if (const void *P = ::memchr(First + From, C, size() - From))
return size_t(static_cast<const char *>(P) - First);
}
return npos;
}
StringView substr(size_t From, size_t To) const {
if (To >= size())
To = size() - 1;
if (From >= size())
From = size() - 1;
return StringView(First + From, First + To);
}
StringView dropFront(size_t N = 1) const {
if (N >= size())
N = size();
@@ -105,7 +101,7 @@ public:
bool startsWith(StringView Str) const {
if (Str.size() > size())
return false;
return std::equal(Str.begin(), Str.end(), begin());
return std::strncmp(Str.begin(), begin(), Str.size()) == 0;
}
const char &operator[](size_t Idx) const { return *(begin() + Idx); }
@@ -118,7 +114,7 @@ public:
inline bool operator==(const StringView &LHS, const StringView &RHS) {
return LHS.size() == RHS.size() &&
std::equal(LHS.begin(), LHS.end(), RHS.begin());
std::strncmp(LHS.begin(), RHS.begin(), LHS.size()) == 0;
}
DEMANGLE_NAMESPACE_END

View File

@@ -0,0 +1,218 @@
//===--- Utility.h -------------------*- mode:c++;eval:(read-only-mode) -*-===//
// Do not edit! See README.txt.
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Provide some utility classes for use in the demangler.
// There are two copies of this file in the source tree. The one in libcxxabi
// is the original and the one in llvm is the copy. Use cp-to-llvm.sh to update
// the copy. See README.txt for more details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_DEMANGLE_UTILITY_H
#define LLVM_DEMANGLE_UTILITY_H
#include "StringView.h"
#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <limits>
DEMANGLE_NAMESPACE_BEGIN
// Stream that AST nodes write their string representation into after the AST
// has been parsed.
class OutputBuffer {
char *Buffer = nullptr;
size_t CurrentPosition = 0;
size_t BufferCapacity = 0;
// Ensure there are at least N more positions in the buffer.
void grow(size_t N) {
size_t Need = N + CurrentPosition;
if (Need > BufferCapacity) {
// Reduce the number of reallocations, with a bit of hysteresis. The
// number here is chosen so the first allocation will more-than-likely not
// allocate more than 1K.
Need += 1024 - 32;
BufferCapacity *= 2;
if (BufferCapacity < Need)
BufferCapacity = Need;
Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
if (Buffer == nullptr)
std::terminate();
}
}
OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) {
std::array<char, 21> Temp;
char *TempPtr = Temp.data() + Temp.size();
// Output at least one character.
do {
*--TempPtr = char('0' + N % 10);
N /= 10;
} while (N);
// Add negative sign.
if (isNeg)
*--TempPtr = '-';
return operator+=(StringView(TempPtr, Temp.data() + Temp.size()));
}
public:
OutputBuffer(char *StartBuf, size_t Size)
: Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
OutputBuffer() = default;
// Non-copyable
OutputBuffer(const OutputBuffer &) = delete;
OutputBuffer &operator=(const OutputBuffer &) = delete;
operator StringView() const { return StringView(Buffer, CurrentPosition); }
void reset(char *Buffer_, size_t BufferCapacity_) {
CurrentPosition = 0;
Buffer = Buffer_;
BufferCapacity = BufferCapacity_;
}
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
/// into the pack that we're currently printing.
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
/// When zero, we're printing template args and '>' needs to be parenthesized.
/// Use a counter so we can simply increment inside parentheses.
unsigned GtIsGt = 1;
bool isGtInsideTemplateArgs() const { return GtIsGt == 0; }
void printOpen(char Open = '(') {
GtIsGt++;
*this += Open;
}
void printClose(char Close = ')') {
GtIsGt--;
*this += Close;
}
OutputBuffer &operator+=(StringView R) {
if (size_t Size = R.size()) {
grow(Size);
std::memcpy(Buffer + CurrentPosition, R.begin(), Size);
CurrentPosition += Size;
}
return *this;
}
OutputBuffer &operator+=(char C) {
grow(1);
Buffer[CurrentPosition++] = C;
return *this;
}
OutputBuffer &prepend(StringView R) {
size_t Size = R.size();
grow(Size);
std::memmove(Buffer + Size, Buffer, CurrentPosition);
std::memcpy(Buffer, R.begin(), Size);
CurrentPosition += Size;
return *this;
}
OutputBuffer &operator<<(StringView R) { return (*this += R); }
OutputBuffer &operator<<(char C) { return (*this += C); }
OutputBuffer &operator<<(long long N) {
return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0);
}
OutputBuffer &operator<<(unsigned long long N) {
return writeUnsigned(N, false);
}
OutputBuffer &operator<<(long N) {
return this->operator<<(static_cast<long long>(N));
}
OutputBuffer &operator<<(unsigned long N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
OutputBuffer &operator<<(int N) {
return this->operator<<(static_cast<long long>(N));
}
OutputBuffer &operator<<(unsigned int N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
void insert(size_t Pos, const char *S, size_t N) {
assert(Pos <= CurrentPosition);
if (N == 0)
return;
grow(N);
std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos);
std::memcpy(Buffer + Pos, S, N);
CurrentPosition += N;
}
size_t getCurrentPosition() const { return CurrentPosition; }
void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
char back() const {
assert(CurrentPosition);
return Buffer[CurrentPosition - 1];
}
bool empty() const { return CurrentPosition == 0; }
char *getBuffer() { return Buffer; }
char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
size_t getBufferCapacity() const { return BufferCapacity; }
};
template <class T> class ScopedOverride {
T &Loc;
T Original;
public:
ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {}
ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) {
Loc_ = std::move(NewVal);
}
~ScopedOverride() { Loc = std::move(Original); }
ScopedOverride(const ScopedOverride &) = delete;
ScopedOverride &operator=(const ScopedOverride &) = delete;
};
inline bool initializeOutputBuffer(char *Buf, size_t *N, OutputBuffer &OB,
size_t InitSize) {
size_t BufferSize;
if (Buf == nullptr) {
Buf = static_cast<char *>(std::malloc(InitSize));
if (Buf == nullptr)
return false;
BufferSize = InitSize;
} else
BufferSize = *N;
OB.reset(Buf, BufferSize);
return true;
}
DEMANGLE_NAMESPACE_END
#endif

View File

@@ -0,0 +1,578 @@
//===--- DLangDemangle.cpp ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines a demangler for the D programming language as specified
/// in the ABI specification, available at:
/// https://dlang.org/spec/abi.html#name_mangling
///
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/Demangle.h"
#include "llvm/Demangle/StringView.h"
#include "llvm/Demangle/Utility.h"
#include <cctype>
#include <cstring>
#include <limits>
using namespace llvm;
using llvm::itanium_demangle::OutputBuffer;
using llvm::itanium_demangle::StringView;
namespace {
/// Demangle information structure.
struct Demangler {
/// Initialize the information structure we use to pass around information.
///
/// \param Mangled String to demangle.
Demangler(const char *Mangled);
/// Extract and demangle the mangled symbol and append it to the output
/// string.
///
/// \param Demangled Output buffer to write the demangled name.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#name_mangling .
/// \see https://dlang.org/spec/abi.html#MangledName .
const char *parseMangle(OutputBuffer *Demangled);
private:
/// Extract and demangle a given mangled symbol and append it to the output
/// string.
///
/// \param Demangled output buffer to write the demangled name.
/// \param Mangled mangled symbol to be demangled.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#name_mangling .
/// \see https://dlang.org/spec/abi.html#MangledName .
const char *parseMangle(OutputBuffer *Demangled, const char *Mangled);
/// Extract the number from a given string.
///
/// \param Mangled string to extract the number.
/// \param Ret assigned result value.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \note A result larger than UINT_MAX is considered a failure.
///
/// \see https://dlang.org/spec/abi.html#Number .
const char *decodeNumber(const char *Mangled, unsigned long &Ret);
/// Extract the back reference position from a given string.
///
/// \param Mangled string to extract the back reference position.
/// \param Ret assigned result value.
///
/// \return the remaining string on success or nullptr on failure.
///
/// \note Ret is always >= 0 on success, and unspecified on failure
///
/// \see https://dlang.org/spec/abi.html#back_ref .
/// \see https://dlang.org/spec/abi.html#NumberBackRef .
const char *decodeBackrefPos(const char *Mangled, long &Ret);
/// Extract the symbol pointed by the back reference form a given string.
///
/// \param Mangled string to extract the back reference position.
/// \param Ret assigned result value.
///
/// \return the remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#back_ref .
const char *decodeBackref(const char *Mangled, const char *&Ret);
/// Extract and demangle backreferenced symbol from a given mangled symbol
/// and append it to the output string.
///
/// \param Demangled output buffer to write the demangled name.
/// \param Mangled mangled symbol to be demangled.
///
/// \return the remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#back_ref .
/// \see https://dlang.org/spec/abi.html#IdentifierBackRef .
const char *parseSymbolBackref(OutputBuffer *Demangled, const char *Mangled);
/// Extract and demangle backreferenced type from a given mangled symbol
/// and append it to the output string.
///
/// \param Mangled mangled symbol to be demangled.
///
/// \return the remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#back_ref .
/// \see https://dlang.org/spec/abi.html#TypeBackRef .
const char *parseTypeBackref(const char *Mangled);
/// Check whether it is the beginning of a symbol name.
///
/// \param Mangled string to extract the symbol name.
///
/// \return true on success, false otherwise.
///
/// \see https://dlang.org/spec/abi.html#SymbolName .
bool isSymbolName(const char *Mangled);
/// Extract and demangle an identifier from a given mangled symbol append it
/// to the output string.
///
/// \param Demangled Output buffer to write the demangled name.
/// \param Mangled Mangled symbol to be demangled.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#SymbolName .
const char *parseIdentifier(OutputBuffer *Demangled, const char *Mangled);
/// Extract and demangle the plain identifier from a given mangled symbol and
/// prepend/append it to the output string, with a special treatment for some
/// magic compiler generated symbols.
///
/// \param Demangled Output buffer to write the demangled name.
/// \param Mangled Mangled symbol to be demangled.
/// \param Len Length of the mangled symbol name.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#LName .
const char *parseLName(OutputBuffer *Demangled, const char *Mangled,
unsigned long Len);
/// Extract and demangle the qualified symbol from a given mangled symbol
/// append it to the output string.
///
/// \param Demangled Output buffer to write the demangled name.
/// \param Mangled Mangled symbol to be demangled.
///
/// \return The remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#QualifiedName .
const char *parseQualified(OutputBuffer *Demangled, const char *Mangled);
/// Extract and demangle a type from a given mangled symbol append it to
/// the output string.
///
/// \param Mangled mangled symbol to be demangled.
///
/// \return the remaining string on success or nullptr on failure.
///
/// \see https://dlang.org/spec/abi.html#Type .
const char *parseType(const char *Mangled);
/// The string we are demangling.
const char *Str;
/// The index of the last back reference.
int LastBackref;
};
} // namespace
const char *Demangler::decodeNumber(const char *Mangled, unsigned long &Ret) {
// Return nullptr if trying to extract something that isn't a digit.
if (Mangled == nullptr || !std::isdigit(*Mangled))
return nullptr;
unsigned long Val = 0;
do {
unsigned long Digit = Mangled[0] - '0';
// Check for overflow.
if (Val > (std::numeric_limits<unsigned int>::max() - Digit) / 10)
return nullptr;
Val = Val * 10 + Digit;
++Mangled;
} while (std::isdigit(*Mangled));
if (*Mangled == '\0')
return nullptr;
Ret = Val;
return Mangled;
}
const char *Demangler::decodeBackrefPos(const char *Mangled, long &Ret) {
// Return nullptr if trying to extract something that isn't a digit
if (Mangled == nullptr || !std::isalpha(*Mangled))
return nullptr;
// Any identifier or non-basic type that has been emitted to the mangled
// symbol before will not be emitted again, but is referenced by a special
// sequence encoding the relative position of the original occurrence in the
// mangled symbol name.
// Numbers in back references are encoded with base 26 by upper case letters
// A-Z for higher digits but lower case letters a-z for the last digit.
// NumberBackRef:
// [a-z]
// [A-Z] NumberBackRef
// ^
unsigned long Val = 0;
while (std::isalpha(*Mangled)) {
// Check for overflow
if (Val > (std::numeric_limits<unsigned long>::max() - 25) / 26)
break;
Val *= 26;
if (Mangled[0] >= 'a' && Mangled[0] <= 'z') {
Val += Mangled[0] - 'a';
if ((long)Val <= 0)
break;
Ret = Val;
return Mangled + 1;
}
Val += Mangled[0] - 'A';
++Mangled;
}
return nullptr;
}
const char *Demangler::decodeBackref(const char *Mangled, const char *&Ret) {
assert(Mangled != nullptr && *Mangled == 'Q' && "Invalid back reference!");
Ret = nullptr;
// Position of 'Q'
const char *Qpos = Mangled;
long RefPos;
++Mangled;
Mangled = decodeBackrefPos(Mangled, RefPos);
if (Mangled == nullptr)
return nullptr;
if (RefPos > Qpos - Str)
return nullptr;
// Set the position of the back reference.
Ret = Qpos - RefPos;
return Mangled;
}
const char *Demangler::parseSymbolBackref(OutputBuffer *Demangled,
const char *Mangled) {
// An identifier back reference always points to a digit 0 to 9.
// IdentifierBackRef:
// Q NumberBackRef
// ^
const char *Backref;
unsigned long Len;
// Get position of the back reference
Mangled = decodeBackref(Mangled, Backref);
// Must point to a simple identifier
Backref = decodeNumber(Backref, Len);
if (Backref == nullptr || strlen(Backref) < Len)
return nullptr;
Backref = parseLName(Demangled, Backref, Len);
if (Backref == nullptr)
return nullptr;
return Mangled;
}
const char *Demangler::parseTypeBackref(const char *Mangled) {
// A type back reference always points to a letter.
// TypeBackRef:
// Q NumberBackRef
// ^
const char *Backref;
// If we appear to be moving backwards through the mangle string, then
// bail as this may be a recursive back reference.
if (Mangled - Str >= LastBackref)
return nullptr;
int SaveRefPos = LastBackref;
LastBackref = Mangled - Str;
// Get position of the back reference.
Mangled = decodeBackref(Mangled, Backref);
// Can't decode back reference.
if (Backref == nullptr)
return nullptr;
// TODO: Add support for function type back references.
Backref = parseType(Backref);
LastBackref = SaveRefPos;
if (Backref == nullptr)
return nullptr;
return Mangled;
}
bool Demangler::isSymbolName(const char *Mangled) {
long Ret;
const char *Qref = Mangled;
if (std::isdigit(*Mangled))
return true;
// TODO: Handle template instances.
if (*Mangled != 'Q')
return false;
Mangled = decodeBackrefPos(Mangled + 1, Ret);
if (Mangled == nullptr || Ret > Qref - Str)
return false;
return std::isdigit(Qref[-Ret]);
}
const char *Demangler::parseMangle(OutputBuffer *Demangled,
const char *Mangled) {
// A D mangled symbol is comprised of both scope and type information.
// MangleName:
// _D QualifiedName Type
// _D QualifiedName Z
// ^
// The caller should have guaranteed that the start pointer is at the
// above location.
// Note that type is never a function type, but only the return type of
// a function or the type of a variable.
Mangled += 2;
Mangled = parseQualified(Demangled, Mangled);
if (Mangled != nullptr) {
// Artificial symbols end with 'Z' and have no type.
if (*Mangled == 'Z')
++Mangled;
else {
Mangled = parseType(Mangled);
}
}
return Mangled;
}
const char *Demangler::parseQualified(OutputBuffer *Demangled,
const char *Mangled) {
// Qualified names are identifiers separated by their encoded length.
// Nested functions also encode their argument types without specifying
// what they return.
// QualifiedName:
// SymbolFunctionName
// SymbolFunctionName QualifiedName
// ^
// SymbolFunctionName:
// SymbolName
// SymbolName TypeFunctionNoReturn
// SymbolName M TypeFunctionNoReturn
// SymbolName M TypeModifiers TypeFunctionNoReturn
// The start pointer should be at the above location.
// Whether it has more than one symbol
size_t NotFirst = false;
do {
// Skip over anonymous symbols.
if (*Mangled == '0') {
do
++Mangled;
while (*Mangled == '0');
continue;
}
if (NotFirst)
*Demangled << '.';
NotFirst = true;
Mangled = parseIdentifier(Demangled, Mangled);
} while (Mangled && isSymbolName(Mangled));
return Mangled;
}
const char *Demangler::parseIdentifier(OutputBuffer *Demangled,
const char *Mangled) {
unsigned long Len;
if (Mangled == nullptr || *Mangled == '\0')
return nullptr;
if (*Mangled == 'Q')
return parseSymbolBackref(Demangled, Mangled);
// TODO: Parse lengthless template instances.
const char *Endptr = decodeNumber(Mangled, Len);
if (Endptr == nullptr || Len == 0)
return nullptr;
if (strlen(Endptr) < Len)
return nullptr;
Mangled = Endptr;
// TODO: Parse template instances with a length prefix.
// There can be multiple different declarations in the same function that
// have the same mangled name. To make the mangled names unique, a fake
// parent in the form `__Sddd' is added to the symbol.
if (Len >= 4 && Mangled[0] == '_' && Mangled[1] == '_' && Mangled[2] == 'S') {
const char *NumPtr = Mangled + 3;
while (NumPtr < (Mangled + Len) && std::isdigit(*NumPtr))
++NumPtr;
if (Mangled + Len == NumPtr) {
// Skip over the fake parent.
Mangled += Len;
return parseIdentifier(Demangled, Mangled);
}
// Else demangle it as a plain identifier.
}
return parseLName(Demangled, Mangled, Len);
}
const char *Demangler::parseType(const char *Mangled) {
if (*Mangled == '\0')
return nullptr;
switch (*Mangled) {
// TODO: Parse type qualifiers.
// TODO: Parse function types.
// TODO: Parse compound types.
// TODO: Parse delegate types.
// TODO: Parse tuple types.
// Basic types.
case 'i':
++Mangled;
// TODO: Add type name dumping
return Mangled;
// TODO: Add support for the rest of the basic types.
// Back referenced type.
case 'Q':
return parseTypeBackref(Mangled);
default: // unhandled.
return nullptr;
}
}
const char *Demangler::parseLName(OutputBuffer *Demangled, const char *Mangled,
unsigned long Len) {
switch (Len) {
case 6:
if (strncmp(Mangled, "__initZ", Len + 1) == 0) {
// The static initializer for a given symbol.
Demangled->prepend("initializer for ");
Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);
Mangled += Len;
return Mangled;
}
if (strncmp(Mangled, "__vtblZ", Len + 1) == 0) {
// The vtable symbol for a given class.
Demangled->prepend("vtable for ");
Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);
Mangled += Len;
return Mangled;
}
break;
case 7:
if (strncmp(Mangled, "__ClassZ", Len + 1) == 0) {
// The classinfo symbol for a given class.
Demangled->prepend("ClassInfo for ");
Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);
Mangled += Len;
return Mangled;
}
break;
case 11:
if (strncmp(Mangled, "__InterfaceZ", Len + 1) == 0) {
// The interface symbol for a given class.
Demangled->prepend("Interface for ");
Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);
Mangled += Len;
return Mangled;
}
break;
case 12:
if (strncmp(Mangled, "__ModuleInfoZ", Len + 1) == 0) {
// The ModuleInfo symbol for a given module.
Demangled->prepend("ModuleInfo for ");
Demangled->setCurrentPosition(Demangled->getCurrentPosition() - 1);
Mangled += Len;
return Mangled;
}
break;
}
*Demangled << StringView(Mangled, Len);
Mangled += Len;
return Mangled;
}
Demangler::Demangler(const char *Mangled)
: Str(Mangled), LastBackref(strlen(Mangled)) {}
const char *Demangler::parseMangle(OutputBuffer *Demangled) {
return parseMangle(Demangled, this->Str);
}
char *llvm::dlangDemangle(const char *MangledName) {
if (MangledName == nullptr || strncmp(MangledName, "_D", 2) != 0)
return nullptr;
OutputBuffer Demangled;
if (!initializeOutputBuffer(nullptr, nullptr, Demangled, 1024))
return nullptr;
if (strcmp(MangledName, "_Dmain") == 0) {
Demangled << "D main";
} else {
Demangler D = Demangler(MangledName);
MangledName = D.parseMangle(&Demangled);
// Check that the entire symbol was successfully demangled.
if (MangledName == nullptr || *MangledName != '\0') {
std::free(Demangled.getBuffer());
return nullptr;
}
}
// OutputBuffer's internal buffer is not null terminated and therefore we need
// to add it to comply with C null terminated strings.
if (Demangled.getCurrentPosition() > 0) {
Demangled << '\0';
Demangled.setCurrentPosition(Demangled.getCurrentPosition() - 1);
return Demangled.getBuffer();
}
std::free(Demangled.getBuffer());
return nullptr;
}

View File

@@ -0,0 +1,64 @@
//===-- Demangle.cpp - Common demangling functions ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file This file contains definitions of common demangling functions.
///
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/Demangle.h"
#include <cstdlib>
#include <cstring>
static bool isItaniumEncoding(const char *S) {
// Itanium encoding requires 1 or 3 leading underscores, followed by 'Z'.
return std::strncmp(S, "_Z", 2) == 0 || std::strncmp(S, "___Z", 4) == 0;
}
static bool isRustEncoding(const char *S) { return S[0] == '_' && S[1] == 'R'; }
static bool isDLangEncoding(const std::string &MangledName) {
return MangledName.size() >= 2 && MangledName[0] == '_' &&
MangledName[1] == 'D';
}
std::string llvm::demangle(const std::string &MangledName) {
std::string Result;
const char *S = MangledName.c_str();
if (nonMicrosoftDemangle(S, Result))
return Result;
if (S[0] == '_' && nonMicrosoftDemangle(S + 1, Result))
return Result;
if (char *Demangled =
microsoftDemangle(S, nullptr, nullptr, nullptr, nullptr)) {
Result = Demangled;
std::free(Demangled);
return Result;
}
return MangledName;
}
bool llvm::nonMicrosoftDemangle(const char *MangledName, std::string &Result) {
char *Demangled = nullptr;
if (isItaniumEncoding(MangledName))
Demangled = itaniumDemangle(MangledName, nullptr, nullptr, nullptr);
else if (isRustEncoding(MangledName))
Demangled = rustDemangle(MangledName);
else if (isDLangEncoding(MangledName))
Demangled = dlangDemangle(MangledName);
if (!Demangled)
return false;
Result = Demangled;
std::free(Demangled);
return true;
}

View File

@@ -19,9 +19,7 @@
#include <cstdlib>
#include <cstring>
#include <functional>
#include <numeric>
#include <utility>
#include <vector>
using namespace llvm;
using namespace llvm::itanium_demangle;
@@ -174,6 +172,50 @@ struct DumpVisitor {
return printStr("TemplateParamKind::Template");
}
}
void print(Node::Prec P) {
switch (P) {
case Node::Prec::Primary:
return printStr("Node::Prec::Primary");
case Node::Prec::Postfix:
return printStr("Node::Prec::Postfix");
case Node::Prec::Unary:
return printStr("Node::Prec::Unary");
case Node::Prec::Cast:
return printStr("Node::Prec::Cast");
case Node::Prec::PtrMem:
return printStr("Node::Prec::PtrMem");
case Node::Prec::Multiplicative:
return printStr("Node::Prec::Multiplicative");
case Node::Prec::Additive:
return printStr("Node::Prec::Additive");
case Node::Prec::Shift:
return printStr("Node::Prec::Shift");
case Node::Prec::Spaceship:
return printStr("Node::Prec::Spaceship");
case Node::Prec::Relational:
return printStr("Node::Prec::Relational");
case Node::Prec::Equality:
return printStr("Node::Prec::Equality");
case Node::Prec::And:
return printStr("Node::Prec::And");
case Node::Prec::Xor:
return printStr("Node::Prec::Xor");
case Node::Prec::Ior:
return printStr("Node::Prec::Ior");
case Node::Prec::AndIf:
return printStr("Node::Prec::AndIf");
case Node::Prec::OrIf:
return printStr("Node::Prec::OrIf");
case Node::Prec::Conditional:
return printStr("Node::Prec::Conditional");
case Node::Prec::Assign:
return printStr("Node::Prec::Assign");
case Node::Prec::Comma:
return printStr("Node::Prec::Comma");
case Node::Prec::Default:
return printStr("Node::Prec::Default");
}
}
void newLine() {
printStr("\n");
@@ -333,21 +375,21 @@ char *llvm::itaniumDemangle(const char *MangledName, char *Buf,
int InternalStatus = demangle_success;
Demangler Parser(MangledName, MangledName + std::strlen(MangledName));
OutputStream S;
OutputBuffer OB;
Node *AST = Parser.parse();
if (AST == nullptr)
InternalStatus = demangle_invalid_mangled_name;
else if (!initializeOutputStream(Buf, N, S, 1024))
else if (!initializeOutputBuffer(Buf, N, OB, 1024))
InternalStatus = demangle_memory_alloc_failure;
else {
assert(Parser.ForwardTemplateRefs.empty());
AST->print(S);
S += '\0';
AST->print(OB);
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
Buf = S.getBuffer();
*N = OB.getCurrentPosition();
Buf = OB.getBuffer();
}
if (Status)
@@ -385,14 +427,14 @@ bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) {
}
static char *printNode(const Node *RootNode, char *Buf, size_t *N) {
OutputStream S;
if (!initializeOutputStream(Buf, N, S, 128))
OutputBuffer OB;
if (!initializeOutputBuffer(Buf, N, OB, 128))
return nullptr;
RootNode->print(S);
S += '\0';
RootNode->print(OB);
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
return S.getBuffer();
*N = OB.getCurrentPosition();
return OB.getBuffer();
}
char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {
@@ -406,8 +448,8 @@ char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const {
case Node::KAbiTagAttr:
Name = static_cast<const AbiTagAttr *>(Name)->Base;
continue;
case Node::KStdQualifiedName:
Name = static_cast<const StdQualifiedName *>(Name)->Child;
case Node::KModuleEntity:
Name = static_cast<const ModuleEntity *>(Name)->Name;
continue;
case Node::KNestedName:
Name = static_cast<const NestedName *>(Name)->Name;
@@ -430,8 +472,8 @@ char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,
return nullptr;
const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName();
OutputStream S;
if (!initializeOutputStream(Buf, N, S, 128))
OutputBuffer OB;
if (!initializeOutputBuffer(Buf, N, OB, 128))
return nullptr;
KeepGoingLocalFunction:
@@ -447,27 +489,27 @@ char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf,
break;
}
if (Name->getKind() == Node::KModuleEntity)
Name = static_cast<const ModuleEntity *>(Name)->Name;
switch (Name->getKind()) {
case Node::KStdQualifiedName:
S += "std";
break;
case Node::KNestedName:
static_cast<const NestedName *>(Name)->Qual->print(S);
static_cast<const NestedName *>(Name)->Qual->print(OB);
break;
case Node::KLocalName: {
auto *LN = static_cast<const LocalName *>(Name);
LN->Encoding->print(S);
S += "::";
LN->Encoding->print(OB);
OB += "::";
Name = LN->Entity;
goto KeepGoingLocalFunction;
}
default:
break;
}
S += '\0';
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
return S.getBuffer();
*N = OB.getCurrentPosition();
return OB.getBuffer();
}
char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const {
@@ -483,17 +525,17 @@ char *ItaniumPartialDemangler::getFunctionParameters(char *Buf,
return nullptr;
NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams();
OutputStream S;
if (!initializeOutputStream(Buf, N, S, 128))
OutputBuffer OB;
if (!initializeOutputBuffer(Buf, N, OB, 128))
return nullptr;
S += '(';
Params.printWithComma(S);
S += ')';
S += '\0';
OB += '(';
Params.printWithComma(OB);
OB += ')';
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
return S.getBuffer();
*N = OB.getCurrentPosition();
return OB.getBuffer();
}
char *ItaniumPartialDemangler::getFunctionReturnType(
@@ -501,18 +543,18 @@ char *ItaniumPartialDemangler::getFunctionReturnType(
if (!isFunction())
return nullptr;
OutputStream S;
if (!initializeOutputStream(Buf, N, S, 128))
OutputBuffer OB;
if (!initializeOutputBuffer(Buf, N, OB, 128))
return nullptr;
if (const Node *Ret =
static_cast<const FunctionEncoding *>(RootNode)->getReturnType())
Ret->print(S);
Ret->print(OB);
S += '\0';
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
return S.getBuffer();
*N = OB.getCurrentPosition();
return OB.getBuffer();
}
char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const {
@@ -552,8 +594,8 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const {
case Node::KNestedName:
N = static_cast<const NestedName *>(N)->Name;
break;
case Node::KStdQualifiedName:
N = static_cast<const StdQualifiedName *>(N)->Child;
case Node::KModuleEntity:
N = static_cast<const ModuleEntity *>(N)->Name;
break;
}
}

View File

@@ -245,8 +245,8 @@ demanglePointerCVQualifiers(StringView &MangledName) {
}
StringView Demangler::copyString(StringView Borrowed) {
char *Stable = Arena.allocUnalignedBuffer(Borrowed.size() + 1);
std::strcpy(Stable, Borrowed.begin());
char *Stable = Arena.allocUnalignedBuffer(Borrowed.size());
std::memcpy(Stable, Borrowed.begin(), Borrowed.size());
return {Stable, Borrowed.size()};
}
@@ -823,11 +823,15 @@ SymbolNode *Demangler::parse(StringView &MangledName) {
}
TagTypeNode *Demangler::parseTagUniqueName(StringView &MangledName) {
if (!MangledName.consumeFront(".?A"))
if (!MangledName.consumeFront(".?A")) {
Error = true;
return nullptr;
}
MangledName.consumeFront(".?A");
if (MangledName.empty())
if (MangledName.empty()) {
Error = true;
return nullptr;
}
return demangleClassType(MangledName);
}
@@ -965,17 +969,14 @@ NamedIdentifierNode *Demangler::demangleBackRefName(StringView &MangledName) {
void Demangler::memorizeIdentifier(IdentifierNode *Identifier) {
// Render this class template name into a string buffer so that we can
// memorize it for the purpose of back-referencing.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
OutputBuffer OB;
if (!initializeOutputBuffer(nullptr, nullptr, OB, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
Identifier->output(OS, OF_Default);
OS << '\0';
char *Name = OS.getBuffer();
StringView Owned = copyString(Name);
Identifier->output(OB, OF_Default);
StringView Owned = copyString(OB);
memorizeString(Owned);
std::free(Name);
std::free(OB.getBuffer());
}
IdentifierNode *
@@ -1107,7 +1108,7 @@ static void writeHexDigit(char *Buffer, uint8_t Digit) {
*Buffer = (Digit < 10) ? ('0' + Digit) : ('A' + Digit - 10);
}
static void outputHex(OutputStream &OS, unsigned C) {
static void outputHex(OutputBuffer &OB, unsigned C) {
assert (C != 0);
// It's easier to do the math if we can work from right to left, but we need
@@ -1130,43 +1131,43 @@ static void outputHex(OutputStream &OS, unsigned C) {
TempBuffer[Pos--] = 'x';
assert(Pos >= 0);
TempBuffer[Pos--] = '\\';
OS << StringView(&TempBuffer[Pos + 1]);
OB << StringView(&TempBuffer[Pos + 1]);
}
static void outputEscapedChar(OutputStream &OS, unsigned C) {
static void outputEscapedChar(OutputBuffer &OB, unsigned C) {
switch (C) {
case '\0': // nul
OS << "\\0";
OB << "\\0";
return;
case '\'': // single quote
OS << "\\\'";
OB << "\\\'";
return;
case '\"': // double quote
OS << "\\\"";
OB << "\\\"";
return;
case '\\': // backslash
OS << "\\\\";
OB << "\\\\";
return;
case '\a': // bell
OS << "\\a";
OB << "\\a";
return;
case '\b': // backspace
OS << "\\b";
OB << "\\b";
return;
case '\f': // form feed
OS << "\\f";
OB << "\\f";
return;
case '\n': // new line
OS << "\\n";
OB << "\\n";
return;
case '\r': // carriage return
OS << "\\r";
OB << "\\r";
return;
case '\t': // tab
OS << "\\t";
OB << "\\t";
return;
case '\v': // vertical tab
OS << "\\v";
OB << "\\v";
return;
default:
break;
@@ -1174,11 +1175,11 @@ static void outputEscapedChar(OutputStream &OS, unsigned C) {
if (C > 0x1F && C < 0x7F) {
// Standard ascii char.
OS << (char)C;
OB << (char)C;
return;
}
outputHex(OS, C);
outputHex(OB, C);
}
static unsigned countTrailingNullBytes(const uint8_t *StringBytes, int Length) {
@@ -1273,18 +1274,17 @@ FunctionSymbolNode *Demangler::demangleVcallThunkNode(StringView &MangledName) {
EncodedStringLiteralNode *
Demangler::demangleStringLiteral(StringView &MangledName) {
// This function uses goto, so declare all variables up front.
OutputStream OS;
OutputBuffer OB;
StringView CRC;
uint64_t StringByteSize;
bool IsWcharT = false;
bool IsNegative = false;
size_t CrcEndPos = 0;
char *ResultBuffer = nullptr;
EncodedStringLiteralNode *Result = Arena.alloc<EncodedStringLiteralNode>();
// Must happen before the first `goto StringLiteralError`.
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
if (!initializeOutputBuffer(nullptr, nullptr, OB, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
@@ -1329,7 +1329,7 @@ Demangler::demangleStringLiteral(StringView &MangledName) {
goto StringLiteralError;
wchar_t W = demangleWcharLiteral(MangledName);
if (StringByteSize != 2 || Result->IsTruncated)
outputEscapedChar(OS, W);
outputEscapedChar(OB, W);
StringByteSize -= 2;
if (Error)
goto StringLiteralError;
@@ -1371,19 +1371,17 @@ Demangler::demangleStringLiteral(StringView &MangledName) {
unsigned NextChar =
decodeMultiByteChar(StringBytes, CharIndex, CharBytes);
if (CharIndex + 1 < NumChars || Result->IsTruncated)
outputEscapedChar(OS, NextChar);
outputEscapedChar(OB, NextChar);
}
}
OS << '\0';
ResultBuffer = OS.getBuffer();
Result->DecodedString = copyString(ResultBuffer);
std::free(ResultBuffer);
Result->DecodedString = copyString(OB);
std::free(OB.getBuffer());
return Result;
StringLiteralError:
Error = true;
std::free(OS.getBuffer());
std::free(OB.getBuffer());
return nullptr;
}
@@ -1447,18 +1445,17 @@ Demangler::demangleLocallyScopedNamePiece(StringView &MangledName) {
return nullptr;
// Render the parent symbol's name into a buffer.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
OutputBuffer OB;
if (!initializeOutputBuffer(nullptr, nullptr, OB, 1024))
// FIXME: Propagate out-of-memory as an error?
std::terminate();
OS << '`';
Scope->output(OS, OF_Default);
OS << '\'';
OS << "::`" << Number << "'";
OS << '\0';
char *Result = OS.getBuffer();
Identifier->Name = copyString(Result);
std::free(Result);
OB << '`';
Scope->output(OB, OF_Default);
OB << '\'';
OB << "::`" << Number << "'";
Identifier->Name = copyString(OB);
std::free(OB.getBuffer());
return Identifier;
}
@@ -1711,6 +1708,10 @@ CallingConv Demangler::demangleCallingConvention(StringView &MangledName) {
return CallingConv::Eabi;
case 'Q':
return CallingConv::Vectorcall;
case 'S':
return CallingConv::Swift;
case 'W':
return CallingConv::SwiftAsync;
}
return CallingConv::None;
@@ -2309,19 +2310,19 @@ void Demangler::dumpBackReferences() {
(int)Backrefs.FunctionParamCount);
// Create an output stream so we can render each type.
OutputStream OS;
if (!initializeOutputStream(nullptr, nullptr, OS, 1024))
OutputBuffer OB;
if (!initializeOutputBuffer(nullptr, nullptr, OB, 1024))
std::terminate();
for (size_t I = 0; I < Backrefs.FunctionParamCount; ++I) {
OS.setCurrentPosition(0);
OB.setCurrentPosition(0);
TypeNode *T = Backrefs.FunctionParams[I];
T->output(OS, OF_Default);
T->output(OB, OF_Default);
std::printf(" [%d] - %.*s\n", (int)I, (int)OS.getCurrentPosition(),
OS.getBuffer());
StringView B = OB;
std::printf(" [%d] - %.*s\n", (int)I, (int)B.size(), B.begin());
}
std::free(OS.getBuffer());
std::free(OB.getBuffer());
if (Backrefs.FunctionParamCount > 0)
std::printf("\n");
@@ -2338,7 +2339,7 @@ char *llvm::microsoftDemangle(const char *MangledName, size_t *NMangled,
char *Buf, size_t *N,
int *Status, MSDemangleFlags Flags) {
Demangler D;
OutputStream S;
OutputBuffer OB;
StringView Name{MangledName};
SymbolNode *AST = D.parse(Name);
@@ -2357,18 +2358,20 @@ char *llvm::microsoftDemangle(const char *MangledName, size_t *NMangled,
OF = OutputFlags(OF | OF_NoReturnType);
if (Flags & MSDF_NoMemberType)
OF = OutputFlags(OF | OF_NoMemberType);
if (Flags & MSDF_NoVariableType)
OF = OutputFlags(OF | OF_NoVariableType);
int InternalStatus = demangle_success;
if (D.Error)
InternalStatus = demangle_invalid_mangled_name;
else if (!initializeOutputStream(Buf, N, S, 1024))
else if (!initializeOutputBuffer(Buf, N, OB, 1024))
InternalStatus = demangle_memory_alloc_failure;
else {
AST->output(S, OF);
S += '\0';
AST->output(OB, OF);
OB += '\0';
if (N != nullptr)
*N = S.getCurrentPosition();
Buf = S.getBuffer();
*N = OB.getCurrentPosition();
Buf = OB.getBuffer();
}
if (Status)

View File

@@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/MicrosoftDemangleNodes.h"
#include "llvm/Demangle/DemangleConfig.h"
#include "llvm/Demangle/Utility.h"
#include <cctype>
#include <string>
@@ -21,91 +20,97 @@ using namespace ms_demangle;
#define OUTPUT_ENUM_CLASS_VALUE(Enum, Value, Desc) \
case Enum::Value: \
OS << Desc; \
OB << Desc; \
break;
// Writes a space if the last token does not end with a punctuation.
static void outputSpaceIfNecessary(OutputStream &OS) {
if (OS.empty())
static void outputSpaceIfNecessary(OutputBuffer &OB) {
if (OB.empty())
return;
char C = OS.back();
char C = OB.back();
if (std::isalnum(C) || C == '>')
OS << " ";
OB << " ";
}
static void outputSingleQualifier(OutputStream &OS, Qualifiers Q) {
static void outputSingleQualifier(OutputBuffer &OB, Qualifiers Q) {
switch (Q) {
case Q_Const:
OS << "const";
OB << "const";
break;
case Q_Volatile:
OS << "volatile";
OB << "volatile";
break;
case Q_Restrict:
OS << "__restrict";
OB << "__restrict";
break;
default:
break;
}
}
static bool outputQualifierIfPresent(OutputStream &OS, Qualifiers Q,
static bool outputQualifierIfPresent(OutputBuffer &OB, Qualifiers Q,
Qualifiers Mask, bool NeedSpace) {
if (!(Q & Mask))
return NeedSpace;
if (NeedSpace)
OS << " ";
OB << " ";
outputSingleQualifier(OS, Mask);
outputSingleQualifier(OB, Mask);
return true;
}
static void outputQualifiers(OutputStream &OS, Qualifiers Q, bool SpaceBefore,
static void outputQualifiers(OutputBuffer &OB, Qualifiers Q, bool SpaceBefore,
bool SpaceAfter) {
if (Q == Q_None)
return;
size_t Pos1 = OS.getCurrentPosition();
SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Const, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Volatile, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OS, Q, Q_Restrict, SpaceBefore);
size_t Pos2 = OS.getCurrentPosition();
size_t Pos1 = OB.getCurrentPosition();
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Const, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Volatile, SpaceBefore);
SpaceBefore = outputQualifierIfPresent(OB, Q, Q_Restrict, SpaceBefore);
size_t Pos2 = OB.getCurrentPosition();
if (SpaceAfter && Pos2 > Pos1)
OS << " ";
OB << " ";
}
static void outputCallingConvention(OutputStream &OS, CallingConv CC) {
outputSpaceIfNecessary(OS);
static void outputCallingConvention(OutputBuffer &OB, CallingConv CC) {
outputSpaceIfNecessary(OB);
switch (CC) {
case CallingConv::Cdecl:
OS << "__cdecl";
OB << "__cdecl";
break;
case CallingConv::Fastcall:
OS << "__fastcall";
OB << "__fastcall";
break;
case CallingConv::Pascal:
OS << "__pascal";
OB << "__pascal";
break;
case CallingConv::Regcall:
OS << "__regcall";
OB << "__regcall";
break;
case CallingConv::Stdcall:
OS << "__stdcall";
OB << "__stdcall";
break;
case CallingConv::Thiscall:
OS << "__thiscall";
OB << "__thiscall";
break;
case CallingConv::Eabi:
OS << "__eabi";
OB << "__eabi";
break;
case CallingConv::Vectorcall:
OS << "__vectorcall";
OB << "__vectorcall";
break;
case CallingConv::Clrcall:
OS << "__clrcall";
OB << "__clrcall";
break;
case CallingConv::Swift:
OB << "__attribute__((__swiftcall__)) ";
break;
case CallingConv::SwiftAsync:
OB << "__attribute__((__swiftasynccall__)) ";
break;
default:
break;
@@ -113,14 +118,16 @@ static void outputCallingConvention(OutputStream &OS, CallingConv CC) {
}
std::string Node::toString(OutputFlags Flags) const {
OutputStream OS;
initializeOutputStream(nullptr, nullptr, OS, 1024);
this->output(OS, Flags);
OS << '\0';
return {OS.getBuffer()};
OutputBuffer OB;
initializeOutputBuffer(nullptr, nullptr, OB, 1024);
this->output(OB, Flags);
StringView SV = OB;
std::string Owned(SV.begin(), SV.end());
std::free(OB.getBuffer());
return Owned;
}
void PrimitiveTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
void PrimitiveTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
switch (PrimKind) {
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Void, "void");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Bool, "bool");
@@ -144,107 +151,107 @@ void PrimitiveTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Ldouble, "long double");
OUTPUT_ENUM_CLASS_VALUE(PrimitiveKind, Nullptr, "std::nullptr_t");
}
outputQualifiers(OS, Quals, true, false);
outputQualifiers(OB, Quals, true, false);
}
void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags) const {
output(OS, Flags, ", ");
void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags) const {
output(OB, Flags, ", ");
}
void NodeArrayNode::output(OutputStream &OS, OutputFlags Flags,
void NodeArrayNode::output(OutputBuffer &OB, OutputFlags Flags,
StringView Separator) const {
if (Count == 0)
return;
if (Nodes[0])
Nodes[0]->output(OS, Flags);
Nodes[0]->output(OB, Flags);
for (size_t I = 1; I < Count; ++I) {
OS << Separator;
Nodes[I]->output(OS, Flags);
OB << Separator;
Nodes[I]->output(OB, Flags);
}
}
void EncodedStringLiteralNode::output(OutputStream &OS,
void EncodedStringLiteralNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
switch (Char) {
case CharKind::Wchar:
OS << "L\"";
OB << "L\"";
break;
case CharKind::Char:
OS << "\"";
OB << "\"";
break;
case CharKind::Char16:
OS << "u\"";
OB << "u\"";
break;
case CharKind::Char32:
OS << "U\"";
OB << "U\"";
break;
}
OS << DecodedString << "\"";
OB << DecodedString << "\"";
if (IsTruncated)
OS << "...";
OB << "...";
}
void IntegerLiteralNode::output(OutputStream &OS, OutputFlags Flags) const {
void IntegerLiteralNode::output(OutputBuffer &OB, OutputFlags Flags) const {
if (IsNegative)
OS << '-';
OS << Value;
OB << '-';
OB << Value;
}
void TemplateParameterReferenceNode::output(OutputStream &OS,
void TemplateParameterReferenceNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (ThunkOffsetCount > 0)
OS << "{";
OB << "{";
else if (Affinity == PointerAffinity::Pointer)
OS << "&";
OB << "&";
if (Symbol) {
Symbol->output(OS, Flags);
Symbol->output(OB, Flags);
if (ThunkOffsetCount > 0)
OS << ", ";
OB << ", ";
}
if (ThunkOffsetCount > 0)
OS << ThunkOffsets[0];
OB << ThunkOffsets[0];
for (int I = 1; I < ThunkOffsetCount; ++I) {
OS << ", " << ThunkOffsets[I];
OB << ", " << ThunkOffsets[I];
}
if (ThunkOffsetCount > 0)
OS << "}";
OB << "}";
}
void IdentifierNode::outputTemplateParameters(OutputStream &OS,
void IdentifierNode::outputTemplateParameters(OutputBuffer &OB,
OutputFlags Flags) const {
if (!TemplateParams)
return;
OS << "<";
TemplateParams->output(OS, Flags);
OS << ">";
OB << "<";
TemplateParams->output(OB, Flags);
OB << ">";
}
void DynamicStructorIdentifierNode::output(OutputStream &OS,
void DynamicStructorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (IsDestructor)
OS << "`dynamic atexit destructor for ";
OB << "`dynamic atexit destructor for ";
else
OS << "`dynamic initializer for ";
OB << "`dynamic initializer for ";
if (Variable) {
OS << "`";
Variable->output(OS, Flags);
OS << "''";
OB << "`";
Variable->output(OB, Flags);
OB << "''";
} else {
OS << "'";
Name->output(OS, Flags);
OS << "''";
OB << "'";
Name->output(OB, Flags);
OB << "''";
}
}
void NamedIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const {
OS << Name;
outputTemplateParameters(OS, Flags);
void NamedIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
OB << Name;
outputTemplateParameters(OB, Flags);
}
void IntrinsicFunctionIdentifierNode::output(OutputStream &OS,
void IntrinsicFunctionIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
switch (Operator) {
OUTPUT_ENUM_CLASS_VALUE(IntrinsicFunctionKind, New, "operator new");
@@ -342,188 +349,188 @@ void IntrinsicFunctionIdentifierNode::output(OutputStream &OS,
case IntrinsicFunctionKind::None:
break;
}
outputTemplateParameters(OS, Flags);
outputTemplateParameters(OB, Flags);
}
void LocalStaticGuardIdentifierNode::output(OutputStream &OS,
void LocalStaticGuardIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
if (IsThread)
OS << "`local static thread guard'";
OB << "`local static thread guard'";
else
OS << "`local static guard'";
OB << "`local static guard'";
if (ScopeIndex > 0)
OS << "{" << ScopeIndex << "}";
OB << "{" << ScopeIndex << "}";
}
void ConversionOperatorIdentifierNode::output(OutputStream &OS,
void ConversionOperatorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OS << "operator";
outputTemplateParameters(OS, Flags);
OS << " ";
TargetType->output(OS, Flags);
OB << "operator";
outputTemplateParameters(OB, Flags);
OB << " ";
TargetType->output(OB, Flags);
}
void StructorIdentifierNode::output(OutputStream &OS, OutputFlags Flags) const {
void StructorIdentifierNode::output(OutputBuffer &OB, OutputFlags Flags) const {
if (IsDestructor)
OS << "~";
Class->output(OS, Flags);
outputTemplateParameters(OS, Flags);
OB << "~";
Class->output(OB, Flags);
outputTemplateParameters(OB, Flags);
}
void LiteralOperatorIdentifierNode::output(OutputStream &OS,
void LiteralOperatorIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OS << "operator \"\"" << Name;
outputTemplateParameters(OS, Flags);
OB << "operator \"\"" << Name;
outputTemplateParameters(OB, Flags);
}
void FunctionSignatureNode::outputPre(OutputStream &OS,
void FunctionSignatureNode::outputPre(OutputBuffer &OB,
OutputFlags Flags) const {
if (!(Flags & OF_NoAccessSpecifier)) {
if (FunctionClass & FC_Public)
OS << "public: ";
OB << "public: ";
if (FunctionClass & FC_Protected)
OS << "protected: ";
OB << "protected: ";
if (FunctionClass & FC_Private)
OS << "private: ";
OB << "private: ";
}
if (!(Flags & OF_NoMemberType)) {
if (!(FunctionClass & FC_Global)) {
if (FunctionClass & FC_Static)
OS << "static ";
OB << "static ";
}
if (FunctionClass & FC_Virtual)
OS << "virtual ";
OB << "virtual ";
if (FunctionClass & FC_ExternC)
OS << "extern \"C\" ";
OB << "extern \"C\" ";
}
if (!(Flags & OF_NoReturnType) && ReturnType) {
ReturnType->outputPre(OS, Flags);
OS << " ";
ReturnType->outputPre(OB, Flags);
OB << " ";
}
if (!(Flags & OF_NoCallingConvention))
outputCallingConvention(OS, CallConvention);
outputCallingConvention(OB, CallConvention);
}
void FunctionSignatureNode::outputPost(OutputStream &OS,
void FunctionSignatureNode::outputPost(OutputBuffer &OB,
OutputFlags Flags) const {
if (!(FunctionClass & FC_NoParameterList)) {
OS << "(";
OB << "(";
if (Params)
Params->output(OS, Flags);
Params->output(OB, Flags);
else
OS << "void";
OB << "void";
if (IsVariadic) {
if (OS.back() != '(')
OS << ", ";
OS << "...";
if (OB.back() != '(')
OB << ", ";
OB << "...";
}
OS << ")";
OB << ")";
}
if (Quals & Q_Const)
OS << " const";
OB << " const";
if (Quals & Q_Volatile)
OS << " volatile";
OB << " volatile";
if (Quals & Q_Restrict)
OS << " __restrict";
OB << " __restrict";
if (Quals & Q_Unaligned)
OS << " __unaligned";
OB << " __unaligned";
if (IsNoexcept)
OS << " noexcept";
OB << " noexcept";
if (RefQualifier == FunctionRefQualifier::Reference)
OS << " &";
OB << " &";
else if (RefQualifier == FunctionRefQualifier::RValueReference)
OS << " &&";
OB << " &&";
if (!(Flags & OF_NoReturnType) && ReturnType)
ReturnType->outputPost(OS, Flags);
ReturnType->outputPost(OB, Flags);
}
void ThunkSignatureNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
OS << "[thunk]: ";
void ThunkSignatureNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
OB << "[thunk]: ";
FunctionSignatureNode::outputPre(OS, Flags);
FunctionSignatureNode::outputPre(OB, Flags);
}
void ThunkSignatureNode::outputPost(OutputStream &OS, OutputFlags Flags) const {
void ThunkSignatureNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
if (FunctionClass & FC_StaticThisAdjust) {
OS << "`adjustor{" << ThisAdjust.StaticOffset << "}'";
OB << "`adjustor{" << ThisAdjust.StaticOffset << "}'";
} else if (FunctionClass & FC_VirtualThisAdjust) {
if (FunctionClass & FC_VirtualThisAdjustEx) {
OS << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", "
OB << "`vtordispex{" << ThisAdjust.VBPtrOffset << ", "
<< ThisAdjust.VBOffsetOffset << ", " << ThisAdjust.VtordispOffset
<< ", " << ThisAdjust.StaticOffset << "}'";
} else {
OS << "`vtordisp{" << ThisAdjust.VtordispOffset << ", "
OB << "`vtordisp{" << ThisAdjust.VtordispOffset << ", "
<< ThisAdjust.StaticOffset << "}'";
}
}
FunctionSignatureNode::outputPost(OS, Flags);
FunctionSignatureNode::outputPost(OB, Flags);
}
void PointerTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
void PointerTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
if (Pointee->kind() == NodeKind::FunctionSignature) {
// If this is a pointer to a function, don't output the calling convention.
// It needs to go inside the parentheses.
const FunctionSignatureNode *Sig =
static_cast<const FunctionSignatureNode *>(Pointee);
Sig->outputPre(OS, OF_NoCallingConvention);
Sig->outputPre(OB, OF_NoCallingConvention);
} else
Pointee->outputPre(OS, Flags);
Pointee->outputPre(OB, Flags);
outputSpaceIfNecessary(OS);
outputSpaceIfNecessary(OB);
if (Quals & Q_Unaligned)
OS << "__unaligned ";
OB << "__unaligned ";
if (Pointee->kind() == NodeKind::ArrayType) {
OS << "(";
OB << "(";
} else if (Pointee->kind() == NodeKind::FunctionSignature) {
OS << "(";
OB << "(";
const FunctionSignatureNode *Sig =
static_cast<const FunctionSignatureNode *>(Pointee);
outputCallingConvention(OS, Sig->CallConvention);
OS << " ";
outputCallingConvention(OB, Sig->CallConvention);
OB << " ";
}
if (ClassParent) {
ClassParent->output(OS, Flags);
OS << "::";
ClassParent->output(OB, Flags);
OB << "::";
}
switch (Affinity) {
case PointerAffinity::Pointer:
OS << "*";
OB << "*";
break;
case PointerAffinity::Reference:
OS << "&";
OB << "&";
break;
case PointerAffinity::RValueReference:
OS << "&&";
OB << "&&";
break;
default:
assert(false);
}
outputQualifiers(OS, Quals, false, false);
outputQualifiers(OB, Quals, false, false);
}
void PointerTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {
void PointerTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
if (Pointee->kind() == NodeKind::ArrayType ||
Pointee->kind() == NodeKind::FunctionSignature)
OS << ")";
OB << ")";
Pointee->outputPost(OS, Flags);
Pointee->outputPost(OB, Flags);
}
void TagTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
void TagTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
if (!(Flags & OF_NoTagSpecifier)) {
switch (Tag) {
OUTPUT_ENUM_CLASS_VALUE(TagKind, Class, "class");
@@ -531,59 +538,59 @@ void TagTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
OUTPUT_ENUM_CLASS_VALUE(TagKind, Union, "union");
OUTPUT_ENUM_CLASS_VALUE(TagKind, Enum, "enum");
}
OS << " ";
OB << " ";
}
QualifiedName->output(OS, Flags);
outputQualifiers(OS, Quals, true, false);
QualifiedName->output(OB, Flags);
outputQualifiers(OB, Quals, true, false);
}
void TagTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {}
void TagTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
void ArrayTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
ElementType->outputPre(OS, Flags);
outputQualifiers(OS, Quals, true, false);
void ArrayTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
ElementType->outputPre(OB, Flags);
outputQualifiers(OB, Quals, true, false);
}
void ArrayTypeNode::outputOneDimension(OutputStream &OS, OutputFlags Flags,
void ArrayTypeNode::outputOneDimension(OutputBuffer &OB, OutputFlags Flags,
Node *N) const {
assert(N->kind() == NodeKind::IntegerLiteral);
IntegerLiteralNode *ILN = static_cast<IntegerLiteralNode *>(N);
if (ILN->Value != 0)
ILN->output(OS, Flags);
ILN->output(OB, Flags);
}
void ArrayTypeNode::outputDimensionsImpl(OutputStream &OS,
void ArrayTypeNode::outputDimensionsImpl(OutputBuffer &OB,
OutputFlags Flags) const {
if (Dimensions->Count == 0)
return;
outputOneDimension(OS, Flags, Dimensions->Nodes[0]);
outputOneDimension(OB, Flags, Dimensions->Nodes[0]);
for (size_t I = 1; I < Dimensions->Count; ++I) {
OS << "][";
outputOneDimension(OS, Flags, Dimensions->Nodes[I]);
OB << "][";
outputOneDimension(OB, Flags, Dimensions->Nodes[I]);
}
}
void ArrayTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {
OS << "[";
outputDimensionsImpl(OS, Flags);
OS << "]";
void ArrayTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {
OB << "[";
outputDimensionsImpl(OB, Flags);
OB << "]";
ElementType->outputPost(OS, Flags);
ElementType->outputPost(OB, Flags);
}
void SymbolNode::output(OutputStream &OS, OutputFlags Flags) const {
Name->output(OS, Flags);
void SymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Name->output(OB, Flags);
}
void FunctionSymbolNode::output(OutputStream &OS, OutputFlags Flags) const {
Signature->outputPre(OS, Flags);
outputSpaceIfNecessary(OS);
Name->output(OS, Flags);
Signature->outputPost(OS, Flags);
void FunctionSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Signature->outputPre(OB, Flags);
outputSpaceIfNecessary(OB);
Name->output(OB, Flags);
Signature->outputPost(OB, Flags);
}
void VariableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const {
void VariableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
const char *AccessSpec = nullptr;
bool IsStatic = true;
switch (SC) {
@@ -601,53 +608,52 @@ void VariableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const {
break;
}
if (!(Flags & OF_NoAccessSpecifier) && AccessSpec)
OS << AccessSpec << ": ";
OB << AccessSpec << ": ";
if (!(Flags & OF_NoMemberType) && IsStatic)
OS << "static ";
OB << "static ";
if (Type) {
Type->outputPre(OS, Flags);
outputSpaceIfNecessary(OS);
if (!(Flags & OF_NoVariableType) && Type) {
Type->outputPre(OB, Flags);
outputSpaceIfNecessary(OB);
}
Name->output(OS, Flags);
if (Type)
Type->outputPost(OS, Flags);
Name->output(OB, Flags);
if (!(Flags & OF_NoVariableType) && Type)
Type->outputPost(OB, Flags);
}
void CustomTypeNode::outputPre(OutputStream &OS, OutputFlags Flags) const {
Identifier->output(OS, Flags);
void CustomTypeNode::outputPre(OutputBuffer &OB, OutputFlags Flags) const {
Identifier->output(OB, Flags);
}
void CustomTypeNode::outputPost(OutputStream &OS, OutputFlags Flags) const {}
void CustomTypeNode::outputPost(OutputBuffer &OB, OutputFlags Flags) const {}
void QualifiedNameNode::output(OutputStream &OS, OutputFlags Flags) const {
Components->output(OS, Flags, "::");
void QualifiedNameNode::output(OutputBuffer &OB, OutputFlags Flags) const {
Components->output(OB, Flags, "::");
}
void RttiBaseClassDescriptorNode::output(OutputStream &OS,
void RttiBaseClassDescriptorNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OS << "`RTTI Base Class Descriptor at (";
OS << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", "
OB << "`RTTI Base Class Descriptor at (";
OB << NVOffset << ", " << VBPtrOffset << ", " << VBTableOffset << ", "
<< this->Flags;
OS << ")'";
OB << ")'";
}
void LocalStaticGuardVariableNode::output(OutputStream &OS,
void LocalStaticGuardVariableNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
Name->output(OS, Flags);
Name->output(OB, Flags);
}
void VcallThunkIdentifierNode::output(OutputStream &OS,
void VcallThunkIdentifierNode::output(OutputBuffer &OB,
OutputFlags Flags) const {
OS << "`vcall'{" << OffsetInVTable << ", {flat}}";
OB << "`vcall'{" << OffsetInVTable << ", {flat}}";
}
void SpecialTableSymbolNode::output(OutputStream &OS, OutputFlags Flags) const {
outputQualifiers(OS, Quals, false, true);
Name->output(OS, Flags);
void SpecialTableSymbolNode::output(OutputBuffer &OB, OutputFlags Flags) const {
outputQualifiers(OB, Quals, false, true);
Name->output(OB, Flags);
if (TargetName) {
OS << "{for `";
TargetName->output(OS, Flags);
OS << "'}";
OB << "{for `";
TargetName->output(OB, Flags);
OB << "'}";
}
return;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(LLVMDemangle)
set(CMAKE_CXX_STANDARD 17)
add_library(LLVMDemangle STATIC
Demangle/Demangle.cpp
Demangle/ItaniumDemangle.cpp
Demangle/MicrosoftDemangle.cpp
Demangle/MicrosoftDemangleNodes.cpp
)
target_include_directories(LLVMDemangle PUBLIC include)

View File

@@ -1,36 +0,0 @@
//===-- Demangle.cpp - Common demangling functions ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file This file contains definitions of common demangling functions.
///
//===----------------------------------------------------------------------===//
#include "llvm/Demangle/Demangle.h"
#include <cstdlib>
static bool isItaniumEncoding(const std::string &MangledName) {
size_t Pos = MangledName.find_first_not_of('_');
// A valid Itanium encoding requires 1-4 leading underscores, followed by 'Z'.
return Pos > 0 && Pos <= 4 && MangledName[Pos] == 'Z';
}
std::string llvm::demangle(const std::string &MangledName) {
char *Demangled;
if (isItaniumEncoding(MangledName))
Demangled = itaniumDemangle(MangledName.c_str(), nullptr, nullptr, nullptr);
else
Demangled = microsoftDemangle(MangledName.c_str(), nullptr, nullptr,
nullptr, nullptr);
if (!Demangled)
return MangledName;
std::string Ret = Demangled;
free(Demangled);
return Ret;
}

View File

@@ -1,191 +0,0 @@
//===--- Utility.h ----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Provide some utility classes for use in the demangler(s).
//
//===----------------------------------------------------------------------===//
#ifndef DEMANGLE_UTILITY_H
#define DEMANGLE_UTILITY_H
#include "StringView.h"
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <limits>
DEMANGLE_NAMESPACE_BEGIN
// Stream that AST nodes write their string representation into after the AST
// has been parsed.
class OutputStream {
char *Buffer = nullptr;
size_t CurrentPosition = 0;
size_t BufferCapacity = 0;
// Ensure there is at least n more positions in buffer.
void grow(size_t N) {
if (N + CurrentPosition >= BufferCapacity) {
BufferCapacity *= 2;
if (BufferCapacity < N + CurrentPosition)
BufferCapacity = N + CurrentPosition;
Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity));
if (Buffer == nullptr)
std::terminate();
}
}
void writeUnsigned(uint64_t N, bool isNeg = false) {
// Handle special case...
if (N == 0) {
*this << '0';
return;
}
char Temp[21];
char *TempPtr = std::end(Temp);
while (N) {
*--TempPtr = '0' + char(N % 10);
N /= 10;
}
// Add negative sign...
if (isNeg)
*--TempPtr = '-';
this->operator<<(StringView(TempPtr, std::end(Temp)));
}
public:
OutputStream(char *StartBuf, size_t Size)
: Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {}
OutputStream() = default;
void reset(char *Buffer_, size_t BufferCapacity_) {
CurrentPosition = 0;
Buffer = Buffer_;
BufferCapacity = BufferCapacity_;
}
/// If a ParameterPackExpansion (or similar type) is encountered, the offset
/// into the pack that we're currently printing.
unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max();
unsigned CurrentPackMax = std::numeric_limits<unsigned>::max();
OutputStream &operator+=(StringView R) {
size_t Size = R.size();
if (Size == 0)
return *this;
grow(Size);
std::memmove(Buffer + CurrentPosition, R.begin(), Size);
CurrentPosition += Size;
return *this;
}
OutputStream &operator+=(char C) {
grow(1);
Buffer[CurrentPosition++] = C;
return *this;
}
OutputStream &operator<<(StringView R) { return (*this += R); }
OutputStream &operator<<(char C) { return (*this += C); }
OutputStream &operator<<(long long N) {
if (N < 0)
writeUnsigned(static_cast<unsigned long long>(-N), true);
else
writeUnsigned(static_cast<unsigned long long>(N));
return *this;
}
OutputStream &operator<<(unsigned long long N) {
writeUnsigned(N, false);
return *this;
}
OutputStream &operator<<(long N) {
return this->operator<<(static_cast<long long>(N));
}
OutputStream &operator<<(unsigned long N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
OutputStream &operator<<(int N) {
return this->operator<<(static_cast<long long>(N));
}
OutputStream &operator<<(unsigned int N) {
return this->operator<<(static_cast<unsigned long long>(N));
}
size_t getCurrentPosition() const { return CurrentPosition; }
void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; }
char back() const {
return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0';
}
bool empty() const { return CurrentPosition == 0; }
char *getBuffer() { return Buffer; }
char *getBufferEnd() { return Buffer + CurrentPosition - 1; }
size_t getBufferCapacity() const { return BufferCapacity; }
};
template <class T> class SwapAndRestore {
T &Restore;
T OriginalValue;
bool ShouldRestore = true;
public:
SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {}
SwapAndRestore(T &Restore_, T NewVal)
: Restore(Restore_), OriginalValue(Restore) {
Restore = std::move(NewVal);
}
~SwapAndRestore() {
if (ShouldRestore)
Restore = std::move(OriginalValue);
}
void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; }
void restoreNow(bool Force) {
if (!Force && !ShouldRestore)
return;
Restore = std::move(OriginalValue);
ShouldRestore = false;
}
SwapAndRestore(const SwapAndRestore &) = delete;
SwapAndRestore &operator=(const SwapAndRestore &) = delete;
};
inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S,
size_t InitSize) {
size_t BufferSize;
if (Buf == nullptr) {
Buf = static_cast<char *>(std::malloc(InitSize));
if (Buf == nullptr)
return false;
BufferSize = InitSize;
} else
BufferSize = *N;
S.reset(Buf, BufferSize);
return true;
}
DEMANGLE_NAMESPACE_END
#endif

View File

@@ -10,16 +10,13 @@ set_target_properties(imgui PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/microtar ${CMAKE_CURRENT_BINARY_DIR}/external/microtar EXCLUDE_FROM_ALL)
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(NFD_PORTAL ON CACHE BOOL "Use Portals for Linux file dialogs" FORCE)
set(NFD_USE_ALLOWEDCONTENTTYPES OFF CACHE BOOL "Disable allowedContentTypes for macOS file dialogs" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/nativefiledialog ${CMAKE_CURRENT_BINARY_DIR}/external/nativefiledialog EXCLUDE_FROM_ALL)
set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_compile_definitions(nfd PUBLIC NFD_MACOS_ALLOWEDCONTENTTYPES=0)
set(LIBROMFS_RESOURCE_LOCATION ${IMHEX_BASE_FOLDER}/resources/romfs)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/external/libromfs EXCLUDE_FROM_ALL)
set_target_properties(libromfs PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/intervaltree ${CMAKE_CURRENT_BINARY_DIR}/external/intervaltree EXCLUDE_FROM_ALL)
set_target_properties(intervaltree PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(XDGPP_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../external/xdgpp")
set(CURL_USE_MBEDTLS ON)
set(BUILD_CURL_EXE OFF)
@@ -28,6 +25,16 @@ set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "")
# Find packages
find_package(PkgConfig REQUIRED)
if (NOT USE_SYSTEM_NFD)
set(NFD_PORTAL ON CACHE BOOL "Use Portals for Linux file dialogs" FORCE)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/nativefiledialog ${CMAKE_CURRENT_BINARY_DIR}/external/nativefiledialog EXCLUDE_FROM_ALL)
set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(NFD_LIBRARIES nfd)
else()
find_package(nfd)
set(NFD_LIBRARIES nfd)
endif()
if(NOT USE_SYSTEM_NLOHMANN_JSON)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/nlohmann_json ${CMAKE_CURRENT_BINARY_DIR}/external/nlohmann_json EXCLUDE_FROM_ALL)
set(NLOHMANN_JSON_LIBRARIES nlohmann_json)
@@ -55,7 +62,7 @@ else()
endif()
if (NOT USE_SYSTEM_LLVM)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/llvm ${CMAKE_CURRENT_BINARY_DIR}/external/llvm EXCLUDE_FROM_ALL)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/llvm-demangle ${CMAKE_CURRENT_BINARY_DIR}/external/llvm-demangle EXCLUDE_FROM_ALL)
set_target_properties(LLVMDemangle PROPERTIES POSITION_INDEPENDENT_CODE ON)
else()
find_package(LLVM REQUIRED Demangle)
@@ -112,6 +119,7 @@ set(LIBIMHEX_SOURCES
source/api/keybinding.cpp
source/api/plugin_manager.cpp
source/api/localization.cpp
source/api/project_file_manager.cpp
source/data_processor/attribute.cpp
source/data_processor/link.cpp
@@ -125,7 +133,6 @@ set(LIBIMHEX_SOURCES
source/helpers/file.cpp
source/helpers/socket.cpp
source/helpers/patches.cpp
source/helpers/project_file_handler.cpp
source/helpers/encoding_file.cpp
source/helpers/logger.cpp
source/helpers/tar.cpp
@@ -165,4 +172,4 @@ if (APPLE)
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
endif ()
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 libpl)
target_link_libraries(libimhex PUBLIC dl imgui ${NFD_LIBRARIES} magic ${CAPSTONE_LIBRARIES} LLVMDemangle microtar ${NLOHMANN_JSON_LIBRARIES} ${YARA_LIBRARIES} ${LIBCURL_LIBRARIES} ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${Python_LIBRARIES} libromfs libpl intervaltree)

View File

@@ -224,7 +224,7 @@ namespace hex {
namespace impl {
using CreatorFunction = std::function<dp::Node *()>;
using CreatorFunction = std::function<std::unique_ptr<dp::Node>()>;
struct Entry {
std::string category;
@@ -239,11 +239,15 @@ namespace hex {
template<std::derived_from<dp::Node> T, typename... Args>
void add(const std::string &unlocalizedCategory, const std::string &unlocalizedName, Args &&...args) {
add(impl::Entry { unlocalizedCategory.c_str(), unlocalizedName.c_str(), [=] {
auto node = new T(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
return node;
} });
add(impl::Entry {
unlocalizedCategory.c_str(),
unlocalizedName.c_str(),
[=] {
auto node = std::make_unique<T>(std::forward<Args>(args)...);
node->setUnlocalizedName(unlocalizedName);
return node;
}
});
}
void addSeparator();
@@ -330,20 +334,22 @@ namespace hex {
}
template<std::derived_from<hex::prv::Provider> T>
void add(const std::string &unlocalizedName, bool addToList = true) {
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = unlocalizedName](const std::string &name, hex::prv::Provider **provider) {
void add(bool addToList = true) {
auto typeName = T().getTypeName();
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = typeName](const std::string &name, bool skipLoadInterface, hex::prv::Provider **provider) {
if (name != expectedName) return;
auto newProvider = new T();
prv::Provider *newProvider = new T();
hex::ImHexApi::Provider::add(newProvider);
hex::ImHexApi::Provider::add(newProvider, skipLoadInterface);
if (provider != nullptr)
*provider = newProvider;
});
if (addToList)
impl::addProviderName(unlocalizedName);
impl::addProviderName(typeName);
}
std::vector<std::string> &getEntries();

View File

@@ -18,16 +18,12 @@
struct GLFWwindow;
namespace pl {
class Pattern;
}
namespace hex {
class EventId {
public:
explicit constexpr EventId(const char *func = __builtin_FUNCTION(), u32 line = __builtin_LINE()) {
this->m_hash = line ^ 123456789;
this->m_hash = line ^ 987654321;
for (auto c : std::string_view(func)) {
this->m_hash = (this->m_hash >> 5) | (this->m_hash << 27);
this->m_hash ^= c;
@@ -111,13 +107,14 @@ namespace hex {
EVENT_DEF(EventHighlightingChanged);
EVENT_DEF(EventWindowClosing, GLFWwindow *);
EVENT_DEF(EventRegionSelected, Region);
EVENT_DEF(EventProjectFileStore);
EVENT_DEF(EventProjectFileLoad);
EVENT_DEF(EventSettingsChanged);
EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventOSThemeChanged);
EVENT_DEF(EventProviderCreated, prv::Provider *);
EVENT_DEF(EventProviderChanged, prv::Provider *, prv::Provider *);
EVENT_DEF(EventProviderOpened, prv::Provider *);
EVENT_DEF(EventProviderClosing, prv::Provider *, bool *);
EVENT_DEF(EventProviderClosed, prv::Provider *);
EVENT_DEF(EventProviderDeleted, prv::Provider *);
EVENT_DEF(EventFrameBegin);
EVENT_DEF(EventFrameEnd);
@@ -132,7 +129,7 @@ namespace hex {
EVENT_DEF(RequestOpenFile, std::fs::path);
EVENT_DEF(RequestChangeTheme, u32);
EVENT_DEF(RequestOpenPopup, std::string);
EVENT_DEF(RequestCreateProvider, std::string, hex::prv::Provider **);
EVENT_DEF(RequestCreateProvider, std::string, bool, hex::prv::Provider **);
EVENT_DEF(RequestShowInfoPopup, std::string);
EVENT_DEF(RequestShowErrorPopup, std::string);

View File

@@ -119,6 +119,13 @@ namespace hex {
namespace Provider {
namespace impl {
void resetClosingProvider();
prv::Provider* getClosingProvider();
}
prv::Provider *get();
const std::vector<prv::Provider *> &getProviders();
@@ -126,14 +133,20 @@ namespace hex {
bool isValid();
void add(prv::Provider *provider);
void markDirty();
void resetDirty();
bool isDirty();
void add(prv::Provider *provider, bool skipLoadInterface = false);
template<std::derived_from<prv::Provider> T>
void add(auto &&...args) {
add(new T(std::forward<decltype(args)>(args)...));
}
void remove(prv::Provider *provider);
void remove(prv::Provider *provider, bool noQuestions = false);
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface = false);
}

View File

@@ -181,8 +181,8 @@ namespace hex {
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));
static constexpr auto SHIFT = Key(static_cast<Keys>(0x4000'0000));
static constexpr auto SUPER = Key(static_cast<Keys>(0x8000'0000));
class ShortcutManager {
public:

View File

@@ -0,0 +1,58 @@
#pragma once
#include <list>
#include <string>
#include <string_view>
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/concepts.hpp>
#include <hex/helpers/tar.hpp>
namespace hex {
class ProjectFile {
public:
struct Handler {
using Function = std::function<bool(const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
Function load, store;
};
struct ProviderHandler {
using Function = std::function<bool(prv::Provider *provider, const std::fs::path &, Tar &tar)>;
std::fs::path basePath;
Function load, store;
};
static bool load(const std::fs::path &filePath);
static bool store(std::optional<std::fs::path> filePath = std::nullopt);
static void registerHandler(const Handler &handler) {
getHandlers().push_back(handler);
}
static void registerPerProviderHandler(const ProviderHandler &handler) {
getProviderHandlers().push_back(handler);
}
static std::vector<Handler>& getHandlers() {
return s_handlers;
}
static std::vector<ProviderHandler>& getProviderHandlers() {
return s_providerHandlers;
}
private:
ProjectFile() = default;
static std::fs::path s_currProjectPath;
static std::vector<Handler> s_handlers;
static std::vector<ProviderHandler> s_providerHandlers;
};
}

View File

@@ -42,7 +42,10 @@ namespace hex::dp {
virtual void store(nlohmann::json &j) { hex::unused(j); }
virtual void load(nlohmann::json &j) { hex::unused(j); }
using NodeError = std::pair<Node *, std::string>;
struct NodeError {
Node *node;
std::string message;
};
void resetOutputData() {
for (auto &attribute : this->m_attributes)

View File

@@ -65,6 +65,12 @@ namespace hex::fs {
else return size;
}
static inline bool isSubPath(const std::fs::path& base, const std::fs::path& destination) {
const auto relative = std::fs::relative(destination, base).string();
return relative.size() == 1 || (relative[0] != '.' && relative[1] != '.');
}
bool isPathWritable(const std::fs::path &path);
std::fs::path toShortPath(const std::fs::path &path);
@@ -90,7 +96,8 @@ namespace hex::fs {
Resources,
Constants,
Encodings,
Logs
Logs,
Recent
};
std::optional<std::fs::path> getExecutablePath();

View File

@@ -1,105 +0,0 @@
#pragma once
#include <list>
#include <string>
#include <string_view>
#include "patches.hpp"
#include <hex/api/imhex_api.hpp>
#include <hex/api/event.hpp>
#include <hex/helpers/fs.hpp>
namespace hex {
class ProjectFile {
public:
ProjectFile() = delete;
static bool load(const std::fs::path &filePath);
static bool store(std::fs::path filePath = {});
[[nodiscard]] static bool hasUnsavedChanges() {
return ProjectFile::s_hasUnsavedChanged;
}
static void markDirty() {
bool setWindowTitle = !hasUnsavedChanges();
ProjectFile::s_hasUnsavedChanged = true;
if (setWindowTitle)
EventManager::post<RequestChangeWindowTitle>(std::fs::path(getFilePath()).filename().string());
}
[[nodiscard]] static const std::fs::path &getProjectFilePath() {
return ProjectFile::s_currProjectFilePath;
}
static void clearProjectFilePath() {
ProjectFile::s_currProjectFilePath.clear();
}
[[nodiscard]] static const std::fs::path &getFilePath() {
return ProjectFile::s_filePath;
}
static void setFilePath(const std::fs::path &filePath) {
ProjectFile::s_filePath = filePath;
EventManager::post<RequestChangeWindowTitle>(filePath.filename().string());
}
[[nodiscard]] static const std::string &getPattern() {
return ProjectFile::s_pattern;
}
static void setPattern(const std::string &pattern) {
markDirty();
ProjectFile::s_pattern = pattern;
}
[[nodiscard]] static const Patches &getPatches() {
return ProjectFile::s_patches;
}
static void setPatches(const Patches &patches) {
markDirty();
ProjectFile::s_patches = patches;
}
[[nodiscard]] static const std::list<ImHexApi::Bookmarks::Entry> &getBookmarks() {
return ProjectFile::s_bookmarks;
}
static void setBookmarks(const std::list<ImHexApi::Bookmarks::Entry> &bookmarks) {
markDirty();
ProjectFile::s_bookmarks = bookmarks;
}
[[nodiscard]] static const std::string &getDataProcessorContent() {
return ProjectFile::s_dataProcessorContent;
}
static void setDataProcessorContent(const std::string &json) {
markDirty();
ProjectFile::s_dataProcessorContent = json;
}
private:
static std::fs::path s_currProjectFilePath;
static bool s_hasUnsavedChanged;
static std::fs::path s_filePath;
static std::string s_pattern;
static Patches s_patches;
static std::list<ImHexApi::Bookmarks::Entry> s_bookmarks;
static std::string s_dataProcessorContent;
};
}

View File

@@ -19,17 +19,31 @@ namespace hex {
Tar() = default;
Tar(const std::fs::path &path, Mode mode);
~Tar();
Tar(const Tar&) = delete;
Tar(Tar&&) noexcept;
Tar &operator=(Tar &&other) noexcept;
void close();
[[nodiscard]] std::vector<u8> read(const std::fs::path &path);
[[nodiscard]] std::string readString(const std::fs::path &path);
std::vector<u8> read(const std::fs::path &path);
void write(const std::fs::path &path, const std::vector<u8> &data);
void write(const std::fs::path &path, const std::string &data);
std::vector<std::fs::path> listEntries();
[[nodiscard]] std::vector<std::fs::path> listEntries(const std::fs::path &basePath = "/");
[[nodiscard]] bool contains(const std::fs::path &path);
void extract(const std::fs::path &path, const std::fs::path &outputPath);
void extractAll(const std::fs::path &outputPath);
[[nodiscard]] bool isValid() const { return this->m_valid; }
private:
mtar_t m_ctx = { };
bool m_valid = false;
};
}

View File

@@ -21,11 +21,11 @@ namespace hex {
size_t size;
[[nodiscard]] constexpr bool isWithin(const Region &other) const {
return (this->getStartAddress() >= other.getStartAddress()) && (this->getEndAddress() <= other.getEndAddress());
return (this->getStartAddress() >= other.getStartAddress()) && (this->getEndAddress() <= other.getEndAddress()) && *this != Invalid() && other != Invalid();
}
[[nodiscard]] constexpr bool overlaps(const Region &other) const {
return (this->getEndAddress() >= other.getStartAddress()) && (this->getStartAddress() < other.getEndAddress());
return (this->getEndAddress() >= other.getStartAddress()) && (this->getStartAddress() < other.getEndAddress()) && *this != Invalid() && other != Invalid();
}
[[nodiscard]] constexpr u64 getStartAddress() const {
@@ -39,6 +39,14 @@ namespace hex {
[[nodiscard]] constexpr size_t getSize() const {
return this->size;
}
constexpr bool operator==(const Region &other) const {
return this->address == other.address && this->size == other.size;
}
constexpr static Region Invalid() {
return { 0, 0 };
}
};
}

View File

@@ -81,6 +81,19 @@ namespace hex::prv {
return copy;
}
Iterator& operator--() {
this->m_address--;
return *this;
}
Iterator operator--(int) {
auto copy = *this;
this->m_address--;
return copy;
}
Iterator& operator+=(i64 offset) {
this->m_address += offset;
@@ -101,6 +114,10 @@ namespace hex::prv {
return this->m_address;
}
void setAddress(u64 address) {
this->m_address = address;
}
difference_type operator-(const Iterator &other) const {
return this->m_address - other.m_address;
}
@@ -152,6 +169,19 @@ namespace hex::prv {
return copy;
}
ReverseIterator& operator--() {
this->m_address++;
return *this;
}
ReverseIterator operator--(int) {
auto copy = *this;
this->m_address++;
return copy;
}
ReverseIterator& operator+=(i64 offset) {
this->m_address -= offset;
@@ -172,6 +202,10 @@ namespace hex::prv {
return this->m_address;
}
void setAddress(u64 address) {
this->m_address = address;
}
difference_type operator-(const ReverseIterator &other) const {
return other.m_address - this->m_address;
}
@@ -205,7 +239,7 @@ namespace hex::prv {
}
Iterator end() {
return { this, this->m_endAddress };
return { this, this->m_endAddress + 1 };
}
ReverseIterator rbegin() {
@@ -219,7 +253,7 @@ namespace hex::prv {
private:
void updateBuffer(u64 address, size_t size) {
if (!this->m_bufferValid || address < this->m_bufferAddress || address + size > (this->m_bufferAddress + this->m_buffer.size())) {
const auto remainingBytes = this->m_endAddress - address;
const auto remainingBytes = (this->m_endAddress - address) + 1;
if (remainingBytes < this->m_maxBufferSize)
this->m_buffer.resize(remainingBytes);

View File

@@ -8,9 +8,12 @@
#include <string>
#include <vector>
#include <hex/api/imhex_api.hpp>
#include <hex/providers/overlay.hpp>
#include <hex/helpers/fs.hpp>
#include <nlohmann/json.hpp>
namespace pl {
class PatternLanguage;
}
@@ -67,8 +70,8 @@ namespace hex::prv {
[[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;
[[nodiscard]] virtual bool open();
virtual void close();
void addPatch(u64 offset, const void *buffer, size_t size, bool createUndo = false);
void createUndoPoint();
@@ -79,13 +82,30 @@ namespace hex::prv {
[[nodiscard]] bool canUndo() const;
[[nodiscard]] bool canRedo() const;
[[nodiscard]] virtual bool hasFilePicker() const;
virtual bool handleFilePicker();
[[nodiscard]] virtual bool hasLoadInterface() const;
[[nodiscard]] virtual bool hasInterface() const;
virtual void drawLoadInterface();
virtual void drawInterface();
pl::PatternLanguage &getPatternLanguageRuntime() { return *this->m_patternLanguageRuntime; }
std::string &getPatternLanguageSourceCode() { return this->m_patternLanguageSourceCode; }
[[nodiscard]] u32 getID() const {
return this->m_id;
}
[[nodiscard]] virtual nlohmann::json storeSettings(nlohmann::json settings = { }) const;
virtual void loadSettings(const nlohmann::json &settings);
[[nodiscard]] virtual std::string getTypeName() const = 0;
void markDirty(bool dirty = true) { this->m_dirty = dirty; }
[[nodiscard]] bool isDirty() const { return this->m_dirty; }
virtual std::pair<Region, bool> getRegionValidity(u64 address) const;
void skipLoadInterface() { this->m_skipLoadInterface = true; }
[[nodiscard]] bool shouldSkipLoadInterface() const { return this->m_skipLoadInterface; }
protected:
u32 m_currPage = 0;
@@ -95,8 +115,13 @@ namespace hex::prv {
std::list<std::map<u64, u8>> m_patches;
std::list<Overlay *> m_overlays;
std::unique_ptr<pl::PatternLanguage> m_patternLanguageRuntime;
std::string m_patternLanguageSourceCode;
u32 m_id;
bool m_dirty = false;
bool m_skipLoadInterface = false;
private:
static u32 s_idCounter;
};
}

View File

@@ -139,4 +139,5 @@ namespace ImGui {
void HideTooltip();
bool BitCheckbox(const char* label, bool* v);
}

View File

@@ -1,6 +1,7 @@
#include <hex/api/content_registry.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/ui/view.hpp>
@@ -10,6 +11,8 @@
#include <nlohmann/json.hpp>
#include <hex/data_processor/node.hpp>
namespace hex {
namespace ContentRegistry::Settings {
@@ -19,10 +22,10 @@ namespace hex {
void load() {
bool loaded = false;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
std::ifstream settingsFile(dir / SettingsFile);
fs::File file(dir / SettingsFile, fs::File::Mode::Read);
if (settingsFile.good()) {
settingsFile >> getSettingsData();
if (file.isValid()) {
getSettingsData() = nlohmann::json::parse(file.readString());
loaded = true;
break;
}
@@ -34,10 +37,10 @@ namespace hex {
void store() {
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Config)) {
std::ofstream settingsFile(dir / SettingsFile, std::ios::trunc);
fs::File file(dir / SettingsFile, fs::File::Mode::Create);
if (settingsFile.good()) {
settingsFile << getSettingsData();
if (file.isValid()) {
file.write(getSettingsData().dump(4));
break;
}
}
@@ -241,9 +244,11 @@ namespace hex {
std::unique_ptr<pl::PatternLanguage> createDefaultRuntime(prv::Provider *provider) {
auto runtime = std::make_unique<pl::PatternLanguage>();
runtime->setDataSource([provider](u64 offset, u8 *buffer, size_t size) {
provider->read(offset, buffer, size);
}, 0, 0);
if (provider != nullptr) {
runtime->setDataSource([provider](u64 offset, u8 *buffer, size_t size) {
provider->read(offset, buffer, size);
}, provider->getBaseAddress(), provider->getActualSize());
}
runtime->setIncludePaths(fs::getDefaultPaths(fs::ImHexPath::PatternsInclude));

View File

@@ -211,9 +211,22 @@ namespace hex {
namespace ImHexApi::Provider {
static u32 s_currentProvider;
static u32 s_currentProvider = std::numeric_limits<u32>::max();
static std::vector<prv::Provider *> s_providers;
namespace impl {
static prv::Provider *s_closingProvider = nullptr;
void resetClosingProvider() {
s_closingProvider = nullptr;
}
prv::Provider* getClosingProvider() {
return s_closingProvider;
}
}
prv::Provider *get() {
if (!ImHexApi::Provider::isValid())
return nullptr;
@@ -240,23 +253,50 @@ namespace hex {
return !s_providers.empty() && s_currentProvider < s_providers.size();
}
void add(prv::Provider *provider) {
void markDirty() {
get()->markDirty();
}
void resetDirty() {
for (auto &provider : s_providers)
provider->markDirty(false);
}
bool isDirty() {
return std::ranges::any_of(s_providers, [](const auto &provider) {
return provider->isDirty();
});
}
void add(prv::Provider *provider, bool skipLoadInterface) {
if (Task::getRunningTaskCount() > 0)
return;
if (skipLoadInterface)
provider->skipLoadInterface();
s_providers.push_back(provider);
EventManager::post<EventProviderCreated>(provider);
setCurrentProvider(s_providers.size() - 1);
}
void remove(prv::Provider *provider) {
void remove(prv::Provider *provider, bool noQuestions) {
if (provider == nullptr)
return;
if (Task::getRunningTaskCount() > 0)
return;
if (!noQuestions) {
impl::s_closingProvider = provider;
bool shouldClose = true;
EventManager::post<EventProviderClosing>(provider, &shouldClose);
if (!shouldClose)
return;
}
auto it = std::find(s_providers.begin(), s_providers.end(), provider);
if (it == s_providers.end())
return;
@@ -265,12 +305,21 @@ namespace hex {
s_providers.erase(it);
if (it - s_providers.begin() == s_currentProvider && !s_providers.empty())
if (s_providers.empty())
EventManager::post<EventProviderChanged>(provider, nullptr);
else if (it - s_providers.begin() == s_currentProvider)
setCurrentProvider(0);
delete provider;
}
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface) {
prv::Provider* result = nullptr;
EventManager::post<RequestCreateProvider>(unlocalizedName, skipLoadInterface, &result);
return result;
}
}
@@ -436,6 +485,7 @@ namespace hex {
s_systemThemeDetection = enabled;
EventManager::post<EventSettingsChanged>();
EventManager::post<EventOSThemeChanged>();
}
bool usesSystemThemeDetection() {

View File

@@ -0,0 +1,107 @@
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/tar.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/providers/provider.hpp>
namespace hex {
constexpr static auto MetadataHeaderMagic = "HEX";
constexpr static auto MetadataPath = "IMHEX_METADATA";
std::vector<ProjectFile::Handler> ProjectFile::s_handlers;
std::vector<ProjectFile::ProviderHandler> ProjectFile::s_providerHandlers;
std::fs::path ProjectFile::s_currProjectPath;
bool ProjectFile::load(const std::fs::path &filePath) {
if (!fs::exists(filePath) || !fs::isRegularFile(filePath))
return false;
if (filePath.extension() != ".hexproj")
return false;
Tar tar(filePath, Tar::Mode::Read);
if (!tar.isValid())
return false;
if (!tar.contains(MetadataPath))
return false;
{
const auto metadataContent = tar.read(MetadataPath);
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic))
return false;
}
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.load(handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.load(provider, basePath / handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
}
ProjectFile::s_currProjectPath = filePath;
return result;
}
bool ProjectFile::store(std::optional<std::fs::path> filePath) {
if (!filePath.has_value())
filePath = ProjectFile::s_currProjectPath;
Tar tar(*filePath, Tar::Mode::Create);
if (!tar.isValid())
return false;
bool result = true;
for (const auto &handler : ProjectFile::getHandlers()) {
try {
if (!handler.store(handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
for (const auto &provider : ImHexApi::Provider::getProviders()) {
const auto basePath = std::fs::path(std::to_string(provider->getID()));
for (const auto &handler: ProjectFile::getProviderHandlers()) {
try {
if (!handler.store(provider, basePath / handler.basePath, tar))
result = false;
} catch (std::exception &e) {
log::info("{}", e.what());
result = false;
}
}
}
{
const auto metadataContent = hex::format("{}\n{}", MetadataHeaderMagic, IMHEX_VERSION);
tar.write(MetadataPath, metadataContent);
}
return result;
}
}

View File

@@ -137,11 +137,8 @@ namespace hex::fs {
#endif
for (auto &path : paths) {
for (auto &path : paths)
path = path / "imhex";
}
#if defined(OS_MACOS)
@@ -175,6 +172,9 @@ namespace hex::fs {
auto dataDirs = xdg::DataDirs();
std::copy(dataDirs.begin(), dataDirs.end(), std::back_inserter(paths));
for (auto &path : paths)
path = path / "imhex";
return paths;
#endif
}
@@ -232,6 +232,9 @@ namespace hex::fs {
case ImHexPath::Yara:
result = appendPath(getDataPaths(), "yara");
break;
case ImHexPath::Recent:
result = appendPath(getConfigPaths(), "recent");
break;
}
if (!listNonExisting) {

View File

@@ -1,115 +0,0 @@
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/imhex_api.hpp>
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
namespace hex {
std::fs::path ProjectFile::s_currProjectFilePath;
bool ProjectFile::s_hasUnsavedChanged = false;
std::fs::path ProjectFile::s_filePath;
std::string ProjectFile::s_pattern;
Patches ProjectFile::s_patches;
std::list<ImHexApi::Bookmarks::Entry> ProjectFile::s_bookmarks;
std::string ProjectFile::s_dataProcessorContent;
void to_json(json &j, const ImHexApi::Bookmarks::Entry &b) {
j = json {
{"address", b.region.address},
{ "size", b.region.size },
{ "name", b.name.data() },
{ "comment", b.comment.data()},
{ "locked", b.locked },
{ "color", b.color }
};
}
void from_json(const json &j, ImHexApi::Bookmarks::Entry &b) {
std::string name, comment;
if (j.contains("address")) j.at("address").get_to(b.region.address);
if (j.contains("size")) j.at("size").get_to(b.region.size);
if (j.contains("name")) j.at("name").get_to(name);
if (j.contains("comment")) j.at("comment").get_to(comment);
if (j.contains("locked")) j.at("locked").get_to(b.locked);
if (j.contains("color")) j.at("color").get_to(b.color);
std::copy(name.begin(), name.end(), std::back_inserter(b.name));
b.name.push_back('\0');
std::copy(comment.begin(), comment.end(), std::back_inserter(b.comment));
b.comment.push_back('\0');
}
bool ProjectFile::load(const std::fs::path &filePath) {
ProjectFile::s_hasUnsavedChanged = false;
json projectFileData;
try {
std::ifstream projectFile(filePath);
projectFile >> projectFileData;
ProjectFile::s_filePath = std::fs::path(projectFileData["filePath"].get<std::u8string>());
ProjectFile::s_pattern = projectFileData["pattern"];
ProjectFile::s_patches = projectFileData["patches"].get<Patches>();
ProjectFile::s_dataProcessorContent = projectFileData["dataProcessor"];
ProjectFile::s_bookmarks.clear();
for (auto &element : projectFileData["bookmarks"].items()) {
ImHexApi::Bookmarks::Entry entry;
from_json(element.value(), entry);
ProjectFile::s_bookmarks.emplace_back(std::move(entry));
}
} catch (json::exception &e) {
return false;
} catch (std::ofstream::failure &e) {
return false;
}
ProjectFile::s_currProjectFilePath = filePath;
EventManager::post<EventProjectFileLoad>();
return true;
}
bool ProjectFile::store(std::fs::path filePath) {
EventManager::post<EventProjectFileStore>();
json projectFileData;
if (filePath.empty())
filePath = ProjectFile::s_currProjectFilePath;
try {
projectFileData["filePath"] = ProjectFile::s_filePath.u8string();
projectFileData["pattern"] = ProjectFile::s_pattern;
projectFileData["patches"] = ProjectFile::s_patches;
projectFileData["dataProcessor"] = ProjectFile::s_dataProcessorContent;
for (auto &bookmark : ProjectFile::s_bookmarks) {
to_json(projectFileData["bookmarks"].emplace_back(), bookmark);
}
std::ofstream projectFile(filePath.c_str(), std::fstream::trunc);
projectFile << projectFileData;
} catch (json::exception &e) {
return false;
} catch (std::ifstream::failure &e) {
return false;
}
ProjectFile::s_hasUnsavedChanged = false;
ProjectFile::s_currProjectFilePath = filePath;
return true;
}
}

View File

@@ -7,26 +7,50 @@ namespace hex {
using namespace hex::literals;
Tar::Tar(const std::fs::path &path, Mode mode) {
int error = MTAR_ESUCCESS;
if (mode == Tar::Mode::Read)
mtar_open(&this->m_ctx, path.string().c_str(), "r");
error = mtar_open(&this->m_ctx, path.string().c_str(), "r");
else if (mode == Tar::Mode::Write)
mtar_open(&this->m_ctx, path.string().c_str(), "a");
error = mtar_open(&this->m_ctx, path.string().c_str(), "a");
else if (mode == Tar::Mode::Create)
mtar_open(&this->m_ctx, path.string().c_str(), "w");
error = mtar_open(&this->m_ctx, path.string().c_str(), "w");
else
error = MTAR_EFAILURE;
this->m_valid = (error == MTAR_ESUCCESS);
}
Tar::~Tar() {
mtar_finalize(&this->m_ctx);
mtar_close(&this->m_ctx);
this->close();
}
std::vector<std::fs::path> Tar::listEntries() {
Tar::Tar(hex::Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
this->m_valid = other.m_valid;
other.m_ctx = { };
other.m_valid = false;
}
Tar &Tar::operator=(Tar &&other) noexcept {
this->m_ctx = other.m_ctx;
other.m_ctx = { };
this->m_valid = other.m_valid;
other.m_valid = false;
return *this;
}
std::vector<std::fs::path> Tar::listEntries(const std::fs::path &basePath) {
std::vector<std::fs::path> result;
const std::string PaxHeaderName = "@PaxHeader";
mtar_header_t header;
while (mtar_read_header(&this->m_ctx, &header) != MTAR_ENULLRECORD) {
if (header.name != PaxHeaderName) {
std::fs::path path = header.name;
if (header.name != PaxHeaderName && fs::isSubPath(basePath, path)) {
result.emplace_back(header.name);
}
@@ -36,9 +60,29 @@ namespace hex {
return result;
}
bool Tar::contains(const std::fs::path &path) {
mtar_header_t header;
return mtar_find(&this->m_ctx, path.string().c_str(), &header) == MTAR_ESUCCESS;
}
void Tar::close() {
if (this->m_valid) {
mtar_finalize(&this->m_ctx);
mtar_close(&this->m_ctx);
}
this->m_ctx = { };
this->m_valid = false;
}
std::vector<u8> Tar::read(const std::fs::path &path) {
mtar_header_t header;
mtar_find(&this->m_ctx, path.string().c_str(), &header);
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_find(&this->m_ctx, fixedPath.c_str(), &header);
std::vector<u8> result(header.size, 0x00);
mtar_read_data(&this->m_ctx, result.data(), result.size());
@@ -46,19 +90,37 @@ namespace hex {
return result;
}
std::string Tar::readString(const std::fs::path &path) {
auto result = this->read(path);
return { result.begin(), result.end() };
}
void Tar::write(const std::fs::path &path, const std::vector<u8> &data) {
if (path.has_parent_path()) {
std::fs::path pathPart;
for (const auto &part : path.parent_path()) {
pathPart /= part;
mtar_write_dir_header(&this->m_ctx, pathPart.string().c_str());
auto fixedPath = pathPart.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_dir_header(&this->m_ctx, fixedPath.c_str());
}
}
mtar_write_file_header(&this->m_ctx, path.string().c_str(), data.size());
auto fixedPath = path.string();
#if defined(OS_WINDOWS)
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
#endif
mtar_write_file_header(&this->m_ctx, fixedPath.c_str(), data.size());
mtar_write_data(&this->m_ctx, data.data(), data.size());
}
void Tar::write(const std::fs::path &path, const std::string &data) {
this->write(path, std::vector<u8>(data.begin(), data.end()));
}
static void writeFile(mtar_t *ctx, mtar_header_t *header, const std::fs::path &path) {
constexpr static u64 BufferSize = 1_MiB;

View File

@@ -1,7 +1,7 @@
#include <hex/providers/provider.hpp>
#include <hex.hpp>
#include <hex/api/content_registry.hpp>
#include <hex/api/event.hpp>
#include <cmath>
#include <cstring>
@@ -10,14 +10,16 @@
namespace hex::prv {
Provider::Provider() {
u32 Provider::s_idCounter = 0;
Provider::Provider() : m_id(s_idCounter++) {
this->m_patches.emplace_back();
this->m_patternLanguageRuntime = ContentRegistry::PatternLanguage::createDefaultRuntime(this);
}
Provider::~Provider() {
for (auto &overlay : this->m_overlays)
this->deleteOverlay(overlay);
this->close();
}
void Provider::read(u64 offset, void *buffer, size_t size, bool overlays) {
@@ -28,6 +30,7 @@ namespace hex::prv {
void Provider::write(u64 offset, const void *buffer, size_t size) {
this->writeRaw(offset - this->getBaseAddress(), buffer, size);
this->markDirty();
}
void Provider::save() { }
@@ -36,7 +39,9 @@ namespace hex::prv {
}
void Provider::resize(size_t newSize) {
this->m_patternLanguageRuntime->setDataSize(newSize);
hex::unused(newSize);
this->markDirty();
}
void Provider::insert(u64 offset, size_t size) {
@@ -53,6 +58,8 @@ namespace hex::prv {
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address + size, value });
this->markDirty();
}
void Provider::remove(u64 offset, size_t size) {
@@ -69,6 +76,8 @@ namespace hex::prv {
patches.erase(address);
for (const auto &[address, value] : patchesToMove)
patches.insert({ address - size, value });
this->markDirty();
}
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) {
@@ -104,6 +113,7 @@ namespace hex::prv {
for (auto &[patchAddress, patch] : getPatches()) {
this->writeRaw(patchAddress - this->getBaseAddress(), &patch, 1);
}
this->markDirty();
}
@@ -122,7 +132,7 @@ namespace hex::prv {
u32 Provider::getPageCount() const {
return std::ceil(this->getActualSize() / double(PageSize));
return std::max(1.0, std::ceil(this->getActualSize() / double(PageSize)));
}
u32 Provider::getCurrentPage() const {
@@ -137,7 +147,7 @@ namespace hex::prv {
void Provider::setBaseAddress(u64 address) {
this->m_baseAddress = address;
this->m_patternLanguageRuntime->setDataBaseAddress(address);
this->markDirty();
}
u64 Provider::getBaseAddress() const {
@@ -161,6 +171,14 @@ namespace hex::prv {
return page;
}
bool Provider::open() {
EventManager::post<EventProviderOpened>(this);
return true;
}
void Provider::close() {
EventManager::post<EventProviderClosed>(this);
}
void Provider::addPatch(u64 offset, const void *buffer, size_t size, bool createUndo) {
if (this->m_patchTreeOffset > 0) {
auto iter = this->m_patches.end();
@@ -184,6 +202,8 @@ namespace hex::prv {
else
getPatches()[offset + i] = patch;
}
this->markDirty();
}
void Provider::createUndoPoint() {
@@ -208,6 +228,13 @@ namespace hex::prv {
return this->m_patchTreeOffset > 0;
}
bool Provider::hasFilePicker() const {
return false;
}
bool Provider::handleFilePicker() {
return false;
}
bool Provider::hasLoadInterface() const {
return false;
@@ -223,4 +250,51 @@ namespace hex::prv {
void Provider::drawInterface() {
}
nlohmann::json Provider::storeSettings(nlohmann::json settings) const {
settings["displayName"] = this->getName();
settings["type"] = this->getTypeName();
settings["baseAddress"] = this->m_baseAddress;
settings["currPage"] = this->m_currPage;
return settings;
}
void Provider::loadSettings(const nlohmann::json &settings) {
this->m_baseAddress = settings["baseAddress"];
this->m_currPage = settings["currPage"];
}
std::pair<Region, bool> Provider::getRegionValidity(u64 address) const {
if (address > this->getActualSize())
return { Region::Invalid(), false };
bool insideValidRegion = false;
std::optional<u64> nextRegionAddress;
for (const auto &overlay : this->m_overlays) {
Region overlayRegion = { overlay->getAddress(), overlay->getSize() };
if (!nextRegionAddress.has_value() || overlay->getAddress() < nextRegionAddress) {
nextRegionAddress = overlayRegion.getStartAddress();
}
if (Region { address, 1 }.overlaps(overlayRegion)) {
insideValidRegion = true;
}
}
for (const auto &[patchAddress, value] : this->m_patches.back()) {
if (!nextRegionAddress.has_value() || patchAddress < nextRegionAddress)
nextRegionAddress = patchAddress;
if (address == patchAddress)
insideValidRegion = true;
}
if (!nextRegionAddress.has_value())
return { Region { address, this->getActualSize() - address }, true };
else
return { Region { address, *nextRegionAddress - address }, insideValidRegion };
}
}

View File

@@ -59,18 +59,20 @@ namespace hex::init {
bool createDirectories() {
bool result = true;
using enum fs::ImHexPath;
constexpr std::array paths = {
fs::ImHexPath::Patterns,
fs::ImHexPath::PatternsInclude,
fs::ImHexPath::Magic,
fs::ImHexPath::Plugins,
fs::ImHexPath::Resources,
fs::ImHexPath::Config,
fs::ImHexPath::Constants,
fs::ImHexPath::Yara,
fs::ImHexPath::Encodings,
fs::ImHexPath::Python,
fs::ImHexPath::Logs
Patterns,
PatternsInclude,
Magic,
Plugins,
Resources,
Config,
Constants,
Yara,
Encodings,
Python,
Logs,
Recent
};
// Check if ImHex is installed in portable mode

View File

@@ -8,11 +8,12 @@
#include "init/splash_window.hpp"
#include "init/tasks.hpp"
#include <hex/api/project_file_manager.hpp>
int main(int argc, char **argv, char **envp) {
using namespace hex;
ImHexApi::System::impl::setProgramArguments(argc, argv, envp);
#if defined(OS_WINDOWS)
ImHexApi::System::impl::setBorderlessWindowMode(true);
#endif

View File

@@ -22,6 +22,8 @@
#include <dwmapi.h>
#include <windowsx.h>
#include <csignal>
#include <imgui_impl_glfw.h>
namespace hex {
@@ -159,10 +161,11 @@ namespace hex {
auto message = reinterpret_cast<COPYDATASTRUCT *>(lParam);
if (message == nullptr) break;
auto path = reinterpret_cast<const char *>(message->lpData);
if (path == nullptr) break;
auto data = reinterpret_cast<const char8_t *>(message->lpData);
if (data == nullptr) break;
log::info("Opening file in existing instance: {}", path);
std::fs::path path = data;
log::info("Opening file in existing instance: {}", path.string());
EventManager::post<RequestOpenFile>(path);
break;
}
@@ -263,6 +266,21 @@ namespace hex {
::SetWindowPos(hwnd, nullptr, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE);
::SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) | WS_OVERLAPPEDWINDOW);
}
// Catch heap corruption
{
::AddVectoredExceptionHandler(TRUE, [](PEXCEPTION_POINTERS exception) -> LONG {
if ((exception->ExceptionRecord->ExceptionCode & 0xF000'0000) == 0xC000'0000) {
log::fatal("Exception raised: 0x{:08X}", exception->ExceptionRecord->ExceptionCode);
if (exception->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION) {
log::fatal("Heap corruption detected!");
std::raise(SIGABRT);
}
}
return EXCEPTION_CONTINUE_SEARCH;
});
}
}
void Window::beginNativeWindowFrame() {

View File

@@ -32,7 +32,7 @@
#include <fonts/codicons_font.h>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <GLFW/glfw3.h>
@@ -63,6 +63,25 @@ namespace hex {
buf->append("\n");
}
static void signalHandler(int signalNumber) {
log::fatal("Terminating with signal {}", signalNumber);
EventManager::post<EventAbnormalTermination>(signalNumber);
if (std::uncaught_exceptions() > 0) {
log::fatal("Uncaught exception thrown!");
}
// Let's not loop on this...
std::signal(signalNumber, nullptr);
#if defined(DEBUG)
assert(false);
#else
std::raise(signalNumber);
#endif
};
Window::Window() {
{
for (const auto &[argument, value] : ImHexApi::System::getInitArguments()) {
@@ -80,28 +99,28 @@ namespace hex {
this->initImGui();
this->setupNativeWindow();
// Initialize default theme
EventManager::post<RequestChangeTheme>(1);
EventManager::subscribe<RequestCloseImHex>(this, [this](bool noQuestions) {
glfwSetWindowShouldClose(this->m_window, true);
glfwSetWindowShouldClose(this->m_window, GLFW_TRUE);
if (!noQuestions)
EventManager::post<EventWindowClosing>(this->m_window);
});
EventManager::subscribe<EventProviderDeleted>(this, [](const auto*) {
EventManager::post<RequestChangeWindowTitle>("");
});
EventManager::subscribe<RequestChangeWindowTitle>(this, [this](const std::string &windowTitle) {
std::string title = "ImHex";
if (ImHexApi::Provider::isValid()) {
auto provider = ImHexApi::Provider::get();
if (!windowTitle.empty())
title += " - " + windowTitle;
if (ProjectFile::hasUnsavedChanges())
if (provider->isDirty())
title += " (*)";
if (!ImHexApi::Provider::get()->isWritable())
if (!provider->isWritable())
title += " (Read Only)";
}
@@ -114,11 +133,11 @@ namespace hex {
EventManager::subscribe<EventAbnormalTermination>(this, [this, CrashBackupFileName](int) {
ImGui::SaveIniSettingsToDisk(this->m_imguiSettingsPath.string().c_str());
if (!ProjectFile::hasUnsavedChanges())
if (!ImHexApi::Provider::isDirty())
return;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Config)) {
if (ProjectFile::store(std::fs::path(path) / CrashBackupFileName))
if (ProjectFile::store((std::fs::path(path) / CrashBackupFileName).string()))
break;
}
});
@@ -127,29 +146,11 @@ namespace hex {
this->m_popupsToOpen.push_back(name);
});
auto signalHandler = [](int signalNumber) {
EventManager::post<EventAbnormalTermination>(signalNumber);
if (std::uncaught_exceptions() > 0) {
log::fatal("Uncaught exception thrown!");
}
// Let's not loop on this...
std::signal(signalNumber, nullptr);
#if defined(DEBUG)
assert(false);
#else
std::raise(signalNumber);
#endif
};
std::signal(SIGTERM, signalHandler);
std::signal(SIGSEGV, signalHandler);
std::signal(SIGINT, signalHandler);
std::signal(SIGILL, signalHandler);
std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler);
std::signal(SIGSEGV, signalHandler);
std::signal(SIGILL, signalHandler);
std::signal(SIGABRT, signalHandler);
std::signal(SIGFPE, signalHandler);
std::set_terminate([]{ signalHandler(SIGABRT); });
auto imhexLogo = romfs::get("logo.png");
this->m_logoTexture = ImGui::LoadImageFromMemory(reinterpret_cast<const ImU8 *>(imhexLogo.data()), imhexLogo.size());
@@ -199,6 +200,13 @@ namespace hex {
this->frameBegin();
this->frame();
this->frameEnd();
const auto targetFps = ImHexApi::System::getTargetFPS();
if (targetFps <= 200)
std::this_thread::sleep_for(std::chrono::milliseconds(u64((this->m_lastFrameTime + 1 / targetFps - glfwGetTime()) * 1000)));
this->m_lastFrameTime = glfwGetTime();
this->m_hadEvent = false;
}
}
@@ -493,12 +501,6 @@ namespace hex {
glfwMakeContextCurrent(backup_current_context);
glfwSwapBuffers(this->m_window);
const auto targetFps = ImHexApi::System::getTargetFPS();
if (targetFps <= 200)
std::this_thread::sleep_for(std::chrono::milliseconds(u64((this->m_lastFrameTime + 1 / targetFps - glfwGetTime()) * 1000)));
this->m_lastFrameTime = glfwGetTime();
}
void Window::initGLFW() {

View File

@@ -28,6 +28,8 @@ add_library(${PROJECT_NAME} SHARED
source/content/providers/file_provider.cpp
source/content/providers/gdb_provider.cpp
source/content/providers/disk_provider.cpp
source/content/providers/intel_hex_provider.cpp
source/content/providers/motorola_srec_provider.cpp
source/content/views/view_hex_editor.cpp
source/content/views/view_pattern_editor.cpp

View File

@@ -15,7 +15,7 @@ namespace hex::plugin::builtin::prv {
class DiskProvider : public hex::prv::Provider {
public:
DiskProvider();
~DiskProvider() override;
~DiskProvider() override = default;
[[nodiscard]] bool isAvailable() const override;
[[nodiscard]] bool isReadable() const override;
@@ -38,6 +38,15 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] bool hasLoadInterface() const override { return true; }
void drawLoadInterface() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings = { }) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.disk";
}
std::pair<Region, bool> getRegionValidity(u64 address) const override;
protected:
void reloadDrives();

View File

@@ -7,19 +7,23 @@
#include <sys/stat.h>
#if defined(OS_WINDOWS)
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#include <sys/fcntl.h>
#endif
namespace hex::plugin::builtin::prv {
class FileProvider : public hex::prv::Provider {
public:
explicit FileProvider();
~FileProvider() override;
FileProvider() = default;;
~FileProvider() override = default;;
[[nodiscard]] bool isAvailable() const override;
[[nodiscard]] bool isReadable() const override;
@@ -45,18 +49,34 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] std::string getName() const override;
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override;
bool hasFilePicker() const override { return true; }
bool handleFilePicker() override;
void setPath(const std::fs::path &path);
[[nodiscard]] bool open() override;
void close() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.file";
}
std::pair<Region, bool> getRegionValidity(u64 address) const override;
protected:
#if defined(OS_WINDOWS)
HANDLE m_file = INVALID_HANDLE_VALUE;
HANDLE m_mapping = INVALID_HANDLE_VALUE;
#else
int m_file = -1;
#endif
#if defined(OS_WINDOWS)
HANDLE m_file = INVALID_HANDLE_VALUE;
HANDLE m_mapping = INVALID_HANDLE_VALUE;
#else
int m_file = -1;
#endif
std::fs::path m_path;
void *m_mappedFile = nullptr;

View File

@@ -12,8 +12,8 @@ namespace hex::plugin::builtin::prv {
class GDBProvider : public hex::prv::Provider {
public:
explicit GDBProvider();
~GDBProvider() override;
GDBProvider();
~GDBProvider() override = default;;
[[nodiscard]] bool isAvailable() const override;
[[nodiscard]] bool isReadable() const override;
@@ -42,6 +42,15 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] bool hasLoadInterface() const override { return true; }
void drawLoadInterface() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.gdb";
}
std::pair<Region, bool> getRegionValidity(u64 address) const override;
protected:
hex::Socket m_socket;

View File

@@ -0,0 +1,50 @@
#pragma once
#include <hex/providers/provider.hpp>
#include <IntervalTree.h>
namespace hex::plugin::builtin::prv {
class IntelHexProvider : public hex::prv::Provider {
public:
IntelHexProvider() = default;
~IntelHexProvider() override = default;
[[nodiscard]] bool isAvailable() const override { return this->m_dataValid; }
[[nodiscard]] bool isReadable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return false; }
[[nodiscard]] bool isResizable() const override { return false; }
[[nodiscard]] bool isSavable() const override { return false; }
void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override;
[[nodiscard]] size_t getActualSize() const override;
bool open() override;
void close() override;
[[nodiscard]] std::string getName() const override;
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override { return { }; }
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.intel_hex";
}
bool hasFilePicker() const override { return true; }
bool handleFilePicker() override;
std::pair<Region, bool> getRegionValidity(u64 address) const override;
protected:
bool m_dataValid = false;
size_t m_dataSize = 0x00;
interval_tree::IntervalTree<u64, std::vector<u8>> m_data;
std::fs::path m_sourceFilePath;
};
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <content/providers/intel_hex_provider.hpp>
namespace hex::plugin::builtin::prv {
class MotorolaSRECProvider : public IntelHexProvider {
public:
MotorolaSRECProvider() = default;
~MotorolaSRECProvider() override = default;
bool open() override;
void close() override;
[[nodiscard]] std::string getName() const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.motorola_srec";
}
bool handleFilePicker() override;
private:
};
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <hex/providers/provider.hpp>
namespace hex::plugin::builtin::prv {
class NullProvider : public hex::prv::Provider {
public:
explicit NullProvider() = default;
~NullProvider() override = default;
[[nodiscard]] bool isAvailable() const override { return true; }
[[nodiscard]] bool isReadable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return false; }
[[nodiscard]] bool isResizable() const override { return false; }
[[nodiscard]] bool isSavable() const override { return false; }
void readRaw(u64 offset, void *buffer, size_t size) override { hex::unused(offset, buffer, size); }
void writeRaw(u64 offset, const void *buffer, size_t size) override { hex::unused(offset, buffer, size); }
[[nodiscard]] size_t getActualSize() const override { return 0x00; }
[[nodiscard]] std::string getName() const override { return "None"; }
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override { return { }; }
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.null";
}
};
}

View File

@@ -15,7 +15,11 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::list<ImHexApi::Bookmarks::Entry> m_bookmarks;
static bool importBookmarks(prv::Provider *provider, const nlohmann::json &json);
static bool exportBookmarks(prv::Provider *provider, nlohmann::json &json);
void registerMenuItems();
private:
std::string m_currFilter;
};

View File

@@ -20,25 +20,17 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::list<dp::Node *> m_endNodes;
std::list<dp::Node *> m_nodes;
std::list<dp::Link> m_links;
std::vector<hex::prv::Overlay *> m_dataOverlays;
int m_rightClickedId = -1;
ImVec2 m_rightClickedCoords;
std::optional<dp::Node::NodeError> m_currNodeError;
bool m_continuousEvaluation = false;
void eraseLink(u32 id);
void eraseNodes(const std::vector<int> &ids);
void processNodes();
std::string saveNodes();
void loadNodes(const std::string &data);
std::string saveNodes(prv::Provider *provider);
void loadNodes(prv::Provider *provider, const std::string &data);
};
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <hex/ui/view.hpp>
#include <ui/widgets.hpp>
#include <hex/helpers/disassembler.hpp>
@@ -29,9 +30,9 @@ namespace hex::plugin::builtin {
private:
bool m_disassembling = false;
u64 m_baseAddress = 0;
u64 m_codeRegion[2] = { 0 };
bool m_shouldMatchSelection = false;
u64 m_baseAddress = 0;
ui::SelectedRegion m_range = ui::SelectedRegion::EntireData;
Region m_codeRegion = { 0, 0 };
Architecture m_architecture = Architecture::ARM;
cs_mode m_mode = cs_mode(0);

View File

@@ -4,10 +4,13 @@
#include <imgui.h>
#include <hex/ui/view.hpp>
#include <ui/widgets.hpp>
#include <atomic>
#include <vector>
#include <IntervalTree.h>
namespace hex::plugin::builtin {
class ViewFind : public View {
@@ -29,7 +32,7 @@ namespace hex::plugin::builtin {
};
struct SearchSettings {
int range = 0;
ui::SelectedRegion range = ui::SelectedRegion::EntireData;
enum class Mode : int {
Strings,
@@ -66,7 +69,11 @@ namespace hex::plugin::builtin {
} binaryPattern;
} m_searchSettings, m_decodeSettings;
using OccurrenceTree = interval_tree::IntervalTree<u64, Occurrence>;
std::map<prv::Provider*, std::vector<Occurrence>> m_foundOccurrences, m_sortedOccurrences;
std::map<prv::Provider*, OccurrenceTree> m_occurrenceTree;
std::atomic<bool> m_searchRunning;
bool m_settingsValid = false;

View File

@@ -21,7 +21,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
private:
std::map<prv::Provider *, std::vector<pl::Pattern*>> m_sortedPatterns;
std::map<prv::Provider *, std::vector<pl::ptrn::Pattern*>> m_sortedPatterns;
hex::PatternDrawer m_patternDrawer;
};

View File

@@ -2,6 +2,7 @@
#include <hex/ui/view.hpp>
#include <pl/pattern_language.hpp>
#include <pl/core/errors/error.hpp>
#include <hex/providers/provider.hpp>
#include <cstring>
@@ -12,7 +13,7 @@
#include <TextEditor.h>
namespace pl { class Pattern; }
namespace pl::ptrn { class Pattern; }
namespace hex::plugin::builtin {
@@ -29,8 +30,8 @@ namespace hex::plugin::builtin {
bool inVariable;
bool outVariable;
pl::Token::ValueType type;
pl::Token::Literal value;
pl::core::Token::ValueType type;
pl::core::Token::Literal value;
};
enum class EnvVarType
@@ -44,7 +45,7 @@ namespace hex::plugin::builtin {
struct EnvVar {
u64 id;
std::string name;
pl::Token::Literal value;
pl::core::Token::Literal value;
EnvVarType type;
bool operator==(const EnvVar &other) const {
@@ -67,9 +68,9 @@ namespace hex::plugin::builtin {
bool m_lastEvaluationProcessed = true;
bool m_lastEvaluationResult = false;
std::optional<pl::PatternLanguageError> m_lastEvaluationError;
std::vector<std::pair<pl::LogConsole::Level, std::string>> m_lastEvaluationLog;
std::map<std::string, pl::Token::Literal> m_lastEvaluationOutVars;
std::optional<pl::core::err::PatternLanguageError> m_lastEvaluationError;
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> m_lastEvaluationLog;
std::map<std::string, pl::core::Token::Literal> m_lastEvaluationOutVars;
std::atomic<u32> m_runningEvaluators = 0;
std::atomic<u32> m_runningParsers = 0;
@@ -79,7 +80,7 @@ namespace hex::plugin::builtin {
bool m_acceptPatternWindowOpen = false;
TextEditor m_textEditor;
std::vector<std::pair<pl::LogConsole::Level, std::string>> m_console;
std::vector<std::pair<pl::core::LogConsole::Level, std::string>> m_console;
std::map<std::string, PatternVariable> m_patternVariables;
@@ -97,7 +98,7 @@ namespace hex::plugin::builtin {
void drawEnvVars(ImVec2 size);
void drawVariableSettings(ImVec2 size);
void drawPatternTooltip(pl::Pattern *pattern);
void drawPatternTooltip(pl::ptrn::Pattern *pattern);
void loadPatternFile(const std::fs::path &path);

View File

@@ -7,7 +7,7 @@
namespace hex {
template<typename T>
concept ArrayPattern = requires(u64 displayEnd, T pattern, std::function<void(int, pl::Pattern&)> fn) {
concept ArrayPattern = requires(u64 displayEnd, T pattern, std::function<void(int, pl::ptrn::Pattern&)> fn) {
{ pattern.forEachArrayEntry(displayEnd, fn) } -> std::same_as<void>;
};
@@ -15,32 +15,32 @@ namespace hex {
public:
PatternDrawer() = default;
void visit(pl::PatternArrayDynamic& pattern) override;
void visit(pl::PatternArrayStatic& pattern) override;
void visit(pl::PatternBitfieldField& pattern) override;
void visit(pl::PatternBitfield& pattern) override;
void visit(pl::PatternBoolean& pattern) override;
void visit(pl::PatternCharacter& pattern) override;
void visit(pl::PatternEnum& pattern) override;
void visit(pl::PatternFloat& pattern) override;
void visit(pl::PatternPadding& pattern) override;
void visit(pl::PatternPointer& pattern) override;
void visit(pl::PatternSigned& pattern) override;
void visit(pl::PatternString& pattern) override;
void visit(pl::PatternStruct& pattern) override;
void visit(pl::PatternUnion& pattern) override;
void visit(pl::PatternUnsigned& pattern) override;
void visit(pl::PatternWideCharacter& pattern) override;
void visit(pl::PatternWideString& pattern) override;
void visit(pl::ptrn::PatternArrayDynamic& pattern) override;
void visit(pl::ptrn::PatternArrayStatic& pattern) override;
void visit(pl::ptrn::PatternBitfieldField& pattern) override;
void visit(pl::ptrn::PatternBitfield& pattern) override;
void visit(pl::ptrn::PatternBoolean& pattern) override;
void visit(pl::ptrn::PatternCharacter& pattern) override;
void visit(pl::ptrn::PatternEnum& pattern) override;
void visit(pl::ptrn::PatternFloat& pattern) override;
void visit(pl::ptrn::PatternPadding& pattern) override;
void visit(pl::ptrn::PatternPointer& pattern) override;
void visit(pl::ptrn::PatternSigned& pattern) override;
void visit(pl::ptrn::PatternString& pattern) override;
void visit(pl::ptrn::PatternStruct& pattern) override;
void visit(pl::ptrn::PatternUnion& pattern) override;
void visit(pl::ptrn::PatternUnsigned& pattern) override;
void visit(pl::ptrn::PatternWideCharacter& pattern) override;
void visit(pl::ptrn::PatternWideString& pattern) override;
private:
void createDefaultEntry(const pl::Pattern &pattern, const std::string &value, pl::Token::Literal &&literal) const;
void createLeafNode(const pl::Pattern& pattern) const;
bool createTreeNode(const pl::Pattern& pattern) const;
void createDefaultEntry(const pl::ptrn::Pattern &pattern, const std::string &value, pl::core::Token::Literal &&literal) const;
void createLeafNode(const pl::ptrn::Pattern& pattern) const;
bool createTreeNode(const pl::ptrn::Pattern& pattern) const;
void makeSelectable(const pl::Pattern &pattern) const;
void makeSelectable(const pl::ptrn::Pattern &pattern) const;
void draw(pl::Pattern& pattern);
void draw(pl::ptrn::Pattern& pattern);
template<ArrayPattern T>
void drawArray(T& pattern) {
@@ -56,20 +56,20 @@ namespace hex {
this->drawArrayEnd(pattern, opened);
}
bool drawArrayRoot(pl::Pattern& pattern, size_t entryCount, bool isInlined);
void drawArrayNode(u64 idx, u64& displayEnd, pl::Pattern& pattern);
void drawArrayEnd(pl::Pattern& pattern, bool opened);
bool drawArrayRoot(pl::ptrn::Pattern& pattern, size_t entryCount, bool isInlined);
void drawArrayNode(u64 idx, u64& displayEnd, pl::ptrn::Pattern& pattern);
void drawArrayEnd(pl::ptrn::Pattern& pattern, bool opened);
void drawCommentTooltip(const pl::Pattern &pattern) const;
void drawTypenameColumn(const pl::Pattern& pattern, const std::string& pattern_name) const;
void drawNameColumn(const pl::Pattern& pattern) const;
void drawColorColumn(const pl::Pattern& pattern) const;
void drawOffsetColumn(const pl::Pattern& pattern) const;
void drawSizeColumn(const pl::Pattern& pattern) const;
void drawCommentTooltip(const pl::ptrn::Pattern &pattern) const;
void drawTypenameColumn(const pl::ptrn::Pattern& pattern, const std::string& pattern_name) const;
void drawNameColumn(const pl::ptrn::Pattern& pattern) const;
void drawColorColumn(const pl::ptrn::Pattern& pattern) const;
void drawOffsetColumn(const pl::ptrn::Pattern& pattern) const;
void drawSizeColumn(const pl::ptrn::Pattern& pattern) const;
u64& getDisplayEnd(const pl::Pattern& pattern);
u64& getDisplayEnd(const pl::ptrn::Pattern& pattern);
private:
std::map<const pl::Pattern*, u64> m_displayEnd;
std::map<const pl::ptrn::Pattern*, u64> m_displayEnd;
};
};

View File

@@ -0,0 +1,56 @@
#pragma once
#include <map>
#include <hex/providers/provider.hpp>
#include <pl/pattern_language.hpp>
#include <hex/data_processor/attribute.hpp>
#include <hex/data_processor/node.hpp>
#include <hex/data_processor/link.hpp>
namespace hex::plugin::builtin {
class ProviderExtraData {
public:
struct Data {
bool dataDirty = false;
struct {
std::string sourceCode;
std::unique_ptr<pl::PatternLanguage> runtime;
} patternLanguage;
std::list<ImHexApi::Bookmarks::Entry> bookmarks;
struct {
std::list<dp::Node*> endNodes;
std::list<std::unique_ptr<dp::Node>> nodes;
std::list<dp::Link> links;
std::vector<hex::prv::Overlay *> dataOverlays;
std::optional<dp::Node::NodeError> currNodeError;
} dataProcessor;
};
static Data& getCurrent() {
return get(ImHexApi::Provider::get());
}
static Data& get(hex::prv::Provider *provider) {
return s_data[provider];
}
static void erase(hex::prv::Provider *provider) {
s_data.erase(provider);
}
static bool markDirty() {
return getCurrent().dataDirty = true;
}
private:
ProviderExtraData() = default;
static inline std::map<hex::prv::Provider*, Data> s_data = {};
};
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
namespace hex::plugin::builtin::ui {
enum class SelectedRegion : int {
EntireData,
Selection
};
inline void regionSelectionPicker(SelectedRegion *region, bool showHeader = true, bool firstEntry = false) {
if (showHeader)
ImGui::Header("hex.builtin.common.range"_lang, firstEntry);
if (ImGui::RadioButton("hex.builtin.common.range.entire_data"_lang, *region == SelectedRegion::EntireData))
*region = SelectedRegion::EntireData;
if (ImGui::RadioButton("hex.builtin.common.range.selection"_lang, *region == SelectedRegion::Selection))
*region = SelectedRegion::Selection;
}
}

View File

@@ -196,11 +196,11 @@ namespace hex::plugin::builtin {
class NodeComment : public dp::Node {
public:
NodeComment() : Node("hex.builtin.nodes.constants.comment.header", {}) {
this->m_comment.resize(0xFFF, 0x00);
}
void drawNode() override {
ImGui::InputTextMultiline("##string", reinterpret_cast<char *>(this->m_comment.data()), this->m_comment.size() - 1, ImVec2(150, 100));
ImGui::InputTextMultiline("##string", this->m_comment, scaled(ImVec2(150, 100)));
}
void process() override {

View File

@@ -3,61 +3,54 @@
#include <hex/providers/provider.hpp>
#include <hex/ui/view.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imgui.h>
#include <nlohmann/json.hpp>
#include "provider_extra_data.hpp"
#include "content/providers/file_provider.hpp"
namespace hex::plugin::builtin {
static void openFile(const std::fs::path &path) {
hex::prv::Provider *provider = nullptr;
EventManager::post<RequestCreateProvider>("hex.builtin.provider.file", &provider);
if (auto fileProvider = dynamic_cast<prv::FileProvider *>(provider)) {
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<prv::FileProvider*>(provider); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!fileProvider->open()) {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Provider::remove(provider);
return;
}
(void)fileProvider->open();
}
if (!provider->isWritable()) {
View::showErrorPopup("hex.builtin.popup.error.read_only"_lang);
}
if (!provider->isAvailable()) {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Provider::remove(provider);
return;
}
ProjectFile::setFilePath(path);
EventManager::post<EventFileLoaded>(path);
EventManager::post<EventDataChanged>();
EventManager::post<EventHighlightingChanged>();
}
void registerEventHandlers() {
EventManager::subscribe<EventProjectFileLoad>([]() {
EventManager::post<RequestOpenFile>(ProjectFile::getFilePath());
});
EventManager::subscribe<EventWindowClosing>([](GLFWwindow *window) {
if (ProjectFile::hasUnsavedChanges()) {
if (ImHexApi::Provider::isDirty()) {
glfwSetWindowShouldClose(window, GLFW_FALSE);
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.exit_application.title"_lang); });
}
});
EventManager::subscribe<EventProviderClosing>([](hex::prv::Provider *provider, bool *shouldClose) {
if (provider->isDirty()) {
*shouldClose = false;
ImHexApi::Tasks::doLater([] { ImGui::OpenPopup("hex.builtin.popup.close_provider.title"_lang); });
}
});
EventManager::subscribe<EventProviderChanged>([](hex::prv::Provider *oldProvider, hex::prv::Provider *newProvider) {
hex::unused(oldProvider);
if (newProvider != nullptr) {
EventManager::post<RequestChangeWindowTitle>(newProvider->getName());
} else {
EventManager::post<RequestChangeWindowTitle>("");
}
});
EventManager::subscribe<RequestOpenFile>(openFile);
EventManager::subscribe<RequestOpenWindow>([](const std::string &name) {
@@ -75,9 +68,7 @@ namespace hex::plugin::builtin {
EventManager::post<RequestOpenFile>(path);
});
} else if (name == "Open File") {
fs::openFileBrowser(fs::DialogMode::Open, {}, [](const auto &path) {
EventManager::post<RequestOpenFile>(path);
});
ImHexApi::Provider::createProvider("hex.builtin.provider.file");
} else if (name == "Open Project") {
fs::openFileBrowser(fs::DialogMode::Open, { {"Project File", "hexproj"} },
[](const auto &path) {
@@ -91,9 +82,34 @@ namespace hex::plugin::builtin {
});
EventManager::subscribe<EventProviderCreated>([](hex::prv::Provider *provider) {
if (provider->hasLoadInterface())
if (provider->shouldSkipLoadInterface())
return;
if (provider->hasFilePicker()) {
if (!provider->handleFilePicker()) {
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
return;
}
if (!provider->open()) {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
return;
}
}
else if (provider->hasLoadInterface())
EventManager::post<RequestOpenPopup>(View::toWindowName("hex.builtin.view.provider_settings.load_popup"));
else {
if (!provider->open() || !provider->isAvailable()) {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
}
}
});
EventManager::subscribe<EventProviderDeleted>([](hex::prv::Provider *provider) {
ProviderExtraData::erase(provider);
});
}
}

View File

@@ -4,9 +4,10 @@
#include <implot.h>
#include <hex/ui/view.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/patches.hpp>
#include <thread>
@@ -32,7 +33,7 @@ namespace hex::plugin::builtin {
for (const auto &unlocalizedProviderName : ContentRegistry::Provider::getEntries()) {
if (ImGui::MenuItem(LangEntry(unlocalizedProviderName))) {
EventManager::post<RequestCreateProvider>(unlocalizedProviderName, nullptr);
ImHexApi::Provider::createProvider(unlocalizedProviderName);
}
}
@@ -69,18 +70,14 @@ namespace hex::plugin::builtin {
}
if (ImGui::MenuItem("hex.builtin.menu.file.save_project"_lang, "", false, providerValid && provider->isWritable())) {
if (ProjectFile::getProjectFilePath() == "") {
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"}
},
[](std::fs::path path) {
if (path.extension() != ".hexproj") {
path.replace_extension(".hexproj");
}
fs::openFileBrowser(fs::DialogMode::Save, { {"Project File", "hexproj"} },
[](std::fs::path path) {
if (path.extension() != ".hexproj") {
path.replace_extension(".hexproj");
}
ProjectFile::store(path);
});
} else
ProjectFile::store();
ProjectFile::store(path);
});
}
});
@@ -254,15 +251,6 @@ namespace hex::plugin::builtin {
provider->redo();
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1050, [&] {
bool providerValid = ImHexApi::Provider::isValid();
auto selection = ImHexApi::HexEditor::getSelection();
if (ImGui::MenuItem("hex.builtin.menu.edit.bookmark"_lang, nullptr, false, selection.has_value() && providerValid)) {
ImHexApi::Bookmarks::add(selection->getStartAddress(), selection->getSize(), {}, {});
}
});
}
static void createViewMenu() {

View File

@@ -2,18 +2,18 @@
#include <hex/helpers/net.hpp>
#include <pl/token.hpp>
#include <pl/log_console.hpp>
#include <pl/evaluator.hpp>
#include <pl/core/token.hpp>
#include <pl/core/log_console.hpp>
#include <pl/core/evaluator.hpp>
#include <pl/patterns/pattern.hpp>
namespace hex::plugin::builtin {
void registerPatternLanguageFunctions() {
using namespace pl;
using namespace pl::core;
using FunctionParameterCount = pl::api::FunctionParameterCount;
api::Namespace nsStdHttp = { "builtin", "std", "http" };
pl::api::Namespace nsStdHttp = { "builtin", "std", "http" };
{
/* get(url) */
ContentRegistry::PatternLanguage::addDangerousFunction(nsStdHttp, "get", FunctionParameterCount::exactly(1), [](Evaluator *, auto params) -> std::optional<Token::Literal> {

View File

@@ -1,64 +1,11 @@
#include <hex/api/content_registry.hpp>
#include <hex/providers/provider.hpp>
#include <pl/evaluator.hpp>
#include <pl/core/evaluator.hpp>
namespace hex::plugin::builtin {
void registerPatternLanguagePragmas() {
ContentRegistry::PatternLanguage::addPragma("endian", [](pl::PatternLanguage &runtime, const std::string &value) {
if (value == "big") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::big);
return true;
} else if (value == "little") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::little);
return true;
} else if (value == "native") {
runtime.getInternals().evaluator->setDefaultEndian(std::endian::native);
return true;
} else
return false;
});
ContentRegistry::PatternLanguage::addPragma("eval_depth", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setEvaluationDepth(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("array_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setArrayLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("pattern_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setPatternLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("loop_limit", [](pl::PatternLanguage &runtime, const std::string &value) {
auto limit = strtol(value.c_str(), nullptr, 0);
if (limit <= 0)
return false;
runtime.getInternals().evaluator->setLoopLimit(limit);
return true;
});
ContentRegistry::PatternLanguage::addPragma("base_address", [](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
@@ -69,18 +16,6 @@ namespace hex::plugin::builtin {
return true;
});
ContentRegistry::PatternLanguage::addPragma("bitfield_order", [](pl::PatternLanguage &runtime, const std::string &value) {
if (value == "left_to_right") {
runtime.getInternals().evaluator->setBitfieldOrder(pl::BitfieldOrder::LeftToRight);
return true;
} else if (value == "right_to_left") {
runtime.getInternals().evaluator->setBitfieldOrder(pl::BitfieldOrder::RightToLeft);
return true;
} else {
return false;
}
});
ContentRegistry::PatternLanguage::addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
}

View File

@@ -2,15 +2,69 @@
#include "content/providers/gdb_provider.hpp"
#include "content/providers/file_provider.hpp"
#include "content/providers/null_provider.hpp"
#include "content/providers/disk_provider.hpp"
#include "content/providers/intel_hex_provider.hpp"
#include "content/providers/motorola_srec_provider.hpp"
#include <hex/api/project_file_manager.hpp>
#include <nlohmann/json.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::plugin::builtin {
void registerProviders() {
ContentRegistry::Provider::add<prv::FileProvider>("hex.builtin.provider.file", false);
ContentRegistry::Provider::add<prv::GDBProvider>("hex.builtin.provider.gdb");
ContentRegistry::Provider::add<prv::DiskProvider>("hex.builtin.provider.disk");
ContentRegistry::Provider::add<prv::FileProvider>(false);
ContentRegistry::Provider::add<prv::NullProvider>(false);
ContentRegistry::Provider::add<prv::DiskProvider>();
ContentRegistry::Provider::add<prv::GDBProvider>();
ContentRegistry::Provider::add<prv::IntelHexProvider>();
ContentRegistry::Provider::add<prv::MotorolaSRECProvider>();
ProjectFile::registerHandler({
.basePath = "providers",
.load = [](const std::fs::path &basePath, Tar &tar) {
auto json = nlohmann::json::parse(tar.readString(basePath / "providers.json"));
auto providerIds = json["providers"].get<std::vector<int>>();
bool success = true;
for (const auto &id : providerIds) {
auto providerSettings = nlohmann::json::parse(tar.readString(basePath / hex::format("{}.json", id)));
auto provider = ImHexApi::Provider::createProvider(providerSettings["type"].get<std::string>());
if (provider == nullptr) {
success = false;
continue;
}
provider->loadSettings(providerSettings["settings"]);
if (!provider->open())
success = false;
}
return success;
},
.store = [](const std::fs::path &basePath, Tar &tar) {
std::vector<int> providerIds;
for (const auto &provider : ImHexApi::Provider::getProviders()) {
auto id = provider->getID();
providerIds.push_back(id);
nlohmann::json json;
json["type"] = provider->getTypeName();
json["settings"] = provider->storeSettings();
tar.write(basePath / hex::format("{}.json", id), json.dump(4));
}
tar.write(basePath / "providers.json",
nlohmann::json({ {"providers", providerIds } }).dump(4)
);
return true;
}
});
}
}

View File

@@ -12,17 +12,21 @@
#include <imgui.h>
#if defined(OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#define lseek lseek64
#elif defined(OS_MACOS)
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif
namespace hex::plugin::builtin::prv {
@@ -31,16 +35,16 @@ namespace hex::plugin::builtin::prv {
this->reloadDrives();
}
DiskProvider::~DiskProvider() {
this->close();
}
bool DiskProvider::isAvailable() const {
#if defined(OS_WINDOWS)
return this->m_diskHandle != INVALID_HANDLE_VALUE;
#else
return this->m_diskHandle != -1;
#endif
#if defined(OS_WINDOWS)
return this->m_diskHandle != INVALID_HANDLE_VALUE;
#else
return this->m_diskHandle != -1;
#endif
}
bool DiskProvider::isReadable() const {
@@ -68,113 +72,120 @@ namespace hex::plugin::builtin::prv {
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
const auto &path = this->m_path.native();
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_diskHandle = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
if (this->m_diskHandle == INVALID_HANDLE_VALUE)
return false;
}
{
DISK_GEOMETRY_EX diskGeometry = { };
DWORD bytesRead = 0;
if (DeviceIoControl(
this->m_diskHandle,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
nullptr,
0,
&diskGeometry,
sizeof(DISK_GEOMETRY_EX),
&bytesRead,
nullptr)) {
this->m_diskSize = diskGeometry.DiskSize.QuadPart;
this->m_sectorSize = diskGeometry.Geometry.BytesPerSector;
this->m_sectorBuffer.resize(this->m_sectorSize);
if (this->m_diskHandle == INVALID_HANDLE_VALUE)
return false;
}
}
if (this->m_diskHandle == nullptr || this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_readable = false;
this->m_diskHandle = nullptr;
CloseHandle(this->m_diskHandle);
{
DISK_GEOMETRY_EX diskGeometry = { };
DWORD bytesRead = 0;
if (DeviceIoControl(
this->m_diskHandle,
IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
nullptr,
0,
&diskGeometry,
sizeof(DISK_GEOMETRY_EX),
&bytesRead,
nullptr)) {
this->m_diskSize = diskGeometry.DiskSize.QuadPart;
this->m_sectorSize = diskGeometry.Geometry.BytesPerSector;
this->m_sectorBuffer.resize(this->m_sectorSize);
}
}
return false;
}
if (this->m_diskHandle == nullptr || this->m_diskHandle == INVALID_HANDLE_VALUE) {
this->m_readable = false;
this->m_diskHandle = nullptr;
CloseHandle(this->m_diskHandle);
return false;
}
#else
const auto &path = this->m_path.native();
#else
struct stat driveStat;
const auto &path = this->m_path.native();
if (::stat(path.c_str(), &driveStat) == 0)
this->m_diskSize = driveStat.st_size;
else
this->m_diskSize = 0;
struct stat driveStat;
this->m_sectorSize = 0;
if (::stat(path.c_str(), &driveStat) == 0)
this->m_diskSize = driveStat.st_size;
else
this->m_diskSize = 0;
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
this->m_sectorSize = 0;
if (this->m_diskHandle == -1) {
this->m_readable = false;
return false;
}
this->m_diskHandle = ::open(path.c_str(), O_RDWR);
if (this->m_diskHandle == -1) {
this->m_diskHandle = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
}
#endif
if (this->m_diskHandle == -1) {
this->m_readable = false;
return false;
}
Provider::resize(this->getActualSize());
#endif
return true;
return Provider::open();
}
void DiskProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
#if defined(OS_WINDOWS)
this->m_diskHandle = INVALID_HANDLE_VALUE;
#else
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
if (this->m_diskHandle != INVALID_HANDLE_VALUE)
::CloseHandle(this->m_diskHandle);
this->m_diskHandle = -1;
#endif
this->m_diskHandle = INVALID_HANDLE_VALUE;
#else
if (this->m_diskHandle != -1)
::close(this->m_diskHandle);
this->m_diskHandle = -1;
#endif
Provider::close();
}
void DiskProvider::readRaw(u64 offset, void *buffer, size_t size) {
#if defined(OS_WINDOWS)
DWORD bytesRead = 0;
#if defined(OS_WINDOWS)
u64 startOffset = offset;
DWORD bytesRead = 0;
while (size > 0) {
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
u64 startOffset = offset;
if (this->m_sectorBufferAddress != static_cast<u64>(seekPosition.QuadPart)) {
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::ReadFile(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size(), &bytesRead, nullptr);
this->m_sectorBufferAddress = seekPosition.QuadPart;
while (size > 0) {
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
if (this->m_sectorBufferAddress != static_cast<u64>(seekPosition.QuadPart)) {
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::ReadFile(this->m_diskHandle, this->m_sectorBuffer.data(), this->m_sectorBuffer.size(), &bytesRead, nullptr);
this->m_sectorBufferAddress = seekPosition.QuadPart;
}
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size));
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
std::memcpy(reinterpret_cast<u8 *>(buffer) + (offset - startOffset), this->m_sectorBuffer.data() + (offset & (this->m_sectorSize - 1)), std::min(this->m_sectorSize, size));
#else
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#else
u64 startOffset = offset;
while (size > 0) {
@@ -193,59 +204,61 @@ namespace hex::plugin::builtin::prv {
size = std::max<ssize_t>(static_cast<ssize_t>(size) - this->m_sectorSize, 0);
offset += this->m_sectorSize;
}
#endif
#endif
}
void DiskProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
#if defined(OS_WINDOWS)
DWORD bytesWritten = 0;
#if defined(OS_WINDOWS)
u64 startOffset = offset;
DWORD bytesWritten = 0;
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
u64 startOffset = offset;
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::WriteFile(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size(), &bytesWritten, nullptr);
LARGE_INTEGER seekPosition;
seekPosition.LowPart = (offset & 0xFFFF'FFFF) - (offset % this->m_sectorSize);
seekPosition.HighPart = offset >> 32;
offset += currSize;
size -= currSize;
}
::SetFilePointer(this->m_diskHandle, seekPosition.LowPart, &seekPosition.HighPart, FILE_BEGIN);
::WriteFile(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size(), &bytesWritten, nullptr);
#else
offset += currSize;
size -= currSize;
}
u64 startOffset = offset;
#else
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
u64 startOffset = offset;
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
std::vector<u8> modifiedSectorBuffer;
modifiedSectorBuffer.resize(this->m_sectorSize);
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
while (size > 0) {
u64 sectorBase = offset - (offset % this->m_sectorSize);
size_t currSize = std::min(size, this->m_sectorSize);
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
this->readRaw(sectorBase, modifiedSectorBuffer.data(), modifiedSectorBuffer.size());
std::memcpy(modifiedSectorBuffer.data() + ((offset - sectorBase) % this->m_sectorSize), reinterpret_cast<const u8 *>(buffer) + (startOffset - offset), currSize);
offset += currSize;
size -= currSize;
}
::lseek(this->m_diskHandle, sectorBase, SEEK_SET);
if (::write(this->m_diskHandle, modifiedSectorBuffer.data(), modifiedSectorBuffer.size()) < 0)
break;
#endif
offset += currSize;
size -= currSize;
}
#endif
}
size_t DiskProvider::getActualSize() const {
@@ -266,66 +279,89 @@ namespace hex::plugin::builtin::prv {
void DiskProvider::reloadDrives() {
#if defined(OS_WINDOWS)
this->m_availableDrives.clear();
std::bitset<32> drives = ::GetLogicalDrives();
for (char i = 0; i < 26; i++) {
if (drives[i])
this->m_availableDrives.insert(hex::format(R"(\\.\{:c}:)", 'A' + i));
}
#if defined(OS_WINDOWS)
auto logicalDrives = this->m_availableDrives;
for (const auto &drive : logicalDrives) {
auto handle = reinterpret_cast<HANDLE>(::CreateFile(drive.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
if (handle == INVALID_HANDLE_VALUE) continue;
VOLUME_DISK_EXTENTS diskExtents = { };
DWORD bytesRead = 0;
auto result = ::DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr,
0,
&diskExtents,
sizeof(VOLUME_DISK_EXTENTS),
&bytesRead,
nullptr);
if (result) {
auto diskPath = hex::format(R"(\\.\PhysicalDrive{})", diskExtents.Extents[0].DiskNumber);
this->m_availableDrives.insert(diskPath);
this->m_availableDrives.clear();
std::bitset<32> drives = ::GetLogicalDrives();
for (char i = 0; i < 26; i++) {
if (drives[i])
this->m_availableDrives.insert(hex::format(R"(\\.\{:c}:)", 'A' + i));
}
::CloseHandle(handle);
}
#endif
auto logicalDrives = this->m_availableDrives;
for (const auto &drive : logicalDrives) {
auto handle = reinterpret_cast<HANDLE>(::CreateFile(drive.data(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr));
if (handle == INVALID_HANDLE_VALUE) continue;
VOLUME_DISK_EXTENTS diskExtents = { };
DWORD bytesRead = 0;
auto result = ::DeviceIoControl(
handle,
IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
nullptr,
0,
&diskExtents,
sizeof(VOLUME_DISK_EXTENTS),
&bytesRead,
nullptr);
if (result) {
auto diskPath = hex::format(R"(\\.\PhysicalDrive{})", diskExtents.Extents[0].DiskNumber);
this->m_availableDrives.insert(diskPath);
}
::CloseHandle(handle);
}
#endif
}
void DiskProvider::drawLoadInterface() {
#if defined(OS_WINDOWS)
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
#if defined(OS_WINDOWS)
for (const auto &drive : this->m_availableDrives) {
if (ImGui::Selectable(drive.c_str(), this->m_path == drive))
this->m_path = drive;
if (ImGui::BeginListBox("hex.builtin.provider.disk.selected_disk"_lang)) {
for (const auto &drive : this->m_availableDrives) {
if (ImGui::Selectable(drive.c_str(), this->m_path == drive))
this->m_path = drive;
}
ImGui::EndListBox();
}
ImGui::EndListBox();
ImGui::SameLine();
if (ImGui::Button("hex.builtin.provider.disk.reload"_lang)) {
this->reloadDrives();
}
ImGui::SameLine();
#else
if (ImGui::Button("hex.builtin.provider.disk.reload"_lang)) {
this->reloadDrives();
}
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
#else
#endif
}
if (ImGui::InputText("hex.builtin.provider.disk.selected_disk"_lang, this->m_pathBuffer.data(), this->m_pathBuffer.size(), ImGuiInputTextFlags_CallbackResize, ImGui::UpdateStringSizeCallback, &this->m_pathBuffer))
this->m_path = this->m_pathBuffer;
nlohmann::json DiskProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_path.string();
#endif
return Provider::storeSettings(settings);
}
void DiskProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->setPath(settings["path"].get<std::string>());
this->reloadDrives();
}
std::pair<Region, bool> DiskProvider::getRegionValidity(u64 address) const {
if (address < this->getActualSize())
return { Region { address, this->getActualSize() - address }, true };
else
return { Region::Invalid(), false };
}
}

View File

@@ -2,28 +2,22 @@
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
FileProvider::FileProvider() : Provider() {
}
FileProvider::~FileProvider() {
this->close();
}
bool FileProvider::isAvailable() const {
#if defined(OS_WINDOWS)
return this->m_file != INVALID_HANDLE_VALUE && this->m_mapping != INVALID_HANDLE_VALUE && this->m_mappedFile != nullptr;
#else
return this->m_file != -1 && this->m_mappedFile != nullptr;
#endif
#if defined(OS_WINDOWS)
return this->m_file != INVALID_HANDLE_VALUE && this->m_mapping != INVALID_HANDLE_VALUE && this->m_mappedFile != nullptr;
#else
return this->m_file != -1 && this->m_mappedFile != nullptr;
#endif
}
bool FileProvider::isReadable() const {
@@ -194,6 +188,12 @@ namespace hex::plugin::builtin::prv {
return result;
}
bool FileProvider::handleFilePicker() {
return fs::openFileBrowser(fs::DialogMode::Open, {}, [this](const auto &path) {
this->setPath(path);
});
}
void FileProvider::setPath(const std::fs::path &path) {
this->m_path = path;
}
@@ -202,121 +202,144 @@ namespace hex::plugin::builtin::prv {
this->m_readable = true;
this->m_writable = true;
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
#if defined(OS_WINDOWS)
const auto &path = this->m_path.native();
this->m_fileStatsValid = wstat(path.c_str(), &this->m_fileStats) == 0;
this->m_fileStatsValid = wstat(path.c_str(), &this->m_fileStats) == 0;
LARGE_INTEGER fileSize = {};
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
LARGE_INTEGER fileSize = {};
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
GetFileSizeEx(this->m_file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
CloseHandle(this->m_file);
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
}
auto fileCleanup = SCOPE_GUARD {
GetFileSizeEx(this->m_file, &fileSize);
this->m_fileSize = fileSize.QuadPart;
CloseHandle(this->m_file);
this->m_readable = false;
this->m_file = nullptr;
};
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return false;
}
if (this->m_fileSize > 0) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READONLY, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE)
return false;
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
this->m_file = reinterpret_cast<HANDLE>(CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
this->m_writable = false;
}
auto mappingCleanup = SCOPE_GUARD {
CloseHandle(this->m_mapping);
auto fileCleanup = SCOPE_GUARD {
CloseHandle(this->m_file);
this->m_mapping = nullptr;
this->m_readable = false;
this->m_file = nullptr;
};
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_READ, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
return false;
}
if (this->m_file == nullptr || this->m_file == INVALID_HANDLE_VALUE) {
return false;
}
mappingCleanup.release();
if (this->m_fileSize > 0) {
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READWRITE, 0, 0, nullptr);
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE) {
ProjectFile::setFilePath(this->m_path);
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
} else {
return false;
}
this->m_mapping = CreateFileMapping(this->m_file, nullptr, PAGE_READONLY, 0, 0, nullptr);
fileCleanup.release();
if (this->m_mapping == nullptr || this->m_mapping == INVALID_HANDLE_VALUE)
return false;
}
#else
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
auto mappingCleanup = SCOPE_GUARD {
CloseHandle(this->m_mapping);
int mmapprot = PROT_READ | PROT_WRITE;
this->m_mapping = nullptr;
this->m_readable = false;
};
this->m_file = ::open(path.c_str(), O_RDWR);
if (this->m_file == -1) {
this->m_file = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
mmapprot &= ~(PROT_WRITE);
}
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_ALL_ACCESS, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
if (this->m_file == -1) {
this->m_readable = false;
return false;
}
this->m_mappedFile = MapViewOfFile(this->m_mapping, FILE_MAP_READ, 0, 0, this->m_fileSize);
if (this->m_mappedFile == nullptr) {
this->m_readable = false;
this->m_fileSize = this->m_fileStats.st_size;
return false;
}
}
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, mmapprot, MAP_SHARED, this->m_file, 0);
if (this->m_mappedFile == MAP_FAILED) {
::close(this->m_file);
this->m_file = -1;
mappingCleanup.release();
} else if (!this->m_emptyFile) {
this->m_emptyFile = true;
this->resize(1);
} else {
return false;
}
return false;
}
#endif
fileCleanup.release();
Provider::resize(this->getActualSize());
#else
return true;
const auto &path = this->m_path.native();
this->m_fileStatsValid = stat(path.c_str(), &this->m_fileStats) == 0;
int mmapprot = PROT_READ | PROT_WRITE;
this->m_file = ::open(path.c_str(), O_RDWR);
if (this->m_file == -1) {
this->m_file = ::open(path.c_str(), O_RDONLY);
this->m_writable = false;
mmapprot &= ~(PROT_WRITE);
}
if (this->m_file == -1) {
this->m_readable = false;
return false;
}
this->m_fileSize = this->m_fileStats.st_size;
this->m_mappedFile = ::mmap(nullptr, this->m_fileSize, mmapprot, MAP_SHARED, this->m_file, 0);
if (this->m_mappedFile == MAP_FAILED) {
::close(this->m_file);
this->m_file = -1;
return false;
}
#endif
return Provider::open();
}
void FileProvider::close() {
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
::UnmapViewOfFile(this->m_mappedFile);
if (this->m_mapping != nullptr)
::CloseHandle(this->m_mapping);
if (this->m_file != nullptr)
::CloseHandle(this->m_file);
#else
::munmap(this->m_mappedFile, this->m_fileSize);
::close(this->m_file);
#endif
#if defined(OS_WINDOWS)
if (this->m_mappedFile != nullptr)
::UnmapViewOfFile(this->m_mappedFile);
if (this->m_mapping != nullptr)
::CloseHandle(this->m_mapping);
if (this->m_file != nullptr)
::CloseHandle(this->m_file);
#else
::munmap(this->m_mappedFile, this->m_fileSize);
::close(this->m_file);
#endif
Provider::close();
}
void FileProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->setPath(settings["path"].get<std::string>());
}
nlohmann::json FileProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_path.string();
return Provider::storeSettings(settings);
}
std::pair<Region, bool> FileProvider::getRegionValidity(u64 address) const {
if (address < this->getActualSize())
return { Region { address, this->getActualSize() - address }, true };
else
return { Region::Invalid(), false };
}
}

View File

@@ -11,6 +11,8 @@
#include <hex/helpers/crypto.hpp>
#include <hex/api/localization.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
using namespace std::chrono_literals;
@@ -119,11 +121,6 @@ namespace hex::plugin::builtin::prv {
GDBProvider::GDBProvider() : Provider(), m_size(0xFFFF'FFFF) {
}
GDBProvider::~GDBProvider() {
this->close();
}
bool GDBProvider::isAvailable() const {
return this->m_socket.isConnected();
}
@@ -276,9 +273,7 @@ namespace hex::plugin::builtin::prv {
}
});
Provider::resize(this->getActualSize());
return true;
return Provider::open();
} else {
return false;
}
@@ -290,6 +285,8 @@ namespace hex::plugin::builtin::prv {
if (this->m_cacheUpdateThread.joinable()) {
this->m_cacheUpdateThread.join();
}
Provider::close();
}
bool GDBProvider::isConnected() const {
@@ -311,4 +308,27 @@ namespace hex::plugin::builtin::prv {
this->m_port = 0xFFFF;
}
void GDBProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->m_ipAddress = settings["ip"].get<std::string>();
this->m_port = settings["port"].get<int>();
this->m_size = settings["size"].get<size_t>();
}
nlohmann::json GDBProvider::storeSettings(nlohmann::json settings) const {
settings["ip"] = this->m_ipAddress;
settings["port"] = this->m_port;
settings["size"] = this->m_size;
return Provider::storeSettings(settings);
}
std::pair<Region, bool> GDBProvider::getRegionValidity(u64 address) const {
if (address < this->getActualSize())
return { Region { address, this->getActualSize() - address }, true };
else
return { Region::Invalid(), false };
}
}

View File

@@ -0,0 +1,248 @@
#include "content/providers/intel_hex_provider.hpp"
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fmt.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
namespace intel_hex {
u8 parseHexDigit(char c) {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
throw std::runtime_error("Failed to parse hex digit");
}
std::map<u64, std::vector<u8>> parseIntelHex(const std::string &string) {
std::map<u64, std::vector<u8>> result;
u8 checksum = 0x00;
u64 offset = 0x00;
u8 byteCount = 0x00;
u32 segmentAddress = 0x0000'0000;
u32 extendedLinearAddress = 0x0000'0000;
u16 address = 0x0000;
std::vector<u8> data;
enum class RecordType {
Data = 0x00,
EndOfFile = 0x01,
ExtendedSegmentAddress = 0x02,
StartSegmentAddress = 0x03,
ExtendedLinearAddress = 0x04,
StartLinearAddress = 0x05
} recordType;
auto c = [&]() {
while (std::isspace(string[offset]) && offset < string.length())
offset++;
if (offset >= string.length())
throw std::runtime_error("Unexpected end of file");
return string[offset++];
};
auto parseValue = [&](u8 byteCount) {
u64 value = 0x00;
for (u8 i = 0; i < byteCount; i++) {
u8 byte = (parseHexDigit(c()) << 4) | parseHexDigit(c());
value <<= 8;
value |= byte;
checksum += byte;
}
return value;
};
bool endOfFile = false;
try {
while (offset < string.length()) {
// Parse start code
if (c() != ':')
return { };
checksum = 0x00;
if (endOfFile)
throw std::runtime_error("Unexpected end of file");
// Parse byte count
byteCount = parseValue(1);
// Parse address
address = parseValue(2);
// Parse record type
recordType = static_cast<RecordType>(parseValue(1));
data.clear();
for (u32 i = 0; i < byteCount; i++) {
data.push_back(parseValue(1));
}
parseValue(1);
if (!data.empty() && checksum != 0x00)
throw std::runtime_error("Checksum mismatch");
while (std::isspace(string[offset]) && offset < string.length())
offset++;
// Construct region
switch (recordType) {
case RecordType::Data: {
result[extendedLinearAddress | (segmentAddress + address)] = data;
break;
}
case RecordType::EndOfFile: {
endOfFile = true;
break;
}
case RecordType::ExtendedSegmentAddress: {
if (byteCount != 2)
throw std::runtime_error("Unexpected byte count");
segmentAddress = (data[0] << 8 | data[1]) * 16;
break;
}
case RecordType::StartSegmentAddress: {
if (byteCount != 4)
throw std::runtime_error("Unexpected byte count");
// Can be safely ignored
break;
}
case RecordType::ExtendedLinearAddress: {
if (byteCount != 2)
throw std::runtime_error("Unexpected byte count");
extendedLinearAddress = (data[0] << 8 | data[1]) << 16;
break;
}
case RecordType::StartLinearAddress: {
if (byteCount != 4)
throw std::runtime_error("Unexpected byte count");
// Can be safely ignored
break;
}
}
}
} catch (const std::runtime_error &e) {
return { };
}
return result;
}
}
void IntelHexProvider::readRaw(u64 offset, void *buffer, size_t size) {
auto intervals = this->m_data.findOverlapping(offset, (offset + size) - 1);
std::memset(buffer, 0x00, size);
auto bytes = reinterpret_cast<u8*>(buffer);
for (const auto &interval : intervals) {
for (u32 i = std::max(interval.start, offset); i <= interval.stop && (i - offset) < size; i++) {
bytes[i - offset] = interval.value[i - interval.start];
}
}
}
void IntelHexProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
hex::unused(offset, buffer, size);
}
size_t IntelHexProvider::getActualSize() const {
return this->m_dataSize;
}
bool IntelHexProvider::open() {
auto file = fs::File(this->m_sourceFilePath, fs::File::Mode::Read);
if (!file.isValid())
return false;
auto data = intel_hex::parseIntelHex(file.readString());
if (data.empty())
return false;
u64 maxAddress = 0x00;
decltype(this->m_data)::interval_vector intervals;
for (auto &[address, bytes] : data) {
auto endAddress = (address + bytes.size()) - 1;
intervals.emplace_back(address, endAddress, std::move(bytes));
if (endAddress > maxAddress)
maxAddress = endAddress;
}
this->m_data = std::move(intervals);
this->m_dataSize = maxAddress + 1;
this->m_dataValid = true;
return Provider::open();
}
void IntelHexProvider::close() {
Provider::close();
}
[[nodiscard]] std::string IntelHexProvider::getName() const {
return hex::format("hex.builtin.provider.intel_hex.name"_lang, this->m_sourceFilePath.filename().string());
}
bool IntelHexProvider::handleFilePicker() {
auto picked = fs::openFileBrowser(fs::DialogMode::Open, { { "Intel Hex File", "*" } }, [this](const std::fs::path &path) {
this->m_sourceFilePath = path;
});
if (!picked)
return false;
if (!fs::isRegularFile(this->m_sourceFilePath))
return false;
return true;
}
std::pair<Region, bool> IntelHexProvider::getRegionValidity(u64 address) const {
auto intervals = this->m_data.findOverlapping(address, address);
if (intervals.empty()) {
return Provider::getRegionValidity(address);
}
auto closestInterval = intervals.front();
for (const auto &interval : intervals) {
if (interval.start < closestInterval.start)
closestInterval = interval;
}
return { Region { closestInterval.start, (closestInterval.stop - closestInterval.start) + 1}, true };
}
void IntelHexProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->m_sourceFilePath = settings["path"].get<std::string>();
}
nlohmann::json IntelHexProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_sourceFilePath.string();
return Provider::storeSettings(settings);
}
}

View File

@@ -0,0 +1,216 @@
#include "content/providers/motorola_srec_provider.hpp"
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/fmt.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin::prv {
namespace motorola_srec {
u8 parseHexDigit(char c) {
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else
throw std::runtime_error("Failed to parse hex digit");
}
std::map<u64, std::vector<u8>> parseMotorolaSREC(const std::string &string) {
std::map<u64, std::vector<u8>> result;
u64 offset = 0x00;
u8 checksum = 0x00;
u8 byteCount = 0x00;
u32 address = 0x0000'0000;
std::vector<u8> data;
auto c = [&]() {
while (std::isspace(string[offset]) && offset < string.length())
offset++;
if (offset >= string.length())
throw std::runtime_error("Unexpected end of file");
return string[offset++];
};
auto parseValue = [&](u8 byteCount) {
u64 value = 0x00;
for (u8 i = 0; i < byteCount; i++) {
u8 byte = (parseHexDigit(c()) << 4) | parseHexDigit(c());
value <<= 8;
value |= byte;
checksum += byte;
}
return value;
};
enum class RecordType {
Header = 0x00,
Data16 = 0x01,
Data24 = 0x02,
Data32 = 0x03,
Reserved = 0x04,
Count16 = 0x05,
Count24 = 0x06,
StartAddress32 = 0x07,
StartAddress24 = 0x08,
StartAddress16 = 0x09,
} recordType;
bool endOfFile = false;
try {
while (offset < string.length()) {
// Parse record start
if (c() != 'S')
return { };
if (endOfFile)
throw std::runtime_error("Unexpected end of file");
// Parse record type
{
char typeCharacter = c();
if (typeCharacter < '0' || typeCharacter > '9')
throw std::runtime_error("Invalid record type");
recordType = static_cast<RecordType>(typeCharacter - '0');
}
checksum = 0x00;
// Parse byte count
byteCount = parseValue(1);
// Parse address
switch (recordType) {
case RecordType::Reserved:
break;
case RecordType::Header:
case RecordType::Data16:
case RecordType::Count16:
case RecordType::StartAddress16:
byteCount -= 2;
address = parseValue(2);
break;
case RecordType::Data24:
case RecordType::Count24:
case RecordType::StartAddress24:
byteCount -= 3;
address = parseValue(3);
break;
case RecordType::Data32:
case RecordType::StartAddress32:
byteCount -= 4;
address = parseValue(4);
break;
}
byteCount -= 1;
auto readData = [&byteCount, &parseValue]() {
std::vector<u8> bytes;
bytes.resize(byteCount);
for (u8 i = 0; i < byteCount; i++) {
bytes[i] = parseValue(1);
}
return bytes;
};
// Parse data
data = readData();
// Parse checksum
{
auto value = parseValue(1);
if (((checksum - value) ^ 0xFF) != value)
throw std::runtime_error("Invalid checksum");
}
// Construct region
switch (recordType) {
case RecordType::Data16:
case RecordType::Data24:
case RecordType::Data32:
result[address] = data;
break;
case RecordType::Header:
case RecordType::Reserved:
break;
case RecordType::Count16:
case RecordType::Count24:
break;
case RecordType::StartAddress32:
case RecordType::StartAddress24:
case RecordType::StartAddress16:
endOfFile = true;
break;
}
}
} catch (const std::runtime_error &e) {
return { };
}
return result;
}
}
bool MotorolaSRECProvider::open() {
auto file = fs::File(this->m_sourceFilePath, fs::File::Mode::Read);
if (!file.isValid())
return false;
auto data = motorola_srec::parseMotorolaSREC(file.readString());
if (data.empty())
return false;
u64 maxAddress = 0x00;
decltype(this->m_data)::interval_vector intervals;
for (auto &[address, bytes] : data) {
auto endAddress = (address + bytes.size()) - 1;
intervals.emplace_back(address, endAddress, std::move(bytes));
if (endAddress > maxAddress)
maxAddress = endAddress;
}
this->m_data = std::move(intervals);
this->m_dataSize = maxAddress + 1;
this->m_dataValid = true;
return Provider::open();
}
void MotorolaSRECProvider::close() {
Provider::close();
}
[[nodiscard]] std::string MotorolaSRECProvider::getName() const {
return hex::format("hex.builtin.provider.motorola_srec.name"_lang, this->m_sourceFilePath.filename().string());
}
bool MotorolaSRECProvider::handleFilePicker() {
auto picked = fs::openFileBrowser(fs::DialogMode::Open, { { "Motorola SREC File", "*" } }, [this](const std::fs::path &path) {
this->m_sourceFilePath = path;
});
if (!picked)
return false;
if (!fs::isRegularFile(this->m_sourceFilePath))
return false;
return true;
}
}

View File

@@ -37,15 +37,21 @@ namespace hex::plugin::builtin {
using namespace hex::literals;
void drawDemangler() {
static std::vector<char> mangledBuffer(0xF'FFFF, 0x00);
static std::string demangledName;
static std::string mangledName, demangledName;
if (ImGui::InputText("hex.builtin.tools.demangler.mangled"_lang, mangledBuffer.data(), 0xF'FFFF)) {
demangledName = llvm::demangle(mangledBuffer.data());
if (ImGui::InputTextWithHint("hex.builtin.tools.demangler.mangled"_lang, "Itanium, MSVC, Dlang & Rust", mangledName)) {
demangledName = llvm::demangle(mangledName);
if (demangledName == mangledName) {
demangledName = "???";
}
}
ImGui::InputText("hex.builtin.tools.demangler.demangled"_lang, demangledName.data(), demangledName.size(), ImGuiInputTextFlags_ReadOnly);
ImGui::NewLine();
ImGui::Header("hex.builtin.tools.demangler.demangled"_lang);
if (ImGui::BeginChild("demangled", ImVec2(0, 200_scaled), true)) {
ImGui::TextFormattedWrapped("{}", demangledName);
}
ImGui::EndChild();
}
void drawASCIITable() {

View File

@@ -28,8 +28,27 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.popup.exit_application.desc"_lang);
ImGui::NewLine();
View::confirmButtons(
"hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang, [] { ImHexApi::Common::closeImHex(true); }, [] { ImGui::CloseCurrentPopup(); });
View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
[] { ImHexApi::Common::closeImHex(true); },
[] { ImGui::CloseCurrentPopup(); }
);
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
// "Are you sure you want to close provider?" Popup
if (ImGui::BeginPopupModal("hex.builtin.popup.close_provider.title"_lang, nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::NewLine();
ImGui::TextUnformatted("hex.builtin.popup.close_provider.desc"_lang);
ImGui::NewLine();
View::confirmButtons("hex.builtin.common.yes"_lang, "hex.builtin.common.no"_lang,
[] { ImHexApi::Provider::remove(ImHexApi::Provider::impl::getClosingProvider(), true); ImGui::CloseCurrentPopup(); },
[] { ImGui::CloseCurrentPopup(); }
);
if (ImGui::IsKeyDown(ImGui::GetKeyIndex(ImGuiKey_Escape)))
ImGui::CloseCurrentPopup();
@@ -313,7 +332,7 @@ namespace hex::plugin::builtin {
bool open = true;
ImGui::PushID(tabProvider);
if (ImGui::BeginTabItem(tabProvider->getName().c_str(), &open)) {
if (ImGui::BeginTabItem(tabProvider->getName().c_str(), &open, provider->isDirty() ? ImGuiTabItemFlags_UnsavedDocument : ImGuiTabItemFlags_None)) {
ImHexApi::Provider::setCurrentProvider(i);
ImGui::EndTabItem();
}

View File

@@ -1,16 +1,20 @@
#include "content/views/view_bookmarks.hpp"
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <nlohmann/json.hpp>
#include <cstring>
#include <provider_extra_data.hpp>
namespace hex::plugin::builtin {
ViewBookmarks::ViewBookmarks() : View("hex.builtin.view.bookmarks.name") {
EventManager::subscribe<RequestAddBookmark>(this, [this](Region region, std::string name, std::string comment, color_t color) {
EventManager::subscribe<RequestAddBookmark>(this, [](Region region, std::string name, std::string comment, color_t color) {
if (name.empty()) {
name = hex::format("hex.builtin.view.bookmarks.default_title"_lang, region.address, region.address + region.size - 1);
}
@@ -18,34 +22,21 @@ namespace hex::plugin::builtin {
if (color == 0x00)
color = ImGui::GetColorU32(ImGuiCol_Header);
this->m_bookmarks.push_back({ region,
ProviderExtraData::getCurrent().bookmarks.push_back({
region,
name,
std::move(comment),
color,
false
});
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
});
EventManager::subscribe<EventProjectFileLoad>(this, [this] {
this->m_bookmarks = ProjectFile::getBookmarks();
});
EventManager::subscribe<EventProjectFileStore>(this, [this] {
ProjectFile::setBookmarks(this->m_bookmarks);
});
EventManager::subscribe<EventProviderDeleted>(this, [this](const auto*) {
this->m_bookmarks.clear();
});
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
ImHexApi::HexEditor::addBackgroundHighlightingProvider([](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
hex::unused(data);
for (const auto &bookmark : this->m_bookmarks) {
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
if (Region { address, size }.isWithin(bookmark.region))
return bookmark.color;
}
@@ -53,9 +44,9 @@ namespace hex::plugin::builtin {
return std::nullopt;
});
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
ImHexApi::HexEditor::addTooltipProvider([](u64 address, const u8 *data, size_t size) {
hex::unused(data);
for (const auto &bookmark : this->m_bookmarks) {
for (const auto &bookmark : ProviderExtraData::getCurrent().bookmarks) {
if (!Region { address, size }.isWithin(bookmark.region))
continue;
@@ -109,15 +100,34 @@ namespace hex::plugin::builtin {
ImGui::EndTooltip();
}
});
ProjectFile::registerPerProviderHandler({
.basePath = "bookmarks.json",
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
auto fileContent = tar.read(basePath);
if (fileContent.empty())
return true;
auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end());
ProviderExtraData::get(provider).bookmarks.clear();
return ViewBookmarks::importBookmarks(provider, data);
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) -> bool {
nlohmann::json data;
bool result = ViewBookmarks::exportBookmarks(provider, data);
tar.write(basePath, data.dump(4));
return result;
}
});
this->registerMenuItems();
}
ViewBookmarks::~ViewBookmarks() {
EventManager::unsubscribe<RequestAddBookmark>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProviderDeleted>(this);
this->m_bookmarks.clear();
}
void ViewBookmarks::drawContent() {
@@ -130,14 +140,14 @@ namespace hex::plugin::builtin {
ImGui::NewLine();
if (ImGui::BeginChild("##bookmarks")) {
if (this->m_bookmarks.empty()) {
auto &bookmarks = ProviderExtraData::getCurrent().bookmarks;
if (bookmarks.empty()) {
ImGui::TextFormattedCentered("hex.builtin.view.bookmarks.no_bookmarks"_lang);
}
u32 id = 1;
auto bookmarkToRemove = this->m_bookmarks.end();
for (auto iter = this->m_bookmarks.begin(); iter != this->m_bookmarks.end(); iter++) {
auto bookmarkToRemove = bookmarks.end();
for (auto iter = bookmarks.begin(); iter != bookmarks.end(); iter++) {
auto &[region, name, comment, color, locked] = *iter;
if (!this->m_currFilter.empty()) {
@@ -248,9 +258,8 @@ namespace hex::plugin::builtin {
id++;
}
if (bookmarkToRemove != this->m_bookmarks.end()) {
this->m_bookmarks.erase(bookmarkToRemove);
ProjectFile::markDirty();
if (bookmarkToRemove != bookmarks.end()) {
bookmarks.erase(bookmarkToRemove);
}
}
ImGui::EndChild();
@@ -258,4 +267,82 @@ namespace hex::plugin::builtin {
ImGui::End();
}
bool ViewBookmarks::importBookmarks(prv::Provider *provider, const nlohmann::json &json) {
if (!json.contains("bookmarks"))
return false;
auto &bookmarks = ProviderExtraData::get(provider).bookmarks;
for (const auto &bookmark : json["bookmarks"]) {
if (!bookmark.contains("name") || !bookmark.contains("comment") || !bookmark.contains("color") || !bookmark.contains("region") || !bookmark.contains("locked"))
continue;
const auto &region = bookmark["region"];
if (!region.contains("address") || !region.contains("size"))
continue;
bookmarks.push_back({
.region = { region["address"], region["size"] },
.name = bookmark["name"],
.comment = bookmark["comment"],
.color = bookmark["color"],
.locked = bookmark["locked"]
});
}
return true;
}
bool ViewBookmarks::exportBookmarks(prv::Provider *provider, nlohmann::json &json) {
json["bookmarks"] = nlohmann::json::array();
size_t index = 0;
for (const auto &bookmark : ProviderExtraData::get(provider).bookmarks) {
json["bookmarks"][index] = {
{ "name", bookmark.name },
{ "comment", bookmark.comment },
{ "color", bookmark.color },
{ "region", {
{ "address", bookmark.region.address },
{ "size", bookmark.region.size }
}
},
{ "locked", bookmark.locked }
};
index++;
}
return true;
}
void ViewBookmarks::registerMenuItems() {
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.edit", 1050, [&] {
bool providerValid = ImHexApi::Provider::isValid();
auto selection = ImHexApi::HexEditor::getSelection();
if (ImGui::MenuItem("hex.builtin.menu.edit.bookmark.create"_lang, nullptr, false, selection.has_value() && providerValid)) {
ImHexApi::Bookmarks::add(selection->getStartAddress(), selection->getSize(), {}, {});
}
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 4000, [&] {
bool providerValid = ImHexApi::Provider::isValid();
auto selection = ImHexApi::HexEditor::getSelection();
if (ImGui::MenuItem("hex.builtin.menu.file.bookmark.import"_lang, nullptr, false, selection.has_value() && providerValid)) {
fs::openFileBrowser(fs::DialogMode::Open, { { "Bookmarks File", "hexbm"} }, [&](const std::fs::path &path) {
try {
importBookmarks(ImHexApi::Provider::get(), nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString()));
} catch (...) { }
});
}
if (ImGui::MenuItem("hex.builtin.menu.file.bookmark.export"_lang, nullptr, false, selection.has_value() && providerValid)) {
fs::openFileBrowser(fs::DialogMode::Save, { { "Bookmarks File", "hexbm"} }, [&](const std::fs::path &path) {
nlohmann::json json;
exportBookmarks(ImHexApi::Provider::get(), json);
fs::File(path, fs::File::Mode::Create).write(json.dump(4));
});
}
});
}
}

View File

@@ -5,11 +5,13 @@
#include <hex/helpers/file.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imnodes.h>
#include <nlohmann/json.hpp>
#include "provider_extra_data.hpp"
namespace hex::plugin::builtin {
ViewDataProcessor::ViewDataProcessor() : View("hex.builtin.view.data_processor.name") {
@@ -30,24 +32,28 @@ namespace hex::plugin::builtin {
ImNodes::GetStyle().Flags = ImNodesStyleFlags_NodeOutline | ImNodesStyleFlags_GridLines;
});
EventManager::subscribe<EventProjectFileStore>(this, [this] {
ProjectFile::setDataProcessorContent(this->saveNodes());
});
ProjectFile::registerPerProviderHandler({
.basePath = "data_processor.json",
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto save = tar.readString(basePath);
EventManager::subscribe<EventProjectFileLoad>(this, [this] {
try {
this->loadNodes(ProjectFile::getDataProcessorContent());
} catch (nlohmann::json::exception &e) {
this->loadNodes(provider, save);
return true;
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
tar.write(basePath, this->saveNodes(provider));
return true;
}
});
EventManager::subscribe<EventFileLoaded>(this, [this](const std::fs::path &path) {
hex::unused(path);
for (auto &node : this->m_nodes) {
EventManager::subscribe<EventProviderChanged>(this, [](const auto &, const auto &) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
for (auto &node : data.nodes) {
node->setCurrentOverlay(nullptr);
}
this->m_dataOverlays.clear();
data.dataOverlays.clear();
});
EventManager::subscribe<EventDataChanged>(this, [this] {
@@ -56,22 +62,25 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 3000, [&, this] {
bool providerValid = ImHexApi::Provider::isValid();
auto provider = ImHexApi::Provider::get();
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.load_processor"_lang, nullptr, false, providerValid)) {
fs::openFileBrowser(fs::DialogMode::Open, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
[this](const std::fs::path &path) {
[&, this](const std::fs::path &path) {
fs::File file(path, fs::File::Mode::Read);
if (file.isValid())
this->loadNodes(file.readString());
this->loadNodes(provider, file.readString());
});
}
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !this->m_nodes.empty() && providerValid)) {
if (ImGui::MenuItem("hex.builtin.view.data_processor.menu.file.save_processor"_lang, nullptr, false, !data.nodes.empty() && providerValid)) {
fs::openFileBrowser(fs::DialogMode::Save, { {"hex.builtin.view.data_processor.name"_lang, "hexnode"} },
[this](const std::fs::path &path) {
[&, this](const std::fs::path &path) {
fs::File file(path, fs::File::Mode::Create);
if (file.isValid())
file.write(this->saveNodes());
file.write(this->saveNodes(provider));
});
}
});
@@ -80,44 +89,45 @@ namespace hex::plugin::builtin {
fs::File file(path, fs::File::Mode::Read);
if (!file.isValid()) return false;
this->loadNodes(file.readString());
this->loadNodes(ImHexApi::Provider::get(), file.readString());
return true;
});
}
ViewDataProcessor::~ViewDataProcessor() {
for (auto &node : this->m_nodes)
delete node;
EventManager::unsubscribe<RequestChangeTheme>(this);
EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<EventDataChanged>(this);
}
void ViewDataProcessor::eraseLink(u32 id) {
auto link = std::find_if(this->m_links.begin(), this->m_links.end(), [&id](auto link) { return link.getId() == id; });
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (link == this->m_links.end())
auto link = std::find_if(data.links.begin(), data.links.end(), [&id](auto link) { return link.getId() == id; });
if (link == data.links.end())
return;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
attribute.removeConnectedAttribute(id);
}
}
this->m_links.erase(link);
data.links.erase(link);
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::eraseNodes(const std::vector<int> &ids) {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
for (u32 id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node) { return node->getId() == id; });
auto node = std::find_if(data.nodes.begin(), data.nodes.end(),
[&id](const auto &node) {
return node->getId() == id;
});
for (auto &attr : (*node)->getAttributes()) {
std::vector<u32> linksToRemove;
@@ -130,52 +140,51 @@ namespace hex::plugin::builtin {
}
for (u32 id : ids) {
auto node = std::find_if(this->m_nodes.begin(), this->m_nodes.end(), [&id](auto node) { return node->getId() == id; });
auto node = std::find_if(data.nodes.begin(), data.nodes.end(), [&id](const auto &node) { return node->getId() == id; });
std::erase_if(this->m_endNodes, [&id](auto node) { return node->getId() == id; });
std::erase_if(data.endNodes, [&id](const auto &node) { return node->getId() == id; });
delete *node;
this->m_nodes.erase(node);
data.nodes.erase(node);
}
ProjectFile::markDirty();
ImHexApi::Provider::markDirty();
}
void ViewDataProcessor::processNodes() {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (this->m_dataOverlays.size() != this->m_endNodes.size()) {
for (auto overlay : this->m_dataOverlays)
if (data.dataOverlays.size() != data.endNodes.size()) {
for (auto overlay : data.dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay);
this->m_dataOverlays.clear();
data.dataOverlays.clear();
for (u32 i = 0; i < this->m_endNodes.size(); i++)
this->m_dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
for (u32 i = 0; i < data.endNodes.size(); i++)
data.dataOverlays.push_back(ImHexApi::Provider::get()->newOverlay());
u32 overlayIndex = 0;
for (auto endNode : this->m_endNodes) {
endNode->setCurrentOverlay(this->m_dataOverlays[overlayIndex]);
for (auto endNode : data.endNodes) {
endNode->setCurrentOverlay(data.dataOverlays[overlayIndex]);
overlayIndex++;
}
}
this->m_currNodeError.reset();
data.currNodeError.reset();
try {
for (auto &endNode : this->m_endNodes) {
for (auto &endNode : data.endNodes) {
endNode->resetOutputData();
for (auto &node : this->m_nodes)
for (auto &node : data.nodes)
node->resetProcessedInputs();
endNode->process();
}
} catch (dp::Node::NodeError &e) {
this->m_currNodeError = e;
data.currNodeError = e;
for (auto overlay : this->m_dataOverlays)
for (auto overlay : data.dataOverlays)
ImHexApi::Provider::get()->deleteOverlay(overlay);
this->m_dataOverlays.clear();
data.dataOverlays.clear();
} catch (std::runtime_error &e) {
printf("Node implementation bug! %s\n", e.what());
@@ -183,6 +192,8 @@ namespace hex::plugin::builtin {
}
void ViewDataProcessor::drawContent() {
auto &data = ProviderExtraData::getCurrent().dataProcessor;
if (ImGui::Begin(View::toWindowName("hex.builtin.view.data_processor.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
if (ImGui::IsMouseReleased(ImGuiMouseButton_Right) && ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows)) {
@@ -200,7 +211,7 @@ namespace hex::plugin::builtin {
}
if (ImGui::BeginPopup("Context Menu")) {
dp::Node *node = nullptr;
std::unique_ptr<dp::Node> node;
if (ImNodes::NumSelectedNodes() > 0 || ImNodes::NumSelectedLinks() > 0) {
if (ImGui::MenuItem("hex.builtin.view.data_processor.name"_lang)) {
@@ -226,13 +237,11 @@ namespace hex::plugin::builtin {
} else if (unlocalizedCategory.empty()) {
if (ImGui::MenuItem(LangEntry(unlocalizedName))) {
node = function();
ProjectFile::markDirty();
}
} else {
if (ImGui::BeginMenu(LangEntry(unlocalizedCategory))) {
if (ImGui::MenuItem(LangEntry(unlocalizedName))) {
node = function();
ProjectFile::markDirty();
}
ImGui::EndMenu();
}
@@ -240,8 +249,6 @@ namespace hex::plugin::builtin {
}
if (node != nullptr) {
this->m_nodes.push_back(node);
bool hasOutput = false;
bool hasInput = false;
for (auto &attr : node->getAttributes()) {
@@ -253,9 +260,11 @@ namespace hex::plugin::builtin {
}
if (hasInput && !hasOutput)
this->m_endNodes.push_back(node);
data.endNodes.push_back(node.get());
ImNodes::SetNodeScreenSpacePos(node->getId(), this->m_rightClickedCoords);
data.nodes.push_back(std::move(node));
ImHexApi::Provider::markDirty();
}
ImGui::EndPopup();
@@ -277,11 +286,11 @@ namespace hex::plugin::builtin {
{
int nodeId;
if (ImNodes::IsNodeHovered(&nodeId) && this->m_currNodeError.has_value() && this->m_currNodeError->first->getId() == static_cast<u32>(nodeId)) {
if (ImNodes::IsNodeHovered(&nodeId) && data.currNodeError.has_value() && data.currNodeError->node->getId() == static_cast<u32>(nodeId)) {
ImGui::BeginTooltip();
ImGui::TextUnformatted("hex.builtin.common.error"_lang);
ImGui::Separator();
ImGui::TextUnformatted(this->m_currNodeError->second.c_str());
ImGui::TextUnformatted(data.currNodeError->message.c_str());
ImGui::EndTooltip();
}
}
@@ -289,8 +298,8 @@ namespace hex::plugin::builtin {
if (ImGui::BeginChild("##node_editor", ImGui::GetContentRegionAvail() - ImVec2(0, ImGui::GetTextLineHeightWithSpacing() * 1.3))) {
ImNodes::BeginNodeEditor();
for (auto &node : this->m_nodes) {
const bool hasError = this->m_currNodeError.has_value() && this->m_currNodeError->first == node;
for (auto &node : data.nodes) {
const bool hasError = data.currNodeError.has_value() && data.currNodeError->node == node.get();
if (hasError)
ImNodes::PushColorStyle(ImNodesCol_NodeOutline, 0xFF0000FF);
@@ -336,12 +345,12 @@ namespace hex::plugin::builtin {
ImNodes::PopColorStyle();
}
for (const auto &link : this->m_links)
for (const auto &link : data.links)
ImNodes::Link(link.getId(), link.getFromId(), link.getToId());
ImNodes::MiniMap(0.2F, ImNodesMiniMapLocation_BottomRight);
if (this->m_nodes.empty())
if (data.nodes.empty())
ImGui::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang);
ImNodes::EndNodeEditor();
@@ -367,7 +376,7 @@ namespace hex::plugin::builtin {
do {
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
if (attribute.getId() == static_cast<u32>(from))
fromAttr = &attribute;
@@ -388,7 +397,7 @@ namespace hex::plugin::builtin {
if (!toAttr->getConnectedAttributes().empty())
break;
auto newLink = this->m_links.emplace_back(from, to);
auto newLink = data.links.emplace_back(from, to);
fromAttr->addConnectedAttribute(newLink.getId(), toAttr);
toAttr->addConnectedAttribute(newLink.getId(), fromAttr);
@@ -423,12 +432,14 @@ namespace hex::plugin::builtin {
ImGui::End();
}
std::string ViewDataProcessor::saveNodes() {
std::string ViewDataProcessor::saveNodes(prv::Provider *provider) {
auto &data = ProviderExtraData::get(provider).dataProcessor;
using json = nlohmann::json;
json output;
output["nodes"] = json::object();
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
auto id = node->getId();
auto &currNodeOutput = output["nodes"][std::to_string(id)];
auto pos = ImNodes::GetNodeGridSpacePos(id);
@@ -453,7 +464,7 @@ namespace hex::plugin::builtin {
}
output["links"] = json::object();
for (auto &link : this->m_links) {
for (auto &link : data.links) {
auto id = link.getId();
auto &currOutput = output["links"][std::to_string(id)];
@@ -462,30 +473,29 @@ namespace hex::plugin::builtin {
currOutput["to"] = link.getToId();
}
return output.dump();
return output.dump(4);
}
void ViewDataProcessor::loadNodes(const std::string &data) {
void ViewDataProcessor::loadNodes(prv::Provider *provider, const std::string &jsonData) {
if (!ImHexApi::Provider::isValid()) return;
auto &data = ProviderExtraData::get(provider).dataProcessor;
using json = nlohmann::json;
json input = json::parse(data);
json input = json::parse(jsonData);
u32 maxNodeId = 0;
u32 maxAttrId = 0;
u32 maxLinkId = 0;
for (auto &node : this->m_nodes)
delete node;
this->m_nodes.clear();
this->m_endNodes.clear();
this->m_links.clear();
data.nodes.clear();
data.endNodes.clear();
data.links.clear();
auto &nodeEntries = ContentRegistry::DataProcessorNode::getEntries();
for (auto &node : input["nodes"]) {
dp::Node *newNode = nullptr;
std::unique_ptr<dp::Node> newNode;
for (auto &entry : nodeEntries) {
if (entry.name == node["type"])
newNode = entry.creatorFunction();
@@ -520,9 +530,9 @@ namespace hex::plugin::builtin {
newNode->load(node["data"]);
if (hasInput && !hasOutput)
this->m_endNodes.push_back(newNode);
data.endNodes.push_back(newNode.get());
this->m_nodes.push_back(newNode);
data.nodes.push_back(std::move(newNode));
ImNodes::SetNodeGridSpacePos(nodeId, ImVec2(node["pos"]["x"], node["pos"]["y"]));
}
@@ -533,10 +543,10 @@ namespace hex::plugin::builtin {
maxLinkId = std::max(linkId, maxLinkId);
newLink.setID(linkId);
this->m_links.push_back(newLink);
data.links.push_back(newLink);
dp::Attribute *fromAttr = nullptr, *toAttr = nullptr;
for (auto &node : this->m_nodes) {
for (auto &node : data.nodes) {
for (auto &attribute : node->getAttributes()) {
if (attribute.getId() == newLink.getFromId())
fromAttr = &attribute;

View File

@@ -11,17 +11,6 @@ using namespace std::literals::string_literals;
namespace hex::plugin::builtin {
ViewDisassembler::ViewDisassembler() : View("hex.builtin.view.disassembler.name") {
EventManager::subscribe<EventRegionSelected>(this, [this](Region region) {
if (this->m_shouldMatchSelection) {
if (region.address == size_t(-1)) {
this->m_codeRegion[0] = this->m_codeRegion[1] = 0;
} else {
this->m_codeRegion[0] = region.address;
this->m_codeRegion[1] = region.address + region.size - 1;
}
}
});
EventManager::subscribe<EventProviderDeleted>(this, [this](const auto*) {
this->m_disassembly.clear();
});
@@ -49,14 +38,14 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
std::vector<u8> buffer(2048, 0x00);
size_t size = (this->m_codeRegion[1] - this->m_codeRegion[0] + 1);
size_t size = this->m_codeRegion.getSize();
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.disassembler.disassembling", size);
for (u64 address = 0; address < size; address += 2048) {
task.update(address);
size_t bufferSize = std::min(u64(2048), (this->m_codeRegion[1] - this->m_codeRegion[0] + 1) - address);
provider->read(this->m_codeRegion[0] + address, buffer.data(), bufferSize);
size_t bufferSize = std::min(u64(2048), (size - address));
provider->read(this->m_codeRegion.getStartAddress() + address, buffer.data(), bufferSize);
size_t instructionCount = cs_disasm(capstoneHandle, buffer.data(), bufferSize, this->m_baseAddress + address, 0, &instructions);
if (instructionCount == 0)
@@ -69,7 +58,7 @@ namespace hex::plugin::builtin {
const auto &instr = instructions[i];
Disassembly disassembly = { };
disassembly.address = instr.address;
disassembly.offset = this->m_codeRegion[0] + address + usedBytes;
disassembly.offset = this->m_codeRegion.getStartAddress() + address + usedBytes;
disassembly.size = instr.size;
disassembly.mnemonic = instr.mnemonic;
disassembly.operators = instr.op_str;
@@ -105,10 +94,24 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.view.disassembler.position"_lang);
ImGui::Separator();
ImGui::InputScalar("hex.builtin.view.disassembler.base"_lang, ImGuiDataType_U64, &this->m_baseAddress, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputScalarN("hex.builtin.view.disassembler.region"_lang, ImGuiDataType_U64, this->m_codeRegion, 2, nullptr, nullptr, "%08llX", ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputHexadecimal("hex.builtin.view.disassembler.base"_lang, &this->m_baseAddress, ImGuiInputTextFlags_CharsHexadecimal);
ui::regionSelectionPicker(&this->m_range);
switch (this->m_range) {
case ui::SelectedRegion::Selection: {
auto region = ImHexApi::HexEditor::getSelection();
if (region.has_value()) {
this->m_codeRegion = region.value();
}
}
break;
case ui::SelectedRegion::EntireData: {
auto base = provider->getBaseAddress();
this->m_codeRegion = { base, base + provider->getActualSize() - 1 };
}
break;
}
ImGui::Checkbox("hex.builtin.common.match_selection"_lang, &this->m_shouldMatchSelection);
if (ImGui::IsItemEdited()) {
// Force execution of Region Selection Event
ImHexApi::HexEditor::setSelection(0, 0);

View File

@@ -16,26 +16,12 @@ namespace hex::plugin::builtin {
ViewFind::ViewFind() : View("hex.builtin.view.find.name") {
const static auto HighlightColor = [] { return (ImGui::GetCustomColorU32(ImGuiCustomCol_ToolbarPurple) & 0x00FFFFFF) | 0x70000000; };
const static auto FindOccurrence = [this](u64 address) -> std::optional<Occurrence> {
auto &occurrences = this->m_foundOccurrences[ImHexApi::Provider::get()];
auto it = std::upper_bound(occurrences.begin(), occurrences.end(), address, [](u64 address, Occurrence &occurrence) {
return address < occurrence.region.getStartAddress();
});
if (it != occurrences.begin())
it--;
if (it != occurrences.end() && it->region.getStartAddress() <= address && address <= it->region.getEndAddress())
return *it;
else
return std::nullopt;
};
ImHexApi::HexEditor::addBackgroundHighlightingProvider([](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
ImHexApi::HexEditor::addBackgroundHighlightingProvider([this](u64 address, const u8* data, size_t size) -> std::optional<color_t> {
hex::unused(data, size);
if (FindOccurrence(address).has_value())
auto provider = ImHexApi::Provider::get();
if (!this->m_occurrenceTree[provider].findOverlapping(address, address).empty())
return HighlightColor();
else
return std::nullopt;
@@ -44,57 +30,61 @@ namespace hex::plugin::builtin {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8* data, size_t size) {
hex::unused(data, size);
auto occurrence = FindOccurrence(address);
if (!occurrence.has_value())
auto provider = ImHexApi::Provider::get();
auto occurrences = this->m_occurrenceTree[provider].findOverlapping(address, address);
if (occurrences.empty())
return;
ImGui::BeginTooltip();
ImGui::PushID(&occurrence);
if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
for (const auto &occurrence : occurrences) {
ImGui::PushID(&occurrence);
if (ImGui::BeginTable("##tooltips", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
{
const auto value = this->decodeValue(ImHexApi::Provider::get(), occurrence.value());
{
const auto value = this->decodeValue(ImHexApi::Provider::get(), occurrence.value);
ImGui::ColorButton("##color", ImColor(HighlightColor()));
ImGui::SameLine(0, 10);
ImGui::TextFormatted("{}", value);
ImGui::ColorButton("##color", ImColor(HighlightColor()));
ImGui::SameLine(0, 10);
ImGui::TextFormatted("{}", value);
if (ImGui::GetIO().KeyShift) {
ImGui::Indent();
if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
if (ImGui::GetIO().KeyShift) {
ImGui::Indent();
if (ImGui::BeginTable("##extra_info", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_NoClip)) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("{}: ", "hex.builtin.common.region"_lang.get());
ImGui::TableNextColumn();
ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", occurrence->region.getStartAddress(), occurrence->region.getEndAddress());
auto demangledValue = llvm::demangle(value);
if (value != demangledValue) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("{}: ", "hex.builtin.view.find.demangled"_lang.get());
ImGui::TextFormatted("{}: ", "hex.builtin.common.region"_lang.get());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", demangledValue);
ImGui::TextFormatted("[ 0x{:08X} - 0x{:08X} ]", occurrence.value.region.getStartAddress(), occurrence.value.region.getEndAddress());
auto demangledValue = llvm::demangle(value);
if (value != demangledValue) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("{}: ", "hex.builtin.view.find.demangled"_lang.get());
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", demangledValue);
}
ImGui::EndTable();
}
ImGui::EndTable();
ImGui::Unindent();
}
ImGui::Unindent();
}
ImGui::PushStyleColor(ImGuiCol_TableRowBg, HighlightColor());
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, HighlightColor());
ImGui::EndTable();
ImGui::PopStyleColor(2);
}
ImGui::PushStyleColor(ImGuiCol_TableRowBg, HighlightColor());
ImGui::PushStyleColor(ImGuiCol_TableRowBgAlt, HighlightColor());
ImGui::EndTable();
ImGui::PopStyleColor(2);
ImGui::PopID();
}
ImGui::PopID();
ImGui::EndTooltip();
});
@@ -252,7 +242,7 @@ namespace hex::plugin::builtin {
break;
auto address = occurrence.getAddress();
reader.seek(address + sequence.size());
reader.seek(address + 1);
results.push_back(Occurrence{ Region { address, sequence.size() }, Occurrence::DecodeType::Binary });
task.update(address - searchRegion.getStartAddress());
}
@@ -294,21 +284,26 @@ namespace hex::plugin::builtin {
reader.setEndAddress(searchRegion.getEndAddress());
u32 matchedBytes = 0;
u64 address = searchRegion.getStartAddress();
const size_t patternSize = settings.pattern.size();
for (u8 byte : reader) {
for (auto it = reader.begin(); it != reader.end(); ++it) {
auto byte = *it;
if ((byte & settings.pattern[matchedBytes].mask) == settings.pattern[matchedBytes].value) {
matchedBytes++;
if (matchedBytes == settings.pattern.size()) {
results.push_back(Occurrence { Region { address - (patternSize - 1), patternSize }, Occurrence::DecodeType::Binary });
task.update(address);
auto occurrenceAddress = it.getAddress() - (patternSize - 1);
results.push_back(Occurrence { Region { occurrenceAddress, patternSize }, Occurrence::DecodeType::Binary });
task.update(occurrenceAddress);
it.setAddress(occurrenceAddress);
matchedBytes = 0;
}
} else {
if (matchedBytes > 0)
it -= matchedBytes;
matchedBytes = 0;
}
address++;
}
return results;
@@ -316,7 +311,7 @@ namespace hex::plugin::builtin {
void ViewFind::runSearch() {
Region searchRegion = [this]{
if (this->m_searchSettings.range == 0 || !ImHexApi::HexEditor::isSelectionValid()) {
if (this->m_searchSettings.range == ui::SelectedRegion::EntireData || !ImHexApi::HexEditor::isSelectionValid()) {
auto provider = ImHexApi::Provider::get();
return Region { provider->getBaseAddress(), provider->getActualSize() };
} else {
@@ -345,7 +340,13 @@ namespace hex::plugin::builtin {
break;
}
this->m_sortedOccurrences = this->m_foundOccurrences;
this->m_sortedOccurrences[provider] = this->m_foundOccurrences[provider];
OccurrenceTree::interval_vector intervals;
for (const auto &occurrence : this->m_foundOccurrences[provider])
intervals.push_back(OccurrenceTree::interval(occurrence.region.getStartAddress(), occurrence.region.getEndAddress(), occurrence));
this->m_occurrenceTree[provider] = std::move(intervals);
this->m_searchRunning = false;
}).detach();
}
@@ -409,9 +410,7 @@ namespace hex::plugin::builtin {
ImGui::BeginDisabled(this->m_searchRunning);
{
ImGui::Header("hex.builtin.view.find.range"_lang, true);
ImGui::RadioButton("hex.builtin.view.find.range.entire_data"_lang, &this->m_searchSettings.range, 0);
ImGui::RadioButton("hex.builtin.view.find.range.selection"_lang, &this->m_searchSettings.range, 1);
ui::regionSelectionPicker(&this->m_searchSettings.range, true, true);
ImGui::NewLine();

View File

@@ -1,7 +1,6 @@
#include "content/views/view_hex_editor.hpp"
#include <hex/api/content_registry.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/providers/buffered_reader.hpp>
#include <hex/helpers/crypto.hpp>
@@ -20,8 +19,8 @@ namespace hex::plugin::builtin {
void draw(ViewHexEditor *editor) override {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.goto"_lang);
if (ImGui::BeginTabBar("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) {
if (ImGui::BeginTabItem("Absolute")) {
if (ImGui::BeginTabBar("goto_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.goto.offset.absolute"_lang)) {
this->m_mode = Mode::Absolute;
ImGui::EndTabItem();
}
@@ -95,6 +94,53 @@ namespace hex::plugin::builtin {
MathEvaluator<i128> m_evaluator;
};
class PopupSelect : public ViewHexEditor::Popup {
public:
void draw(ViewHexEditor *editor) override {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.file.select"_lang);
if (ImGui::BeginTabBar("select_tabs")) {
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.region"_lang)) {
u64 inputA = this->m_region.getStartAddress();
u64 inputB = this->m_region.getEndAddress();
ImGui::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll);
ImGui::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.end"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll);
if (inputB < inputA)
inputB = inputA;
this->m_region = { inputA, (inputB - inputA) + 1 };
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("hex.builtin.view.hex_editor.select.offset.size"_lang)) {
u64 inputA = this->m_region.getStartAddress();
u64 inputB = this->m_region.getSize();
ImGui::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.begin"_lang, &inputA, ImGuiInputTextFlags_AutoSelectAll);
ImGui::InputHexadecimal("hex.builtin.view.hex_editor.select.offset.size"_lang, &inputB, ImGuiInputTextFlags_AutoSelectAll);
if (inputB <= 0)
inputB = 1;
this->m_region = { inputA, inputB };
ImGui::EndTabItem();
}
if (ImGui::Button("hex.builtin.view.hex_editor.select.select"_lang) || (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Enter)))) {
editor->setSelection(this->m_region.getStartAddress(), this->m_region.getEndAddress());
editor->jumpToSelection();
}
ImGui::EndTabBar();
}
}
private:
Region m_region = { 0, 1 };
};
class PopupFind : public ViewHexEditor::Popup {
public:
void draw(ViewHexEditor *editor) override {
@@ -252,7 +298,7 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.set_base"_lang);
ImGui::InputHexadecimal("##base_address", &this->m_baseAddress);
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Enter))) {
setBaseAddress(this->m_baseAddress);
editor->closePopup();
}
@@ -285,7 +331,7 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted("hex.builtin.view.hex_editor.menu.edit.resize"_lang);
ImGui::InputHexadecimal("##resize", &this->m_size);
if (ImGui::IsItemFocused() && ImGui::IsKeyPressed(ImGuiKey_Enter)) {
if (ImGui::IsItemFocused() && (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_Enter))) {
resize(static_cast<size_t>(this->m_size));
editor->closePopup();
}
@@ -533,11 +579,6 @@ namespace hex::plugin::builtin {
ImGui::SetKeyboardFocusHere();
ImGui::SetNextFrameWantCaptureKeyboard(true);
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
this->m_editingAddress = std::nullopt;
this->m_shouldModifyValue = false;
}
if (this->m_currDataVisualizer->drawEditing(*this->m_editingAddress, this->m_editingBytes.data(), this->m_editingBytes.size(), this->m_upperCaseHex, this->m_enteredEditingMode) || this->m_shouldModifyValue) {
provider->write(*this->m_editingAddress, this->m_editingBytes.data(), this->m_editingBytes.size());
@@ -558,6 +599,11 @@ namespace hex::plugin::builtin {
this->m_shouldUpdateEditingValue = true;
}
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !hovered) {
this->m_editingAddress = std::nullopt;
this->m_shouldModifyValue = false;
}
this->m_enteredEditingMode = false;
}
}
@@ -682,6 +728,16 @@ namespace hex::plugin::builtin {
if (ImHexApi::Provider::isValid()) {
auto provider = ImHexApi::Provider::get();
std::pair<Region, bool> validRegion = { Region::Invalid(), false };
const auto isCurrRegionValid = [&validRegion, &provider](u64 address){
auto &[currRegion, currRegionValid] = validRegion;
if (!Region{ address, 1 }.isWithin(currRegion)) {
validRegion = provider->getRegionValidity(address);
}
return currRegionValid;
};
ImGuiListClipper clipper;
clipper.Begin(std::ceil(provider->getSize() / (long double)(this->m_bytesPerRow)), CharacterSize.y);
@@ -689,7 +745,7 @@ namespace hex::plugin::builtin {
this->m_visibleRowCount = clipper.DisplayEnd - clipper.DisplayStart;
// Loop over rows
for (i128 y = clipper.DisplayStart; y < u64(clipper.DisplayEnd); y++) {
for (u64 y = u64(clipper.DisplayStart); y < u64(clipper.DisplayEnd); y++) {
// Draw address column
ImGui::TableNextRow();
@@ -699,7 +755,7 @@ namespace hex::plugin::builtin {
const u8 validBytes = std::min<u64>(this->m_bytesPerRow, provider->getSize() - y * this->m_bytesPerRow);
std::vector<u8> bytes(validBytes);
std::vector<u8> bytes(this->m_bytesPerRow, 0x00);
provider->read(y * this->m_bytesPerRow + provider->getBaseAddress() + provider->getCurrentPageAddress(), bytes.data(), validBytes);
std::vector<std::tuple<std::optional<color_t>, std::optional<color_t>>> cellColors;
@@ -710,7 +766,7 @@ namespace hex::plugin::builtin {
const auto cellBytes = std::min<u64>(validBytes, bytesPerCell);
// Query cell colors
if (x < validBytes / bytesPerCell) {
if (x < std::ceil(float(validBytes) / bytesPerCell)) {
const auto foregroundColor = queryForegroundColor(byteAddress, &bytes[x * cellBytes], cellBytes);
const auto backgroundColor = [&]{
auto color = queryBackgroundColor(byteAddress, &bytes[x * cellBytes], cellBytes);
@@ -732,6 +788,11 @@ namespace hex::plugin::builtin {
foregroundColor,
backgroundColor
);
} else {
cellColors.emplace_back(
std::nullopt,
std::nullopt
);
}
}
}
@@ -746,16 +807,17 @@ namespace hex::plugin::builtin {
if (isColumnSeparatorColumn(x, columnCount))
ImGui::TableNextColumn();
if (x < validBytes / bytesPerCell) {
if (x < std::ceil(float(validBytes) / bytesPerCell)) {
auto cellStartPos = getCellPosition();
auto cellSize = (CharacterSize * ImVec2(this->m_currDataVisualizer->getMaxCharsPerCell(), 1) + (ImVec2(3, 2) * ImGui::GetStyle().CellPadding) - ImVec2(1, 0) * ImGui::GetStyle().CellPadding) + ImVec2(1, 0);
auto maxCharsPerCell = this->m_currDataVisualizer->getMaxCharsPerCell();
auto [foregroundColor, backgroundColor] = cellColors[x];
if (isColumnSeparatorColumn(x + 1, columnCount) && selectionMax != x + y * columnCount) {
cellSize.x += SeparatorColumWidth + 1;
}
if (y == clipper.DisplayStart)
if (y == u64(clipper.DisplayStart))
cellSize.y -= (ImGui::GetStyle().CellPadding.y + 1);
// Draw highlights and selection
@@ -779,8 +841,11 @@ namespace hex::plugin::builtin {
// Draw cell content
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::PushItemWidth((CharacterSize * this->m_currDataVisualizer->getMaxCharsPerCell()).x);
this->drawCell(byteAddress, &bytes[x * bytesPerCell], bytesPerCell, cellHovered);
ImGui::PushItemWidth((CharacterSize * maxCharsPerCell).x);
if (isCurrRegionValid(byteAddress))
this->drawCell(byteAddress, &bytes[x * bytesPerCell], bytesPerCell, cellHovered);
else
ImGui::TextFormatted("{}", std::string(maxCharsPerCell, '?'));
ImGui::PopItemWidth();
ImGui::PopStyleVar();
@@ -824,7 +889,9 @@ namespace hex::plugin::builtin {
this->drawSelectionFrame(x, y, byteAddress, 1, cellStartPos, cellSize);
}
if (std::isprint(bytes[x]))
if (!isCurrRegionValid(byteAddress))
ImGui::TextFormatted("?");
else if (std::isprint(bytes[x]))
ImGui::TextFormatted("{:c}", bytes[x]);
else
ImGui::TextDisabled(".");
@@ -866,7 +933,7 @@ namespace hex::plugin::builtin {
const auto x = address % this->m_bytesPerRow;
if (x < validBytes / bytesPerCell) {
if (x < validBytes && isCurrRegionValid(address)) {
auto [foregroundColor, backgroundColor] = cellColors[x / bytesPerCell];
// Draw highlights and selection
@@ -900,13 +967,13 @@ namespace hex::plugin::builtin {
if ((ImGui::IsMouseDown(ImGuiMouseButton_Left) && this->m_selectionStart != this->m_selectionEnd)) {
auto fractionPerLine = 1.0 / (this->m_visibleRowCount + 1);
if (y == clipper.DisplayStart + 2) {
if (y == u64(clipper.DisplayStart + 2)) {
if (i128(this->m_selectionEnd - provider->getBaseAddress() - provider->getCurrentPageAddress()) <= (i64(clipper.DisplayStart + 2) * this->m_bytesPerRow)) {
this->m_shouldScrollToSelection = false;
ImGui::SetScrollHereY(fractionPerLine * 4);
}
} else if (y == (clipper.DisplayEnd - 2)) {
} else if (y == u64(clipper.DisplayEnd - 2)) {
if (i128(this->m_selectionEnd - provider->getBaseAddress() - provider->getCurrentPageAddress()) >= (i64(clipper.DisplayEnd - 2) * this->m_bytesPerRow)) {
this->m_shouldScrollToSelection = false;
ImGui::SetScrollHereY(fractionPerLine * (this->m_visibleRowCount - 1));
@@ -994,8 +1061,14 @@ namespace hex::plugin::builtin {
{
auto selection = this->getSelection();
std::string value;
if (this->isSelectionValid())
value = hex::format("0x{0:08X} - 0x{1:08X} ({2})", selection.getStartAddress(), selection.getEndAddress(), hex::toByteString(selection.getSize()));
if (this->isSelectionValid()) {
value = hex::format("0x{0:08X} - 0x{1:08X} (0x{2:X} | {3})",
selection.getStartAddress(),
selection.getEndAddress(),
selection.getSize(),
hex::toByteString(selection.getSize())
);
}
else
value = std::string("hex.builtin.view.hex_editor.selection.none"_lang);
@@ -1005,7 +1078,11 @@ namespace hex::plugin::builtin {
// Loaded data size
ImGui::TableNextColumn();
{
ImGui::TextFormatted("{0}: 0x{1:08X} ({2})", "hex.builtin.view.hex_editor.data_size"_lang, provider->getActualSize(), hex::toByteString(provider->getActualSize()));
ImGui::TextFormatted("{0}: 0x{1:08X} (0x{2:X} | {3})", "hex.builtin.view.hex_editor.data_size"_lang,
provider->getActualSize(),
provider->getActualSize(),
hex::toByteString(provider->getActualSize())
);
}
ImGui::EndTable();
@@ -1127,6 +1204,12 @@ namespace hex::plugin::builtin {
this->setSelection(size_t(0), ImHexApi::Provider::get()->getActualSize());
});
// Select range
ShortcutManager::addShortcut(this, CTRL + SHIFT + Keys::A, [this] {
if (ImHexApi::Provider::isValid())
this->openPopup<PopupSelect>();
});
// Remove selection
ShortcutManager::addShortcut(this, Keys::Escape, [this] {
this->m_selectionStart = InvalidSelection;
@@ -1383,6 +1466,10 @@ namespace hex::plugin::builtin {
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.goto"_lang, "CTRL + G", false, providerValid)) {
this->openPopup<PopupGoto>();
}
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.file.select"_lang, "CTRL + SHIFT + A", false, providerValid)) {
this->openPopup<PopupSelect>();
}
});

View File

@@ -2,7 +2,8 @@
#include <hex/providers/provider.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <nlohmann/json.hpp>
#include <string>
@@ -11,22 +12,26 @@ using namespace std::literals::string_literals;
namespace hex::plugin::builtin {
ViewPatches::ViewPatches() : View("hex.builtin.view.patches.name") {
EventManager::subscribe<EventProjectFileStore>(this, [] {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid())
ProjectFile::setPatches(provider->getPatches());
});
EventManager::subscribe<EventProjectFileLoad>(this, [] {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid())
provider->getPatches() = ProjectFile::getPatches();
ProjectFile::registerPerProviderHandler({
.basePath = "patches.json",
.load = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
auto json = nlohmann::json::parse(tar.read(basePath));
provider->getPatches() = json["patches"].get<std::map<u64, u8>>();
return true;
},
.store = [](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
nlohmann::json json;
json["patches"] = provider->getPatches();
tar.write(basePath, json.dump(4));
return true;
}
});
}
ViewPatches::~ViewPatches() {
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
}
void ViewPatches::drawContent() {
@@ -86,7 +91,6 @@ namespace hex::plugin::builtin {
if (ImGui::BeginPopup("PatchContextMenu")) {
if (ImGui::MenuItem("hex.builtin.view.patches.remove"_lang)) {
patches.erase(this->m_selectedPatch);
ProjectFile::markDirty();
}
ImGui::EndPopup();
}

View File

@@ -5,6 +5,8 @@
#include <pl/pattern_language.hpp>
#include <pl/patterns/pattern.hpp>
#include <provider_extra_data.hpp>
namespace hex::plugin::builtin {
ViewPatternData::ViewPatternData() : View("hex.builtin.view.pattern_data.name") {
@@ -20,7 +22,7 @@ namespace hex::plugin::builtin {
EventManager::unsubscribe<EventHighlightingChanged>(this);
}
static bool sortPatterns(prv::Provider *provider, const ImGuiTableSortSpecs* sortSpecs, const pl::Pattern * left, const pl::Pattern * right) {
static bool sortPatterns(prv::Provider *provider, const ImGuiTableSortSpecs* sortSpecs, const pl::ptrn::Pattern * left, const pl::ptrn::Pattern * right) {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("name")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left->getDisplayName() > right->getDisplayName();
@@ -67,7 +69,7 @@ namespace hex::plugin::builtin {
return false;
}
static bool beginPatternTable(prv::Provider *&provider, const std::vector<std::shared_ptr<pl::Pattern>> &patterns, std::vector<pl::Pattern*> &sortedPatterns) {
static bool beginPatternTable(prv::Provider *&provider, const std::vector<std::shared_ptr<pl::ptrn::Pattern>> &patterns, std::vector<pl::ptrn::Pattern*> &sortedPatterns) {
if (ImGui::BeginTable("##Patterntable", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.builtin.view.pattern_data.var_name"_lang, 0, 0, ImGui::GetID("name"));
@@ -81,16 +83,16 @@ namespace hex::plugin::builtin {
if (sortSpecs->SpecsDirty || sortedPatterns.empty()) {
sortedPatterns.clear();
std::transform(patterns.begin(), patterns.end(), std::back_inserter(sortedPatterns), [](const std::shared_ptr<pl::Pattern> &pattern) {
std::transform(patterns.begin(), patterns.end(), std::back_inserter(sortedPatterns), [](const std::shared_ptr<pl::ptrn::Pattern> &pattern) {
return pattern.get();
});
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](pl::Pattern *left, pl::Pattern *right) -> bool {
std::sort(sortedPatterns.begin(), sortedPatterns.end(), [&sortSpecs, &provider](pl::ptrn::Pattern *left, pl::ptrn::Pattern *right) -> bool {
return sortPatterns(provider, sortSpecs, left, right);
});
for (auto &pattern : sortedPatterns)
pattern->sort([&sortSpecs, &provider](const pl::Pattern *left, const pl::Pattern *right){
pattern->sort([&sortSpecs, &provider](const pl::ptrn::Pattern *left, const pl::ptrn::Pattern *right){
return sortPatterns(provider, sortSpecs, left, right);
});
@@ -109,7 +111,7 @@ namespace hex::plugin::builtin {
if (ImHexApi::Provider::isValid() && provider->isReadable()) {
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternTable(provider, provider->getPatternLanguageRuntime().getPatterns(), sortedPatterns)) {
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getPatterns(), sortedPatterns)) {
ImGui::TableHeadersRow();
if (!sortedPatterns.empty()) {

View File

@@ -2,18 +2,20 @@
#include <hex/api/content_registry.hpp>
#include <pl/preprocessor.hpp>
#include <pl/patterns/pattern.hpp>
#include <pl/ast/ast_node_variable_decl.hpp>
#include <pl/ast/ast_node_type_decl.hpp>
#include <pl/ast/ast_node_builtin_type.hpp>
#include <pl/core/preprocessor.hpp>
#include <pl/core/ast/ast_node_variable_decl.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/file.hpp>
#include <hex/helpers/project_file_handler.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/helpers/magic.hpp>
#include <provider_extra_data.hpp>
#include <nlohmann/json.hpp>
namespace hex::plugin::builtin {
@@ -86,15 +88,6 @@ namespace hex::plugin::builtin {
this->m_envVarEntries.push_back({ 0, "", 0, EnvVarType::Integer });
this->m_envVarIdCounter = 1;
EventManager::subscribe<EventProjectFileStore>(this, [this]() {
ProjectFile::setPattern(this->m_textEditor.GetText());
});
EventManager::subscribe<EventProjectFileLoad>(this, [this]() {
this->m_textEditor.SetText(ProjectFile::getPattern());
this->evaluatePattern(this->m_textEditor.GetText());
});
EventManager::subscribe<RequestSetPatternLanguageCode>(this, [this](const std::string &code) {
this->m_textEditor.SetText(code);
});
@@ -115,21 +108,24 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderCreated>(this, [this](prv::Provider *provider) {
EventManager::subscribe<EventProviderOpened>(this, [this](prv::Provider *provider) {
auto &patternLanguageData = ProviderExtraData::get(provider).patternLanguage;
patternLanguageData.runtime = ContentRegistry::PatternLanguage::createDefaultRuntime(provider);
if (!this->m_autoLoadPatterns)
return;
// Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) {
provider->getPatternLanguageSourceCode() = this->m_textEditor.GetText();
patternLanguageData.sourceCode = this->m_textEditor.GetText();
}
auto &runtime = provider->getPatternLanguageRuntime();
auto &runtime = patternLanguageData.runtime;
auto mimeType = magic::getMIMEType(ImHexApi::Provider::get());
auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false;
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
if (value == mimeType) {
@@ -152,14 +148,16 @@ namespace hex::plugin::builtin {
if (!file.isValid())
continue;
runtime.getInternals().preprocessor->preprocess(runtime, file.readString());
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
}
}
runtime.addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
@@ -168,14 +166,14 @@ namespace hex::plugin::builtin {
}
});
EventManager::subscribe<EventProviderDeleted>(this, [](auto *provider) {
provider->getPatternLanguageRuntime().abort();
});
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {
if (!this->m_syncPatternSourceCode) {
if (oldProvider != nullptr) oldProvider->getPatternLanguageSourceCode() = this->m_textEditor.GetText();
if (newProvider != nullptr) this->m_textEditor.SetText(newProvider->getPatternLanguageSourceCode());
if (oldProvider != nullptr) ProviderExtraData::get(oldProvider).patternLanguage.sourceCode = this->m_textEditor.GetText();
if (newProvider != nullptr)
this->m_textEditor.SetText(ProviderExtraData::get(newProvider).patternLanguage.sourceCode);
else
this->m_textEditor.SetText("");
auto lines = this->m_textEditor.GetTextLines();
lines.pop_back();
@@ -255,7 +253,7 @@ namespace hex::plugin::builtin {
return std::nullopt;
std::optional<ImColor> color;
for (const auto &pattern : ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(address)) {
for (const auto &pattern : ProviderExtraData::getCurrent().patternLanguage.runtime->getPatterns(address)) {
if (pattern->isHidden())
continue;
@@ -271,7 +269,7 @@ namespace hex::plugin::builtin {
ImHexApi::HexEditor::addTooltipProvider([this](u64 address, const u8 *data, size_t size) {
hex::unused(data, size);
auto patterns = ImHexApi::Provider::get()->getPatternLanguageRuntime().getPatterns(address);
auto patterns = ProviderExtraData::getCurrent().patternLanguage.runtime->getPatterns(address);
if (!patterns.empty() && !std::all_of(patterns.begin(), patterns.end(), [](const auto &pattern) { return pattern->isHidden(); })) {
ImGui::BeginTooltip();
for (const auto &pattern : patterns) {
@@ -296,11 +294,37 @@ namespace hex::plugin::builtin {
ImGui::EndTooltip();
}
});
ProjectFile::registerPerProviderHandler({
.basePath = "pattern_source_code.hexpat",
.load = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
std::string sourceCode = tar.readString(basePath);
if (!this->m_syncPatternSourceCode)
ProviderExtraData::get(provider).patternLanguage.sourceCode = sourceCode;
this->m_textEditor.SetText(sourceCode);
return true;
},
.store = [this](prv::Provider *provider, const std::fs::path &basePath, Tar &tar) {
std::string sourceCode;
if (provider == ImHexApi::Provider::get())
ProviderExtraData::get(provider).patternLanguage.sourceCode = this->m_textEditor.GetText();
if (this->m_syncPatternSourceCode)
sourceCode = this->m_textEditor.GetText();
else
sourceCode = ProviderExtraData::get(provider).patternLanguage.sourceCode;
tar.write(basePath, sourceCode);
return true;
}
});
}
ViewPatternEditor::~ViewPatternEditor() {
EventManager::unsubscribe<EventProjectFileStore>(this);
EventManager::unsubscribe<EventProjectFileLoad>(this);
EventManager::unsubscribe<RequestSetPatternLanguageCode>(this);
EventManager::unsubscribe<EventFileLoaded>(this);
EventManager::unsubscribe<EventProviderDeleted>(this);
@@ -340,10 +364,10 @@ namespace hex::plugin::builtin {
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1);
auto &runtime = provider->getPatternLanguageRuntime();
if (runtime.isRunning()) {
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
if (runtime->isRunning()) {
if (ImGui::IconButton(ICON_VS_DEBUG_STOP, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed)))
runtime.abort();
runtime->abort();
} else {
if (ImGui::IconButton(ICON_VS_DEBUG_START, ImGui::GetCustomColorVec4(ImGuiCustomCol_ToolbarGreen))) {
this->evaluatePattern(this->m_textEditor.GetText());
@@ -367,13 +391,13 @@ namespace hex::plugin::builtin {
ImGui::SameLine();
ImGui::TextFormatted("{} / {}",
provider->getPatternLanguageRuntime().getCreatedPatternCount(),
provider->getPatternLanguageRuntime().getMaximumPatternCount());
runtime->getCreatedPatternCount(),
runtime->getMaximumPatternCount());
}
if (this->m_textEditor.IsTextChanged()) {
ProjectFile::markDirty();
this->m_hasUnevaluatedChanges = true;
ImHexApi::Provider::markDirty();
}
if (this->m_hasUnevaluatedChanges && this->m_runningEvaluators == 0 && this->m_runningParsers == 0) {
@@ -418,7 +442,7 @@ namespace hex::plugin::builtin {
if (!this->m_lastEvaluationResult) {
if (this->m_lastEvaluationError) {
TextEditor::ErrorMarkers errorMarkers = {
{this->m_lastEvaluationError->getLineNumber(), this->m_lastEvaluationError->what()}
{ this->m_lastEvaluationError->line, this->m_lastEvaluationError->message }
};
this->m_textEditor.SetErrorMarkers(errorMarkers);
}
@@ -447,16 +471,18 @@ namespace hex::plugin::builtin {
const auto &[level, message] = this->m_console[i];
switch (level) {
case pl::LogConsole::Level::Debug:
using enum pl::core::LogConsole::Level;
case Debug:
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Comment)]);
break;
case pl::LogConsole::Level::Info:
case Info:
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Default)]);
break;
case pl::LogConsole::Level::Warning:
case Warning:
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::Preprocessor)]);
break;
case pl::LogConsole::Level::Error:
case Error:
ImGui::PushStyleColor(ImGuiCol_Text, this->m_textEditor.GetPalette()[u32(TextEditor::PaletteIndex::ErrorMarker)]);
break;
default:
@@ -591,27 +617,27 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
if (variable.outVariable) {
ImGui::TextUnformatted(pl::Token::literalToString(variable.value, true).c_str());
ImGui::TextUnformatted(pl::core::Token::literalToString(variable.value, true).c_str());
} else if (variable.inVariable) {
const std::string label { "##" + name };
if (pl::Token::isSigned(variable.type)) {
if (pl::core::Token::isSigned(variable.type)) {
i64 value = hex::get_or<i128>(variable.value, 0);
ImGui::InputScalar(label.c_str(), ImGuiDataType_S64, &value);
variable.value = i128(value);
} else if (pl::Token::isUnsigned(variable.type)) {
} else if (pl::core::Token::isUnsigned(variable.type)) {
u64 value = hex::get_or<u128>(variable.value, 0);
ImGui::InputScalar(label.c_str(), ImGuiDataType_U64, &value);
variable.value = u128(value);
} else if (pl::Token::isFloatingPoint(variable.type)) {
} else if (pl::core::Token::isFloatingPoint(variable.type)) {
double value = hex::get_or<double>(variable.value, 0.0);
ImGui::InputScalar(label.c_str(), ImGuiDataType_Double, &value);
variable.value = value;
} else if (variable.type == pl::Token::ValueType::Boolean) {
} else if (variable.type == pl::core::Token::ValueType::Boolean) {
bool value = hex::get_or<bool>(variable.value, false);
ImGui::Checkbox(label.c_str(), &value);
variable.value = value;
} else if (variable.type == pl::Token::ValueType::Character) {
} else if (variable.type == pl::core::Token::ValueType::Character) {
char buffer[2];
ImGui::InputText(label.c_str(), buffer, 2);
variable.value = buffer[0];
@@ -667,7 +693,7 @@ namespace hex::plugin::builtin {
}
void ViewPatternEditor::drawPatternTooltip(pl::Pattern *pattern) {
void ViewPatternEditor::drawPatternTooltip(pl::ptrn::Pattern *pattern) {
ImGui::PushID(pattern);
{
ImGui::ColorButton(pattern->getVariableName().c_str(), ImColor(pattern->getColor()));
@@ -689,32 +715,32 @@ namespace hex::plugin::builtin {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("Type: ");
ImGui::TextFormatted("{}: ", "hex.builtin.common.type"_lang);
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", pattern->getTypeName());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("Address: ");
ImGui::TextFormatted("{}: ", "hex.builtin.common.address"_lang);
ImGui::TableNextColumn();
ImGui::TextFormatted("0x{:08X}", pattern->getOffset());
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("Size: ");
ImGui::TextFormatted("{}: ", "hex.builtin.common.size"_lang);
ImGui::TableNextColumn();
ImGui::TextFormatted("{} {}", pattern->getSize(), pattern->getSize() > 1 ? "Bytes" : "Byte");
ImGui::TextFormatted("{}", hex::toByteString(pattern->getSize()));
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("Endian: ");
ImGui::TextFormatted("{}: ", "hex.builtin.common.endian"_lang);
ImGui::TableNextColumn();
ImGui::TextFormatted("{}", pattern->getEndian() == std::endian::little ? "Little" : "Big");
ImGui::TextFormatted("{}", pattern->getEndian() == std::endian::little ? "hex.builtin.common.little"_lang : "hex.builtin.common.big"_lang);
if (const auto &comment = pattern->getComment(); comment.has_value()) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextFormatted("Comment: ");
ImGui::TextFormatted("{}: ", "hex.builtin.common.comment"_lang);
ImGui::TableNextColumn();
ImGui::TextWrapped("\"%s\"", pattern->getComment()->c_str());
}
@@ -741,6 +767,8 @@ namespace hex::plugin::builtin {
}
void ViewPatternEditor::parsePattern(const std::string &code) {
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.pattern_editor.evaluating", 1);
this->m_runningParsers++;
auto ast = this->m_parserRuntime->parseString(code);
@@ -749,11 +777,11 @@ namespace hex::plugin::builtin {
if (ast) {
for (auto &node : *ast) {
if (auto variableDecl = dynamic_cast<pl::ASTNodeVariableDecl *>(node.get())) {
auto type = dynamic_cast<pl::ASTNodeTypeDecl *>(variableDecl->getType().get());
if (auto variableDecl = dynamic_cast<pl::core::ast::ASTNodeVariableDecl *>(node.get())) {
auto type = dynamic_cast<pl::core::ast::ASTNodeTypeDecl *>(variableDecl->getType().get());
if (type == nullptr) continue;
auto builtinType = dynamic_cast<pl::ASTNodeBuiltinType *>(type->getType().get());
auto builtinType = dynamic_cast<pl::core::ast::ASTNodeBuiltinType *>(type->getType().get());
if (builtinType == nullptr) continue;
PatternVariable variable = {
@@ -779,27 +807,29 @@ namespace hex::plugin::builtin {
this->m_textEditor.SetErrorMarkers({});
this->m_console.clear();
ImHexApi::Provider::get()->getPatternLanguageRuntime().reset();
auto &runtime = ProviderExtraData::getCurrent().patternLanguage.runtime;
runtime->reset();
EventManager::post<EventHighlightingChanged>();
std::thread([this, code] {
std::map<std::string, pl::Token::Literal> envVars;
std::thread([this, code, &runtime] {
auto task = ImHexApi::Tasks::createTask("hex.builtin.view.pattern_editor.evaluating", 1);
std::map<std::string, pl::core::Token::Literal> envVars;
for (const auto &[id, name, value, type] : this->m_envVarEntries)
envVars.insert({ name, value });
this->parsePattern(code);
std::map<std::string, pl::Token::Literal> inVariables;
std::map<std::string, pl::core::Token::Literal> inVariables;
for (auto &[name, variable] : this->m_patternVariables) {
if (variable.inVariable)
inVariables[name] = variable.value;
}
auto provider = ImHexApi::Provider::get();
auto &runtime = provider->getPatternLanguageRuntime();
runtime.setDangerousFunctionCallHandler([this]{
runtime->setDangerousFunctionCallHandler([this]{
this->m_dangerousFunctionCalled = true;
while (this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Ask) {
@@ -809,15 +839,15 @@ namespace hex::plugin::builtin {
return this->m_dangerousFunctionsAllowed == DangerousFunctionPerms::Allow;
});
this->m_lastEvaluationResult = runtime.executeString(code, envVars, inVariables);
this->m_lastEvaluationResult = runtime->executeString(code, envVars, inVariables);
if (!this->m_lastEvaluationResult) {
this->m_lastEvaluationError = runtime.getError();
this->m_lastEvaluationError = runtime->getError();
}
runtime.flattenPatterns();
runtime->flattenPatterns();
this->m_lastEvaluationLog = runtime.getConsoleLog();
this->m_lastEvaluationOutVars = runtime.getOutVariables();
this->m_lastEvaluationLog = runtime->getConsoleLog();
this->m_lastEvaluationOutVars = runtime->getOutVariables();
this->m_runningEvaluators--;
this->m_lastEvaluationProcessed = false;

View File

@@ -28,7 +28,6 @@ namespace hex::plugin::builtin {
if (ImGui::BeginPopupModal(View::toWindowName("hex.builtin.view.provider_settings.load_popup").c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
auto provider = hex::ImHexApi::Provider::get();
if (provider != nullptr) {
provider->drawLoadInterface();
@@ -36,8 +35,9 @@ namespace hex::plugin::builtin {
ImGui::Separator();
if (ImGui::Button("hex.builtin.common.open"_lang)) {
if (provider->open())
if (provider->open()) {
ImGui::CloseCurrentPopup();
}
else {
View::showErrorPopup("hex.builtin.view.provider_settings.load_error"_lang);
ImHexApi::Provider::remove(provider);

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