Compare commits

..

133 Commits

Author SHA1 Message Date
WerWolv
16b91eb715 fix: ImHex not loading python libraries correctly 2024-03-24 16:55:56 +01:00
WerWolv
8b9d09aa97 impr: Restructure and cleanup script loader and templates 2024-03-24 15:51:05 +01:00
WerWolv
ccb78246ab feat: Implemented full script loader API in python 2024-03-24 13:43:14 +01:00
WerWolv
3144d79605 feat: Added support for writing scripts in Python 2024-03-24 11:06:01 +01:00
WerWolv
e984fde966 fix: Crash on some platforms due to empty main menu bar
#1600
2024-03-23 12:26:23 +01:00
WerWolv
5d0b474a7e build: Move renaming of the macOS bundle to the CI 2024-03-23 00:33:46 +01:00
WerWolv
d09f2c4f26 build: Fix creating the macOS bundle from within the M1 docker 2024-03-22 23:47:24 +01:00
WerWolv
dd20a16d3a git: Fix macOS bundle upload path 2024-03-22 22:35:44 +01:00
WerWolv
7d22b71e86 git: Fix bundle path for signing 2024-03-22 22:24:25 +01:00
WerWolv
567ccbfc3a build: Don't postprocess libimhex.dylib 2024-03-22 22:18:18 +01:00
WerWolv
ca40775678 git: Make sure CI bundles correct macOS bundle into dmg 2024-03-22 21:57:33 +01:00
WerWolv
4916e5542a fix: Splash screen being scaled incorrectly 2024-03-22 17:52:10 +01:00
WerWolv
ac8ec2b622 fix: Icons not being scaled correctly anymore 2024-03-22 17:52:00 +01:00
WerWolv
9b9f7e2a1d fix: Decompress functions not extracting full data
Thanks a lot to tocklime
2024-03-22 17:34:49 +01:00
WerWolv
28ea91e6c3 build: Move macOS rpath fixing to install step 2024-03-22 17:31:53 +01:00
WerWolv
0d58307e82 build: Fix bundling issues on macOS 2024-03-22 17:24:44 +01:00
WerWolv
c8ca84ede9 fix: Prevent view providers from pointing to themselves and being saved as recent provider
#1607
2024-03-22 00:16:28 +01:00
WerWolv
ed2939c39e impr: Better UI and UX for the hex editor footer 2024-03-21 23:50:34 +01:00
WerWolv
d36bd253e8 feat: Allow shift-selecting multiple find view occurrences 2024-03-21 23:50:13 +01:00
WerWolv
4615dce0a9 build: Try fixing packaging issues with macOS bundles 2024-03-21 21:56:27 +01:00
WerWolv
7ce8aa3638 impr: Added better error logging in script loader init 2024-03-21 21:39:29 +01:00
WerWolv
9236b92dc1 fix: Memory leak when closing ImHex 2024-03-21 21:39:29 +01:00
Nobutaka Mantani
05ffcab911 build: Added support patches for FreeBSD (#1584)
This pull request fixes build on FreeBSD. The changes are conditioned
with `#if defined(__FreeBSD__)` preprocessor macro and they should not
affect build for other operating systems.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
Co-authored-by: iTrooz <hey@itrooz.fr>
2024-03-21 21:31:17 +01:00
WerWolv
61b9c0970b impr: Load unifont at correct size
Fixes #1604
2024-03-21 21:27:50 +01:00
paxcut
3b3701135f impr: Various fixes and an enhancement for the pattern editor (#1528)
Fixed console error messages using doc comment syntax highlights. Fixed
results of find not updating when march case was toggled. Fixed syntax
highlights of nested ifdefs. Fixed editor cursor blinks if OS focus goes
to another window. Fixed Highlights of "\\\"" was incorrectly handled.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2024-03-21 12:58:20 +00:00
iTrooz
f5987fde5a git: Fix MacOS packaging workflow in PRs (#1601)
Fix GeekyEggo/delete-artifact failing in PRs

https://github.com/GeekyEggo/delete-artifact/issues/25
2024-03-21 13:33:46 +01:00
iTrooz
e56b34f174 build: Mark tryDemangle as [[maybe_unused]] (#1606) 2024-03-21 13:33:23 +01:00
iTrooz
0fb43ccc2b fix: Use find_library() instead of find_file() to find system yara library (#1581)
Discord discussion:
https://discord.com/channels/789833418631675954/789840633414025246/1213564050848485427
2024-03-19 20:23:33 +01:00
WerWolv
48db4df028 build: Updated libromfs 2024-03-17 13:31:03 +01:00
WerWolv
86a0693081 fix: Crash when trying to open unopenable file 2024-03-17 13:20:02 +01:00
WerWolv
6295c1d0c3 feat: Added table pattern visualizer 2024-03-17 13:19:37 +01:00
WerWolv
35d29c8e30 patterns: Updated pattern language 2024-03-16 14:59:26 +01:00
WerWolv
ca78c4c2fc fix: Build errors when stacktrace headers are not present 2024-03-16 14:59:05 +01:00
WerWolv
f276409cde patterns: Updated pattern language 2024-03-16 10:03:23 +01:00
WerWolv
682f7bee72 patterns: Updated pattern language 2024-03-15 21:11:36 +01:00
WerWolv
43bec6a636 fix: Make sure pattern runtime is always properly configured 2024-03-15 21:08:03 +01:00
WerWolv
6eb9c750a7 fix: File open achievement not triggering when dropping a file onto ImHex 2024-03-15 21:07:45 +01:00
WerWolv
31c93c8c5c impr: Properly clear pattern editor when closing last provider 2024-03-15 21:07:21 +01:00
WerWolv
5aa1046541 fix: Potential crash when log file is unavailable 2024-03-15 21:06:47 +01:00
WerWolv
0f4504476a fix: File changed popup showing up when saving memory mapped file 2024-03-15 17:57:12 +01:00
WerWolv
3897245a7e fix: Control characters ending up in log files 2024-03-15 17:57:12 +01:00
WerWolv
373db3de95 fix: Potential crash on Linux when loading external libraries 2024-03-15 17:54:09 +01:00
WerWolv
a1437658af impr: Generate more useful stack traces on Linux 2024-03-15 17:53:12 +01:00
WerWolv
f4ec69021d impr: Manually implement VSync because GPU manufacturers are terrible at writing drivers 2024-03-14 21:18:57 +01:00
WerWolv
6012f20fb3 fix: Remove unused member variable in script loader provider wrapper 2024-03-14 20:56:08 +01:00
WerWolv
95da957f73 impr: Try to improve framerate limiting once more 2024-03-14 19:56:09 +01:00
WerWolv
642722bdb1 build: Enable cimgui on web builds again 2024-03-14 18:58:39 +01:00
WerWolv
cbc31f3c18 feat: Added short forms for commonly used commands 2024-03-14 18:24:31 +01:00
WerWolv
f2309ba079 impr: Make export selection task cancelable 2024-03-14 18:24:01 +01:00
WerWolv
246ed15d6d fix: Infinite loop when exporting selection to file 2024-03-14 17:52:44 +01:00
WerWolv
88756c83c7 fix: Right clicking reverse selected regions deselecting it 2024-03-14 17:49:46 +01:00
WerWolv
cf320266df fix: Linux build issues 2024-03-14 17:49:04 +01:00
WerWolv
47e7e80afe fix: Various issues with the virtual file system 2024-03-14 13:26:53 +01:00
WerWolv
0d880babfb fix: Advanced data information not showing up correctly 2024-03-13 22:39:21 +01:00
WerWolv
28ba34f1bf fix: Diffing option popup flickering when opening 2024-03-13 22:39:00 +01:00
WerWolv
e786cb8180 feat: Added option to create menu items from scripts 2024-03-13 19:50:05 +01:00
WerWolv
458584d778 feat: Added logger module to script loader 2024-03-13 19:49:48 +01:00
WerWolv
2c711ea206 feat: Load additional libraries from ImHex's /lib folder 2024-03-13 19:49:04 +01:00
WerWolv
7b25be51a5 fix: Base address issues with the data inspector and copy as array option
Fixes #1595
2024-03-13 16:38:44 +01:00
WerWolv
45b05a4846 fix: Denying server contact leaving crash upload option enabled
Fixes #1594
2024-03-13 09:41:04 +01:00
WerWolv
6972736abf fix: Remaining build issues 2024-03-13 09:40:37 +01:00
WerWolv
3798654f92 fix: Unix build issues with dladdr 2024-03-13 08:38:40 +01:00
WerWolv
fdf01dfb50 impr: Get rid of cimgui shared library by hooking pinvoke handler 2024-03-12 23:17:49 +01:00
WerWolv
876f091244 build: Make cimgui a shared library again 2024-03-12 19:44:21 +01:00
WerWolv
2988561f01 build: Try fix building issues with web build and Fedora 2024-03-12 19:09:01 +01:00
WerWolv
fbfc319ac1 build: Make dotnet script loader initialize properly with newer SDK version 2024-03-12 19:08:14 +01:00
WerWolv
9b1417f32d fix: ImHex using a ton of CPU power on Linux 2024-03-12 09:06:58 +01:00
WerWolv
c727762940 impr: Added AxCut to the about page 2024-03-11 21:26:03 +01:00
WerWolv
e3565d5bcb feat: Added support for creating views and drawing ImGui elemts from C# 2024-03-11 21:09:56 +01:00
WerWolv
a3f550c585 fix: Toasts not printing their message to the console correctly 2024-03-11 21:09:56 +01:00
WerWolv
c610d804b1 build: Added cimgui 2024-03-11 21:09:56 +01:00
WerWolv
3d592dbc79 fix: Highlighting not updating correctly when changing bookmark region
Fixes #1591
2024-03-11 14:00:52 +01:00
WerWolv
0186f2f456 feat: Added support for adding custom providers through C# 2024-03-10 22:05:26 +01:00
WerWolv
d817a813b0 fix: Update all task progress not increasing correctly 2024-03-10 18:59:35 +01:00
WerWolv
1d219ba511 build: Updated libwolv 2024-03-10 17:32:46 +01:00
WerWolv
285afb6d4b build: Updated libwolv 2024-03-10 15:58:57 +01:00
WerWolv
ca3708df71 fix: std::bind_front not supporting member functions everywhere 2024-03-10 15:40:32 +01:00
WerWolv
c2aafb14c2 fix: View provider not saving top-level data to project file 2024-03-10 15:32:01 +01:00
WerWolv
d4d1acb555 feat: Added --verbose command line option to enable debug logs 2024-03-10 15:22:14 +01:00
WerWolv
d1a59f8c1b fix: View provider not correctly saving its state to a project file 2024-03-10 15:17:15 +01:00
WerWolv
2fd17f97b6 build: Updated libwolv 2024-03-10 14:38:28 +01:00
WerWolv
45a3bdffe0 impr: Load small files into memory, open larger files as read-only by default
#841, #1585
2024-03-10 14:31:39 +01:00
iTrooz
f050c69ccd fix: fix ui plugin linking to pl when libimhex already does it (#1583) 2024-03-03 15:31:40 +00:00
WerWolv
c31a2551f1 git: Update release actions 2024-03-02 17:42:19 +01:00
WerWolv
90e93492a7 fix: Updater not working correctly on Windows 2024-03-02 17:42:02 +01:00
WerWolv
54266bf63b fix: Remove debug popup function in release mode to avoid build errors 2024-03-02 14:23:52 +01:00
WerWolv
ba12f7aec9 impr: Added testers to About screen 2024-03-02 11:57:30 +01:00
WerWolv
deafb6fe08 build: Allow precompiled headers to be turned off 2024-03-02 11:51:33 +01:00
WerWolv
091be1440a build: Added option to disable precompiled headers 2024-03-02 11:28:24 +01:00
WerWolv
030aee17f5 build: Disable precompiled headers in plugins again 2024-03-02 10:57:51 +01:00
WerWolv
bbbf836374 fix: Race condition when downloading multiple elements from the content store 2024-03-02 10:57:37 +01:00
WerWolv
f1b91ef360 fix: MIME based auto loading not working correctly 2024-03-02 10:57:13 +01:00
WerWolv
f6c59b456f impr: Move selection information to the top of the hex editor footer 2024-03-02 09:52:40 +01:00
WerWolv
8f3f941600 impr: Prevent ImHex from getting stuck in an infinite crash loop 2024-03-02 09:52:09 +01:00
WerWolv
e561f49e80 impr: Make welcome screen background look more interesting 2024-03-01 20:57:07 +01:00
WerWolv
2ff884fd11 feat: Replaced debug button functions with full debug menu 2024-03-01 20:56:46 +01:00
WerWolv
296af748ee fix: Platform window not being updated when recovering from a crash 2024-03-01 20:55:03 +01:00
WerWolv
0cb10fcc34 fix: Endless loop when throwing exception in deferred tasks 2024-03-01 20:54:27 +01:00
WerWolv
4a67ea0b29 patterns: Updated pattern language 2024-03-01 18:38:11 +01:00
WerWolv
8e94acc98f fix: Compile error 2024-03-01 18:37:28 +01:00
WerWolv
39cda3764b patterns: Updated pattern language 2024-03-01 18:23:45 +01:00
iTrooz
97f5175c84 impr: Better recovery from exceptions thrown in main thread (#1577)
This PR improves many things which can be seen by the commit name, but
the most important thing is the addition of a popup telling the user
when an exception is thrown


![image](https://github.com/WerWolv/ImHex/assets/42669835/db796416-9cce-4aa5-ad60-c22f05b5fc73)
2024-03-01 18:21:15 +01:00
WerWolv
78f8e5055e impr: Show hint if advanced data information section doesn't yield any result 2024-03-01 16:51:02 +01:00
WerWolv
735d896260 git: Run workflows on release branches 2024-02-29 22:28:29 +01:00
WerWolv
cb7a6596ba patterns: Updated pattern language 2024-02-29 22:23:34 +01:00
WerWolv
fdaa56fd86 build: Disable unknown pragmas warning 2024-02-29 21:49:36 +01:00
WerWolv
667b940feb fix: Build with precompiled headers for WebAssembly 2024-02-29 21:29:26 +01:00
WerWolv
bb3de7d510 fix: Build with precompiled headers on Linux 2024-02-29 21:15:51 +01:00
WerWolv
7bdde15796 build: Don't add defines to libimhex after precompiling headers 2024-02-29 20:39:14 +01:00
WerWolv
c412ba66d8 git: Updated compile instructions 2024-02-29 20:16:03 +01:00
WerWolv
dd62bee264 build: Added precompiled headers 2024-02-29 19:57:20 +01:00
WerWolv
f886eac7b5 build: Updated libwolv 2024-02-28 23:52:39 +01:00
WerWolv
623079ca40 fix: Pressing buttons while window is unfocused not working 2024-02-28 23:52:07 +01:00
WerWolv
ce9bd796d6 fix: Inserting bytes and resizing files not working correctly 2024-02-28 22:21:14 +01:00
WerWolv
d5f323a2cd feat: Allow workspaces to be deleted the same way as layouts
Fixed #1576
2024-02-28 22:10:48 +01:00
WerWolv
40592a93ac fix: MIME-based pattern loading not working correctly
Fixes #1574
2024-02-28 20:54:51 +01:00
WerWolv
dc1a5a860c fix: Buggy window detachment 2024-02-28 20:36:22 +01:00
WerWolv
f7b431902d fix: Make sure glfw waits properly on Wayland 2024-02-28 20:16:15 +01:00
WerWolv
686d47a59e fix: Frame limiting not working correctly on Linux 2024-02-28 18:48:01 +01:00
WerWolv
eaa4688182 fix: Crash when using CRC hashes 2024-02-27 22:39:18 +01:00
WerWolv
72645aa800 fix: Read-only file toast showing up for all providers 2024-02-27 20:20:28 +01:00
WerWolv
7044fc8004 fix: Import menu being disabled with read-only providers
Fixes #1573
2024-02-27 19:41:33 +01:00
Justus Garbe
9e8c780d66 fix: Use explicit string argument according to -Wformat-security on clang (#1572) 2024-02-27 19:07:44 +01:00
WerWolv
e1795d687f impr: Implement a better algorithm to determine if the frame content has changed 2024-02-27 18:55:18 +01:00
WerWolv
607f7cba8d fix: Yara rules not being read correctly in data information section 2024-02-27 18:01:30 +01:00
WerWolv
2572e23928 impr: Add better error handling when loading projects 2024-02-26 21:48:56 +01:00
WerWolv
60921031bd fix: Opening project files through the command line opening them as regular files 2024-02-26 21:43:28 +01:00
WerWolv
77550d902c feat: Added option to disable annotations in byte type graph 2024-02-26 21:41:43 +01:00
WerWolv
41935781fb fix: Build error due to use of localized strings 2024-02-26 21:13:57 +01:00
iTrooz
47362559ef tests: Add infrastructure for testing plugins (#1538)
This PR adds a test architecture to be able to test plugins

Main infrastructure done by @WerWolv

---------

Co-authored-by: WerWolv <werwolv98@gmail.com>
2024-02-26 20:51:08 +01:00
WerWolv
032ef0722d patterns: Updated pattern language 2024-02-26 20:49:42 +01:00
WerWolv
6e32f03a6b feat: Added plain text and similar bytes regions to byte types diagram 2024-02-26 20:49:35 +01:00
WerWolv
5731dcf135 impr: Make hex editor minimap rows stay a fixed height 2024-02-26 20:49:15 +01:00
130 changed files with 13242 additions and 997 deletions

View File

@@ -213,21 +213,45 @@ jobs:
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
.. ..
- name: 🛠️ Build - name: 🛠️ Build
run: cd build && ninja package run: cd build && ninja install
- name: ✒️ Fix Signature
run: |
set -x
cd build/install
mv imhex.app ImHex.app
codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app
- name: 📁 Fix permissions
run: |
set -x
cd build/install
chmod -R 755 ImHex.app/
- name: 📦 Create DMG
run: |
set -x
mkdir bundle
mv build/install/ImHex.app bundle
cd bundle
ln -s /Applications Applications
cd ..
hdiutil create -volname "ImHex" -srcfolder bundle -ov -format UDZO imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64.dmg
- name: ⬆️ Upload DMG - name: ⬆️ Upload DMG
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
if-no-files-found: error if-no-files-found: error
name: macOS DMG${{matrix.suffix}} x86_64 name: macOS DMG${{matrix.suffix}} x86_64
path: build/*.dmg path: ./*.dmg
macos-arm64-build: macos-arm64-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@@ -287,15 +311,15 @@ jobs:
path: out path: out
- name: 🗑️ Delete artifact - name: 🗑️ Delete artifact
uses: geekyeggo/delete-artifact@v4 uses: geekyeggo/delete-artifact@v5
with: with:
token: ${{ secrets.GITHUB_TOKEN }}
name: macos_arm64_intermediate name: macos_arm64_intermediate
- name: ✒️ Fix Signature - name: ✒️ Fix Signature
run: | run: |
set -x set -x
cd out cd out
mv imhex.app ImHex.app
codesign --remove-signature ImHex.app codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app codesign --force --deep --sign - ImHex.app
@@ -627,11 +651,11 @@ jobs:
- name: ✒️ Modify spec file - name: ✒️ Modify spec file
run: | run: |
sed -i \ sed -i \
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/g' \ -e 's/Version: VERSION$/Version: ${{env.IMHEX_VERSION}}/g' \
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \ -e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \ -e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
-e '/BuildRequires: cmake/a BuildRequires: git-core' \ -e '/BuildRequires: cmake/a BuildRequires: git-core' \
-e '/%files/a %{_datadir}/%{name}/' \ -e '/%files/a %{_datadir}/%{name}/' \
$GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec
- name: 📜 Fix ccache on EL9 - name: 📜 Fix ccache on EL9

View File

@@ -90,7 +90,7 @@ jobs:
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
- name: ⬇️ Download artifacts from latest workflow - name: ⬇️ Download artifacts from latest workflow
uses: dawidd6/action-download-artifact@v2 uses: dawidd6/action-download-artifact@v3
with: with:
github_token: ${{secrets.GITHUB_TOKEN}} github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml workflow: build.yml
@@ -119,7 +119,7 @@ jobs:
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
- name: ⬆️ Upload everything to release - name: ⬆️ Upload everything to release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with: with:
files: '*' files: '*'

View File

@@ -53,6 +53,7 @@ jobs:
cd build cd build
CC=gcc-12 CXX=g++-12 cmake \ CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DIMHEX_ENABLE_UNIT_TESTS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \ -DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \

View File

@@ -40,6 +40,9 @@ project(imhex
HOMEPAGE_URL "https://imhex.werwolv.net" HOMEPAGE_URL "https://imhex.werwolv.net"
) )
# Add ImHex sources
add_custom_target(imhex_all ALL)
# Make sure project is configured correctly # Make sure project is configured correctly
setDefaultBuiltTypeIfUnset() setDefaultBuiltTypeIfUnset()
detectBadClone() detectBadClone()
@@ -56,17 +59,19 @@ configurePackingResources()
setUninstallTarget() setUninstallTarget()
addBundledLibraries() addBundledLibraries()
# Add ImHex sources
add_custom_target(imhex_all ALL)
add_subdirectory(lib/libimhex) add_subdirectory(lib/libimhex)
add_subdirectory(main) add_subdirectory(main)
addPluginDirectories()
# Add unit tests # Add unit tests
enable_testing() if (IMHEX_ENABLE_UNIT_TESTS)
add_subdirectory(tests EXCLUDE_FROM_ALL) enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
endif ()
# Configure more resources that will be added to the install package # Configure more resources that will be added to the install package
createPackage()
generatePDBs() generatePDBs()
generateSDKDirectory() generateSDKDirectory()
# Handle package generation
createPackage()

View File

@@ -1 +1 @@
1.33.2 1.33.0

View File

@@ -92,6 +92,8 @@ macro(detectOS)
endmacro() endmacro()
macro(configurePackingResources) macro(configurePackingResources)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if (WIN32) if (WIN32)
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(APPLICATION_TYPE WIN32) set(APPLICATION_TYPE WIN32)
@@ -145,9 +147,7 @@ macro(configurePackingResources)
endif() endif()
endmacro() endmacro()
macro(createPackage) macro(addPluginDirectories)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
file(MAKE_DIRECTORY "plugins") file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS) foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}") add_subdirectory("plugins/${plugin}")
@@ -178,9 +178,9 @@ macro(createPackage)
add_dependencies(imhex_all ${plugin}) add_dependencies(imhex_all ${plugin})
endif () endif ()
endforeach() endforeach()
endmacro()
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) macro(createPackage)
if (WIN32) if (WIN32)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex. # Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_BINDIR ".")
@@ -217,7 +217,6 @@ macro(createPackage)
endforeach() endforeach()
]]) ]])
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./") downloadImHexPatternsFiles("./")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
@@ -228,7 +227,6 @@ macro(createPackage)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex) 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}/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 ${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") downloadImHexPatternsFiles("./share/imhex")
# install AppStream file # install AppStream file
@@ -249,17 +247,15 @@ macro(createPackage)
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST}) set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath # Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main> || true) install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
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-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") add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS") downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources") install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".") install(TARGETS main BUNDLE DESTINATION ".")
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
install(FILES $<TARGET_FILE:updater> DESTINATION "${IMHEX_BUNDLE_PATH}")
# Update library references to make the bundle portable # Update library references to make the bundle portable
postprocess_bundle(imhex_all main) postprocess_bundle(imhex_all main)
@@ -268,15 +264,17 @@ macro(createPackage)
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns") set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist") set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist")
if (IMHEX_RESIGN_BUNDLE) if (IMHEX_RESIGN_BUNDLE)
message(STATUS "Resigning bundle...")
find_program(CODESIGN_PATH codesign) find_program(CODESIGN_PATH codesign)
if (CODESIGN_PATH) if (CODESIGN_PATH)
add_custom_command(TARGET imhex_all POST_BUILD COMMAND "codesign" ARGS "--force" "--deep" "--sign" "-" "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}") install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")")
install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)")
endif() endif()
endif() endif()
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
endif() endif()
else() else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -660,20 +658,20 @@ macro(addBundledLibraries)
if (${Backtrace_FOUND}) if (${Backtrace_FOUND})
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}") message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
if (Backtrace_HEADER STREQUAL "execinfo.h") if (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY}) set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR}) set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\") add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_EXECINFO)
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_BACKTRACE) add_compile_definitions(HEX_HAS_BACKTRACE)
endif () elseif (Backtrace_HEADER STREQUAL "execinfo.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_EXECINFO)
endif()
endif() endif()
endif () endif()
endif () endif()
endmacro() endmacro()
function(enableUnityBuild TARGET) function(enableUnityBuild TARGET)
@@ -739,7 +737,7 @@ function(generateSDKDirectory)
if (WIN32) if (WIN32)
set(SDK_PATH "./sdk") set(SDK_PATH "./sdk")
elseif (APPLE) elseif (APPLE)
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk") set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
else() else()
set(SDK_PATH "share/imhex/sdk") set(SDK_PATH "share/imhex/sdk")
endif() endif()

View File

@@ -14,12 +14,11 @@ if(CMAKE_GENERATOR)
# Being called as include(PostprocessBundle), so define a helper function. # Being called as include(PostprocessBundle), so define a helper function.
set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}") set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
function(postprocess_bundle out_target in_target) function(postprocess_bundle out_target in_target)
add_custom_command(TARGET ${out_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -DBUNDLE_PATH="$<TARGET_FILE_DIR:${in_target}>/../.." install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})")
-DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_CERTIFICATE_ID}" install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})")
-DEXTRA_BUNDLE_LIBRARY_PATHS="${EXTRA_BUNDLE_LIBRARY_PATHS}" install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})")
-P "${_POSTPROCESS_BUNDLE_MODULE_LOCATION}" install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})
)
endfunction() endfunction()
return() return()
endif() endif()
@@ -42,7 +41,7 @@ file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug")
# makes it sometimes break on libraries that do weird things with @rpath. Specify # makes it sometimes break on libraries that do weird things with @rpath. Specify
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625 # equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
# is fixed and in our minimum CMake version. # is fixed and in our minimum CMake version.
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins") set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins" "${BUNDLE_PATH}/Contents/Frameworks")
message(STATUS "Fixing up application bundle: ${extra_dirs}") message(STATUS "Fixing up application bundle: ${extra_dirs}")
# BundleUtilities is overly verbose, so disable most of its messages # BundleUtilities is overly verbose, so disable most of its messages

View File

@@ -18,11 +18,11 @@ docker buildx build . -f <DOCKERFILE_PATH> --progress plain --build-arg 'JOBS=4'
where `<DOCKERFILE_PATH>` should be replaced by the wanted Dockerfile base d on the build you want to do: where `<DOCKERFILE_PATH>` should be replaced by the wanted Dockerfile base d on the build you want to do:
| Wanted build | Dockerfile path | | Wanted build | Dockerfile path | Target |
|--------------|-----------------------------| |--------------|-----------------------------|--------|
| MacOS M1 | dist/macOS/arm64.Dockerfile | | MacOS M1 | dist/macOS/arm64.Dockerfile | - |
| AppImage | dist/appimage/Dockerfile | | AppImage | dist/appimage/Dockerfile | - |
| Web version | dist/web/Dockerfile | | Web version | dist/web/Dockerfile | raw |
We'll explain this command in the next section We'll explain this command in the next section
@@ -43,6 +43,7 @@ In the command saw earlier:
- `.` is the base folder that the Dockerfile will be allowed to see - `.` is the base folder that the Dockerfile will be allowed to see
- `-f <path>` is to specify the Dockerfile path - `-f <path>` is to specify the Dockerfile path
- `--progress plain` is to allow you to see the output of instructions - `--progress plain` is to allow you to see the output of instructions
- `--build-arg <key>=<value>` is to allow to to specify arguments to the build (like -DKEY=VALUE in CMake) - `--build-arg <key>=<value>` is to allow to specify arguments to the build (like -DKEY=VALUE in CMake)
- `--build-context key=<folder>` is to specify folders other than the base folder that the Dockerfile is allowed to see - `--build-context key=<folder>` is to specify folders other than the base folder that the Dockerfile is allowed to see
- `--output <path>` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want) - `--output <path>` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want)
- `--target <target>` specifies which docker target to build

View File

@@ -13,14 +13,8 @@ CC=gcc-12 CXX=g++-12 \
cmake -G "Ninja" \ cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="/usr" \ -DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
.. ..
make -j 4 install ninja install
``` ```
All paths follow the XDG Base Directories standard, and can thus be modified All paths follow the XDG Base Directories standard, and can thus be modified

View File

@@ -15,7 +15,7 @@ OBJC=$(brew --prefix llvm)/bin/clang \
OBJCXX=$(brew --prefix llvm)/bin/clang++ \ OBJCXX=$(brew --prefix llvm)/bin/clang++ \
cmake -G "Ninja" \ cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=./install \ -DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_GENERATE_PACKAGE=ON \ -DIMHEX_GENERATE_PACKAGE=ON \
.. ..
ninja install ninja install

View File

@@ -14,7 +14,7 @@ mkdir build
cd build cd build
cmake -G "Ninja" \ cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$PWD/install" \ -DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \ -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
.. ..
ninja install ninja install

View File

@@ -170,4 +170,4 @@ EOF
FROM scratch FROM scratch
COPY --from=build /mnt/ImHex/build/install/imhex.app ImHex.app COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app

6
dist/rpm/imhex.spec vendored
View File

@@ -1,5 +1,5 @@
Name: imhex Name: imhex
Version: 1.26.2 Version: VERSION
Release: 0%{?dist} Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers Summary: A hex editor for reverse engineers and programmers
@@ -95,10 +95,6 @@ CXXFLAGS+=" -std=gnu++2b"
%set_build_flags %set_build_flags
CXXFLAGS+=" -std=gnu++2b" CXXFLAGS+=" -std=gnu++2b"
%endif %endif
# build binaries required for tests
%cmake_build --target unit_tests
%ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)'
# Helpers/*API exclude tests that require network access
%install %install

View File

@@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
source/helpers/tar.cpp source/helpers/tar.cpp
source/helpers/debugging.cpp source/helpers/debugging.cpp
source/test/tests.cpp
source/providers/provider.cpp source/providers/provider.cpp
source/providers/memory_provider.cpp source/providers/memory_provider.cpp
source/providers/undo/stack.cpp source/providers/undo/stack.cpp
@@ -146,3 +148,6 @@ target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIE
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE) set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
add_dependencies(imhex_all libimhex) add_dependencies(imhex_all libimhex)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

View File

@@ -931,7 +931,7 @@ namespace hex {
void addProviderName(const UnlocalizedString &unlocalizedName); void addProviderName(const UnlocalizedString &unlocalizedName);
using ProviderCreationFunction = std::unique_ptr<prv::Provider>(*)(); using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
void add(const std::string &typeName, ProviderCreationFunction creationFunction); void add(const std::string &typeName, ProviderCreationFunction creationFunction);
const std::vector<std::string>& getEntries(); const std::vector<std::string>& getEntries();

View File

@@ -3,6 +3,7 @@
#include <hex.hpp> #include <hex.hpp>
#include <list> #include <list>
#include <mutex>
#include <map> #include <map>
#include <string_view> #include <string_view>
#include <functional> #include <functional>

View File

@@ -664,6 +664,12 @@ namespace hex {
*/ */
std::optional<InitialWindowProperties> getInitialWindowProperties(); std::optional<InitialWindowProperties> getInitialWindowProperties();
/**
* @brief Gets the module handle of libimhex
* @return Module handle
*/
void* getLibImHexModuleHandle();
} }
/** /**

View File

@@ -46,7 +46,13 @@ namespace hex {
* @brief Get a list of all layouts * @brief Get a list of all layouts
* @return List of all added layouts * @return List of all added layouts
*/ */
static std::vector<Layout> getLayouts(); static const std::vector<Layout> &getLayouts();
/**
* @brief Removes the layout with the given name
* @param name Name of the layout
*/
static void removeLayout(const std::string &name);
/** /**
* @brief Handles loading of layouts if needed * @brief Handles loading of layouts if needed

View File

@@ -15,8 +15,10 @@ struct ImGuiContext;
namespace hex { namespace hex {
struct SubCommand { struct SubCommand {
std::string commandKey; std::string commandLong;
std::string commandDesc; std::string commandShort;
std::string commandDescription;
std::function<void(const std::vector<std::string>&)> callback; std::function<void(const std::vector<std::string>&)> callback;
}; };
@@ -104,6 +106,10 @@ namespace hex {
static bool load(); static bool load();
static bool load(const std::fs::path &pluginFolder); static bool load(const std::fs::path &pluginFolder);
static bool loadLibraries();
static bool loadLibraries(const std::fs::path &libraryFolder);
static void unload(); static void unload();
static void reload(); static void reload();
static void initializeNewPlugins(); static void initializeNewPlugins();
@@ -111,6 +117,7 @@ namespace hex {
static void addPlugin(const std::string &name, PluginFunctions functions); static void addPlugin(const std::string &name, PluginFunctions functions);
static Plugin* getPlugin(const std::string &name);
static const std::list<Plugin>& getPlugins(); static const std::list<Plugin>& getPlugins();
static const std::vector<std::fs::path>& getPluginPaths(); static const std::vector<std::fs::path>& getPluginPaths();
static const std::vector<std::fs::path>& getPluginLoadPaths(); static const std::vector<std::fs::path>& getPluginLoadPaths();
@@ -121,6 +128,7 @@ namespace hex {
static std::list<Plugin>& getPluginsMutable(); static std::list<Plugin>& getPluginsMutable();
static AutoReset<std::vector<std::fs::path>> s_pluginPaths, s_pluginLoadPaths; static AutoReset<std::vector<std::fs::path>> s_pluginPaths, s_pluginLoadPaths;
static AutoReset<std::vector<uintptr_t>> s_loadedLibraries;
}; };
} }

View File

@@ -13,18 +13,22 @@ namespace hex {
struct Workspace { struct Workspace {
std::string layout; std::string layout;
std::fs::path path; std::fs::path path;
bool builtin;
}; };
static void createWorkspace(const std::string &name, const std::string &layout = ""); static void createWorkspace(const std::string &name, const std::string &layout = "");
static void switchWorkspace(const std::string &name); static void switchWorkspace(const std::string &name);
static void importFromFile(const std::fs::path &path); static void importFromFile(const std::fs::path &path);
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}); static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false);
static void removeWorkspace(const std::string &name);
static const auto& getWorkspaces() { return *s_workspaces; } static const auto& getWorkspaces() { return *s_workspaces; }
static const auto& getCurrentWorkspace() { return s_currentWorkspace; } static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
static void reset(); static void reset();
static void reload();
static void process(); static void process();
@@ -32,7 +36,7 @@ namespace hex {
WorkspaceManager() = default; WorkspaceManager() = default;
static AutoReset<std::map<std::string, Workspace>> s_workspaces; static AutoReset<std::map<std::string, Workspace>> s_workspaces;
static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace; static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace, s_workspaceToRemove;
}; };
} }

View File

@@ -8,6 +8,7 @@
#include <fmt/color.h> #include <fmt/color.h>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::log { namespace hex::log {
@@ -21,6 +22,7 @@ namespace hex::log {
[[nodiscard]] std::recursive_mutex& getLoggerMutex(); [[nodiscard]] std::recursive_mutex& getLoggerMutex();
[[nodiscard]] bool isLoggingSuspended(); [[nodiscard]] bool isLoggingSuspended();
[[nodiscard]] bool isDebugLoggingEnabled();
struct LogEntry { struct LogEntry {
std::string project; std::string project;
@@ -65,13 +67,14 @@ namespace hex::log {
void suspendLogging(); void suspendLogging();
void resumeLogging(); void resumeLogging();
void enableDebugLogging();
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) { [[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
#if defined(DEBUG) if (impl::isDebugLoggingEnabled()) [[unlikely]] {
hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...); hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
#else } else {
impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...)); impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
#endif }
} }
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) { [[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
@@ -90,7 +93,6 @@ namespace hex::log {
hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...); hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
} }
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) { [[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::getLoggerMutex()); std::scoped_lock lock(impl::getLoggerMutex());

View File

@@ -331,4 +331,14 @@ namespace hex {
[[nodiscard]] std::string formatSystemError(i32 error); [[nodiscard]] std::string formatSystemError(i32 error);
/**
* Gets the shared library handle for a given pointer
* @param symbol Pointer to any function or variable in the shared library
* @return The module handle
* @warning Important! Calling this function on functions defined in other modules will return the handle of the current module!
* This is because you're not actually passing a pointer to the function in the other module but rather a pointer to a thunk
* that is defined in the current module.
*/
[[nodiscard]] void* getContainingModule(void* symbol);
} }

View File

@@ -35,8 +35,6 @@ namespace hex::prv {
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); } [[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
void resizeRaw(u64 newSize) override; void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
[[nodiscard]] std::string getName() const override { return m_name; } [[nodiscard]] std::string getName() const override { return m_name; }

View File

@@ -161,13 +161,13 @@ namespace hex::prv {
*/ */
[[nodiscard]] virtual std::string getName() const = 0; [[nodiscard]] virtual std::string getName() const = 0;
void resize(u64 newSize); bool resize(u64 newSize);
void insert(u64 offset, u64 size); void insert(u64 offset, u64 size);
void remove(u64 offset, u64 size); void remove(u64 offset, u64 size);
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); } virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); } virtual void insertRaw(u64 offset, u64 size);
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); } virtual void removeRaw(u64 offset, u64 size);
virtual void save(); virtual void save();
virtual void saveAs(const std::fs::path &path); virtual void saveAs(const std::fs::path &path);

View File

@@ -27,6 +27,7 @@ namespace hex::prv::undo {
void groupOperations(u32 count, const UnlocalizedString &unlocalizedName); void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);
void apply(const Stack &otherStack); void apply(const Stack &otherStack);
void reapply();
[[nodiscard]] bool canUndo() const; [[nodiscard]] bool canUndo() const;
[[nodiscard]] bool canRedo() const; [[nodiscard]] bool canRedo() const;

View File

@@ -5,6 +5,7 @@
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/api/plugin_manager.hpp>
#include <wolv/utils/preproc.hpp> #include <wolv/utils/preproc.hpp>
@@ -28,16 +29,20 @@
} \ } \
} while (0) } while (0)
#define INIT_PLUGIN(name) \
if (!hex::test::initPluginImpl(name)) TEST_FAIL();
namespace hex::test { namespace hex::test {
using Function = int(*)();
struct Test { struct Test {
std::function<int()> function; Function function;
bool shouldFail; bool shouldFail;
}; };
class Tests { class Tests {
public: public:
static auto addTest(const std::string &name, const std::function<int()> &func, bool shouldFail) noexcept { static auto addTest(const std::string &name, Function func, bool shouldFail) noexcept {
s_tests.insert({ s_tests.insert({
name, {func, shouldFail} name, {func, shouldFail}
}); });
@@ -50,7 +55,7 @@ namespace hex::test {
} }
private: private:
static inline std::map<std::string, Test> s_tests; static std::map<std::string, Test> s_tests;
}; };
template<class F> template<class F>
@@ -86,4 +91,5 @@ namespace hex::test {
return TestSequence<F>(executor.getName(), std::forward<F>(f), executor.shouldFail()); return TestSequence<F>(executor.getName(), std::forward<F>(f), executor.shouldFail());
} }
bool initPluginImpl(std::string name);
} }

