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_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
..
- 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
uses: actions/upload-artifact@v4
with:
if-no-files-found: error
name: macOS DMG${{matrix.suffix}} x86_64
path: build/*.dmg
path: ./*.dmg
macos-arm64-build:
runs-on: ubuntu-22.04
@@ -287,15 +311,15 @@ jobs:
path: out
- name: 🗑️ Delete artifact
uses: geekyeggo/delete-artifact@v4
uses: geekyeggo/delete-artifact@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: macos_arm64_intermediate
- name: ✒️ Fix Signature
run: |
set -x
cd out
mv imhex.app ImHex.app
codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app
@@ -627,11 +651,11 @@ jobs:
- name: ✒️ Modify spec file
run: |
sed -i \
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/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 '/BuildRequires: cmake/a BuildRequires: git-core' \
-e '/%files/a %{_datadir}/%{name}/' \
-e 's/Version: VERSION$/Version: ${{env.IMHEX_VERSION}}/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 '/BuildRequires: cmake/a BuildRequires: git-core' \
-e '/%files/a %{_datadir}/%{name}/' \
$GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec
- name: 📜 Fix ccache on EL9

View File

@@ -90,7 +90,7 @@ jobs:
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
- name: ⬇️ Download artifacts from latest workflow
uses: dawidd6/action-download-artifact@v2
uses: dawidd6/action-download-artifact@v3
with:
github_token: ${{secrets.GITHUB_TOKEN}}
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
- name: ⬆️ Upload everything to release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
files: '*'

View File

@@ -53,6 +53,7 @@ jobs:
cd build
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DIMHEX_ENABLE_UNIT_TESTS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-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"
)
# Add ImHex sources
add_custom_target(imhex_all ALL)
# Make sure project is configured correctly
setDefaultBuiltTypeIfUnset()
detectBadClone()
@@ -56,17 +59,19 @@ configurePackingResources()
setUninstallTarget()
addBundledLibraries()
# Add ImHex sources
add_custom_target(imhex_all ALL)
add_subdirectory(lib/libimhex)
add_subdirectory(main)
addPluginDirectories()
# Add unit tests
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
if (IMHEX_ENABLE_UNIT_TESTS)
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
endif ()
# Configure more resources that will be added to the install package
createPackage()
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()
macro(configurePackingResources)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if (WIN32)
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(APPLICATION_TYPE WIN32)
@@ -145,9 +147,7 @@ macro(configurePackingResources)
endif()
endmacro()
macro(createPackage)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
macro(addPluginDirectories)
file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}")
@@ -178,9 +178,9 @@ macro(createPackage)
add_dependencies(imhex_all ${plugin})
endif ()
endforeach()
endmacro()
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
macro(createPackage)
if (WIN32)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".")
@@ -217,7 +217,6 @@ macro(createPackage)
endforeach()
]])
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./")
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}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./share/imhex")
# install AppStream file
@@ -249,17 +247,15 @@ macro(createPackage)
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main> || 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-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(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
postprocess_bundle(imhex_all main)
@@ -268,15 +264,17 @@ macro(createPackage)
set(CPACK_GENERATOR "DragNDrop")
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)
message(STATUS "Resigning bundle...")
find_program(CODESIGN_PATH codesign)
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()
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
endif()
else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -660,20 +658,20 @@ macro(addBundledLibraries)
if (${Backtrace_FOUND})
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_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
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(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
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()
endmacro()
function(enableUnityBuild TARGET)
@@ -739,7 +737,7 @@ function(generateSDKDirectory)
if (WIN32)
set(SDK_PATH "./sdk")
elseif (APPLE)
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk")
set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
else()
set(SDK_PATH "share/imhex/sdk")
endif()

View File

@@ -14,12 +14,11 @@ if(CMAKE_GENERATOR)
# Being called as include(PostprocessBundle), so define a helper function.
set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
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}>/../.."
-DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_CERTIFICATE_ID}"
-DEXTRA_BUNDLE_LIBRARY_PATHS="${EXTRA_BUNDLE_LIBRARY_PATHS}"
-P "${_POSTPROCESS_BUNDLE_MODULE_LOCATION}"
)
install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})")
install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})")
install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})")
install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})
endfunction()
return()
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
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
# 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}")
# 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:
| Wanted build | Dockerfile path |
|--------------|-----------------------------|
| MacOS M1 | dist/macOS/arm64.Dockerfile |
| AppImage | dist/appimage/Dockerfile |
| Web version | dist/web/Dockerfile |
| Wanted build | Dockerfile path | Target |
|--------------|-----------------------------|--------|
| MacOS M1 | dist/macOS/arm64.Dockerfile | - |
| AppImage | dist/appimage/Dockerfile | - |
| Web version | dist/web/Dockerfile | raw |
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
- `-f <path>` is to specify the Dockerfile path
- `--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
- `--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" \
-DCMAKE_BUILD_TYPE=Release \
-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

View File

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

View File

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

View File

@@ -170,4 +170,4 @@ EOF
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
Version: 1.26.2
Version: VERSION
Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers
@@ -95,10 +95,6 @@ CXXFLAGS+=" -std=gnu++2b"
%set_build_flags
CXXFLAGS+=" -std=gnu++2b"
%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

View File

@@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
source/helpers/tar.cpp
source/helpers/debugging.cpp
source/test/tests.cpp
source/providers/provider.cpp
source/providers/memory_provider.cpp
source/providers/undo/stack.cpp
@@ -145,4 +147,7 @@ target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIE
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);
using ProviderCreationFunction = std::unique_ptr<prv::Provider>(*)();
using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
const std::vector<std::string>& getEntries();

View File

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

View File

@@ -664,6 +664,12 @@ namespace hex {
*/
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
* @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

View File