View File

@@ -75,7 +75,11 @@ namespace hex {
static AutoReset<std::optional<ProviderRegion>> s_currentSelection; static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
void setCurrentSelection(const std::optional<ProviderRegion> &region) { void setCurrentSelection(const std::optional<ProviderRegion> &region) {
*s_currentSelection = region; if (region == Region::Invalid()) {
clearSelection();
} else {
*s_currentSelection = region;
}
} }
static PerProvider<std::optional<Region>> s_hoveredRegion; static PerProvider<std::optional<Region>> s_hoveredRegion;
@@ -599,6 +603,11 @@ namespace hex {
return impl::s_initialWindowProperties; return impl::s_initialWindowProperties;
} }
void* getLibImHexModuleHandle() {
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
}
const std::map<std::string, std::string>& getInitArguments() { const std::map<std::string, std::string>& getInitArguments() {
return *impl::s_initArguments; return *impl::s_initArguments;
} }

View File

@@ -64,10 +64,25 @@ namespace hex {
} }
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() { const std::vector<LayoutManager::Layout>& LayoutManager::getLayouts() {
return s_layouts; return s_layouts;
} }
void LayoutManager::removeLayout(const std::string& name) {
for (const auto &layout : *s_layouts) {
if (layout.name == name) {
if (wolv::io::File(layout.path, wolv::io::File::Mode::Write).remove()) {
log::info("Removed layout '{}'", name);
LayoutManager::reload();
} else {
log::error("Failed to remove layout '{}'", name);
}
return;
}
}
}
void LayoutManager::closeAllViews() { void LayoutManager::closeAllViews() {
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries()) for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
view->getWindowOpenState() = false; view->getWindowOpenState() = false;

View File

@@ -18,24 +18,50 @@
namespace hex { namespace hex {
static uintptr_t loadLibrary(const std::fs::path &path) {
#if defined(OS_WINDOWS)
auto handle = uintptr_t(LoadLibraryW(path.c_str()));
if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) {
log::error("Loading library '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
return 0;
}
return handle;
#else
auto handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
if (handle == 0) {
log::error("Loading library '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
return 0;
}
return handle;
#endif
}
static void unloadLibrary(uintptr_t handle, const std::fs::path &path) {
#if defined(OS_WINDOWS)
if (handle != 0) {
if (FreeLibrary(HMODULE(handle)) == FALSE) {
log::error("Error when unloading library '{}': {}!", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError()));
}
}
#else
if (handle != 0) {
if (dlclose(reinterpret_cast<void*>(handle)) != 0) {
log::error("Error when unloading library '{}': {}!", path.filename().string(), dlerror());
}
}
#endif
}
Plugin::Plugin(const std::fs::path &path) : m_path(path) { Plugin::Plugin(const std::fs::path &path) : m_path(path) {
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename())); log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
#if defined(OS_WINDOWS) m_handle = loadLibrary(path);
m_handle = uintptr_t(LoadLibraryW(path.c_str())); if (m_handle == 0)
return;
if (m_handle == uintptr_t(INVALID_HANDLE_VALUE) || m_handle == 0) {
log::error("Loading plugin '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
return;
}
#else
m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
if (m_handle == 0) {
log::error("Loading plugin '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
return;
}
#endif
const auto fileName = path.stem().string(); const auto fileName = path.stem().string();
@@ -89,15 +115,7 @@ namespace hex {
log::info("Trying to unload plugin '{}'", getPluginName()); log::info("Trying to unload plugin '{}'", getPluginName());
} }
#if defined(OS_WINDOWS) unloadLibrary(m_handle, m_path);
if (m_handle != 0)
if (FreeLibrary(HMODULE(m_handle)) == FALSE) {
log::error("Error when unloading plugin '{}': {}!", wolv::util::toUTF8String(m_path.filename()), hex::formatSystemError(::GetLastError()));
}
#else
if (m_handle != 0)
dlclose(reinterpret_cast<void*>(m_handle));
#endif
} }
bool Plugin::initializePlugin() const { bool Plugin::initializePlugin() const {
@@ -284,6 +302,35 @@ namespace hex {
return true; return true;
} }
AutoReset<std::vector<uintptr_t>> PluginManager::s_loadedLibraries;
bool PluginManager::loadLibraries() {
bool success = true;
for (const auto &loadPath : fs::getDefaultPaths(fs::ImHexPath::Libraries))
success = PluginManager::loadLibraries(loadPath) && success;
return success;
}
bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) {
bool success = true;
for (const auto &entry : std::fs::directory_iterator(libraryFolder)) {
if (!(entry.path().extension() == ".dll" || entry.path().extension() == ".so" || entry.path().extension() == ".dylib"))
continue;
auto handle = loadLibrary(entry);
if (handle == 0) {
success = false;
}
PluginManager::s_loadedLibraries->push_back(handle);
}
return success;
}
void PluginManager::initializeNewPlugins() { void PluginManager::initializeNewPlugins() {
for (const auto &plugin : getPlugins()) { for (const auto &plugin : getPlugins()) {
if (!plugin.isLoaded()) if (!plugin.isLoaded())
@@ -304,6 +351,11 @@ namespace hex {
plugins.pop_back(); plugins.pop_back();
} }
while (!s_loadedLibraries->empty()) {
unloadLibrary(s_loadedLibraries->back(), "");
s_loadedLibraries->pop_back();
}
getPluginsMutable() = std::move(savedPlugins); getPluginsMutable() = std::move(savedPlugins);
} }
@@ -321,6 +373,15 @@ namespace hex {
return plugins; return plugins;
} }
Plugin* PluginManager::getPlugin(const std::string &name) {
for (auto &plugin : getPluginsMutable()) {
if (plugin.getPluginName() == name)
return &plugin;
}
return nullptr;
}
const std::vector<std::fs::path>& PluginManager::getPluginPaths() { const std::vector<std::fs::path>& PluginManager::getPluginPaths() {
return s_pluginPaths; return s_pluginPaths;
} }
@@ -335,5 +396,4 @@ namespace hex {
}); });
} }
} }

View File

@@ -9,22 +9,28 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <imgui.h> #include <imgui.h>
#include <wolv/utils/string.hpp>
namespace hex { namespace hex {
AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces; AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces;
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end();
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end();
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_workspaceToRemove = s_workspaces->end();
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) { void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace { s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
.layout = layout.empty() ? LayoutManager::saveToString() : layout, .layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {} .path = {},
.builtin = false
}).first; }).first;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) { for (const auto &workspaceFolder : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
if (exportToFile(path / (name + ".hexws"))) const auto workspacePath = workspaceFolder / (name + ".hexws");
if (exportToFile(workspacePath)) {
s_currentWorkspace->second.path = workspacePath;
break; break;
}
} }
} }
@@ -37,6 +43,9 @@ namespace hex {
} }
void WorkspaceManager::importFromFile(const std::fs::path& path) { void WorkspaceManager::importFromFile(const std::fs::path& path) {
if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; }))
return;
wolv::io::File file(path, wolv::io::File::Mode::Read); wolv::io::File file(path, wolv::io::File::Mode::Read);
if (!file.isValid()) { if (!file.isValid()) {
log::error("Failed to load workspace from file '{}'", path.string()); log::error("Failed to load workspace from file '{}'", path.string());
@@ -50,10 +59,12 @@ namespace hex {
const std::string name = json["name"]; const std::string name = json["name"];
std::string layout = json["layout"]; std::string layout = json["layout"];
const bool builtin = json.value("builtin", false);
(*s_workspaces)[name] = Workspace { (*s_workspaces)[name] = Workspace {
.layout = std::move(layout), .layout = std::move(layout),
.path = path .path = path,
.builtin = builtin
}; };
} catch (nlohmann::json::exception &e) { } catch (nlohmann::json::exception &e) {
log::error("Failed to load workspace from file '{}': {}", path.string(), e.what()); log::error("Failed to load workspace from file '{}': {}", path.string(), e.what());
@@ -61,7 +72,7 @@ namespace hex {
} }
} }
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) { bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) {
if (path.empty()) { if (path.empty()) {
if (s_currentWorkspace == s_workspaces->end()) if (s_currentWorkspace == s_workspaces->end())
return false; return false;
@@ -80,22 +91,46 @@ namespace hex {
nlohmann::json json; nlohmann::json json;
json["name"] = workspaceName; json["name"] = workspaceName;
json["layout"] = LayoutManager::saveToString(); json["layout"] = LayoutManager::saveToString();
json["builtin"] = builtin;
file.writeString(json.dump(4)); file.writeString(json.dump(4));
return true; return true;
} }
void WorkspaceManager::removeWorkspace(const std::string& name) {
for (const auto &[workspaceName, workspace] : *s_workspaces) {
if (workspaceName == name) {
log::info("{}", wolv::util::toUTF8String(workspace.path));
if (wolv::io::File(workspace.path, wolv::io::File::Mode::Write).remove()) {
log::info("Removed workspace '{}'", name);
switchWorkspace(s_workspaces->begin()->first);
s_workspaces->erase(workspaceName);
} else {
log::error("Failed to remove workspace '{}'", name);
}
return;
}
}
}
void WorkspaceManager::process() { void WorkspaceManager::process() {
if (s_previousWorkspace != s_currentWorkspace) { if (s_previousWorkspace != s_currentWorkspace) {
log::info("Updating workspace");
if (s_previousWorkspace != s_workspaces->end()) if (s_previousWorkspace != s_workspaces->end())
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first); exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin);
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str()); ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
s_previousWorkspace = s_currentWorkspace; s_previousWorkspace = s_currentWorkspace;
if (s_workspaceToRemove != s_workspaces->end()) {
s_workspaces->erase(s_workspaceToRemove);
s_workspaceToRemove = s_workspaces->end();
}
} }
} }
@@ -106,6 +141,24 @@ namespace hex {
s_previousWorkspace = s_workspaces->end(); s_previousWorkspace = s_workspaces->end();
} }
void WorkspaceManager::reload() {
WorkspaceManager::reset();
for (const auto &defaultPath : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
for (const auto &entry : std::fs::directory_iterator(defaultPath)) {
if (!entry.is_regular_file())
continue;
const auto &path = entry.path();
if (path.extension() != ".hexws")
continue;
WorkspaceManager::importFromFile(path);
}
}
}
} }

View File

@@ -1,4 +1,6 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/api/event_manager.hpp>
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
@@ -20,6 +22,7 @@ namespace hex::log {
bool s_colorOutputEnabled = false; bool s_colorOutputEnabled = false;
std::recursive_mutex s_loggerMutex; std::recursive_mutex s_loggerMutex;
bool s_loggingSuspended = false; bool s_loggingSuspended = false;
bool s_debugLoggingEnabled = false;
} }
@@ -31,6 +34,10 @@ namespace hex::log {
s_loggingSuspended = false; s_loggingSuspended = false;
} }
void enableDebugLogging() {
s_debugLoggingEnabled = true;
}
namespace impl { namespace impl {
std::recursive_mutex& getLoggerMutex() { std::recursive_mutex& getLoggerMutex() {
@@ -41,6 +48,14 @@ namespace hex::log {
return s_loggingSuspended; return s_loggingSuspended;
} }
bool isDebugLoggingEnabled() {
#if defined(DEBUG)
return true;
#else
return s_debugLoggingEnabled;
#endif
}
FILE *getDestination() { FILE *getDestination() {
if (s_loggerFile.isValid()) if (s_loggerFile.isValid())
return s_loggerFile.getHandle(); return s_loggerFile.getHandle();

View File

@@ -16,9 +16,11 @@
#include <wolv/utils/guards.hpp> #include <wolv/utils/guards.hpp>
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_linux.hpp> #include <hex/helpers/utils_linux.hpp>
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_macos.hpp> #include <hex/helpers/utils_macos.hpp>
#elif defined(OS_WEB) #elif defined(OS_WEB)
#include "emscripten.h" #include "emscripten.h"
@@ -784,4 +786,24 @@ namespace hex {
#endif #endif
} }
void* getContainingModule(void* symbol) {
#if defined(OS_WINDOWS)
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(symbol, &mbi, sizeof(mbi)))
return mbi.AllocationBase;
return nullptr;
#elif !defined(OS_WEB)
Dl_info info = {};
if (dladdr(symbol, &info) == 0)
return nullptr;
return dlopen(info.dli_fname, RTLD_LAZY);
#else
hex::unused(symbol);
return nullptr;
#endif
}
} }

View File

@@ -31,41 +31,4 @@ namespace hex::prv {
m_data.resize(newSize); m_data.resize(newSize);
} }
void MemoryProvider::insertRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
this->resizeRaw(oldSize + size);
std::vector<u8> buffer(0x1000);
const std::vector<u8> zeroBuffer(0x1000);
auto position = oldSize;
while (position > offset) {
const auto readSize = std::min<size_t>(position - offset, buffer.size());
position -= readSize;
this->readRaw(position, buffer.data(), readSize);
this->writeRaw(position, zeroBuffer.data(), readSize);
this->writeRaw(position + size, buffer.data(), readSize);
}
}
void MemoryProvider::removeRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
std::vector<u8> buffer(0x1000);
const auto newSize = oldSize - size;
auto position = offset;
while (position < newSize) {
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
this->readRaw(position + size, buffer.data(), readSize);
this->writeRaw(position, buffer.data(), readSize);
position += readSize;
}
this->resizeRaw(oldSize - size);
}
} }

View File

@@ -74,7 +74,11 @@ namespace hex::prv {
} }
} }
void Provider::resize(u64 newSize) { bool Provider::resize(u64 newSize) {
if (newSize >> 63) {
log::error("new provider size is very large ({}). Is it a negative number ?", newSize);
return false;
}
i64 difference = newSize - this->getActualSize(); i64 difference = newSize - this->getActualSize();
if (difference > 0) if (difference > 0)
@@ -83,6 +87,7 @@ namespace hex::prv {
EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference); EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);
this->markDirty(); this->markDirty();
return true;
} }
void Provider::insert(u64 offset, u64 size) { void Provider::insert(u64 offset, u64 size) {
@@ -97,6 +102,52 @@ namespace hex::prv {
this->markDirty(); this->markDirty();
} }
void Provider::insertRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
this->resizeRaw(oldSize + size);
std::vector<u8> buffer(0x1000);
const std::vector<u8> zeroBuffer(0x1000);
auto position = oldSize;
while (position > offset) {
const auto readSize = std::min<size_t>(position - offset, buffer.size());
position -= readSize;
this->readRaw(position, buffer.data(), readSize);
this->writeRaw(position, zeroBuffer.data(), readSize);
this->writeRaw(position + size, buffer.data(), readSize);
}
}
void Provider::removeRaw(u64 offset, u64 size) {
if (offset > this->getActualSize() || size == 0)
return;
if ((offset + size) > this->getActualSize())
size = this->getActualSize() - offset;
auto oldSize = this->getActualSize();
std::vector<u8> buffer(0x1000);
const auto newSize = oldSize - size;
auto position = offset;
while (position < newSize) {
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
this->readRaw(position + size, buffer.data(), readSize);
this->writeRaw(position, buffer.data(), readSize);
position += readSize;
}
this->resizeRaw(newSize);
}
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const { void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const {
for (auto &overlay : m_overlays) { for (auto &overlay : m_overlays) {
auto overlayOffset = overlay->getAddress(); auto overlayOffset = overlay->getAddress();

View File

@@ -93,6 +93,13 @@ namespace hex::prv::undo {
} }
} }
void Stack::reapply() {
for (const auto &operation : m_undoStack) {
operation->redo(m_provider);
}
}
bool Stack::add(std::unique_ptr<Operation> &&operation) { bool Stack::add(std::unique_ptr<Operation> &&operation) {

View File

@@ -15,7 +15,7 @@ namespace hex::subcommands {
std::optional<SubCommand> findSubCommand(const std::string &arg) { std::optional<SubCommand> findSubCommand(const std::string &arg) {
for (auto &plugin : PluginManager::getPlugins()) { for (auto &plugin : PluginManager::getPlugins()) {
for (auto &subCommand : plugin.getSubCommands()) { for (auto &subCommand : plugin.getSubCommands()) {
if (hex::format("--{}", subCommand.commandKey) == arg) { if (hex::format("--{}", subCommand.commandLong) == arg || hex::format("-{}", subCommand.commandShort) == arg) {
return subCommand; return subCommand;
} }
} }
@@ -112,8 +112,8 @@ namespace hex::subcommands {
std::vector<std::string> args; std::vector<std::string> args;
for (const auto &arg_view : std::views::split(string, char(0x00))) { for (const auto &argument : std::views::split(string, char(0x00))) {
std::string arg(arg_view.data(), arg_view.size()); std::string arg(argument.data(), argument.size());
args.push_back(arg); args.push_back(arg);
} }

View File

@@ -0,0 +1,23 @@
#include <hex/test/tests.hpp>
namespace hex::test {
std::map<std::string, Test> Tests::s_tests;
bool initPluginImpl(std::string name) {
if (name != "Built-in") {
if(!initPluginImpl("Built-in")) return false;
}
hex::Plugin *plugin = hex::PluginManager::getPlugin(name);
if (plugin == nullptr) {
hex::log::fatal("Plugin '{}' was not found !", name);
return false;
}else if (!plugin->initializePlugin()) {
hex::log::fatal("Failed to initialize plugin '{}' !", name);
return false;
}
hex::log::info("Initialized plugin '{}' successfully", name);
return true;
}
}

View File

@@ -4,6 +4,7 @@
#include <imgui_internal.h> #include <imgui_internal.h>
#include <implot.h> #include <implot.h>
#include <implot_internal.h> #include <implot_internal.h>
#include <cimgui.h>
#include <opengl_support.h> #include <opengl_support.h>
#undef IMGUI_DEFINE_MATH_OPERATORS #undef IMGUI_DEFINE_MATH_OPERATORS

View File

@@ -6,9 +6,10 @@ set(CMAKE_CXX_STANDARD 17)
add_library(imgui_all_includes INTERFACE) add_library(imgui_all_includes INTERFACE)
add_subdirectory(imgui) add_subdirectory(imgui)
add_subdirectory(cimgui)
add_subdirectory(implot) add_subdirectory(implot)
add_subdirectory(imnodes) add_subdirectory(imnodes)
add_subdirectory(custom) add_subdirectory(custom)
add_subdirectory(ColorTextEditor) add_subdirectory(ColorTextEditor)
set(IMGUI_LIBRARIES imgui_imgui imgui_implot imgui_imnodes imgui_custom imgui_color_text_editor PARENT_SCOPE) set(IMGUI_LIBRARIES imgui_imgui imgui_cimgui imgui_implot imgui_imnodes imgui_custom imgui_color_text_editor PARENT_SCOPE)

View File

@@ -175,7 +175,7 @@ public:
bool mCaseSensitive; bool mCaseSensitive;
LanguageDefinition() LanguageDefinition()
: mGlobalDocComment("/*!"), mDocComment("/**"), mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true) : mPreprocChar('#'), mAutoIndentation(true), mTokenize(nullptr), mCaseSensitive(true)
{ {
} }

View File

@@ -604,7 +604,7 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
return mPalette[(int)PaletteIndex::Comment]; return mPalette[(int)PaletteIndex::Comment];
if (aGlyph.mMultiLineComment) if (aGlyph.mMultiLineComment)
return mPalette[(int)PaletteIndex::MultiLineComment]; return mPalette[(int)PaletteIndex::MultiLineComment];
if (aGlyph.mDeactivated && !aGlyph.mPreprocessor) if (aGlyph.mDeactivated)
return mPalette[(int)PaletteIndex::PreprocessorDeactivated]; return mPalette[(int)PaletteIndex::PreprocessorDeactivated];
auto const color = mPalette[(int)aGlyph.mColorIndex]; auto const color = mPalette[(int)aGlyph.mColorIndex];
if (aGlyph.mPreprocessor) { if (aGlyph.mPreprocessor) {
@@ -619,10 +619,20 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
} }
void TextEditor::HandleKeyboardInputs() { void TextEditor::HandleKeyboardInputs() {
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift; auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; auto left = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow));
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; auto right = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow));
auto up = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow));
auto down = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow));
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
auto home = io.ConfigMacOSXBehaviors ? io.KeySuper && left : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto end = io.ConfigMacOSXBehaviors ? io.KeySuper && right : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto top = io.ConfigMacOSXBehaviors ? io.KeySuper && up : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home));
auto bottom = io.ConfigMacOSXBehaviors ? io.KeySuper && down : ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End));
auto pageUp = io.ConfigMacOSXBehaviors ? ctrl && up : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp));
auto pageDown = io.ConfigMacOSXBehaviors ? ctrl && down : ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown));
if (ImGui::IsWindowFocused()) { if (ImGui::IsWindowFocused()) {
if (ImGui::IsWindowHovered()) if (ImGui::IsWindowHovered())
@@ -638,25 +648,25 @@ void TextEditor::HandleKeyboardInputs() {
Undo(); Undo();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y))) else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
Redo(); Redo();
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow))) else if (!ctrl && !alt && up)
MoveUp(1, shift); MoveUp(1, shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow))) else if (!ctrl && !alt && down)
MoveDown(1, shift); MoveDown(1, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow))) else if (!alt && left)
MoveLeft(1, shift, ctrl); MoveLeft(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow))) else if (!alt && right)
MoveRight(1, shift, ctrl); MoveRight(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp))) else if (!alt && pageUp)
MoveUp(GetPageSize() - 4, shift); MoveUp(GetPageSize() - 4, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown))) else if (!alt && pageDown)
MoveDown(GetPageSize() - 4, shift); MoveDown(GetPageSize() - 4, shift);
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) else if (!alt && top)
MoveTop(shift); MoveTop(shift);
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) else if (!alt && bottom)
MoveBottom(shift); MoveBottom(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home))) else if (!ctrl && !alt && home)
MoveHome(shift); MoveHome(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End))) else if (!ctrl && !alt && end)
MoveEnd(shift); MoveEnd(shift);
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete))) else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Delete(); Delete();
@@ -722,7 +732,7 @@ void TextEditor::HandleKeyboardInputs() {
void TextEditor::HandleMouseInputs() { void TextEditor::HandleMouseInputs() {
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift; auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl; auto ctrl = io.ConfigMacOSXBehaviors ? io.KeyAlt : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt; auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
if (ImGui::IsWindowHovered()) { if (ImGui::IsWindowHovered()) {
@@ -905,7 +915,10 @@ void TextEditor::Render() {
} }
if (mState.mCursorPosition.mLine == lineNo && mShowCursor) { if (mState.mCursorPosition.mLine == lineNo && mShowCursor) {
auto focused = ImGui::IsWindowFocused(); bool focused = false;
ImGuiViewport *viewport = ImGui::GetWindowViewport();
if (viewport->PlatformUserData != NULL && ImGui::GetPlatformIO().Platform_GetWindowFocus(viewport))
focused = ImGui::IsWindowFocused();
// Highlight the current line (where the cursor is) // Highlight the current line (where the cursor is)
if (!HasSelection()) { if (!HasSelection()) {
@@ -2505,6 +2518,7 @@ void TextEditor::ColorizeInternal() {
auto firstChar = true; // there is no other non-whitespace characters in the line before auto firstChar = true; // there is no other non-whitespace characters in the line before
auto currentLine = 0; auto currentLine = 0;
auto currentIndex = 0; auto currentIndex = 0;
auto commentLength = 0;
auto &startStr = mLanguageDefinition.mCommentStart; auto &startStr = mLanguageDefinition.mCommentStart;
auto &singleStartStr = mLanguageDefinition.mSingleLineComment; auto &singleStartStr = mLanguageDefinition.mSingleLineComment;
auto &docStartStr = mLanguageDefinition.mDocComment; auto &docStartStr = mLanguageDefinition.mDocComment;
@@ -2516,6 +2530,14 @@ void TextEditor::ColorizeInternal() {
while (currentLine < endLine || currentIndex < endIndex) { while (currentLine < endLine || currentIndex < endIndex) {
auto &line = mLines[currentLine]; auto &line = mLines[currentLine];
auto setGlyphFlags = [&](int index) {
line[index].mMultiLineComment = withinComment;
line[index].mComment = withinSingleLineComment;
line[index].mDocComment = withinDocComment;
line[index].mGlobalDocComment = withinGlobalDocComment;
line[index].mDeactivated = withinNotDef;
};
if (currentIndex == 0) { if (currentIndex == 0) {
withinSingleLineComment = false; withinSingleLineComment = false;
withinPreproc = false; withinPreproc = false;
@@ -2532,24 +2554,12 @@ void TextEditor::ColorizeInternal() {
bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
if (withinString) { if (withinString) {
line[currentIndex].mMultiLineComment = withinComment; setGlyphFlags(currentIndex);
line[currentIndex].mComment = withinSingleLineComment; if (c == '\\') {
line[currentIndex].mDocComment = withinDocComment; currentIndex++;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment; setGlyphFlags(currentIndex);
line[currentIndex].mDeactivated = withinNotDef; } else if (c == '\"')
if (c == '\"') { withinString = false;
if (currentIndex > 2 && line[currentIndex - 1].mChar == '\\' && line[currentIndex - 2].mChar != '\\') {
currentIndex += 1;
if (currentIndex < (int)line.size()) {
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
}
} else
withinString = false;
}
} else { } else {
if (firstChar && c == mLanguageDefinition.mPreprocChar) { if (firstChar && c == mLanguageDefinition.mPreprocChar) {
withinPreproc = true; withinPreproc = true;
@@ -2594,7 +2604,6 @@ void TextEditor::ColorizeInternal() {
} }
if (!withinNotDef) { if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end(); bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet); ifDefs.push_back(isConditionMet);
} else } else
ifDefs.push_back(false); ifDefs.push_back(false);
@@ -2608,7 +2617,6 @@ void TextEditor::ColorizeInternal() {
} }
if (!withinNotDef) { if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end(); bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet); ifDefs.push_back(isConditionMet);
} else } else
ifDefs.push_back(false); ifDefs.push_back(false);
@@ -2616,20 +2624,17 @@ void TextEditor::ColorizeInternal() {
} }
} else { } else {
if (directive == "endif") { if (directive == "endif") {
if (ifDefs.size() > 1) if (ifDefs.size() > 1) {
ifDefs.pop_back(); ifDefs.pop_back();
withinNotDef = !ifDefs.back(); withinNotDef = !ifDefs.back();
}
} }
} }
} }
if (c == '\"') { if (c == '\"') {
withinString = true; withinString = true;
line[currentIndex].mMultiLineComment = withinComment; setGlyphFlags(currentIndex);
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
} else { } else {
auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; }; auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; };
@@ -2653,29 +2658,30 @@ void TextEditor::ColorizeInternal() {
if (isGlobalDocComment || isDocComment || isComment) { if (isGlobalDocComment || isDocComment || isComment) {
commentStartLine = currentLine; commentStartLine = currentLine;
commentStartIndex = currentIndex; commentStartIndex = currentIndex;
if (isGlobalDocComment) if (isGlobalDocComment) {
withinGlobalDocComment = true; withinGlobalDocComment = true;
else if (isDocComment) commentLength = 3;
} else if (isDocComment) {
withinDocComment = true; withinDocComment = true;
else commentLength = 3;
} else {
withinComment = true; withinComment = true;
commentLength = 2;
}
} }
} }
inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex)); inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
} }
line[currentIndex].mGlobalDocComment = withinGlobalDocComment; setGlyphFlags(currentIndex);
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDeactivated = withinNotDef;
auto &endStr = mLanguageDefinition.mCommentEnd; auto &endStr = mLanguageDefinition.mCommentEnd;
if (compareBack(endStr, line)) { if (compareBack(endStr, line) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) {
withinComment = false; withinComment = false;
withinDocComment = false; withinDocComment = false;
withinGlobalDocComment = false; withinGlobalDocComment = false;
commentStartLine = endLine; commentStartLine = endLine;
commentStartIndex = endIndex; commentStartIndex = endIndex;
commentLength = 0;
} }
} }
} }
@@ -2684,6 +2690,7 @@ void TextEditor::ColorizeInternal() {
currentIndex += UTF8CharLength(c); currentIndex += UTF8CharLength(c);
if (currentIndex >= (int)line.size()) { if (currentIndex >= (int)line.size()) {
withinNotDef = !ifDefs.back();
currentIndex = 0; currentIndex = 0;
++currentLine; ++currentLine;
} }

View File

@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.16)
project(imgui_cimgui)
set(CMAKE_CXX_STANDARD 17)
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
add_library(imgui_cimgui OBJECT
source/cimgui.cpp
)
target_include_directories(imgui_cimgui PUBLIC
include
)
target_link_libraries(imgui_cimgui PRIVATE imgui_includes)
add_dependencies(imhex_all imgui_cimgui)
endif()

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -65,7 +65,7 @@ namespace hex::crash {
static void printStackTrace() { static void printStackTrace() {
for (const auto &stackFrame : stacktrace::getStackTrace()) { for (const auto &stackFrame : stacktrace::getStackTrace()) {
if (stackFrame.line == 0) if (stackFrame.line == 0)
log::fatal(" {}", stackFrame.function); log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
else else
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function); log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
} }

View File

@@ -59,6 +59,7 @@ namespace hex::init {
// Load all plugins but don't initialize them // Load all plugins but don't initialize them
PluginManager::loadLibraries();
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) { for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
PluginManager::load(dir); PluginManager::load(dir);
} }

View File

@@ -94,6 +94,7 @@ namespace hex::init {
PluginManager::addLoadPath(dir); PluginManager::addLoadPath(dir);
} }
PluginManager::loadLibraries();
PluginManager::load(); PluginManager::load();
#endif #endif