@@ -15,8 +15,10 @@ struct ImGuiContext;
namespace hex {
struct SubCommand {
std::string commandKey;
std::string commandDesc;
std::string commandLong;
std::string commandShort;
std::string commandDescription;
std::function<void(const std::vector<std::string>&)> callback;
};
@@ -104,6 +106,10 @@ namespace hex {
static bool load();
static bool load(const std::fs::path &pluginFolder);
static bool loadLibraries();
static bool loadLibraries(const std::fs::path &libraryFolder);
static void unload();
static void reload();
static void initializeNewPlugins();
@@ -111,6 +117,7 @@ namespace hex {
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::vector<std::fs::path>& getPluginPaths();
static const std::vector<std::fs::path>& getPluginLoadPaths();
@@ -121,6 +128,7 @@ namespace hex {
static std::list<Plugin>& getPluginsMutable();
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 {
std::string layout;
std::fs::path path;
bool builtin;
};
static void createWorkspace(const std::string &name, const std::string &layout = "");
static void switchWorkspace(const std::string &name);
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& getCurrentWorkspace() { return s_currentWorkspace; }
static void reset();
static void reload();
static void process();
@@ -32,7 +36,7 @@ namespace hex {
WorkspaceManager() = default;
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 <wolv/io/file.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::log {
@@ -21,6 +22,7 @@ namespace hex::log {
[[nodiscard]] std::recursive_mutex& getLoggerMutex();
[[nodiscard]] bool isLoggingSuspended();
[[nodiscard]] bool isDebugLoggingEnabled();
struct LogEntry {
std::string project;
@@ -65,13 +67,14 @@ namespace hex::log {
void suspendLogging();
void resumeLogging();
void enableDebugLogging();
[[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...);
#else
} else {
impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
#endif
}
}
[[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...);
}
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::getLoggerMutex());

View File

@@ -331,4 +331,14 @@ namespace hex {
[[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(); }
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; }

View File

@@ -161,13 +161,13 @@ namespace hex::prv {
*/
[[nodiscard]] virtual std::string getName() const = 0;
void resize(u64 newSize);
bool resize(u64 newSize);
void insert(u64 offset, u64 size);
void remove(u64 offset, u64 size);
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); }
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); }
virtual void insertRaw(u64 offset, u64 size);
virtual void removeRaw(u64 offset, u64 size);
virtual void save();
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 apply(const Stack &otherStack);
void reapply();
[[nodiscard]] bool canUndo() const;
[[nodiscard]] bool canRedo() const;

View File

@@ -5,6 +5,7 @@
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/api/plugin_manager.hpp>
#include <wolv/utils/preproc.hpp>
@@ -28,16 +29,20 @@
} \
} while (0)
#define INIT_PLUGIN(name) \
if (!hex::test::initPluginImpl(name)) TEST_FAIL();
namespace hex::test {
using Function = int(*)();
struct Test {
std::function<int()> function;
Function function;
bool shouldFail;
};
class Tests {
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({
name, {func, shouldFail}
});
@@ -50,7 +55,7 @@ namespace hex::test {
}
private:
static inline std::map<std::string, Test> s_tests;
static std::map<std::string, Test> s_tests;
};
template<class F>
@@ -86,4 +91,5 @@ namespace hex::test {
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;
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;
@@ -599,6 +603,11 @@ namespace hex {
return impl::s_initialWindowProperties;
}
void* getLibImHexModuleHandle() {
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
}
const std::map<std::string, std::string>& getInitArguments() {
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;
}
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() {
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
view->getWindowOpenState() = false;

View File

@@ -18,24 +18,50 @@
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) {
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
#if defined(OS_WINDOWS)
m_handle = uintptr_t(LoadLibraryW(path.c_str()));
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
m_handle = loadLibrary(path);
if (m_handle == 0)
return;
const auto fileName = path.stem().string();
@@ -89,15 +115,7 @@ namespace hex {
log::info("Trying to unload plugin '{}'", getPluginName());
}
#if defined(OS_WINDOWS)
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
unloadLibrary(m_handle, m_path);
}
bool Plugin::initializePlugin() const {
@@ -284,6 +302,35 @@ namespace hex {
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() {
for (const auto &plugin : getPlugins()) {
if (!plugin.isLoaded())
@@ -304,6 +351,11 @@ namespace hex {
plugins.pop_back();
}
while (!s_loadedLibraries->empty()) {
unloadLibrary(s_loadedLibraries->back(), "");
s_loadedLibraries->pop_back();
}
getPluginsMutable() = std::move(savedPlugins);
}
@@ -321,6 +373,15 @@ namespace hex {
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() {
return s_pluginPaths;
}
@@ -335,5 +396,4 @@ namespace hex {
});
}
}

View File

@@ -9,22 +9,28 @@
#include <nlohmann/json.hpp>
#include <imgui.h>
#include <wolv/utils/string.hpp>
namespace hex {
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_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) {
s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {}
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {},
.builtin = false
}).first;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
if (exportToFile(path / (name + ".hexws")))
for (const auto &workspaceFolder : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
const auto workspacePath = workspaceFolder / (name + ".hexws");
if (exportToFile(workspacePath)) {
s_currentWorkspace->second.path = workspacePath;
break;
}
}
}
@@ -37,6 +43,9 @@ namespace hex {
}
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);
if (!file.isValid()) {
log::error("Failed to load workspace from file '{}'", path.string());
@@ -50,10 +59,12 @@ namespace hex {
const std::string name = json["name"];
std::string layout = json["layout"];
const bool builtin = json.value("builtin", false);
(*s_workspaces)[name] = Workspace {
.layout = std::move(layout),
.path = path
.path = path,
.builtin = builtin
};
} catch (nlohmann::json::exception &e) {
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 (s_currentWorkspace == s_workspaces->end())
return false;
@@ -80,22 +91,46 @@ namespace hex {
nlohmann::json json;
json["name"] = workspaceName;
json["layout"] = LayoutManager::saveToString();
json["builtin"] = builtin;
file.writeString(json.dump(4));
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() {
if (s_previousWorkspace != s_currentWorkspace) {
log::info("Updating workspace");
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();
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
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();
}
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/api/event_manager.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/fmt.hpp>
@@ -20,6 +22,7 @@ namespace hex::log {
bool s_colorOutputEnabled = false;
std::recursive_mutex s_loggerMutex;
bool s_loggingSuspended = false;
bool s_debugLoggingEnabled = false;
}
@@ -31,6 +34,10 @@ namespace hex::log {
s_loggingSuspended = false;
}
void enableDebugLogging() {
s_debugLoggingEnabled = true;
}
namespace impl {
std::recursive_mutex& getLoggerMutex() {
@@ -41,6 +48,14 @@ namespace hex::log {
return s_loggingSuspended;
}
bool isDebugLoggingEnabled() {
#if defined(DEBUG)
return true;
#else
return s_debugLoggingEnabled;
#endif
}
FILE *getDestination() {
if (s_loggerFile.isValid())
return s_loggerFile.getHandle();

View File

@@ -16,9 +16,11 @@
#include <wolv/utils/guards.hpp>
#elif defined(OS_LINUX)
#include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_linux.hpp>
#elif defined(OS_MACOS)
#include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_macos.hpp>
#elif defined(OS_WEB)
#include "emscripten.h"
@@ -784,4 +786,24 @@ namespace hex {
#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);
}
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();
if (difference > 0)
@@ -83,6 +87,7 @@ namespace hex::prv {
EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);
this->markDirty();
return true;
}
void Provider::insert(u64 offset, u64 size) {
@@ -97,6 +102,52 @@ namespace hex::prv {
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 {
for (auto &overlay : m_overlays) {
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) {

View File

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

View File

@@ -6,9 +6,10 @@ set(CMAKE_CXX_STANDARD 17)
add_library(imgui_all_includes INTERFACE)
add_subdirectory(imgui)
add_subdirectory(cimgui)
add_subdirectory(implot)
add_subdirectory(imnodes)
add_subdirectory(custom)
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;
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];
if (aGlyph.mMultiLineComment)
return mPalette[(int)PaletteIndex::MultiLineComment];
if (aGlyph.mDeactivated && !aGlyph.mPreprocessor)
if (aGlyph.mDeactivated)
return mPalette[(int)PaletteIndex::PreprocessorDeactivated];
auto const color = mPalette[(int)aGlyph.mColorIndex];
if (aGlyph.mPreprocessor) {
@@ -619,10 +619,20 @@ ImU32 TextEditor::GetGlyphColor(const Glyph &aGlyph) const {
}
void TextEditor::HandleKeyboardInputs() {
ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift;
auto ctrl = io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl;
auto alt = io.ConfigMacOSXBehaviors ? io.KeyCtrl : io.KeyAlt;
ImGuiIO &io = ImGui::GetIO();
auto shift = io.KeyShift;
auto left = ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow));
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::IsWindowHovered())
@@ -638,25 +648,25 @@ void TextEditor::HandleKeyboardInputs() {
Undo();
else if (!IsReadOnly() && ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Y)))
Redo();
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_UpArrow)))
else if (!ctrl && !alt && up)
MoveUp(1, shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_DownArrow)))
else if (!ctrl && !alt && down)
MoveDown(1, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_LeftArrow)))
else if (!alt && left)
MoveLeft(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_RightArrow)))
else if (!alt && right)
MoveRight(1, shift, ctrl);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageUp)))
else if (!alt && pageUp)
MoveUp(GetPageSize() - 4, shift);
else if (!alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_PageDown)))
else if (!alt && pageDown)
MoveDown(GetPageSize() - 4, shift);
else if (!alt && ctrl && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
else if (!alt && top)
MoveTop(shift);
else if (ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
else if (!alt && bottom)
MoveBottom(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Home)))
else if (!ctrl && !alt && home)
MoveHome(shift);
else if (!ctrl && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_End)))
else if (!ctrl && !alt && end)
MoveEnd(shift);
else if (!IsReadOnly() && !ctrl && !shift && !alt && ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Delete)))
Delete();
@@ -722,7 +732,7 @@ void TextEditor::HandleKeyboardInputs() {
void TextEditor::HandleMouseInputs() {
ImGuiIO &io = ImGui::GetIO();
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;
if (ImGui::IsWindowHovered()) {
@@ -905,7 +915,10 @@ void TextEditor::Render() {
}
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)
if (!HasSelection()) {
@@ -2505,6 +2518,7 @@ void TextEditor::ColorizeInternal() {
auto firstChar = true; // there is no other non-whitespace characters in the line before
auto currentLine = 0;
auto currentIndex = 0;
auto commentLength = 0;
auto &startStr = mLanguageDefinition.mCommentStart;
auto &singleStartStr = mLanguageDefinition.mSingleLineComment;
auto &docStartStr = mLanguageDefinition.mDocComment;
@@ -2516,6 +2530,14 @@ void TextEditor::ColorizeInternal() {
while (currentLine < endLine || currentIndex < endIndex) {
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) {
withinSingleLineComment = false;
withinPreproc = false;
@@ -2532,24 +2554,12 @@ void TextEditor::ColorizeInternal() {
bool inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
if (withinString) {
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
if (c == '\"') {
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;
}
setGlyphFlags(currentIndex);
if (c == '\\') {
currentIndex++;
setGlyphFlags(currentIndex);
} else if (c == '\"')
withinString = false;
} else {
if (firstChar && c == mLanguageDefinition.mPreprocChar) {
withinPreproc = true;
@@ -2594,7 +2604,6 @@ void TextEditor::ColorizeInternal() {
}
if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) != mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet);
} else
ifDefs.push_back(false);
@@ -2608,7 +2617,6 @@ void TextEditor::ColorizeInternal() {
}
if (!withinNotDef) {
bool isConditionMet = std::find(mDefines.begin(),mDefines.end(),identifier) == mDefines.end();
withinNotDef = !isConditionMet;
ifDefs.push_back(isConditionMet);
} else
ifDefs.push_back(false);
@@ -2616,20 +2624,17 @@ void TextEditor::ColorizeInternal() {
}
} else {
if (directive == "endif") {
if (ifDefs.size() > 1)
if (ifDefs.size() > 1) {
ifDefs.pop_back();
withinNotDef = !ifDefs.back();
withinNotDef = !ifDefs.back();
}
}
}
}
if (c == '\"') {
withinString = true;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDeactivated = withinNotDef;
setGlyphFlags(currentIndex);
} else {
auto pred = [](const char &a, const Glyph &b) { return a == b.mChar; };
@@ -2653,29 +2658,30 @@ void TextEditor::ColorizeInternal() {
if (isGlobalDocComment || isDocComment || isComment) {
commentStartLine = currentLine;
commentStartIndex = currentIndex;
if (isGlobalDocComment)
if (isGlobalDocComment) {
withinGlobalDocComment = true;
else if (isDocComment)
commentLength = 3;
} else if (isDocComment) {
withinDocComment = true;
else
commentLength = 3;
} else {
withinComment = true;
commentLength = 2;
}
}
}
inComment = (commentStartLine < currentLine || (commentStartLine == currentLine && commentStartIndex <= currentIndex));
}
line[currentIndex].mGlobalDocComment = withinGlobalDocComment;
line[currentIndex].mDocComment = withinDocComment;
line[currentIndex].mMultiLineComment = withinComment;
line[currentIndex].mComment = withinSingleLineComment;
line[currentIndex].mDeactivated = withinNotDef;
setGlyphFlags(currentIndex);
auto &endStr = mLanguageDefinition.mCommentEnd;
if (compareBack(endStr, line)) {
withinComment = false;
withinDocComment = false;
withinGlobalDocComment = false;
commentStartLine = endLine;
commentStartIndex = endIndex;
if (compareBack(endStr, line) && ((commentStartLine != currentLine) || (commentStartIndex + commentLength < currentIndex))) {
withinComment = false;
withinDocComment = false;
withinGlobalDocComment = false;
commentStartLine = endLine;
commentStartIndex = endIndex;
commentLength = 0;
}
}
}
@@ -2684,6 +2690,7 @@ void TextEditor::ColorizeInternal() {
currentIndex += UTF8CharLength(c);
if (currentIndex >= (int)line.size()) {
withinNotDef = !ifDefs.back();
currentIndex = 0;
++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() {
for (const auto &stackFrame : stacktrace::getStackTrace()) {
if (stackFrame.line == 0)
log::fatal(" {}", stackFrame.function);
log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
else
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
PluginManager::loadLibraries();
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Plugins)) {
PluginManager::load(dir);
}

View File

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

View File

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

View File

@@ -186,12 +186,11 @@ namespace hex {
// Determine if the application should be in long sleep mode
bool shouldLongSleep = !m_unlockFrameRate;
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter
static i32 lockTimeout = 0;
static double lockTimeout = 0;
if (!shouldLongSleep) {
lockTimeout = m_lastFrameTime * 10'000;
lockTimeout = 0.05;
} else if (lockTimeout > 0) {
lockTimeout -= 1;
lockTimeout -= m_lastFrameTime;
}
if (shouldLongSleep && lockTimeout > 0)
@@ -236,14 +235,28 @@ namespace hex {
// 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
const auto targetFPS = ImHexApi::System::getTargetFPS();
if (targetFPS < 15) {
glfwSwapInterval(1);
} else if (targetFPS > 200) {
glfwSwapInterval(0);
auto targetFPS = ImHexApi::System::getTargetFPS();
if (targetFPS >= 200) {
// Let it rip
} 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) {
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 targetFrameTime = 1.0 / targetFPS;
if (frameTime < targetFrameTime) {
@@ -717,7 +730,9 @@ namespace hex {
glfwSetWindowOpacity(m_window, 1.0F);
glfwMakeContextCurrent(m_window);
glfwSwapInterval(1);
// Disable VSync. Not like any graphics driver actually cares
glfwSwapInterval(0);
// Center window
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
@@ -969,6 +984,8 @@ namespace hex {
void Window::exitImGui() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImNodes::DestroyContext();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}

View File

@@ -2,11 +2,6 @@ cmake_minimum_required(VERSION 3.16)
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(
NAME
builtin
@@ -114,8 +109,6 @@ add_imhex_plugin(
source/content/views/view_achievements.cpp
source/content/views/view_highlight_rules.cpp
source/content/views/view_tutorials.cpp
source/content/helpers/notification.cpp
INCLUDES
include
@@ -128,6 +121,11 @@ add_imhex_plugin(
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)
target_link_libraries(builtin PRIVATE setupapi)
endif ()

View File

@@ -9,6 +9,7 @@ namespace hex::plugin::builtin {
void handleHelpCommand(const std::vector<std::string> &args);
void handlePluginsCommand(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);

View File

@@ -16,6 +16,7 @@
#include <imgui_internal.h>
#include <atomic>
#include <implot_internal.h>
#include <random>
#include <hex/ui/imgui_imhex_extensions.h>
@@ -747,6 +748,20 @@ namespace hex {
};
class DiagramByteTypesDistribution {
private:
struct AnnotationRegion {
UnlocalizedString unlocalizedName;
Region region;
ImColor color;
};
struct Tag {
UnlocalizedString unlocalizedName;
ImU64 value;
ImAxis axis;
ImGuiCol color;
};
public:
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"
};
for (u32 i = 0; i < Names.size(); i++) {
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
// but just a set of bytes, we won't be able to use the drag bar correctly.
if (updateHandle) {
@@ -871,6 +920,12 @@ namespace hex {
for (size_t i = 0; i < typeDist.size(); i++)
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_blockValueCounts = { 0 };
}
@@ -898,6 +953,10 @@ namespace hex {
m_handlePosition = filePosition;
}
void enableAnnotations(bool enabled) {
m_showAnnotations = enabled;
}
private:
static std::array<float, 12> calculateTypeDistribution(const std::array<ImU64, 256> &valueCounts, size_t blockSize) {
std::array<ImU64, 12> counts = {};
@@ -988,6 +1047,23 @@ namespace hex {
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:
// Variables used to store the parameters to process
@@ -1024,5 +1100,10 @@ namespace hex {
// Hold the result of the byte distribution analysis
std::array<std::vector<float>, 12> m_yBlockTypeDistributions, m_yBlockTypeDistributionsSampled;
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 {
public:
constexpr static u64 MaxMemoryFileSize = 128 * 1024 * 1024;
FileProvider() = default;
~FileProvider() override = default;
@@ -21,8 +23,6 @@ namespace hex::plugin::builtin {
[[nodiscard]] bool isSavable() const 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 writeRaw(u64 offset, const void *buffer, size_t size) override;
@@ -57,12 +57,18 @@ namespace hex::plugin::builtin {
private:
void convertToMemoryFile();
void handleFileChange();
protected:
std::fs::path m_path;
wolv::io::File m_file;
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;
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(); }
void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
void save() override;

View File

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

View File

@@ -14,6 +14,8 @@
#include <functional>
#include <TextEditor.h>
#include "popups/popup_file_chooser.hpp"
#include "hex/api/achievement_manager.hpp"
namespace pl::ptrn { class Pattern; }
@@ -170,7 +172,7 @@ namespace hex::plugin::builtin {
u32 color;
};
std::unique_ptr<pl::PatternLanguage> m_editorRuntime;
std::unique_ptr<pl::PatternLanguage> m_editorRuntime;
PerProvider<std::vector<std::fs::path>> m_possiblePatternFiles;
bool m_runAutomatically = false;
@@ -255,6 +257,39 @@ namespace hex::plugin::builtin {
void registerMenuItems();
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 appendVariable(const std::string &type);
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.bookmark": "Bookmark",
"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.popup.create": "Cannot export data. Failed to create file!",
"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.bookmark": "Bookmark",
"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.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",
@@ -402,6 +404,8 @@
"hex.builtin.provider.file.size": "Size",
"hex.builtin.provider.file.menu.open_file": "Open file externally",
"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.ip": "IP Address",
"hex.builtin.provider.gdb.name": "GDB Server <{0}:{1}>",
@@ -500,7 +504,7 @@
"hex.builtin.setting.toolbar.icons": "Toolbar Icons",
"hex.builtin.shortcut.next_provider": "Select next 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.tools.ascii_table": "ASCII table",
"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.lowest_entropy": "Lowest block entropy",
"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.sample_size": "Sample size",
"hex.builtin.information_section.relationship_analysis.brightness": "Brightness",

View File

@@ -9,11 +9,11 @@
#include <hex/helpers/magic.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/literals.hpp>
#include <hex/helpers/utils.hpp>
#include <romfs/romfs.hpp>
#include <hex/api/plugin_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/subcommands/subcommands.hpp>
#include <wolv/utils/string.hpp>
@@ -47,16 +47,30 @@ namespace hex::plugin::builtin {
"Available subcommands:\n"
);
size_t longestCommand = 0;
size_t longestLongCommand = 0, longestShortCommand = 0;
for (const auto &plugin : PluginManager::getPlugins()) {
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 &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]);
}
void handleVerboseCommand(const std::vector<std::string> &) {
hex::log::enableDebugLogging();
}
void handleHashCommand(const std::vector<std::string> &args) {
if (args.size() != 2) {
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(),
provider->getBaseAddress(), provider->getActualSize());
m_byteTypesDistribution.enableAnnotations(m_showAnnotations);
// Create a handle to the file
auto reader = prv::ProviderReader(provider);
reader.seek(region.getStartAddress());
@@ -194,6 +196,7 @@ namespace hex::plugin::builtin {
void drawSettings() override {
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 {
@@ -319,12 +322,14 @@ namespace hex::plugin::builtin {
void load(const nlohmann::json &data) override {
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 {
auto result = InformationSection::store();
result["block_size"] = m_inputChunkSize;
result["block_size"] = m_inputChunkSize;
result["annotations"] = m_showAnnotations;
return result;
}
@@ -341,6 +346,8 @@ namespace hex::plugin::builtin {
u64 m_lowestBlockEntropyAddress = 0x00;
double m_plainTextCharacterPercentage = -1.0;
bool m_showAnnotations = true;
DiagramByteDistribution m_byteDistribution;
DiagramByteTypesDistribution m_byteTypesDistribution;
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 {
static bool s_demoWindowOpen = false;
namespace {
bool noRunningTasks() {
@@ -187,7 +185,7 @@ namespace hex::plugin::builtin {
void exportSelectionToFile() {
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);
if (!outputFile.isValid()) {
TaskManager::doLater([] {
@@ -205,6 +203,7 @@ namespace hex::plugin::builtin {
provider->read(address, bytes.data(), bytes.size());
outputFile.writeVector(bytes);
task.update();
}
});
});
@@ -559,13 +558,6 @@ namespace hex::plugin::builtin {
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() {
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::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()) {
if (ImGui::MenuItem(hex::format("{}{}", name, shift ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) {
if (shift) {
wolv::io::fs::remove(path);
LayoutManager::reload();
if (ImGui::MenuItem(hex::format("{}{}", name, shiftPressed ? " " ICON_VS_X : "").c_str(), "", false, ImHexApi::Provider::isValid())) {
if (shiftPressed) {
LayoutManager::removeLayout(name);
break;
} else {
LayoutManager::load(path);
}
@@ -615,6 +605,8 @@ namespace hex::plugin::builtin {
}
static void createWorkspaceMenu() {
ContentRegistry::Interface::registerMainMenuItem("hex.builtin.menu.workspace", 4000);
createLayoutMenu();
ContentRegistry::Interface::addMenuItemSeparator({ "hex.builtin.menu.workspace" }, 3000);
@@ -627,11 +619,19 @@ namespace hex::plugin::builtin {
ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.workspace" }, 3200, [] {
const auto &workspaces = WorkspaceManager::getWorkspaces();
bool shiftPressed = ImGui::GetIO().KeyShift;
for (auto it = workspaces.begin(); it != workspaces.end(); ++it) {
const auto &[name, workspace] = *it;
if (ImGui::MenuItem(name.c_str(), "", it == WorkspaceManager::getCurrentWorkspace(), ImHexApi::Provider::isValid())) {
WorkspaceManager::switchWorkspace(name);
bool canRemove = shiftPressed && !workspace.builtin;
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();
createExtrasMenu();
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/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <content/helpers/notification.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
@@ -23,26 +22,29 @@ namespace hex::plugin::builtin {
bool load(const std::fs::path &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,
wolv::util::toUTF8String(filePath)
)));
return false;
}
Tar tar(filePath, Tar::Mode::Read);
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,
tar.getOpenErrorString()
)));
return false;
}
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)
));
return false;
}
@@ -50,9 +52,10 @@ namespace hex::plugin::builtin {
const auto metadataContent = tar.readVector(MetadataPath);
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)
));
return false;
}
}
@@ -176,4 +179,4 @@ namespace hex::plugin::builtin {
void registerProjectHandlers() {
hex::ProjectFile::setProjectFunctions(load, store);
}
}
}

View File

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

View File

@@ -16,6 +16,7 @@
#include <nlohmann/json.hpp>
#include <cstring>
#include <popups/popup_question.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
@@ -42,25 +43,38 @@ namespace hex::plugin::builtin {
}
bool FileProvider::isSavable() const {
return m_undoRedoStack.canUndo();
return m_loadedIntoMemory;
}
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0)
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) {
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
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() {
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)
FILETIME ft;
@@ -75,6 +89,9 @@ namespace hex::plugin::builtin {
}
#endif
if (m_loadedIntoMemory)
m_file.close();
Provider::save();
}
@@ -86,54 +103,14 @@ namespace hex::plugin::builtin {
}
void FileProvider::resizeRaw(u64 newSize) {
m_file.setSize(newSize);
if (m_loadedIntoMemory)
m_data.resize(newSize);
else
m_file.setSize(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 {
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;
}
void FileProvider::close() {
m_file.close();
m_data.clear();
s_openedFiles.erase(this);
m_changeTracker.stopTracking();
}
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);
}
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 {
if (m_name.empty())
return Lang("hex.builtin.provider.mem_file.unsaved");

View File

@@ -17,21 +17,15 @@
#include <toasts/toast_notification.hpp>
#include <csignal>
namespace hex::plugin::builtin {
void addTitleBarButtons() {
#if defined(DEBUG)
ContentRegistry::Interface::addTitleBarButton(ICON_VS_DEBUG, "hex.builtin.title_bar_button.debug_build", []{
if (ImGui::GetIO().KeyCtrl) {
// Explicitly trigger a segfault by writing to an invalid memory location
// 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();
if (ImGui::GetIO().KeyShift) {
RequestOpenPopup::post("DebugMenu");
} else {
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 void drawDragNDropOverlay() {
if (!s_drawDragDropOverlay)
@@ -99,6 +173,10 @@ namespace hex::plugin::builtin {
EventFrameEnd::subscribe(drawGlobalPopups);
EventFrameEnd::subscribe(drawDragNDropOverlay);
#if defined(DEBUG)
EventFrameEnd::subscribe(drawDebugPopup);
#endif
EventFileDragged::subscribe([](bool 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) {

View File

@@ -275,6 +275,7 @@ namespace hex::plugin::builtin {
constexpr static std::array Contributors = {
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 { "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 { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", 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_lastSelectedOccurrence = nullptr;
for (const auto &occurrence : m_foundOccurrences.get(provider))
m_occurrenceTree->insert({ occurrence.region.getStartAddress(), occurrence.region.getEndAddress() }, occurrence);
@@ -895,6 +896,7 @@ namespace hex::plugin::builtin {
m_foundOccurrences->clear();
m_sortedOccurrences->clear();
m_occurrenceTree->clear();
m_lastSelectedOccurrence = nullptr;
EventHighlightingChanged::post();
}
@@ -1007,7 +1009,11 @@ namespace hex::plugin::builtin {
ImGuiExt::TextFormatted("{}", value);
ImGui::SameLine();
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;
} else {
for (auto &occurrence : *m_sortedOccurrences)
@@ -1015,6 +1021,8 @@ namespace hex::plugin::builtin {
foundItem.selected = true;
ImHexApi::HexEditor::setSelection(foundItem.region.getStartAddress(), foundItem.region.getSize());
}
m_lastSelectedOccurrence = &foundItem;
}
drawContextMenu(foundItem, value);

View File

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

View File

@@ -3,13 +3,11 @@
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <pl/patterns/pattern.hpp>
#include <pl/core/preprocessor.hpp>
#include <pl/core/parser.hpp>
#include <pl/core/ast/ast_node_variable_decl.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp>
@@ -22,11 +20,9 @@
#include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h>
#include <popups/popup_file_chooser.hpp>
#include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <chrono>
#include <wolv/io/file.hpp>
@@ -34,6 +30,8 @@
#include <wolv/utils/guards.hpp>
#include <wolv/utils/lock.hpp>
#include <content/global_actions.hpp>
namespace hex::plugin::builtin {
using namespace hex::literals;
@@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
langDef.mAutoIndentation = true;
langDef.mPreprocChar = '#';
langDef.mGlobalDocComment = "/*!";
langDef.mDocComment = "/**";
langDef.mName = "Pattern Language";
initialized = true;
@@ -236,6 +237,14 @@ namespace hex::plugin::builtin {
bool clickedMenuFind = false;
bool clickedMenuReplace = false;
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();
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
m_textEditor.Cut();
@@ -306,6 +315,12 @@ namespace hex::plugin::builtin {
m_replaceMode = 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;
@@ -685,6 +700,8 @@ namespace hex::plugin::builtin {
if (altCPressed)
matchCase = !matchCase;
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
count = findReplaceHandler->GetMatches().size();
updateCount = true;
requestFocusFind = true;
}
@@ -1285,7 +1302,8 @@ namespace hex::plugin::builtin {
if (!m_lastCompileError->empty()) {
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)
m_textEditor.SetText(m_sourceCode.get(newProvider));
});
EventProviderClosed::subscribe(this, [this](prv::Provider *) {
if (ImHexApi::Provider::getProviders().empty()) {
else
m_textEditor.SetText("");
}
});
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() {
/* 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,
[this] {
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);
importPatternFile, ImHexApi::Provider::isValid);
/* 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,
[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] {
exportPatternFile, [this] {
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() {
const ImVec2 backdropSize = scaled({ 350, 350 });
ImGui::SetCursorPos((ImGui::GetContentRegionAvail() - backdropSize) / 2);
@@ -361,6 +377,9 @@ namespace hex::plugin::builtin {
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
if (ImGui::Begin("Welcome Screen", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) {
ImGui::BringWindowToDisplayBack(ImGui::GetCurrentWindowRead());
drawWelcomeScreenBackground();
if (s_simplifiedWelcomeScreen)
drawWelcomeScreenContentSimplified();
else

View File

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

View File

@@ -7,23 +7,9 @@
namespace hex::plugin::builtin {
void loadWorkspaces() {
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);
}
}
std::string currentWorkspace = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.curr_workspace", "Default");
WorkspaceManager::reload();
auto currentWorkspace = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.curr_workspace", "Default");
TaskManager::doLater([currentWorkspace] {
WorkspaceManager::switchWorkspace(currentWorkspace);
});

View File

@@ -58,21 +58,22 @@ namespace hex::plugin::builtin {
}
IMHEX_PLUGIN_SUBCOMMANDS() {
{ "help", "Print help about this command", hex::plugin::builtin::handleHelpCommand },
{ "version", "Print ImHex version", hex::plugin::builtin::handleVersionCommand },
{ "plugins", "Lists all plugins that have been installed", hex::plugin::builtin::handlePluginsCommand },
{ "language", "Changes the language ImHex uses", hex::plugin::builtin::handleLanguageCommand },
{ "help", "h", "Print help about this command", hex::plugin::builtin::handleHelpCommand },
{ "version", "", "Print ImHex version", hex::plugin::builtin::handleVersionCommand },
{ "plugins", "", "Lists all plugins that have been installed", hex::plugin::builtin::handlePluginsCommand },
{ "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 },
{ "hash", "Calculate the hash of a file", hex::plugin::builtin::handleHashCommand },
{ "encode", "Encode a string", hex::plugin::builtin::handleEncodeCommand },
{ "decode", "Decode a string", hex::plugin::builtin::handleDecodeCommand },
{ "magic", "Identify file types", hex::plugin::builtin::handleMagicCommand },
{ "pl", "Interact with the pattern language", hex::plugin::builtin::handlePatternLanguageCommand },
{ "hexdump", "Generate a hex dump of the provided file", hex::plugin::builtin::handleHexdumpCommand },
{ "demangle", "Demangle a mangled symbol", hex::plugin::builtin::handleDemangleCommand },
{ "calc", "", "Evaluate a mathematical expression", hex::plugin::builtin::handleCalcCommand },
{ "hash", "", "Calculate the hash of a file", hex::plugin::builtin::handleHashCommand },
{ "encode", "", "Encode a string", hex::plugin::builtin::handleEncodeCommand },
{ "decode", "", "Decode a string", hex::plugin::builtin::handleDecodeCommand },
{ "magic", "", "Identify file types", hex::plugin::builtin::handleMagicCommand },
{ "pl", "", "Interact with the pattern language", hex::plugin::builtin::handlePatternLanguageCommand },
{ "hexdump", "", "Generate a hex dump of the provided file", hex::plugin::builtin::handleHexdumpCommand },
{ "demangle", "", "Demangle a mangled symbol", hex::plugin::builtin::handleDemangleCommand },
};
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;
IMHEX_PLUGIN_FEATURES() {
{ "bzip2 Support", IMHEX_FEATURE_ENABLED(BZIP2) },
{ "zlib Support", IMHEX_FEATURE_ENABLED(ZLIB) },
{ "LZMA Support", IMHEX_FEATURE_ENABLED(LIBLZMA) },
{ "zstd Support", IMHEX_FEATURE_ENABLED(ZSTD) },
{ "bzip2", IMHEX_FEATURE_ENABLED(BZIP2) },
{ "zlib", IMHEX_FEATURE_ENABLED(ZLIB) },
{ "LZMA", IMHEX_FEATURE_ENABLED(LIBLZMA) },
{ "zstd", IMHEX_FEATURE_ENABLED(ZSTD) },
};
IMHEX_PLUGIN_SETUP("Decompressing", "WerWolv", "Support for decompressing data") {

View File

@@ -1,24 +1,11 @@
cmake_minimum_required(VERSION 3.16)
include(ImHexPlugin)
find_package(CoreClrEmbed)
add_imhex_plugin(
NAME
script_loader
SOURCES
source/plugin_script_loader.cpp
INCLUDES
include
LIBRARIES
fonts
ui
)
if (CoreClrEmbed_FOUND)
set(IMHEX_DOTNET_SCRIPT_SUPPORT ON)
add_library(nethost SHARED IMPORTED)
target_include_directories(nethost INTERFACE "${CoreClrEmbed_INCLUDE_DIRS}")
get_filename_component(CoreClrEmbed_FOLDER ${CoreClrEmbed_SHARED_LIBRARIES} DIRECTORY)
@@ -29,24 +16,70 @@ if (CoreClrEmbed_FOUND)
BUILD_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)
if (IMHEX_BUNDLE_DOTNET)
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)
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 {
public:
DotNetLoader() = default;
DotNetLoader() : ScriptLoader(".NET") {}
~DotNetLoader() override = default;
bool initialize() override;
bool loadAll() override;
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;
};

View File

@@ -3,30 +3,44 @@
#include <functional>
#include <string>
#include <vector>
#include <hex/helpers/utils.hpp>
#if defined(OS_WINDOWS)
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
namespace hex::script::loader {
class ScriptLoader;
struct Script {
std::string name;
std::function<void()> entryPoint;
const ScriptLoader *loader;
};
class ScriptLoader {
public:
ScriptLoader() = default;
ScriptLoader(std::string typeName) : m_typeName(std::move(typeName)) {}
virtual ~ScriptLoader() = default;
virtual bool initialize() = 0;
virtual bool loadAll() = 0;
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 {
return m_scripts;
}
const std::string& getTypeName() const {
return m_typeName;
}
protected:
void clearScripts() {
m_scripts.clear();
@@ -34,6 +48,49 @@ namespace hex::script::loader {
private:
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>
#if defined(OS_WINDOWS)
#include <Windows.h>
#define STRING(str) L##str
#else
#include <dlfcn.h>
#define STRING(str) str
#endif
@@ -15,6 +13,8 @@
#include <nethost.h>
#include <coreclr_delegates.h>
#include <hostfxr.h>
#include <imgui.h>
#include <hex/api/plugin_manager.hpp>
#include <hex/helpers/fs.hpp>
#include <wolv/io/fs.hpp>
@@ -22,6 +22,9 @@
#include <wolv/utils/string.hpp>
#include <hex/helpers/fmt.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp>
extern "C" void igSetCurrentContext(ImGuiContext* ctx);
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);
#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_get_runtime_delegate_fn hostfxr_get_runtime_delegate = 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() {
#if defined(OS_WINDOWS)
auto netHostLibrary = loadLibrary(L"nethost.dll");
#elif defined(OS_LINUX)
#elif defined(OS_LINUX)
auto netHostLibrary = loadLibrary("libnethost.so");
#elif defined(OS_MACOS)
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");
std::array<char_t, 300> buffer = { 0 };
std::array<char_t, 300> buffer = { };
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;
}
@@ -108,13 +94,26 @@ namespace hex::script::loader {
= getExport<hostfxr_get_runtime_delegate_fn>(hostfxrLibrary, "hostfxr_get_runtime_delegate");
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
hostfxr_initialize_for_runtime_config != 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) {
@@ -128,9 +127,15 @@ namespace hex::script::loader {
};
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(
ctx,
hostfxr_delegate_type::hdt_load_assembly_and_get_function_pointer,
@@ -138,7 +143,7 @@ namespace hex::script::loader {
);
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;
@@ -175,12 +180,23 @@ namespace hex::script::loader {
);
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;
}
m_loadAssembly = [entryPoint](const std::fs::path &path) -> bool {
auto string = wolv::util::toUTF8String(path);
m_runMethod = [entryPoint](const std::string &methodName, bool keepLoaded, const std::fs::path &path) -> int {
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());
return result == 0;
@@ -211,13 +227,19 @@ namespace hex::script::loader {
if (!std::fs::exists(scriptPath))
continue;
this->addScript(entry.path().stem().string(), [this, scriptPath] {
hex::unused(m_loadAssembly(scriptPath));
});
if (m_methodExists("Main", 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));
}
}
}
return true;
}
}
}

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 <loaders/dotnet/dotnet_loader.hpp>
#include <loaders/python/python_loader.hpp>
#include <romfs/romfs.hpp>
#include <nlohmann/json.hpp>
@@ -15,8 +16,11 @@ using namespace hex;
using namespace hex::script::loader;
using ScriptLoaders = std::tuple<
#if defined(DOTNET_PLUGINS)
DotNetLoader
#if defined(IMHEX_DOTNET_SCRIPT_SUPPORT)
DotNetLoader,
#endif
#if defined(IMHEX_PYTHON_SCRIPT_SUPPORT)
PythonLoader
#endif
>;
@@ -69,10 +73,10 @@ namespace {
}
void addScriptsMenu() {
static std::vector<const Script*> scripts;
static TaskHolder runnerTask, updaterTask;
hex::ContentRegistry::Interface::addMenuItemSubMenu({ "hex.builtin.menu.extras" }, 5000, [] {
static bool menuJustOpened = true;
static std::vector<const Script*> scripts;
if (ImGui::BeginMenu("hex.script_loader.menu.run_script"_lang)) {
if (menuJustOpened) {
@@ -91,9 +95,9 @@ namespace {
}
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&) {
entryPoint();
});
@@ -107,10 +111,19 @@ namespace {
}, [] {
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") {
hex::log::debug("Using romfs: '{}'", romfs::name());
for (auto &path : romfs::list("lang"))
@@ -119,5 +132,4 @@ IMHEX_PLUGIN_SETUP("Script Loader", "WerWolv", "Script Loader plugin") {
if (initializeAllLoaders()) {
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