View File

@@ -2,12 +2,26 @@
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <array> #include <array>
#include <llvm/Demangle/Demangle.h>
namespace {
[[maybe_unused]] std::string tryDemangle(const std::string &symbolName) {
if (auto variant1 = llvm::demangle(symbolName); variant1 != symbolName)
return variant1;
if (auto variant2 = llvm::demangle(std::string("_") + symbolName); variant2 != std::string("_") + symbolName)
return variant2;
return symbolName;
}
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include <llvm/Demangle/Demangle.h>
namespace hex::stacktrace { namespace hex::stacktrace {
@@ -80,14 +94,7 @@
fileName = "??"; fileName = "??";
} }
std::string demangledName; auto demangledName = tryDemangle(symbolName);
if (auto variant1 = llvm::demangle(symbolName); variant1 != symbolName)
demangledName = variant1;
else if (auto variant2 = llvm::demangle(std::string("_") + symbolName); variant2 != std::string("_") + symbolName)
demangledName = variant2;
else
demangledName = symbolName;
stackTrace.push_back(StackFrame { fileName, demangledName, lineNumber }); stackTrace.push_back(StackFrame { fileName, demangledName, lineNumber });
} }
@@ -103,9 +110,8 @@
#if __has_include(BACKTRACE_HEADER) #if __has_include(BACKTRACE_HEADER)
#include BACKTRACE_HEADER #include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <dlfcn.h>
namespace hex::stacktrace { namespace hex::stacktrace {
@@ -116,17 +122,24 @@
std::vector<StackFrame> getStackTrace() { std::vector<StackFrame> getStackTrace() {
static std::vector<StackFrame> result; static std::vector<StackFrame> result;
std::array<void*, 128> addresses; std::array<void*, 128> addresses = {};
size_t count = backtrace(addresses.data(), addresses.size()); const size_t count = backtrace(addresses.data(), addresses.size());
auto functions = backtrace_symbols(addresses.data(), count);
for (size_t i = 0; i < count; i++) Dl_info info;
result.push_back(StackFrame { "", functions[i], 0 }); for (size_t i = 0; i < count; i += 1) {
dladdr(addresses[i], &info);
auto fileName = info.dli_fname != nullptr ? std::fs::path(info.dli_fname).filename().string() : "??";
auto demangledName = info.dli_sname != nullptr ? tryDemangle(info.dli_sname) : "??";
result.push_back(StackFrame { std::move(fileName), std::move(demangledName), 0 });
}
return result; return result;
} }
} }
#endif #endif
#elif defined(HEX_HAS_BACKTRACE) #elif defined(HEX_HAS_BACKTRACE)
@@ -134,7 +147,6 @@
#if __has_include(BACKTRACE_HEADER) #if __has_include(BACKTRACE_HEADER)
#include BACKTRACE_HEADER #include BACKTRACE_HEADER
#include <llvm/Demangle/Demangle.h>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
@@ -161,7 +173,7 @@
if (function == nullptr) if (function == nullptr)
function = "??"; function = "??";
result.push_back(StackFrame { std::fs::path(fileName).filename().string(), llvm::demangle(function), u32(lineNumber) }); result.push_back(StackFrame { std::fs::path(fileName).filename().string(), tryDemangle(function), u32(lineNumber) });
return 0; return 0;
}, nullptr, nullptr); }, nullptr, nullptr);

View File

@@ -186,12 +186,11 @@ namespace hex {
// Determine if the application should be in long sleep mode // Determine if the application should be in long sleep mode
bool shouldLongSleep = !m_unlockFrameRate; bool shouldLongSleep = !m_unlockFrameRate;
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter static double lockTimeout = 0;
static i32 lockTimeout = 0;
if (!shouldLongSleep) { if (!shouldLongSleep) {
lockTimeout = m_lastFrameTime * 10'000; lockTimeout = 0.05;
} else if (lockTimeout > 0) { } else if (lockTimeout > 0) {
lockTimeout -= 1; lockTimeout -= m_lastFrameTime;
} }
if (shouldLongSleep && lockTimeout > 0) if (shouldLongSleep && lockTimeout > 0)
@@ -236,14 +235,28 @@ namespace hex {
// Limit frame rate // Limit frame rate
// If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate // If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate
const auto targetFPS = ImHexApi::System::getTargetFPS(); auto targetFPS = ImHexApi::System::getTargetFPS();
if (targetFPS < 15) { if (targetFPS >= 200) {
glfwSwapInterval(1); // Let it rip
} else if (targetFPS > 200) {
glfwSwapInterval(0);
} else { } else {
// If the target frame rate is below 15, use the current monitor's refresh rate
if (targetFPS < 15) {
// Fall back to 60 FPS if the monitor refresh rate cannot be determined
targetFPS = 60;
if (auto monitor = glfwGetWindowMonitor(m_window); monitor != nullptr) {
if (auto videoMode = glfwGetVideoMode(monitor); videoMode != nullptr) {
targetFPS = videoMode->refreshRate;
}
}
}
// Sleep if we're not in long sleep mode
if (!shouldLongSleep) { if (!shouldLongSleep) {
glfwSwapInterval(0); // If anything goes wrong with these checks, make sure that we're sleeping for at least 1ms
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// Sleep for the remaining time if the frame rate is above the target frame rate
const auto frameTime = glfwGetTime() - m_lastStartFrameTime; const auto frameTime = glfwGetTime() - m_lastStartFrameTime;
const auto targetFrameTime = 1.0 / targetFPS; const auto targetFrameTime = 1.0 / targetFPS;
if (frameTime < targetFrameTime) { if (frameTime < targetFrameTime) {
@@ -717,7 +730,9 @@ namespace hex {
glfwSetWindowOpacity(m_window, 1.0F); glfwSetWindowOpacity(m_window, 1.0F);
glfwMakeContextCurrent(m_window); glfwMakeContextCurrent(m_window);
glfwSwapInterval(1);
// Disable VSync. Not like any graphics driver actually cares
glfwSwapInterval(0);
// Center window // Center window
GLFWmonitor *monitor = glfwGetPrimaryMonitor(); GLFWmonitor *monitor = glfwGetPrimaryMonitor();
@@ -969,6 +984,8 @@ namespace hex {
void Window::exitImGui() { void Window::exitImGui() {
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown(); ImGui_ImplGlfw_Shutdown();
ImNodes::DestroyContext();
ImPlot::DestroyContext(); ImPlot::DestroyContext();
ImGui::DestroyContext(); ImGui::DestroyContext();
} }

View File

@@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.16)
include(ImHexPlugin) include(ImHexPlugin)
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
if (DEFAULT_MAGIC_FILE_PATH)
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
endif ()
add_imhex_plugin( add_imhex_plugin(
NAME NAME
builtin builtin
@@ -114,8 +109,6 @@ add_imhex_plugin(
source/content/views/view_achievements.cpp source/content/views/view_achievements.cpp
source/content/views/view_highlight_rules.cpp source/content/views/view_highlight_rules.cpp
source/content/views/view_tutorials.cpp source/content/views/view_tutorials.cpp
source/content/helpers/notification.cpp
INCLUDES INCLUDES
include include
@@ -128,6 +121,11 @@ add_imhex_plugin(
LLVMDemangle LLVMDemangle
) )
find_file(DEFAULT_MAGIC_FILE_PATH magic.mgc HINTS ${LIBMAGIC_INCLUDE_DIR}/../share/misc)
if (DEFAULT_MAGIC_FILE_PATH)
add_romfs_resource(${DEFAULT_MAGIC_FILE_PATH} always_auto_extract/magic/magic.mgc)
endif ()
if (WIN32) if (WIN32)
target_link_libraries(builtin PRIVATE setupapi) target_link_libraries(builtin PRIVATE setupapi)
endif () endif ()

View File

@@ -9,6 +9,7 @@ namespace hex::plugin::builtin {
void handleHelpCommand(const std::vector<std::string> &args); void handleHelpCommand(const std::vector<std::string> &args);
void handlePluginsCommand(const std::vector<std::string> &args); void handlePluginsCommand(const std::vector<std::string> &args);
void handleLanguageCommand(const std::vector<std::string> &args); void handleLanguageCommand(const std::vector<std::string> &args);
void handleVerboseCommand(const std::vector<std::string> &args);
void handleOpenCommand(const std::vector<std::string> &args); void handleOpenCommand(const std::vector<std::string> &args);

View File

@@ -16,6 +16,7 @@
#include <imgui_internal.h> #include <imgui_internal.h>
#include <atomic> #include <atomic>
#include <implot_internal.h>
#include <random> #include <random>
#include <hex/ui/imgui_imhex_extensions.h> #include <hex/ui/imgui_imhex_extensions.h>
@@ -747,6 +748,20 @@ namespace hex {
}; };
class DiagramByteTypesDistribution { class DiagramByteTypesDistribution {
private:
struct AnnotationRegion {
UnlocalizedString unlocalizedName;
Region region;
ImColor color;
};
struct Tag {
UnlocalizedString unlocalizedName;
ImU64 value;
ImAxis axis;
ImGuiCol color;
};
public: public:
explicit DiagramByteTypesDistribution(u64 blockSize = 256, size_t sampleSize = 0x1000) : m_blockSize(blockSize), m_sampleSize(sampleSize){ } explicit DiagramByteTypesDistribution(u64 blockSize = 256, size_t sampleSize = 0x1000) : m_blockSize(blockSize), m_sampleSize(sampleSize){ }
@@ -771,10 +786,44 @@ namespace hex {
"isupper", "islower", "isdigit", "isxdigit" "isupper", "islower", "isdigit", "isxdigit"
}; };
for (u32 i = 0; i < Names.size(); i++) { for (u32 i = 0; i < Names.size(); i++) {
ImPlot::PlotLine(Names[i], m_xBlockTypeDistributions.data(), m_yBlockTypeDistributionsSampled[i].data(), m_xBlockTypeDistributions.size()); ImPlot::PlotLine(Names[i], m_xBlockTypeDistributions.data(), m_yBlockTypeDistributionsSampled[i].data(), m_xBlockTypeDistributions.size());
} }
if (m_showAnnotations) {
u32 id = 1;
for (const auto &annotation : m_annotationRegions) {
const auto &region = annotation.region;
double xMin = region.getStartAddress();
double xMax = region.getEndAddress();
double yMin = 0.0F;
double yMax = 100.0F;
ImPlot::DragRect(id, &xMin, &yMin, &xMax, &yMax, annotation.color, ImPlotDragToolFlags_NoFit | ImPlotDragToolFlags_NoInputs);
const auto min = ImPlot::PlotToPixels(xMin, yMax);
const auto max = ImPlot::PlotToPixels(xMax, yMin);
const auto mousePos = ImPlot::PixelsToPlot(ImGui::GetMousePos());
if (ImGui::IsMouseHoveringRect(min, max)) {
ImPlot::Annotation(xMin + (xMax - xMin) / 2, mousePos.y, annotation.color, ImVec2(), false, "%s", Lang(annotation.unlocalizedName).get().c_str());
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
ImHexApi::HexEditor::setSelection(annotation.region);
}
}
id += 1;
}
for (const auto &tag : m_tags) {
if (tag.axis == ImAxis_X1)
ImPlot::TagX(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get().c_str());
else if (tag.axis == ImAxis_Y1)
ImPlot::TagY(tag.value, ImGui::GetStyleColorVec4(tag.color), "%s", Lang(tag.unlocalizedName).get().c_str());
}
}
// The parameter updateHandle is used when using the pattern language since we don't have a provider // The parameter updateHandle is used when using the pattern language since we don't have a provider
// but just a set of bytes, we won't be able to use the drag bar correctly. // but just a set of bytes, we won't be able to use the drag bar correctly.
if (updateHandle) { if (updateHandle) {
@@ -871,6 +920,12 @@ namespace hex {
for (size_t i = 0; i < typeDist.size(); i++) for (size_t i = 0; i < typeDist.size(); i++)
m_yBlockTypeDistributions[i].push_back(typeDist[i] * 100); m_yBlockTypeDistributions[i].push_back(typeDist[i] * 100);
if (m_yBlockTypeDistributions[2].back() + m_yBlockTypeDistributions[4].back() >= 95) {
this->addRegion("hex.ui.diagram.byte_type_distribution.plain_text", Region { m_byteCount, m_blockSize }, 0x80FF00FF);
} else if (std::ranges::any_of(m_blockValueCounts, [&](auto count) { return count >= m_blockSize * 0.95F; })) {
this->addRegion("hex.ui.diagram.byte_type_distribution.similar_bytes", Region { m_byteCount, m_blockSize }, 0x8000FF00);
}
m_blockCount += 1; m_blockCount += 1;
m_blockValueCounts = { 0 }; m_blockValueCounts = { 0 };
} }
@@ -898,6 +953,10 @@ namespace hex {
m_handlePosition = filePosition; m_handlePosition = filePosition;
} }
void enableAnnotations(bool enabled) {
m_showAnnotations = enabled;
}
private: private:
static std::array<float, 12> calculateTypeDistribution(const std::array<ImU64, 256> &valueCounts, size_t blockSize) { static std::array<float, 12> calculateTypeDistribution(const std::array<ImU64, 256> &valueCounts, size_t blockSize) {
std::array<ImU64, 12> counts = {}; std::array<ImU64, 12> counts = {};
@@ -988,6 +1047,23 @@ namespace hex {
m_xBlockTypeDistributions.push_back(m_endAddress); m_xBlockTypeDistributions.push_back(m_endAddress);
} }
void addRegion(const UnlocalizedString &name, Region region, ImColor color) {
const auto existingRegion = std::ranges::find_if(m_annotationRegions, [this, &region](const AnnotationRegion &annotation) {
auto difference = i64(region.getEndAddress()) - i64(annotation.region.getEndAddress());
return difference > 0 && difference < i64(m_blockSize * 32);
});
if (existingRegion != m_annotationRegions.end()) {
existingRegion->region.size += region.size;
} else {
m_annotationRegions.push_back({ name, region, color });
}
}
void addTag(const UnlocalizedString &name, u64 value, ImAxis axis, ImGuiCol color) {
m_tags.push_back({ name, value, axis, color });
}
private: private:
// Variables used to store the parameters to process // Variables used to store the parameters to process
@@ -1024,5 +1100,10 @@ namespace hex {
// Hold the result of the byte distribution analysis // Hold the result of the byte distribution analysis
std::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled; std::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled;
std::atomic<bool> m_processing = false; std::atomic<bool> m_processing = false;
std::vector<AnnotationRegion> m_annotationRegions;
std::vector<Tag> m_tags;
bool m_showAnnotations = true;
}; };
} }

View File

@@ -1,8 +0,0 @@
#pragma once
namespace hex::plugin::builtin {
void showError(const std::string& message);
void showWarning(const std::string& message);
}

View File

@@ -11,6 +11,8 @@ namespace hex::plugin::builtin {
class FileProvider : public hex::prv::Provider { class FileProvider : public hex::prv::Provider {
public: public:
constexpr static u64 MaxMemoryFileSize = 128 * 1024 * 1024;
FileProvider() = default; FileProvider() = default;
~FileProvider() override = default; ~FileProvider() override = default;
@@ -21,8 +23,6 @@ namespace hex::plugin::builtin {
[[nodiscard]] bool isSavable() const override; [[nodiscard]] bool isSavable() const override;
void resizeRaw(u64 newSize) override; void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
void readRaw(u64 offset, void *buffer, size_t size) override; void readRaw(u64 offset, void *buffer, size_t size) override;
void writeRaw(u64 offset, const void *buffer, size_t size) override; void writeRaw(u64 offset, const void *buffer, size_t size) override;
@@ -57,12 +57,18 @@ namespace hex::plugin::builtin {
private: private:
void convertToMemoryFile(); void convertToMemoryFile();
void handleFileChange();
protected: protected:
std::fs::path m_path; std::fs::path m_path;
wolv::io::File m_file; wolv::io::File m_file;
size_t m_fileSize = 0; size_t m_fileSize = 0;
wolv::io::ChangeTracker m_changeTracker;
std::vector<u8> m_data;
bool m_loadedIntoMemory = false;
bool m_ignoreNextChangeEvent = false;
std::optional<struct stat> m_fileStats; std::optional<struct stat> m_fileStats;
bool m_readable = false, m_writable = false; bool m_readable = false, m_writable = false;

View File

@@ -24,8 +24,6 @@ namespace hex::plugin::builtin {
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); } [[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
void resizeRaw(u64 newSize) override; void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
void save() override; void save() override;

View File

@@ -101,6 +101,7 @@ namespace hex::plugin::builtin {
using OccurrenceTree = wolv::container::IntervalTree<Occurrence>; using OccurrenceTree = wolv::container::IntervalTree<Occurrence>;
PerProvider<std::vector<Occurrence>> m_foundOccurrences, m_sortedOccurrences; PerProvider<std::vector<Occurrence>> m_foundOccurrences, m_sortedOccurrences;
PerProvider<Occurrence*> m_lastSelectedOccurrence;
PerProvider<OccurrenceTree> m_occurrenceTree; PerProvider<OccurrenceTree> m_occurrenceTree;
PerProvider<std::string> m_currFilter; PerProvider<std::string> m_currFilter;

View File

@@ -14,6 +14,8 @@
#include <functional> #include <functional>
#include <TextEditor.h> #include <TextEditor.h>
#include "popups/popup_file_chooser.hpp"
#include "hex/api/achievement_manager.hpp"
namespace pl::ptrn { class Pattern; } namespace pl::ptrn { class Pattern; }
@@ -170,7 +172,7 @@ namespace hex::plugin::builtin {
u32 color; u32 color;
}; };
std::unique_ptr<pl::PatternLanguage> m_editorRuntime; std::unique_ptr<pl::PatternLanguage> m_editorRuntime;
PerProvider<std::vector<std::fs::path>> m_possiblePatternFiles; PerProvider<std::vector<std::fs::path>> m_possiblePatternFiles;
bool m_runAutomatically = false; bool m_runAutomatically = false;
@@ -255,6 +257,39 @@ namespace hex::plugin::builtin {
void registerMenuItems(); void registerMenuItems();
void registerHandlers(); void registerHandlers();
std::function<void()> importPatternFile = [&] {
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat")
paths.push_back(entry.path());
}
}
ui::PopupFileChooser::open(
basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
}
);
};
std::function<void()> exportPatternFile = [&] {
fs::openFileBrowser(
fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
}
);
};
void appendEditorText(const std::string &text); void appendEditorText(const std::string &text);
void appendVariable(const std::string &type); void appendVariable(const std::string &type);
void appendArray(const std::string &type, size_t size); void appendArray(const std::string &type, size_t size);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -135,6 +135,7 @@
"hex.builtin.menu.file.export.ips32": "IPS32 Patch", "hex.builtin.menu.file.export.ips32": "IPS32 Patch",
"hex.builtin.menu.file.export.bookmark": "Bookmark", "hex.builtin.menu.file.export.bookmark": "Bookmark",
"hex.builtin.menu.file.export.pattern": "Pattern File", "hex.builtin.menu.file.export.pattern": "Pattern File",
"hex.builtin.menu.file.export.pattern_file": "Export pattern File",
"hex.builtin.menu.file.export.data_processor": "Data Processor Workspace", "hex.builtin.menu.file.export.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!", "hex.builtin.menu.file.export.popup.create": "Cannot export data. Failed to create file!",
"hex.builtin.menu.file.export.report": "Report", "hex.builtin.menu.file.export.report": "Report",
@@ -147,6 +148,7 @@
"hex.builtin.menu.file.import.modified_file": "Modified File", "hex.builtin.menu.file.import.modified_file": "Modified File",
"hex.builtin.menu.file.import.bookmark": "Bookmark", "hex.builtin.menu.file.import.bookmark": "Bookmark",
"hex.builtin.menu.file.import.pattern": "Pattern File", "hex.builtin.menu.file.import.pattern": "Pattern File",
"hex.builtin.menu.file.import.pattern_file": "Import pattern File",
"hex.builtin.menu.file.import.data_processor": "Data Processor Workspace", "hex.builtin.menu.file.import.data_processor": "Data Processor Workspace",
"hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File", "hex.builtin.menu.file.import.custom_encoding": "Custom Encoding File",
"hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match", "hex.builtin.menu.file.import.modified_file.popup.invalid_size": "File selected do not have the same size as the current file. Both sizes must match",
@@ -402,6 +404,8 @@
"hex.builtin.provider.file.size": "Size", "hex.builtin.provider.file.size": "Size",
"hex.builtin.provider.file.menu.open_file": "Open file externally", "hex.builtin.provider.file.menu.open_file": "Open file externally",
"hex.builtin.provider.file.menu.open_folder": "Open containing folder", "hex.builtin.provider.file.menu.open_folder": "Open containing folder",
"hex.builtin.provider.file.too_large": "This file is too large to be loaded into memory. Opening it anyways will result in modifications to be written directly to the file. Would you like to open it as Read-Only instead?",
"hex.builtin.provider.file.reload_changes": "The file has been modified by an external source. Do you want to reload it?",
"hex.builtin.provider.gdb": "GDB Server Provider", "hex.builtin.provider.gdb": "GDB Server Provider",
"hex.builtin.provider.gdb.ip": "IP Address", "hex.builtin.provider.gdb.ip": "IP Address",
"hex.builtin.provider.gdb.name": "GDB Server <{0}:{1}>", "hex.builtin.provider.gdb.name": "GDB Server <{0}:{1}>",
@@ -500,7 +504,7 @@
"hex.builtin.setting.toolbar.icons": "Toolbar Icons", "hex.builtin.setting.toolbar.icons": "Toolbar Icons",
"hex.builtin.shortcut.next_provider": "Select next provider", "hex.builtin.shortcut.next_provider": "Select next provider",
"hex.builtin.shortcut.prev_provider": "Select previous provider", "hex.builtin.shortcut.prev_provider": "Select previous provider",
"hex.builtin.title_bar_button.debug_build": "Debug build\nShift+Click to crash using exception\nCtrl+Click to crash using signal", "hex.builtin.title_bar_button.debug_build": "Debug build\n\nSHIFT + Click to open Debug Menu",
"hex.builtin.title_bar_button.feedback": "Leave Feedback", "hex.builtin.title_bar_button.feedback": "Leave Feedback",
"hex.builtin.tools.ascii_table": "ASCII table", "hex.builtin.tools.ascii_table": "ASCII table",
"hex.builtin.tools.ascii_table.octal": "Show octal", "hex.builtin.tools.ascii_table.octal": "Show octal",
@@ -844,6 +848,7 @@
"hex.builtin.information_section.info_analysis.highest_entropy": "Highest block entropy", "hex.builtin.information_section.info_analysis.highest_entropy": "Highest block entropy",
"hex.builtin.information_section.info_analysis.lowest_entropy": "Lowest block entropy", "hex.builtin.information_section.info_analysis.lowest_entropy": "Lowest block entropy",
"hex.builtin.information_section.info_analysis": "Information analysis", "hex.builtin.information_section.info_analysis": "Information analysis",
"hex.builtin.information_section.info_analysis.show_annotations": "Show annotations",
"hex.builtin.information_section.relationship_analysis": "Byte Relationship", "hex.builtin.information_section.relationship_analysis": "Byte Relationship",
"hex.builtin.information_section.relationship_analysis.sample_size": "Sample size", "hex.builtin.information_section.relationship_analysis.sample_size": "Sample size",
"hex.builtin.information_section.relationship_analysis.brightness": "Brightness", "hex.builtin.information_section.relationship_analysis.brightness": "Brightness",

View File

@@ -9,11 +9,11 @@
#include <hex/helpers/magic.hpp> #include <hex/helpers/magic.hpp>
#include <hex/helpers/crypto.hpp> #include <hex/helpers/crypto.hpp>
#include <hex/helpers/literals.hpp> #include <hex/helpers/literals.hpp>
#include <hex/helpers/utils.hpp>
#include <romfs/romfs.hpp> #include <romfs/romfs.hpp>
#include <hex/api/plugin_manager.hpp> #include <hex/api/plugin_manager.hpp>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/subcommands/subcommands.hpp> #include <hex/subcommands/subcommands.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
@@ -47,16 +47,30 @@ namespace hex::plugin::builtin {
"Available subcommands:\n" "Available subcommands:\n"
); );
size_t longestCommand = 0; size_t longestLongCommand = 0, longestShortCommand = 0;
for (const auto &plugin : PluginManager::getPlugins()) { for (const auto &plugin : PluginManager::getPlugins()) {
for (const auto &subCommand : plugin.getSubCommands()) { for (const auto &subCommand : plugin.getSubCommands()) {
longestCommand = std::max(longestCommand, subCommand.commandKey.size()); longestLongCommand = std::max(longestLongCommand, subCommand.commandLong.size());
longestShortCommand = std::max(longestShortCommand, subCommand.commandShort.size());
} }
} }
for (const auto &plugin : PluginManager::getPlugins()) { for (const auto &plugin : PluginManager::getPlugins()) {
for (const auto &subCommand : plugin.getSubCommands()) { for (const auto &subCommand : plugin.getSubCommands()) {
hex::log::println(" --{}{: <{}} {}", subCommand.commandKey, "", longestCommand - subCommand.commandKey.size(), subCommand.commandDesc); hex::log::println(" "
"{}"
"{: <{}}"
"{}"
"{}"
"{: <{}}"
"{}",
subCommand.commandShort.empty() ? " " : "-",
subCommand.commandShort, longestShortCommand,
subCommand.commandShort.empty() ? " " : ", ",
subCommand.commandLong.empty() ? " " : "--",
subCommand.commandLong, longestLongCommand + 5,
subCommand.commandDescription
);
} }
} }
@@ -139,6 +153,10 @@ namespace hex::plugin::builtin {
ImHexApi::System::impl::addInitArgument("language", args[0]); ImHexApi::System::impl::addInitArgument("language", args[0]);
} }
void handleVerboseCommand(const std::vector<std::string> &) {
hex::log::enableDebugLogging();
}
void handleHashCommand(const std::vector<std::string> &args) { void handleHashCommand(const std::vector<std::string> &args) {
if (args.size() != 2) { if (args.size() != 2) {
hex::log::println("usage: imhex --hash <algorithm> <file>"); hex::log::println("usage: imhex --hash <algorithm> <file>");

View File

@@ -164,6 +164,8 @@ namespace hex::plugin::builtin {
m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(), m_chunkBasedEntropy.reset(m_inputChunkSize, region.getStartAddress(), region.getEndAddress(),
provider->getBaseAddress(), provider->getActualSize()); provider->getBaseAddress(), provider->getActualSize());
m_byteTypesDistribution.enableAnnotations(m_showAnnotations);
// Create a handle to the file // Create a handle to the file
auto reader = prv::ProviderReader(provider); auto reader = prv::ProviderReader(provider);
reader.seek(region.getStartAddress()); reader.seek(region.getStartAddress());
@@ -194,6 +196,7 @@ namespace hex::plugin::builtin {
void drawSettings() override { void drawSettings() override {
ImGuiExt::InputHexadecimal("hex.builtin.information_section.info_analysis.block_size"_lang, &m_inputChunkSize); ImGuiExt::InputHexadecimal("hex.builtin.information_section.info_analysis.block_size"_lang, &m_inputChunkSize);
ImGui::Checkbox("hex.builtin.information_section.info_analysis.show_annotations"_lang, &m_showAnnotations);
} }
void drawContent() override { void drawContent() override {
@@ -319,12 +322,14 @@ namespace hex::plugin::builtin {
void load(const nlohmann::json &data) override { void load(const nlohmann::json &data) override {
InformationSection::load(data); InformationSection::load(data);
m_inputChunkSize = data.value("block_size", 0); m_inputChunkSize = data.value("block_size", 0);
m_showAnnotations = data.value("annotations", true);
} }
nlohmann::json store() override { nlohmann::json store() override {
auto result = InformationSection::store(); auto result = InformationSection::store();
result["block_size"] = m_inputChunkSize; result["block_size"] = m_inputChunkSize;
result["annotations"] = m_showAnnotations;
return result; return result;
} }
@@ -341,6 +346,8 @@ namespace hex::plugin::builtin {
u64 m_lowestBlockEntropyAddress = 0x00; u64 m_lowestBlockEntropyAddress = 0x00;
double m_plainTextCharacterPercentage = -1.0; double m_plainTextCharacterPercentage = -1.0;
bool m_showAnnotations = true;
DiagramByteDistribution m_byteDistribution; DiagramByteDistribution m_byteDistribution;
DiagramByteTypesDistribution m_byteTypesDistribution; DiagramByteTypesDistribution m_byteTypesDistribution;
DiagramChunkBasedEntropyAnalysis m_chunkBasedEntropy; DiagramChunkBasedEntropyAnalysis m_chunkBasedEntropy;

View File

@@ -1,16 +0,0 @@
#include <hex/helpers/logger.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
void showError(const std::string& message){
ui::ToastError::open(message);
log::error(message);
}
void showWarning(const std::string& message){
ui::ToastWarning::open(message);
log::warn(message);
}
}

View File

@@ -28,8 +28,6 @@ using namespace wolv::literals;
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
static bool s_demoWindowOpen = false;
namespace { namespace {
bool noRunningTasks() { bool noRunningTasks() {
@@ -187,7 +185,7 @@ namespace hex::plugin::builtin {
void exportSelectionToFile() { void exportSelectionToFile() {
fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) { fs::openFileBrowser(fs::DialogMode::Save, {}, [](const auto &path) {
TaskManager::createTask("hex.ui.common.processing", TaskManager::NoProgress, [path](auto &) { TaskManager::createTask("hex.ui.common.processing", TaskManager::NoProgress, [path](auto &task) {
wolv::io::File outputFile(path, wolv::io::File::Mode::Create); wolv::io::File outputFile(path, wolv::io::File::Mode::Create);
if (!outputFile.isValid()) { if (!outputFile.isValid()) {
TaskManager::doLater([] { TaskManager::doLater([] {
@@ -205,6 +203,7 @@ namespace hex::plugin::builtin {
provider->read(address, bytes.data(), bytes.size()); provider->read(address, bytes.data(), bytes.size());
outputFile.writeVector(bytes); outputFile.writeVector(bytes);
task.update();
} }
}); });
}); });
@@ -559,13 +558,6 @@ namespace hex::plugin::builtin {
state = !state; state = !state;
} }
} }
#if defined(DEBUG)
ImGui::Separator();
ImGui::MenuItem("hex.builtin.menu.view.demo"_lang, "", &s_demoWindowOpen);
ImGui::MenuItem("hex.builtin.menu.view.debug"_lang, "", &hex::dbg::impl::getDebugWindowState());
#endif
}); });
} }
@@ -573,8 +565,6 @@ namespace hex::plugin::builtin {
static void createLayoutMenu() { static void createLayoutMenu() {
LayoutManager::reload(); LayoutManager::reload();
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.workspace", 4000);
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout" }, ICON_VS_LAYOUT, 1050, []{}, ImHexApi::Provider::isValid); ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout" }, ICON_VS_LAYOUT, 1050, []{}, ImHexApi::Provider::isValid);
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout", "hex.builtin.menu.workspace.layout.save" }, 1100, Shortcut::None, [] { ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.workspace", "hex.builtin.menu.workspace.layout", "hex.builtin.menu.workspace.layout.save" }, 1100, Shortcut::None, [] {
@@ -600,12 +590,12 @@ namespace hex::plugin::builtin {
} }
} }
bool shift = ImGui::GetIO().KeyShift; bool shiftPressed = ImGui::GetIO().KeyShift;
for (auto &[name, path] : LayoutManager::getLayouts()) { for (auto &[name, path] : LayoutManager::getLayouts()) {
if (ImGui::MenuItem(hex::format("{}{}", name, shift ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) { if (ImGui::MenuItem(hex::format("{}{}", name, shiftPressed ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) {
if (shift) { if (shiftPressed) {
wolv::io::fs::remove(path); LayoutManager::removeLayout(name);
LayoutManager::reload(); break;
} else { } else {
LayoutManager::load(path); LayoutManager::load(path);
} }
@@ -615,6 +605,8 @@ namespace hex::plugin::builtin {
} }
static void createWorkspaceMenu() { static void createWorkspaceMenu() {
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.workspace", 4000);
createLayoutMenu(); createLayoutMenu();
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.workspace" }, 3000); ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.workspace" }, 3000);
@@ -627,11 +619,19 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace" }, 3200, [] { ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace" }, 3200, [] {
const auto &workspaces = WorkspaceManager::getWorkspaces(); const auto &workspaces = WorkspaceManager::getWorkspaces();
bool shiftPressed = ImGui::GetIO().KeyShift;
for (auto it = workspaces.begin(); it != workspaces.end(); ++it) { for (auto it = workspaces.begin(); it != workspaces.end(); ++it) {
const auto &[name, workspace] = *it; const auto &[name, workspace] = *it;
if (ImGui::MenuItem(name.c_str(), "", it == WorkspaceManager::getCurrentWorkspace(), ImHexApi::Provider::isValid())) { bool canRemove = shiftPressed && !workspace.builtin;
WorkspaceManager::switchWorkspace(name); if (ImGui::MenuItem(hex::format("{}{}", name, canRemove ? " " ICON_VS_X : "").c_str(), "", it == WorkspaceManager::getCurrentWorkspace(), ImHexApi::Provider::isValid())) {
if (canRemove) {
WorkspaceManager::removeWorkspace(name);
break;
} else {
WorkspaceManager::switchWorkspace(name);
}
} }
} }
}); });
@@ -653,13 +653,6 @@ namespace hex::plugin::builtin {
createWorkspaceMenu(); createWorkspaceMenu();
createExtrasMenu(); createExtrasMenu();
createHelpMenu(); createHelpMenu();
(void)EventFrameEnd::subscribe([] {
if (s_demoWindowOpen) {
ImGui::ShowDemoWindow(&s_demoWindowOpen);
ImPlot::ShowDemoWindow(&s_demoWindowOpen);
}
});
} }
} }

View File

@@ -13,8 +13,7 @@
#include <hex/providers/provider.hpp> #include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <toasts/toast_notification.hpp>
#include <content/helpers/notification.hpp>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
@@ -23,26 +22,29 @@ namespace hex::plugin::builtin {
bool load(const std::fs::path &filePath) { bool load(const std::fs::path &filePath) {
if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath)) { if (!wolv::io::fs::exists(filePath) || !wolv::io::fs::isRegularFile(filePath)) {
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.file_not_found"_lang, hex::format("hex.builtin.popup.error.project.load.file_not_found"_lang,
wolv::util::toUTF8String(filePath) wolv::util::toUTF8String(filePath)
))); )));
return false; return false;
} }
Tar tar(filePath, Tar::Mode::Read); Tar tar(filePath, Tar::Mode::Read);
if (!tar.isValid()) { if (!tar.isValid()) {
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_tar"_lang, hex::format("hex.builtin.popup.error.project.load.invalid_tar"_lang,
tar.getOpenErrorString() tar.getOpenErrorString()
))); )));
return false; return false;
} }
if (!tar.contains(MetadataPath)) { if (!tar.contains(MetadataPath)) {
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang) hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
)); ));
return false; return false;
} }
@@ -50,9 +52,10 @@ namespace hex::plugin::builtin {
const auto metadataContent = tar.readVector(MetadataPath); const auto metadataContent = tar.readVector(MetadataPath);
if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic)) { if (!std::string(metadataContent.begin(), metadataContent.end()).starts_with(MetadataHeaderMagic)) {
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang,
hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang) hex::format("hex.builtin.popup.error.project.load.invalid_magic"_lang)
)); ));
return false; return false;
} }
} }

View File

@@ -11,13 +11,13 @@
#include <content/providers/process_memory_provider.hpp> #include <content/providers/process_memory_provider.hpp>
#include <content/providers/base64_provider.hpp> #include <content/providers/base64_provider.hpp>
#include <popups/popup_notification.hpp> #include <popups/popup_notification.hpp>
#include "content/helpers/notification.hpp"
#include <hex/api/project_file_manager.hpp> #include <hex/api/project_file_manager.hpp>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <toasts/toast_notification.hpp>
#include <wolv/utils/guards.hpp> #include <wolv/utils/guards.hpp>
@@ -70,9 +70,11 @@ namespace hex::plugin::builtin {
if (newProvider == nullptr) { if (newProvider == nullptr) {
// If a provider is not created, it will be overwritten when saving the project, // If a provider is not created, it will be overwritten when saving the project,
// so we should prevent the project from loading at all // so we should prevent the project from loading at all
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(
hex::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType) hex::format("hex.builtin.popup.error.project.load"_lang,
)); hex::format("hex.builtin.popup.error.project.load.create_provider"_lang, providerType)
)
);
success = false; success = false;
break; break;
} }
@@ -94,29 +96,27 @@ namespace hex::plugin::builtin {
} }
} }
std::string warningMsg; std::string warningMessage;
for (const auto &warning : providerWarnings){ for (const auto &warning : providerWarnings){
ImHexApi::Provider::remove(warning.first); ImHexApi::Provider::remove(warning.first);
warningMsg.append( warningMessage.append(
hex::format("\n - {} : {}", warning.first->getName(), warning.second)); hex::format("\n - {} : {}", warning.first->getName(), warning.second));
} }
// If no providers were opened, display an error with // If no providers were opened, display an error with
// the warnings that happened when opening them // the warnings that happened when opening them
if (ImHexApi::Provider::getProviders().size() == 0) { if (ImHexApi::Provider::getProviders().empty()) {
showError(hex::format("hex.builtin.popup.error.project.load"_lang, ui::ToastError::open(hex::format("{}{}", "hex.builtin.popup.error.project.load"_lang, "hex.builtin.popup.error.project.load.no_providers"_lang, warningMessage));
hex::format("hex.builtin.popup.error.project.load.no_providers"_lang)) + warningMsg);
return false; return false;
} else { } else {
// Else, if there are warnings, still display them
// Else, if are warnings, still display them if (warningMessage.empty()) {
if (warningMsg.empty()) {
return true; return true;
} else { } else {
showWarning( ui::ToastWarning::open(hex::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMessage));
hex::format("hex.builtin.popup.error.project.load.some_providers_failed"_lang, warningMsg));
} }
return success; return success;
} }
}, },

View File

@@ -16,6 +16,7 @@
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <cstring> #include <cstring>
#include <popups/popup_question.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
@@ -42,25 +43,38 @@ namespace hex::plugin::builtin {
} }
bool FileProvider::isSavable() const { bool FileProvider::isSavable() const {
return m_undoRedoStack.canUndo(); return m_loadedIntoMemory;
} }
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) { void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0) if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0)
return; return;
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size); if (m_loadedIntoMemory)
std::memcpy(buffer, m_data.data() + offset, size);
else
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
} }
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) { void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0) if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
return; return;
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size); if (m_loadedIntoMemory)
std::memcpy(m_data.data() + offset, buffer, size);
else
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
} }
void FileProvider::save() { void FileProvider::save() {
m_file.flush(); if (m_loadedIntoMemory) {
m_ignoreNextChangeEvent = true;
m_file.open();
m_file.writeVectorAtomic(0x00, m_data);
m_file.setSize(m_data.size());
} else {
m_file.flush();
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
FILETIME ft; FILETIME ft;
@@ -75,6 +89,9 @@ namespace hex::plugin::builtin {
} }
#endif #endif
if (m_loadedIntoMemory)
m_file.close();
Provider::save(); Provider::save();
} }
@@ -86,54 +103,14 @@ namespace hex::plugin::builtin {
} }
void FileProvider::resizeRaw(u64 newSize) { void FileProvider::resizeRaw(u64 newSize) {
m_file.setSize(newSize); if (m_loadedIntoMemory)
m_data.resize(newSize);
else
m_file.setSize(newSize);
m_fileSize = newSize; m_fileSize = newSize;
} }
void FileProvider::insertRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
this->resizeRaw(oldSize + size);
std::vector<u8> buffer(0x1000);
const std::vector<u8> zeroBuffer(0x1000);
auto position = oldSize;
while (position > offset) {
const auto readSize = std::min<size_t>(position - offset, buffer.size());
position -= readSize;
this->readRaw(position, buffer.data(), readSize);
this->writeRaw(position, zeroBuffer.data(), readSize);
this->writeRaw(position + size, buffer.data(), readSize);
}
}
void FileProvider::removeRaw(u64 offset, u64 size) {
if (offset > this->getActualSize() || size == 0)
return;
if ((offset + size) > this->getActualSize())
size = this->getActualSize() - offset;
auto oldSize = this->getActualSize();
std::vector<u8> buffer(0x1000);
const auto newSize = oldSize - size;
auto position = offset;
while (position < newSize) {
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
this->readRaw(position + size, buffer.data(), readSize);
this->writeRaw(position, buffer.data(), readSize);
position += readSize;
}
this->resizeRaw(newSize);
}
u64 FileProvider::getActualSize() const { u64 FileProvider::getActualSize() const {
return m_fileSize; return m_fileSize;
} }
@@ -246,12 +223,36 @@ namespace hex::plugin::builtin {
} }
} }
if (m_writable) {
if (m_fileSize < MaxMemoryFileSize) {
m_data = m_file.readVectorAtomic(0x00, m_fileSize);
if (!m_data.empty()) {
m_changeTracker = wolv::io::ChangeTracker(m_file);
m_changeTracker.startTracking([this]{ this->handleFileChange(); });
m_file.close();
m_loadedIntoMemory = true;
}
} else {
m_writable = false;
ui::PopupQuestion::open("hex.builtin.provider.file.too_large"_lang,
[this] {
m_writable = false;
},
[this] {
m_writable = true;
RequestUpdateWindowTitle::post();
});
}
}
return true; return true;
} }
void FileProvider::close() { void FileProvider::close() {
m_file.close(); m_file.close();
m_data.clear();
s_openedFiles.erase(this); s_openedFiles.erase(this);
m_changeTracker.stopTracking();
} }
void FileProvider::loadSettings(const nlohmann::json &settings) { void FileProvider::loadSettings(const nlohmann::json &settings) {
@@ -332,4 +333,19 @@ namespace hex::plugin::builtin {
} }
} }
void FileProvider::handleFileChange() {
if (m_ignoreNextChangeEvent) {
m_ignoreNextChangeEvent = false;
return;
}
ui::PopupQuestion::open("hex.builtin.provider.file.reload_changes"_lang, [this] {
this->close();
(void)this->open();
getUndoStack().reapply();
},
[]{});
}
} }

View File

@@ -69,43 +69,6 @@ namespace hex::plugin::builtin {
m_data.resize(newSize); m_data.resize(newSize);
} }
void MemoryFileProvider::insertRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
this->resizeRaw(oldSize + size);
std::vector<u8> buffer(0x1000);
const std::vector<u8> zeroBuffer(0x1000);
auto position = oldSize;
while (position > offset) {
const auto readSize = std::min<size_t>(position - offset, buffer.size());
position -= readSize;
this->readRaw(position, buffer.data(), readSize);
this->writeRaw(position, zeroBuffer.data(), readSize);
this->writeRaw(position + size, buffer.data(), readSize);
}
}
void MemoryFileProvider::removeRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
std::vector<u8> buffer(0x1000);
const auto newSize = oldSize - size;
auto position = offset;
while (position < newSize) {
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
this->readRaw(position + size, buffer.data(), readSize);
this->writeRaw(position, buffer.data(), readSize);
position += readSize;
}
this->resizeRaw(oldSize - size);
}
[[nodiscard]] std::string MemoryFileProvider::getName() const { [[nodiscard]] std::string MemoryFileProvider::getName() const {
if (m_name.empty()) if (m_name.empty())
return Lang("hex.builtin.provider.mem_file.unsaved"); return Lang("hex.builtin.provider.mem_file.unsaved");

View File

@@ -17,21 +17,15 @@
#include <toasts/toast_notification.hpp> #include <toasts/toast_notification.hpp>
#include <csignal>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
void addTitleBarButtons() { void addTitleBarButtons() {
#if defined(DEBUG) #if defined(DEBUG)
ContentRegistry::Interface::addTitleBarButton(ICON_VS_DEBUG, "hex.builtin.title_bar_button.debug_build", []{ ContentRegistry::Interface::addTitleBarButton(ICON_VS_DEBUG, "hex.builtin.title_bar_button.debug_build", []{
if (ImGui::GetIO().KeyCtrl) { if (ImGui::GetIO().KeyShift) {
// Explicitly trigger a segfault by writing to an invalid memory location RequestOpenPopup::post("DebugMenu");
// Used for debugging crashes
*reinterpret_cast<u8 *>(0x10) = 0x10;
std::unreachable();
} else if (ImGui::GetIO().KeyShift) {
// Explicitly trigger an abort by throwing an uncaught exception
// Used for debugging exception errors
throw std::runtime_error("Debug Error");
std::unreachable();
} else { } else {
hex::openWebpage("https://imhex.werwolv.net/debug"); hex::openWebpage("https://imhex.werwolv.net/debug");
} }
@@ -55,6 +49,86 @@ namespace hex::plugin::builtin {
} }
} }
#if defined(DEBUG)
static void drawDebugPopup() {
static bool showImGuiDemo = false;
static bool showImPlotDemo = false;
ImGui::SetNextWindowSize(scaled({ 300, 150 }), ImGuiCond_Always);
if (ImGui::BeginPopup("DebugMenu")) {
if (ImGui::BeginTabBar("DebugTabBar")) {
if (ImGui::BeginTabItem("ImHex")) {
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
ImGui::Checkbox("Show Debug Variables", &dbg::impl::getDebugWindowState());
ImGuiExt::Header("Information");
ImGuiExt::TextFormatted("Running Tasks: {0}", TaskManager::getRunningTaskCount());
ImGuiExt::TextFormatted("Running Background Tasks: {0}", TaskManager::getRunningBackgroundTaskCount());
ImGuiExt::TextFormatted("Last Frame Time: {0:.3f}ms", ImHexApi::System::getLastFrameTime() * 1000.0F);
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("ImGui")) {
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
auto ctx = ImGui::GetCurrentContext();
ImGui::Checkbox("Show ImGui Demo", &showImGuiDemo);
ImGui::Checkbox("Show ImPlot Demo", &showImPlotDemo);
if (ImGui::Button("Trigger Breakpoint in Item") || ctx->DebugItemPickerActive)
ImGui::DebugStartItemPicker();
}
ImGui::EndChild();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Crashes")) {
if (ImGui::BeginChild("Scrolling", ImGui::GetContentRegionAvail())) {
if (ImGui::Button("Throw Exception")) {
TaskManager::doLater([] {
throw std::runtime_error("Test exception");
});
}
if (ImGui::Button("Access Invalid Memory")) {
TaskManager::doLater([] {
*reinterpret_cast<u32*>(0x10) = 0x10;
std::unreachable();
});
}
if (ImGui::Button("Raise SIGSEGV")) {
TaskManager::doLater([] {
raise(SIGSEGV);
});
}
if (ImGui::Button("Corrupt Memory")) {
TaskManager::doLater([] {
auto bytes = new u8[0xFFFFF];
delete[] bytes;
delete[] bytes;
});
}
}
ImGui::EndChild();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::EndPopup();
}
if (showImGuiDemo)
ImGui::ShowDemoWindow(&showImGuiDemo);
if (showImPlotDemo)
ImPlot::ShowDemoWindow(&showImPlotDemo);
}
#endif
static bool s_drawDragDropOverlay = false; static bool s_drawDragDropOverlay = false;
static void drawDragNDropOverlay() { static void drawDragNDropOverlay() {
if (!s_drawDragDropOverlay) if (!s_drawDragDropOverlay)
@@ -99,6 +173,10 @@ namespace hex::plugin::builtin {
EventFrameEnd::subscribe(drawGlobalPopups); EventFrameEnd::subscribe(drawGlobalPopups);
EventFrameEnd::subscribe(drawDragNDropOverlay); EventFrameEnd::subscribe(drawDragNDropOverlay);
#if defined(DEBUG)
EventFrameEnd::subscribe(drawDebugPopup);
#endif
EventFileDragged::subscribe([](bool entered) { EventFileDragged::subscribe([](bool entered) {
s_drawDragDropOverlay = entered; s_drawDragDropOverlay = entered;
}); });
@@ -230,6 +308,16 @@ namespace hex::plugin::builtin {
} }
} }
}); });
ContentRegistry::Interface::addFooterItem([] {
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value()) {
ImGuiExt::TextFormattedSelectable("0x{:02X} - 0x{:02X} ({} bytes)",
selection->getStartAddress(),
selection->getEndAddress(),
selection->getSize()
);
}
});
} }
static void drawProviderContextMenu(prv::Provider *provider) { static void drawProviderContextMenu(prv::Provider *provider) {

View File

@@ -275,6 +275,7 @@ namespace hex::plugin::builtin {
constexpr static std::array Contributors = { constexpr static std::array Contributors = {
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true }, Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/jumanji144", true }, Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/jumanji144", true },
Contributor { "AxCut", "A ton of great pattern language improvements and help with the issue tracker", "https://github.com/paxcut", false },
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false }, Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false }, Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false }, Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },

View File

@@ -531,6 +531,7 @@ namespace hex::plugin::builtin {
} }
m_sortedOccurrences.get(provider) = m_foundOccurrences.get(provider); m_sortedOccurrences.get(provider) = m_foundOccurrences.get(provider);
m_lastSelectedOccurrence = nullptr;
for (const auto &occurrence : m_foundOccurrences.get(provider)) for (const auto &occurrence : m_foundOccurrences.get(provider))
m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence); m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence);
@@ -895,6 +896,7 @@ namespace hex::plugin::builtin {
m_foundOccurrences->clear(); m_foundOccurrences->clear();
m_sortedOccurrences->clear(); m_sortedOccurrences->clear();
m_occurrenceTree->clear(); m_occurrenceTree->clear();
m_lastSelectedOccurrence = nullptr;
EventHighlightingChanged::post(); EventHighlightingChanged::post();
} }
@@ -1007,7 +1009,11 @@ namespace hex::plugin::builtin {
ImGuiExt::TextFormatted("{}", value); ImGuiExt::TextFormatted("{}", value);
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) { if (ImGui::Selectable("##line", foundItem.selected, ImGuiSelectableFlags_SpanAllColumns)) {
if (ImGui::GetIO().KeyCtrl) { if (ImGui::GetIO().KeyShift && m_lastSelectedOccurrence != nullptr) {
for (auto start = std::min(&foundItem, m_lastSelectedOccurrence.get(provider)); start <= std::max(&foundItem, m_lastSelectedOccurrence.get(provider)); start += 1)
start->selected = true;
} else if (ImGui::GetIO().KeyCtrl) {
foundItem.selected = !foundItem.selected; foundItem.selected = !foundItem.selected;
} else { } else {
for (auto &occurrence : *m_sortedOccurrences) for (auto &occurrence : *m_sortedOccurrences)
@@ -1015,6 +1021,8 @@ namespace hex::plugin::builtin {
foundItem.selected = true; foundItem.selected = true;
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize()); ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
} }
m_lastSelectedOccurrence = &foundItem;
} }
drawContextMenu(foundItem, value); drawContextMenu(foundItem, value);

View File

@@ -50,6 +50,8 @@ namespace hex::plugin::builtin {
return std::nullopt; return std::nullopt;
auto provider = ImHexApi::Provider::get(); auto provider = ImHexApi::Provider::get();
if (!provider->isSavable())
return std::nullopt;
offset -= provider->getBaseAddress(); offset -= provider->getBaseAddress();

View File

@@ -3,13 +3,11 @@
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp> #include <hex/api/project_file_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <pl/patterns/pattern.hpp> #include <pl/patterns/pattern.hpp>
#include <pl/core/preprocessor.hpp> #include <pl/core/preprocessor.hpp>
#include <pl/core/parser.hpp> #include <pl/core/parser.hpp>
#include <pl/core/ast/ast_node_variable_decl.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 <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
@@ -22,11 +20,9 @@
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <popups/popup_file_chooser.hpp>
#include <popups/popup_question.hpp> #include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp> #include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <chrono> #include <chrono>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
@@ -34,6 +30,8 @@
#include <wolv/utils/guards.hpp> #include <wolv/utils/guards.hpp>
#include <wolv/utils/lock.hpp> #include <wolv/utils/lock.hpp>
#include <content/global_actions.hpp>
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
using namespace hex::literals; using namespace hex::literals;
@@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
langDef.mAutoIndentation = true; langDef.mAutoIndentation = true;
langDef.mPreprocChar = '#'; langDef.mPreprocChar = '#';
langDef.mGlobalDocComment = "/*!";
langDef.mDocComment = "/**";
langDef.mName = "Pattern Language"; langDef.mName = "Pattern Language";
initialized = true; initialized = true;
@@ -236,6 +237,14 @@ namespace hex::plugin::builtin {
bool clickedMenuFind = false; bool clickedMenuFind = false;
bool clickedMenuReplace = false; bool clickedMenuReplace = false;
if (ImGui::BeginPopup("##pattern_editor_context_menu")) { if (ImGui::BeginPopup("##pattern_editor_context_menu")) {
// no shortcut for this
if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false))
importPatternFile();
if (ImGui::MenuItem("hex.builtin.menu.file.export.pattern_file"_lang, nullptr, false))
exportPatternFile();
ImGui::Separator();
const bool hasSelection = m_textEditor.HasSelection(); const bool hasSelection = m_textEditor.HasSelection();
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) { if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
m_textEditor.Cut(); m_textEditor.Cut();
@@ -306,6 +315,12 @@ namespace hex::plugin::builtin {
m_replaceMode = true; m_replaceMode = true;
openFindPopup = true; openFindPopup = true;
} }
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::saveProject();
if (ImGui::IsKeyPressed(ImGuiKey_O, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::openProject();
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt && ImGui::GetIO().KeyShift)
hex::plugin::builtin::saveProjectAs();
} }
static std::string findWord; static std::string findWord;
@@ -685,6 +700,8 @@ namespace hex::plugin::builtin {
if (altCPressed) if (altCPressed)
matchCase = !matchCase; matchCase = !matchCase;
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase); findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
count = findReplaceHandler->GetMatches().size();
updateCount = true; updateCount = true;
requestFocusFind = true; requestFocusFind = true;
} }
@@ -1285,7 +1302,8 @@ namespace hex::plugin::builtin {
if (!m_lastCompileError->empty()) { if (!m_lastCompileError->empty()) {
for (const auto &error : *m_lastCompileError) { for (const auto &error : *m_lastCompileError) {
errorMarkers[error.getLocation().line] = processMessage(error.getMessage()); if (error.getLocation().source->source == pl::api::Source::DefaultSource)
errorMarkers[error.getLocation().line] = processMessage(error.getMessage());
} }
} }
@@ -1686,12 +1704,8 @@ namespace hex::plugin::builtin {
if (newProvider != nullptr) if (newProvider != nullptr)
m_textEditor.SetText(m_sourceCode.get(newProvider)); m_textEditor.SetText(m_sourceCode.get(newProvider));
}); else
EventProviderClosed::subscribe(this, [this](prv::Provider *) {
if (ImHexApi::Provider::getProviders().empty()) {
m_textEditor.SetText(""); m_textEditor.SetText("");
}
}); });
RequestAddVirtualFile::subscribe(this, [this](const std::fs::path &path, const std::vector<u8> &data, Region region) { RequestAddVirtualFile::subscribe(this, [this](const std::fs::path &path, const std::vector<u8> &data, Region region) {
@@ -1736,39 +1750,11 @@ namespace hex::plugin::builtin {
void ViewPatternEditor::registerMenuItems() { void ViewPatternEditor::registerMenuItems() {
/* Import Pattern */ /* Import Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None, ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None,
[this] { importPatternFile, ImHexApi::Provider::isValid);
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat") {
paths.push_back(entry.path());
}
}
}
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
});
}, ImHexApi::Provider::isValid);
/* Export Pattern */ /* Export Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None, ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None,
[this] { exportPatternFile, [this] {
fs::openFileBrowser(fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
});
}, [this] {
return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid(); return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid();
} }
); );

View File

@@ -150,6 +150,22 @@ namespace hex::plugin::builtin {
}); });
} }
void drawWelcomeScreenBackground() {
const auto position = ImGui::GetWindowPos();
const auto size = ImGui::GetWindowSize();
auto drawList = ImGui::GetWindowDrawList();
const auto lineDistance = 20_scaled;
const auto lineColor = ImGui::GetColorU32(ImGuiCol_Text, 0.03F);
for (auto x = position.x; x < position.x + size.x + lineDistance; x += lineDistance) {
drawList->AddLine({ x, position.y }, { x, position.y + size.y }, lineColor);
}
for (auto y = position.y; y < position.y + size.y + lineDistance; y += lineDistance) {
drawList->AddLine({ position.x, y }, { position.x + size.x, y }, lineColor);
}
}
void drawWelcomeScreenContentSimplified() { void drawWelcomeScreenContentSimplified() {
const ImVec2 backdropSize = scaled({ 350, 350 }); const ImVec2 backdropSize = scaled({ 350, 350 });
ImGui::SetCursorPos((ImGui::GetContentRegionAvail() - backdropSize) / 2); ImGui::SetCursorPos((ImGui::GetContentRegionAvail() - backdropSize) / 2);
@@ -361,6 +377,9 @@ namespace hex::plugin::builtin {
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID); ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
if (ImGui::Begin("Welcome Screen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) { if (ImGui::Begin("Welcome Screen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
ImGui::BringWindowToDisplayBack(ImGui::GetCurrentWindowRead()); ImGui::BringWindowToDisplayBack(ImGui::GetCurrentWindowRead());
drawWelcomeScreenBackground();
if (s_simplifiedWelcomeScreen) if (s_simplifiedWelcomeScreen)
drawWelcomeScreenContentSimplified(); drawWelcomeScreenContentSimplified();
else else

View File

@@ -250,6 +250,8 @@ namespace hex::plugin::builtin {
#endif #endif
if (ImGui::BeginMainMenuBar()) { if (ImGui::BeginMainMenuBar()) {
ImGui::Dummy({});
auto window = ImHexApi::System::getMainWindowHandle(); auto window = ImHexApi::System::getMainWindowHandle();
ImGui::PopStyleVar(); ImGui::PopStyleVar();

View File

@@ -7,23 +7,9 @@
namespace hex::plugin::builtin { namespace hex::plugin::builtin {
void loadWorkspaces() { void loadWorkspaces() {
WorkspaceManager::reset(); WorkspaceManager::reload();
for (const auto &defaultPath : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
for (const auto &entry : std::fs::directory_iterator(defaultPath)) {
if (!entry.is_regular_file())
continue;
const auto &path = entry.path();
if (path.extension() != ".hexws")
continue;
WorkspaceManager::importFromFile(path);
}
}
std::string currentWorkspace = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.curr_workspace", "Default");
auto currentWorkspace = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.curr_workspace", "Default");
TaskManager::doLater([currentWorkspace] { TaskManager::doLater([currentWorkspace] {
WorkspaceManager::switchWorkspace(currentWorkspace); WorkspaceManager::switchWorkspace(currentWorkspace);
}); });

View File

@@ -58,21 +58,22 @@ namespace hex::plugin::builtin {
} }
IMHEX_PLUGIN_SUBCOMMANDS() { IMHEX_PLUGIN_SUBCOMMANDS() {
{ "help", "Print help about this command", hex::plugin::builtin::handleHelpCommand }, { "help", "h", "Print help about this command", hex::plugin::builtin::handleHelpCommand },
{ "version", "Print ImHex version", hex::plugin::builtin::handleVersionCommand }, { "version", "", "Print ImHex version", hex::plugin::builtin::handleVersionCommand },
{ "plugins", "Lists all plugins that have been installed", hex::plugin::builtin::handlePluginsCommand }, { "plugins", "", "Lists all plugins that have been installed", hex::plugin::builtin::handlePluginsCommand },
{ "language", "Changes the language ImHex uses", hex::plugin::builtin::handleLanguageCommand }, { "language", "", "Changes the language ImHex uses", hex::plugin::builtin::handleLanguageCommand },
{ "verbose", "v", "Enables verbose debug logging", hex::plugin::builtin::handleVerboseCommand },
{ "open", "Open files passed as argument. [default]", hex::plugin::builtin::handleOpenCommand }, { "open", "o", "Open files passed as argument. [default]", hex::plugin::builtin::handleOpenCommand },
{ "calc", "Evaluate a mathematical expression", hex::plugin::builtin::handleCalcCommand }, { "calc", "", "Evaluate a mathematical expression", hex::plugin::builtin::handleCalcCommand },
{ "hash", "Calculate the hash of a file", hex::plugin::builtin::handleHashCommand }, { "hash", "", "Calculate the hash of a file", hex::plugin::builtin::handleHashCommand },
{ "encode", "Encode a string", hex::plugin::builtin::handleEncodeCommand }, { "encode", "", "Encode a string", hex::plugin::builtin::handleEncodeCommand },
{ "decode", "Decode a string", hex::plugin::builtin::handleDecodeCommand }, { "decode", "", "Decode a string", hex::plugin::builtin::handleDecodeCommand },
{ "magic", "Identify file types", hex::plugin::builtin::handleMagicCommand }, { "magic", "", "Identify file types", hex::plugin::builtin::handleMagicCommand },
{ "pl", "Interact with the pattern language", hex::plugin::builtin::handlePatternLanguageCommand }, { "pl", "", "Interact with the pattern language", hex::plugin::builtin::handlePatternLanguageCommand },
{ "hexdump", "Generate a hex dump of the provided file", hex::plugin::builtin::handleHexdumpCommand }, { "hexdump", "", "Generate a hex dump of the provided file", hex::plugin::builtin::handleHexdumpCommand },
{ "demangle", "Demangle a mangled symbol", hex::plugin::builtin::handleDemangleCommand }, { "demangle", "", "Demangle a mangled symbol", hex::plugin::builtin::handleDemangleCommand },
}; };
IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") {

View File

@@ -0,0 +1,19 @@
project(${IMHEX_PLUGIN_NAME}_tests)
# Add new tests here #
set(AVAILABLE_TESTS
Providers/ReadWrite
Providers/InvalidResize
)
add_library(${PROJECT_NAME} SHARED
source/main.cpp
)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_SOURCE_DIR}/plugins/builtin/include)
target_link_libraries(${PROJECT_NAME} PRIVATE libimhex)
foreach (test IN LISTS AVAILABLE_TESTS)
add_test(NAME "Plugin_${IMHEX_PLUGIN_NAME}/${test}" COMMAND $<TARGET_FILE:plugins_test> "${test}" WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
endforeach ()

View File

@@ -0,0 +1,43 @@
#include <iostream>
#include <hex/test/tests.hpp>
#include <hex/api/plugin_manager.hpp>
#include <content/providers/memory_file_provider.hpp>
#include <content/views/view_patches.hpp>
#include <hex/api/task_manager.hpp>
using namespace hex;
using namespace hex::plugin::builtin;
TEST_SEQUENCE("Providers/ReadWrite") {
INIT_PLUGIN("Built-in");
auto &pr = *ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
TEST_ASSERT(pr.getSize() == 0);
TEST_ASSERT(!pr.isDirty());
pr.resize(50);
TEST_ASSERT(pr.getSize() == 50);
TEST_ASSERT(pr.isDirty());
char buf[] = "\x99\x99"; // temporary value that should be overwriten
pr.read(0, buf, 2);
TEST_ASSERT(std::equal(buf, buf+2, "\x00\x00"));
pr.write(0, "\xFF\xFF", 2);
char buf2[] = "\x99\x99"; // temporary value that should be overwriten
pr.read(0, buf2, 2);
TEST_ASSERT(std::equal(buf2, buf2+2, "\xFF\xFF"));
TEST_SUCCESS();
};
TEST_SEQUENCE("Providers/InvalidResize") {
INIT_PLUGIN("Built-in");
auto &pr = *ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
TEST_ASSERT(!pr.resize(-1));
TEST_SUCCESS();
};

View File

@@ -15,10 +15,10 @@ using namespace hex;
using namespace hex::plugin::decompress; using namespace hex::plugin::decompress;
IMHEX_PLUGIN_FEATURES() { IMHEX_PLUGIN_FEATURES() {
{ "bzip2 Support", IMHEX_FEATURE_ENABLED(BZIP2) }, { "bzip2", IMHEX_FEATURE_ENABLED(BZIP2) },
{ "zlib Support", IMHEX_FEATURE_ENABLED(ZLIB) }, { "zlib", IMHEX_FEATURE_ENABLED(ZLIB) },
{ "LZMA Support", IMHEX_FEATURE_ENABLED(LIBLZMA) }, { "LZMA", IMHEX_FEATURE_ENABLED(LIBLZMA) },
{ "zstd Support", IMHEX_FEATURE_ENABLED(ZSTD) }, { "zstd", IMHEX_FEATURE_ENABLED(ZSTD) },
}; };
IMHEX_PLUGIN_SETUP("Decompressing", "WerWolv", "Support for decompressing data") { IMHEX_PLUGIN_SETUP("Decompressing", "WerWolv", "Support for decompressing data") {

View File

@@ -1,24 +1,11 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include(ImHexPlugin) include(ImHexPlugin)
find_package(CoreClrEmbed) find_package(CoreClrEmbed)
add_imhex_plugin(
NAME
script_loader
SOURCES
source/plugin_script_loader.cpp
INCLUDES
include
LIBRARIES
fonts
ui
)
if (CoreClrEmbed_FOUND) if (CoreClrEmbed_FOUND)
set(IMHEX_DOTNET_SCRIPT_SUPPORT ON)
add_library(nethost SHARED IMPORTED) add_library(nethost SHARED IMPORTED)
target_include_directories(nethost INTERFACE "${CoreClrEmbed_INCLUDE_DIRS}") target_include_directories(nethost INTERFACE "${CoreClrEmbed_INCLUDE_DIRS}")
get_filename_component(CoreClrEmbed_FOLDER ${CoreClrEmbed_SHARED_LIBRARIES} DIRECTORY) get_filename_component(CoreClrEmbed_FOLDER ${CoreClrEmbed_SHARED_LIBRARIES} DIRECTORY)
@@ -29,24 +16,70 @@ if (CoreClrEmbed_FOUND)
BUILD_RPATH ${CoreClrEmbed_FOLDER} BUILD_RPATH ${CoreClrEmbed_FOLDER}
INSTALL_RPATH ${CoreClrEmbed_FOLDER}) INSTALL_RPATH ${CoreClrEmbed_FOLDER})
target_link_directories(script_loader PRIVATE ${CoreClrEmbed_FOLDER})
target_include_directories(script_loader PRIVATE ${CoreClrEmbed_INCLUDE_DIRS})
target_compile_definitions(script_loader PRIVATE DOTNET_PLUGINS=1)
target_sources(script_loader PRIVATE
source/loaders/dotnet/dotnet_loader.cpp
source/script_api/v1/mem.cpp
source/script_api/v1/bookmarks.cpp
source/script_api/v1/ui.cpp
)
set(EXTRA_BUNDLE_LIBRARY_PATHS "${CoreClrEmbed_FOLDER}" PARENT_SCOPE) set(EXTRA_BUNDLE_LIBRARY_PATHS "${CoreClrEmbed_FOLDER}" PARENT_SCOPE)
if (IMHEX_BUNDLE_DOTNET) if (IMHEX_BUNDLE_DOTNET)
install(FILES ${CoreClrEmbed_SHARED_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(FILES ${CoreClrEmbed_SHARED_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif () endif()
endif()
add_subdirectory(dotnet) find_package(Python3 COMPONENTS Interpreter Development.Embed)
if (Python3_FOUND)
set(IMHEX_PYTHON_SCRIPT_SUPPORT ON)
get_target_property(PYTHON_LIBRARY Python3::Python IMPORTED_LOCATION)
get_target_property(PYTHON_INCLUDE_DIR Python3::Python INTERFACE_INCLUDE_DIRECTORIES)
endif()
add_subdirectory(support/c)
add_imhex_plugin(
NAME
script_loader
SOURCES
source/plugin_script_loader.cpp
INCLUDES
include
LIBRARIES
c_api
fonts
FEATURES
DOTNET
PYTHON
)
if (IMHEX_DOTNET_SCRIPT_SUPPORT)
message(STATUS "Enabling .NET Scripting support!")
target_link_directories(script_loader PRIVATE ${CoreClrEmbed_FOLDER})
target_include_directories(script_loader PRIVATE ${CoreClrEmbed_INCLUDE_DIRS})
target_compile_definitions(script_loader PRIVATE IMHEX_DOTNET_SCRIPT_SUPPORT=1)
target_sources(script_loader PRIVATE
source/loaders/dotnet/dotnet_loader.cpp
)
add_subdirectory(support/dotnet)
add_dependencies(script_loader AssemblyLoader) add_dependencies(script_loader AssemblyLoader)
enable_plugin_feature(DOTNET)
endif()
endif () if (IMHEX_PYTHON_SCRIPT_SUPPORT)
message(STATUS "Enabling Python Scripting support!")
target_compile_definitions(script_loader PRIVATE IMHEX_PYTHON_SCRIPT_SUPPORT=1)
target_sources(script_loader PRIVATE
source/loaders/python/python_loader.cpp
source/loaders/python/library_wrapper.cpp
)
target_include_directories(script_loader PRIVATE ${PYTHON_INCLUDE_DIR})
target_compile_definitions(script_loader PRIVATE PYTHON_LIBRARY_PATH="${PYTHON_LIBRARY}")
enable_plugin_feature(PYTHON)
endif()

View File

@@ -1,82 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Loader;
namespace ImHex
{
public class EntryPoint
{
public static int ExecuteScript(IntPtr arg, int argLength)
{
try
{
return ExecuteScript(Marshal.PtrToStringUTF8(arg, argLength)) ? 0 : 1;
}
catch (Exception e)
{
Console.WriteLine("[.NET Script] Exception in AssemblyLoader: " + e.ToString());
return 1;
}
}
private static bool ExecuteScript(string path)
{
string? basePath = Path.GetDirectoryName(path);
if (basePath == null)
{
Console.WriteLine("[.NET Script] Failed to get base path");
return false;
}
AssemblyLoadContext? context = new("ScriptDomain_" + basePath, true);
try
{
foreach (var file in Directory.GetFiles(basePath, "*.dll"))
{
context.LoadFromStream(new MemoryStream(File.ReadAllBytes(file)));
}
var assembly = context.LoadFromStream(new MemoryStream(File.ReadAllBytes(path)));
var entryPointType = assembly.GetType("Script");
if (entryPointType == null)
{
Console.WriteLine("[.NET Script] Failed to find Script type");
return false;
}
var entryPointMethod = entryPointType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
if (entryPointMethod == null)
{
Console.WriteLine("[.NET Script] Failed to find ScriptMain method");
return false;
}
entryPointMethod.Invoke(null, null);
}
catch (Exception e)
{
Console.WriteLine("[.NET Script] Exception in AssemblyLoader: " + e.ToString());
return false;
}
finally
{
context.Unload();
context = null;
for (int i = 0; i < 10; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
return true;
}
}
}

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net7.0\publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>

View File

@@ -1,7 +0,0 @@
file(READ "${OUTPUT_RUNTIMECONFIG}" FILE_CONTENTS)
set(VERSION_REGEX [["version": "([0-9]+\.[0-9]+\.[0-9]+)"]])
set(REPLACE_VALUE [["version": "7.0.0"]])
string(REGEX REPLACE "${VERSION_REGEX}" ${REPLACE_VALUE} FILE_CONTENTS_OUT "${FILE_CONTENTS}")
file(WRITE "${OUTPUT_RUNTIMECONFIG}" "${FILE_CONTENTS_OUT}")

View File

@@ -10,14 +10,15 @@ namespace hex::script::loader {
class DotNetLoader : public ScriptLoader { class DotNetLoader : public ScriptLoader {
public: public:
DotNetLoader() = default; DotNetLoader() : ScriptLoader(".NET") {}
~DotNetLoader() override = default; ~DotNetLoader() override = default;
bool initialize() override; bool initialize() override;
bool loadAll() override; bool loadAll() override;
private: private:
std::function<bool(const std::fs::path&)> m_loadAssembly; std::function<int(const std::string &, bool, const std::fs::path&)> m_runMethod;
std::function<bool(const std::string &, const std::fs::path&)> m_methodExists;
std::fs::path::string_type m_assemblyLoaderPathString; std::fs::path::string_type m_assemblyLoaderPathString;
}; };

View File

@@ -3,30 +3,44 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <hex/helpers/utils.hpp>
#if defined(OS_WINDOWS)
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace hex::script::loader { namespace hex::script::loader {
class ScriptLoader;
struct Script { struct Script {
std::string name; std::string name;
std::function<void()> entryPoint; std::function<void()> entryPoint;
const ScriptLoader *loader;
}; };
class ScriptLoader { class ScriptLoader {
public: public:
ScriptLoader() = default; ScriptLoader(std::string typeName) : m_typeName(std::move(typeName)) {}
virtual ~ScriptLoader() = default; virtual ~ScriptLoader() = default;
virtual bool initialize() = 0; virtual bool initialize() = 0;
virtual bool loadAll() = 0; virtual bool loadAll() = 0;
void addScript(std::string name, std::function<void()> entryPoint) { void addScript(std::string name, std::function<void()> entryPoint) {
m_scripts.emplace_back(std::move(name), std::move(entryPoint)); m_scripts.emplace_back(std::move(name), std::move(entryPoint), this);
} }
const auto& getScripts() const { const auto& getScripts() const {
return m_scripts; return m_scripts;
} }
const std::string& getTypeName() const {
return m_typeName;
}
protected: protected:
void clearScripts() { void clearScripts() {
m_scripts.clear(); m_scripts.clear();
@@ -34,6 +48,49 @@ namespace hex::script::loader {
private: private:
std::vector<Script> m_scripts; std::vector<Script> m_scripts;
std::string m_typeName;
}; };
#if defined(OS_WINDOWS)
inline void *loadLibrary(const wchar_t *path) {
try {
HMODULE h = ::LoadLibraryW(path);
return h;
} catch (...) {
return nullptr;
}
}
inline void *loadLibrary(const char *path) {
try {
auto utf16Path = hex::utf8ToUtf16(path);
HMODULE h = ::LoadLibraryW(utf16Path.c_str());
return h;
} catch (...) {
return nullptr;
}
}
template<typename T>
T getExport(void *h, const char *name) {
try {
FARPROC f = ::GetProcAddress(static_cast<HMODULE>(h), name);
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(f));
} catch (...) {
return nullptr;
}
}
#else
inline void *loadLibrary(const char *path) {
void *h = dlopen(path, RTLD_LAZY);
return h;
}
template<typename T>
T getExport(void *h, const char *name) {
void *f = dlsym(h, name);
return reinterpret_cast<T>(f);
}
#endif
} }

View File

@@ -0,0 +1,23 @@
#pragma once
#include <loaders/loader.hpp>
#include <wolv/io/fs.hpp>
#include <functional>
namespace hex::script::loader {
class PythonLoader : public ScriptLoader {
public:
PythonLoader() : ScriptLoader("Python") {}
~PythonLoader() override = default;
bool initialize() override;
bool loadAll() override;
private:
std::vector<void*> m_loadedModules;
};
}

View File

@@ -3,10 +3,8 @@
#include <stdexcept> #include <stdexcept>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <Windows.h>
#define STRING(str) L##str #define STRING(str) L##str
#else #else
#include <dlfcn.h>
#define STRING(str) str #define STRING(str) str
#endif #endif
@@ -15,6 +13,8 @@
#include <nethost.h> #include <nethost.h>
#include <coreclr_delegates.h> #include <coreclr_delegates.h>
#include <hostfxr.h> #include <hostfxr.h>
#include <imgui.h>
#include <hex/api/plugin_manager.hpp>
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
@@ -22,6 +22,9 @@
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
extern "C" void igSetCurrentContext(ImGuiContext* ctx);
namespace hex::script::loader { namespace hex::script::loader {
@@ -29,46 +32,27 @@ namespace hex::script::loader {
using get_hostfxr_path_fn = int(*)(char_t * buffer, size_t * buffer_size, const get_hostfxr_parameters *parameters); using get_hostfxr_path_fn = int(*)(char_t * buffer, size_t * buffer_size, const get_hostfxr_parameters *parameters);
#if defined(OS_WINDOWS)
void *loadLibrary(const char_t *path) {
try {
HMODULE h = ::LoadLibraryW(path);
return h;
} catch (...) {
return nullptr;
}
}
template<typename T>
T getExport(void *h, const char *name) {
try {
FARPROC f = ::GetProcAddress(static_cast<HMODULE>(h), name);
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(f));
} catch (...) {
return nullptr;
}
}
#else
void *loadLibrary(const char_t *path) {
void *h = dlopen(path, RTLD_LAZY);
return h;
}
template<typename T>
T getExport(void *h, const char *name) {
void *f = dlsym(h, name);
return reinterpret_cast<T>(f);
}
#endif
hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr; hostfxr_initialize_for_runtime_config_fn hostfxr_initialize_for_runtime_config = nullptr;
hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr; hostfxr_get_runtime_delegate_fn hostfxr_get_runtime_delegate = nullptr;
hostfxr_close_fn hostfxr_close = nullptr; hostfxr_close_fn hostfxr_close = nullptr;
hostfxr_set_runtime_property_value_fn hostfxr_set_runtime_property_value = nullptr;
hostfxr_set_error_writer_fn hostfxr_set_error_writer = nullptr;
void* pInvokeOverride(const char *libraryName, const char *symbolName) {
auto library = std::string_view(libraryName);
if (library == "cimgui") {
return getExport<void*>(ImHexApi::System::getLibImHexModuleHandle(), symbolName);
} else if (library == "ImHex") {
return getExport<void*>(hex::getContainingModule((void*)&pInvokeOverride), symbolName);
}
return nullptr;
}
bool loadHostfxr() { bool loadHostfxr() {
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
auto netHostLibrary = loadLibrary(L"nethost.dll"); auto netHostLibrary = loadLibrary(L"nethost.dll");
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
auto netHostLibrary = loadLibrary("libnethost.so"); auto netHostLibrary = loadLibrary("libnethost.so");
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
void *netHostLibrary = nullptr; void *netHostLibrary = nullptr;
@@ -88,10 +72,12 @@ namespace hex::script::loader {
auto get_hostfxr_path_ptr = getExport<get_hostfxr_path_fn>(netHostLibrary, "get_hostfxr_path"); auto get_hostfxr_path_ptr = getExport<get_hostfxr_path_fn>(netHostLibrary, "get_hostfxr_path");
std::array<char_t, 300> buffer = { 0 }; std::array<char_t, 300> buffer = { };
size_t bufferSize = buffer.size(); size_t bufferSize = buffer.size();
if (get_hostfxr_path_ptr(buffer.data(), &bufferSize, nullptr) != 0) {
log::error("Could not get hostfxr path!"); auto result = get_hostfxr_path_ptr(buffer.data(), &bufferSize, nullptr);
if (result != 0) {
log::error(hex::format("Could not get hostfxr path! 0x{:X}", result));
return false; return false;
} }
@@ -108,13 +94,26 @@ namespace hex::script::loader {
= getExport<hostfxr_get_runtime_delegate_fn>(hostfxrLibrary, "hostfxr_get_runtime_delegate"); = getExport<hostfxr_get_runtime_delegate_fn>(hostfxrLibrary, "hostfxr_get_runtime_delegate");
hostfxr_close hostfxr_close
= getExport<hostfxr_close_fn>(hostfxrLibrary, "hostfxr_close"); = getExport<hostfxr_close_fn>(hostfxrLibrary, "hostfxr_close");
hostfxr_set_runtime_property_value
= getExport<hostfxr_set_runtime_property_value_fn>(hostfxrLibrary, "hostfxr_set_runtime_property_value");
hostfxr_set_error_writer
= getExport<hostfxr_set_error_writer_fn>(hostfxrLibrary, "hostfxr_set_error_writer");
} }
hostfxr_set_error_writer([] HOSTFXR_CALLTYPE (const char_t *message) {
#if defined(OS_WINDOWS)
log::error("{}", utf16ToUtf8(message));
#else
log::error("{}", message);
#endif
});
return return
hostfxr_initialize_for_runtime_config != nullptr && hostfxr_initialize_for_runtime_config != nullptr &&
hostfxr_get_runtime_delegate != nullptr && hostfxr_get_runtime_delegate != nullptr &&
hostfxr_close != nullptr; hostfxr_close != nullptr &&
hostfxr_set_runtime_property_value != nullptr &&
hostfxr_set_error_writer != nullptr;
} }
load_assembly_and_get_function_pointer_fn getLoadAssemblyFunction(const std::fs::path &path) { load_assembly_and_get_function_pointer_fn getLoadAssemblyFunction(const std::fs::path &path) {
@@ -128,9 +127,15 @@ namespace hex::script::loader {
}; };
if (result > 2 || ctx == nullptr) { if (result > 2 || ctx == nullptr) {
throw std::runtime_error(hex::format("Failed to initialize command line {:X}", result)); throw std::runtime_error(hex::format("Failed to initialize command line 0x{:X}", result));
} }
#if defined (OS_WINDOWS)
hostfxr_set_runtime_property_value(ctx, STRING("PINVOKE_OVERRIDE"), utf8ToUtf16(hex::format("{}", (void*)pInvokeOverride)).c_str());
#else
hostfxr_set_runtime_property_value(ctx, STRING("PINVOKE_OVERRIDE"), hex::format("{}", (void*)pInvokeOverride).c_str());
#endif
result = hostfxr_get_runtime_delegate( result = hostfxr_get_runtime_delegate(
ctx, ctx,
hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer, hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer,
@@ -138,7 +143,7 @@ namespace hex::script::loader {
); );
if (result != 0 || loadAssemblyFunction == nullptr) { if (result != 0 || loadAssemblyFunction == nullptr) {
throw std::runtime_error("Failed to get load_assembly_and_get_function_pointer delegate"); throw std::runtime_error(hex::format("Failed to get load_assembly_and_get_function_pointer delegate 0x{:X}", result));
} }
return loadAssemblyFunction; return loadAssemblyFunction;
@@ -175,12 +180,23 @@ namespace hex::script::loader {
); );
if (result != 0 || entryPoint == nullptr) { if (result != 0 || entryPoint == nullptr) {
log::error("Failed to load assembly loader '{}'", assemblyLoader.string()); log::error("Failed to load assembly loader '{}'! 0x{:X}", assemblyLoader.string(), result);
continue; continue;
} }
m_loadAssembly = [entryPoint](const std::fs::path &path) -> bool { m_runMethod = [entryPoint](const std::string &methodName, bool keepLoaded, const std::fs::path &path) -> int {
auto string = wolv::util::toUTF8String(path); auto pathString = wolv::util::toUTF8String(path);
auto string = hex::format("{}||{}||{}", keepLoaded ? "LOAD" : "EXEC", methodName, pathString);
auto result = entryPoint(string.data(), string.size());
return result;
};
m_methodExists = [entryPoint](const std::string &methodName, const std::fs::path &path) -> bool {
auto pathString = wolv::util::toUTF8String(path);
auto string = hex::format("CHECK||{}||{}", methodName, pathString);
auto result = entryPoint(string.data(), string.size()); auto result = entryPoint(string.data(), string.size());
return result == 0; return result == 0;
@@ -211,9 +227,15 @@ namespace hex::script::loader {
if (!std::fs::exists(scriptPath)) if (!std::fs::exists(scriptPath))
continue; continue;
this->addScript(entry.path().stem().string(), [this, scriptPath] { if (m_methodExists("Main", scriptPath)) {
hex::unused(m_loadAssembly(scriptPath)); this->addScript(entry.path().stem().string(), [this, scriptPath] {
}); hex::unused(m_runMethod("Main", false, scriptPath));
});
}
if (m_methodExists("OnLoad", scriptPath)) {
hex::unused(m_runMethod("OnLoad", true, scriptPath));
}
} }
} }

View File

@@ -0,0 +1,112 @@
#if defined(__declspec)
#undef __declspec
#define __declspec(x)
#endif
#include <Python.h>
#include <loaders/loader.hpp>
#define FUNCTION_DEFINITION(ret, name, args1, args2) \
decltype(name) *name##Func = nullptr; \
extern "C" ret name args1 { \
return name##Func args2; \
}
#define INIT_FUNCTION(name) name##Func = hex::script::loader::getExport<decltype(name##Func)>(pythonLibrary, #name)
FUNCTION_DEFINITION(void, PyPreConfig_InitPythonConfig, (PyPreConfig *config), (config))
FUNCTION_DEFINITION(PyStatus, Py_PreInitialize, (const PyPreConfig *src_config), (src_config))
FUNCTION_DEFINITION(int, PyStatus_Exception, (PyStatus err), (err))
FUNCTION_DEFINITION(void, Py_Initialize, (), ())
FUNCTION_DEFINITION(void, Py_Finalize, (), ())
FUNCTION_DEFINITION(PyInterpreterState *, PyInterpreterState_Get, (), ())
FUNCTION_DEFINITION(PyThreadState *, PyEval_SaveThread, (), ())
FUNCTION_DEFINITION(void, PyEval_RestoreThread, (PyThreadState * state), (state))
FUNCTION_DEFINITION(void, PyErr_Fetch, (PyObject **ptype, PyObject **pvalue, PyObject **ptraceback), (ptype, pvalue, ptraceback))
FUNCTION_DEFINITION(void, PyErr_NormalizeException, (PyObject **ptype, PyObject **pvalue, PyObject **ptraceback), (ptype, pvalue, ptraceback))
FUNCTION_DEFINITION(PyObject *, PyUnicode_FromString, (const char *u), (u))
FUNCTION_DEFINITION(PyObject *, PyImport_Import, (PyObject *name), (name))
FUNCTION_DEFINITION(void, _Py_Dealloc, (PyObject *pobj), (pobj))
FUNCTION_DEFINITION(PyObject *, PyModule_GetDict, (PyObject *pobj), (pobj))
FUNCTION_DEFINITION(PyObject *, PyDict_GetItemString, (PyObject *dp, const char *key), (dp, key))
FUNCTION_DEFINITION(int, PyCallable_Check, (PyObject *dp), (dp))
FUNCTION_DEFINITION(PyObject *, PyTuple_New, (Py_ssize_t len), (len))
FUNCTION_DEFINITION(int, PyTuple_SetItem, (PyObject *p, Py_ssize_t pos, PyObject *o), (p, pos, o))
FUNCTION_DEFINITION(PyObject *, PyObject_CallObject, (PyObject *callable, PyObject *args), (callable, args))
FUNCTION_DEFINITION(PyObject *, PyUnicode_Join, (PyObject *separator, PyObject *iterable), (separator, iterable))
FUNCTION_DEFINITION(const char *, PyUnicode_AsUTF8, (PyObject *unicode), (unicode))
FUNCTION_DEFINITION(void, PyErr_Clear, (), ())
FUNCTION_DEFINITION(int, PyModule_AddStringConstant, (PyObject *module, const char *name, const char *value), (module, name, value))
FUNCTION_DEFINITION(PyObject *, PyEval_GetBuiltins, (), ())
FUNCTION_DEFINITION(int, PyDict_SetItemString, (PyObject *dp, const char *key, PyObject *item), (dp, key, item))
FUNCTION_DEFINITION(PyObject *, PyRun_StringFlags, (const char *str, int start, PyObject *globals, PyObject *locals, PyCompilerFlags *flags), (str, start, globals, locals, flags))
FUNCTION_DEFINITION(void, PyThreadState_Clear, (PyThreadState *tstate), (tstate))
FUNCTION_DEFINITION(void, PyThreadState_DeleteCurrent, (), ())
FUNCTION_DEFINITION(PyThreadState *, PyThreadState_New, (PyInterpreterState *interp), (interp))
FUNCTION_DEFINITION(PyObject *, PyImport_AddModule, (const char *name), (name))
FUNCTION_DEFINITION(PyObject *, PyModule_New, (const char *name), (name))
FUNCTION_DEFINITION(PyObject *, PyObject_GetAttrString, (PyObject *pobj, const char *name), (pobj, name))
FUNCTION_DEFINITION(int, PyObject_HasAttrString, (PyObject *pobj, const char *name), (pobj, name))
FUNCTION_DEFINITION(PyObject*, PySys_GetObject, (const char *name), (name))
FUNCTION_DEFINITION(int, PyList_Append, (PyObject *plist, PyObject *pvalue), (plist, pvalue))
bool initPythonLoader() {
void *pythonLibrary = nullptr;
for (const std::fs::path &path : { std::fs::path(PYTHON_LIBRARY_PATH), std::fs::path(PYTHON_LIBRARY_PATH).filename() }) {
pythonLibrary = hex::script::loader::loadLibrary(path.c_str());
if (pythonLibrary != nullptr) {
break;
}
}
if (pythonLibrary == nullptr)
return false;
INIT_FUNCTION(PyPreConfig_InitPythonConfig);
INIT_FUNCTION(Py_PreInitialize);
INIT_FUNCTION(Py_Initialize);
INIT_FUNCTION(Py_Finalize);
INIT_FUNCTION(PySys_GetObject);
INIT_FUNCTION(PyEval_SaveThread);
INIT_FUNCTION(PyEval_RestoreThread);
INIT_FUNCTION(PyEval_GetBuiltins);
INIT_FUNCTION(PyRun_StringFlags);
INIT_FUNCTION(PyErr_Fetch);
INIT_FUNCTION(PyErr_NormalizeException);
INIT_FUNCTION(PyErr_Clear);
INIT_FUNCTION(PyStatus_Exception);
INIT_FUNCTION(PyThreadState_Clear);
INIT_FUNCTION(PyThreadState_DeleteCurrent);
INIT_FUNCTION(PyThreadState_New);
INIT_FUNCTION(PyInterpreterState_Get);
INIT_FUNCTION(PyUnicode_FromString);
INIT_FUNCTION(PyUnicode_Join);
INIT_FUNCTION(PyUnicode_AsUTF8);
INIT_FUNCTION(PyTuple_New);
INIT_FUNCTION(PyTuple_SetItem);
INIT_FUNCTION(PyImport_Import);
INIT_FUNCTION(PyImport_AddModule);
INIT_FUNCTION(PyModule_New);
INIT_FUNCTION(PyModule_AddStringConstant);
INIT_FUNCTION(PyModule_GetDict);
INIT_FUNCTION(PyDict_GetItemString);
INIT_FUNCTION(PyDict_SetItemString);
INIT_FUNCTION(PyList_Append);
INIT_FUNCTION(PyCallable_Check);
INIT_FUNCTION(PyObject_CallObject);
INIT_FUNCTION(PyObject_GetAttrString);
INIT_FUNCTION(PyObject_HasAttrString);
INIT_FUNCTION(_Py_Dealloc);
return true;
}

View File

@@ -0,0 +1,168 @@
#include <loaders/python/python_loader.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/utils/string.hpp>
#include <hex/helpers/utils.hpp>
#include <romfs/romfs.hpp>
#if defined(__declspec)
#undef __declspec
#define __declspec(x)
#endif
#include <Python.h>
bool initPythonLoader();
namespace hex::script::loader {
static PyInterpreterState *mainThreadState;
bool PythonLoader::initialize() {
if (!initPythonLoader())
return false;
PyPreConfig preconfig;
PyPreConfig_InitPythonConfig(&preconfig);
preconfig.utf8_mode = 1;
auto status = Py_PreInitialize(&preconfig);
if (PyStatus_Exception(status)) {
return false;
}
Py_Initialize();
mainThreadState = PyInterpreterState_Get();
PyEval_SaveThread();
return true;
}
namespace {
std::string getCurrentTraceback() {
PyObject *ptype, *pvalue, *ptraceback;
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
PyObject *pModuleName = PyUnicode_FromString("traceback");
PyObject *pModule = PyImport_Import(pModuleName);
Py_DECREF(pModuleName);
if (pModule != nullptr) {
PyObject *pDict = PyModule_GetDict(pModule);
PyObject *pFunc = PyDict_GetItemString(pDict, "format_exception");
if (pFunc && PyCallable_Check(pFunc)) {
PyObject *pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, ptype);
PyTuple_SetItem(pArgs, 1, pvalue);
PyTuple_SetItem(pArgs, 2, ptraceback);
PyObject *pResult = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pResult != NULL) {
const char *errorMessage = PyUnicode_AsUTF8(PyUnicode_Join(PyUnicode_FromString(""), pResult));
Py_DECREF(pResult);
Py_DECREF(pModule);
return errorMessage;
}
}
Py_DECREF(pModule);
}
PyErr_Clear();
return "";
}
void populateModule(PyObject *pyModule, const std::string &sourceCode) {
PyModule_AddStringConstant(pyModule, "__file__", "");
PyObject *localDict = PyModule_GetDict(pyModule);
PyObject *builtins = PyEval_GetBuiltins();
PyDict_SetItemString(localDict, "__builtins__", builtins);
PyErr_Clear();
PyObject *pyValue = PyRun_String(sourceCode.c_str(), Py_file_input, localDict, localDict);
if (pyValue != nullptr) {
Py_DECREF(pyValue);
} else {
log::error("{}", getCurrentTraceback());
}
}
}
bool PythonLoader::loadAll() {
this->clearScripts();
for (const auto &imhexPath : hex::fs::getDefaultPaths(hex::fs::ImHexPath::Scripts)) {
auto directoryPath = imhexPath / "custom" / "python";
if (!wolv::io::fs::exists(directoryPath))
wolv::io::fs::createDirectories(directoryPath);
if (!wolv::io::fs::exists(directoryPath))
continue;
for (const auto &entry : std::fs::directory_iterator(directoryPath)) {
if (!entry.is_directory())
continue;
const auto &scriptFolder = entry.path();
const auto scriptPath = scriptFolder / "main.py";
if (!std::fs::exists(scriptPath))
continue;
auto scriptPathString = wolv::util::toUTF8String(scriptPath);
wolv::io::File scriptFile(scriptPathString, wolv::io::File::Mode::Read);
if (!scriptFile.isValid())
continue;
PyThreadState* ts = PyThreadState_New(mainThreadState);
PyEval_RestoreThread(ts);
ON_SCOPE_EXIT {
PyThreadState_Clear(ts);
PyThreadState_DeleteCurrent();
};
PyObject* sysPath = PySys_GetObject("path");
PyList_Append(sysPath, PyUnicode_FromString(wolv::util::toUTF8String(scriptFolder).c_str()));
PyObject *imhexInternalModule = PyImport_AddModule("__imhex_internal__");
PyModule_AddStringConstant(imhexInternalModule, "script_loader_handle", hex::format("{}", reinterpret_cast<intptr_t>(hex::getContainingModule((void*)&getCurrentTraceback))).c_str());
PyObject *mainModule = PyModule_New(scriptPathString.c_str());
populateModule(mainModule, scriptFile.readString());
if (PyObject_HasAttrString(mainModule, "main")) {
this->addScript(entry.path().stem().string(), [mainModule] {
PyThreadState* ts = PyThreadState_New(mainThreadState);
PyEval_RestoreThread(ts);
ON_SCOPE_EXIT {
PyThreadState_Clear(ts);
PyThreadState_DeleteCurrent();
};
auto mainFunction = PyObject_GetAttrString(mainModule, "main");
PyObject_CallObject(mainFunction, nullptr);
Py_DECREF(mainFunction);
});
}
m_loadedModules.push_back(mainModule);
}
}
return true;
}
}

View File

@@ -7,6 +7,7 @@
#include <hex/ui/imgui_imhex_extensions.h> #include <hex/ui/imgui_imhex_extensions.h>
#include <loaders/dotnet/dotnet_loader.hpp> #include <loaders/dotnet/dotnet_loader.hpp>
#include <loaders/python/python_loader.hpp>
#include <romfs/romfs.hpp> #include <romfs/romfs.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@@ -15,8 +16,11 @@ using namespace hex;
using namespace hex::script::loader; using namespace hex::script::loader;
using ScriptLoaders = std::tuple< using ScriptLoaders = std::tuple<
#if defined(DOTNET_PLUGINS) #if defined(IMHEX_DOTNET_SCRIPT_SUPPORT)
DotNetLoader DotNetLoader,
#endif
#if defined(IMHEX_PYTHON_SCRIPT_SUPPORT)
PythonLoader
#endif #endif
>; >;
@@ -69,10 +73,10 @@ namespace {
} }
void addScriptsMenu() { void addScriptsMenu() {
static std::vector<const Script*> scripts;
static TaskHolder runnerTask, updaterTask; static TaskHolder runnerTask, updaterTask;
hex::ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.extras" }, 5000, [] { hex::ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.extras" }, 5000, [] {
static bool menuJustOpened = true; static bool menuJustOpened = true;
static std::vector<const Script*> scripts;
if (ImGui::BeginMenu("hex.script_loader.menu.run_script"_lang)) { if (ImGui::BeginMenu("hex.script_loader.menu.run_script"_lang)) {
if (menuJustOpened) { if (menuJustOpened) {
@@ -91,9 +95,9 @@ namespace {
} }
for (const auto &script : scripts) { for (const auto &script : scripts) {
const auto &[name, entryPoint] = *script; const auto &[name, entryPoint, loader] = *script;
if (ImGui::MenuItem(name.c_str())) { if (ImGui::MenuItem(name.c_str(), loader->getTypeName().c_str())) {
runnerTask = TaskManager::createTask("Running script...", TaskManager::NoProgress, [entryPoint](auto&) { runnerTask = TaskManager::createTask("Running script...", TaskManager::NoProgress, [entryPoint](auto&) {
entryPoint(); entryPoint();
}); });
@@ -107,10 +111,19 @@ namespace {
}, [] { }, [] {
return !runnerTask.isRunning(); return !runnerTask.isRunning();
}); });
updaterTask = TaskManager::createBackgroundTask("Updating Scripts...", [] (auto&) {
scripts = loadAllScripts();
});
} }
} }
IMHEX_PLUGIN_FEATURES() {
{ ".NET", IMHEX_FEATURE_ENABLED(DOTNET) },
{ "Python", IMHEX_FEATURE_ENABLED(PYTHON) },
};
IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") { IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") {
hex::log::debug("Using romfs: '{}'", romfs::name()); hex::log::debug("Using romfs: '{}'", romfs::name());
for (auto &path : romfs::list("lang")) for (auto &path : romfs::list("lang"))
@@ -119,5 +132,4 @@ IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") {
if (initializeAllLoaders()) { if (initializeAllLoaders()) {
addScriptsMenu(); addScriptsMenu();
} }
} }

View File

@@ -1,44 +0,0 @@
#include <script_api.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/providers/provider.hpp>
#define VERSION V1
SCRIPT_API(void readMemory, u64 address, size_t size, void *buffer) {
auto provider = hex::ImHexApi::Provider::get();
if (provider == nullptr) {
return;
}
provider->read(address, buffer, size);
}
SCRIPT_API(void writeMemory, u64 address, size_t size, const void *buffer) {
auto provider = hex::ImHexApi::Provider::get();
if (provider == nullptr) {
return;
}
provider->write(address, buffer, size);
}
SCRIPT_API(bool getSelection, u64 *start, u64 *end) {
if (start == nullptr || end == nullptr)
return false;
if (!hex::ImHexApi::Provider::isValid())
return false;
if (!hex::ImHexApi::HexEditor::isSelectionValid())
return false;
auto selection = hex::ImHexApi::HexEditor::getSelection();
*start = selection->getStartAddress();
*end = selection->getEndAddress();
return true;
}

View File

@@ -0,0 +1,14 @@
project(c_api)
add_library(c_api OBJECT
source/script_api/v1/bookmarks.cpp
source/script_api/v1/logger.cpp
source/script_api/v1/mem.cpp
source/script_api/v1/ui.cpp
)
target_include_directories(c_api PUBLIC
include
)
target_link_libraries(c_api PRIVATE libimhex ui)
target_compile_definitions(c_api PRIVATE IMHEX_PROJECT_NAME="Script")

View File

@@ -0,0 +1,34 @@
#include <script_api.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/helpers/logger.hpp>
#define VERSION V1
SCRIPT_API(void logPrint, const char *message) {
hex::log::print("{}", message);
}
SCRIPT_API(void logPrintln, const char *message) {
hex::log::println("{}", message);
}
SCRIPT_API(void logDebug, const char *message) {
hex::log::debug("{}", message);
}
SCRIPT_API(void logInfo, const char *message) {
hex::log::info("{}", message);
}
SCRIPT_API(void logWarn, const char *message) {
hex::log::warn("{}", message);
}
SCRIPT_API(void logError, const char *message) {
hex::log::error("{}", message);
}
SCRIPT_API(void logFatal, const char *message) {
hex::log::fatal("{}", message);
}

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