Compare commits

...

73 Commits

Author SHA1 Message Date
WerWolv baa3329e7f fix: Make sure providers returned by createProvider don't get deleted unexpectedly 2025-12-16 23:36:05 +01:00
WerWolv e696d384c2 feat: Add initial MCP Server support 2025-12-16 20:25:46 +01:00
WerWolv 932c281223 fix: Pattern files not getting truncated correctly when saving
Fixes #2566
2025-12-16 10:02:10 +01:00
WerWolv 858fe0384e impr: Make most windows non-scrolling by default 2025-12-15 21:06:44 +01:00
WerWolv e904cd749f fix: Inverted sorting of find view table
Fixes #2564
2025-12-15 20:13:19 +01:00
WerWolv 6b16f39be4 impr: Allow tutorials to use markdown formatted text 2025-12-15 20:07:43 +01:00
WerWolv 021c7e5fdb impr: Add localization option to store long, formatted texts in external files 2025-12-15 20:07:18 +01:00
WerWolv c161a5c71b fix: Typo in crash popup 2025-12-15 11:31:41 +01:00
WerWolv 76ccdbccea patterns: Update pattern language 2025-12-15 10:10:15 +01:00
WerWolv 553ee89787 fix: Only enable widgets in pattern data view when there's actually any patterns available 2025-12-15 10:00:29 +01:00
WerWolv cb6247b16e fix: Crash when using @ command palette command
Fixes #2563
2025-12-15 09:52:44 +01:00
WerWolv cfac7ff0ba impr: Unionize exception and assertion handling 2025-12-15 09:52:13 +01:00
iTrooz 49bbe7dc77
build: remove IMHEX_PLUGINS_IN_SHARE option + only allow AppImage to load plugins from inside itself
Rationale: The `IMHEX_PLUGINS_IN_SHARE` is a hack to prevent the appimage from loading plugin from system imhex installation, like /usr/lib/imhex/

In reality, I do not think people compile plugins specifically for the AppImage (plugins must be compiled for the specific imhex & compiler version the imhex binary is used), and this lets us remove the hack
2025-12-14 18:29:00 +01:00
iTrooz 07b6fa0e2e
build(web): add `BUILD_TYPE` arg to Dockerfile 2025-12-14 15:02:46 +01:00
iTrooz 67396f2009
chore: fix web Dockerfile ARG syntax 2025-12-14 15:02:46 +01:00
iTrooz a20ff87cc9
chore: update comment 2025-12-14 15:02:46 +01:00
iTrooz e02e57a729
chore: remove `version` attribute from web compose.yaml 2025-12-14 14:19:15 +01:00
iTrooz 225dc53795
build(appimage): use https when querying repos 2025-12-14 02:02:40 +01:00
iTrooz e7404376db
build: do not bundle plugin SDK in AppImage 2025-12-14 01:55:57 +01:00
iTrooz d6aec341fe
build(web): make a shallow clone of vcpkg 2025-12-14 01:43:40 +01:00
iTrooz 3a3c2fb204
build: add defaults to AppImage build for x86 2025-12-14 00:45:56 +01:00
iTrooz e9b5cdbccf
build: fix IMHEX_INCLUDE_PLUGINS defined check 2025-12-14 00:15:08 +01:00
iTrooz 3f30e63d95
chore(web): allow nginx to access files in development docker image 2025-12-14 00:08:11 +01:00
iTrooz f9c6866c7b
build: require all plugins that builtint depends on to be present 2025-12-13 23:54:58 +01:00
iTrooz 388dccfd9f
chore: organise cmake build flags 2025-12-13 23:46:13 +01:00
paxcut 1676342e28
Various fixes for pattern editor (#2561)
- Fix for vertical scroll bar being too far to the left.
- Fix constructor not initializing from const char pointer properly
- maxcolumn not being set for console text lines causing crashes on
empty pattern evaluation
- A replacement using replace all is now undone in one step.
- Find/replace no longer need to have enter or return key to accept
text. You can use arrows or shortcuts.
- More efficient search replace implementation with plans to add even
faster.
- Tooltips added to find/replace window
- Providers now save both horizontal and vertical scroll positions when
switching to another one and restore them when switching back. This is
independent to the cursor position which is also saved.
- Pattern editor no longer takes focus when changing providers via a tab
click. This has the effect that menus won't change by just clicking on a
tab.
- Small fixes and code refactoring.
2025-12-13 05:23:16 -07:00
WerWolv 62732de227 fix: Gaps in-between hex editor highlighting on specific scalings 2025-12-12 22:04:15 +01:00
WerWolv 63e777c84c impr: Intercept glibc++ assertion handler 2025-12-12 22:02:56 +01:00
WerWolv ab95cdf3e5 fix: Minimap not allowing scrolling as far as the scroll bar 2025-12-12 17:19:57 +01:00
WerWolv 827b5b01dd patterns: Update pattern language 2025-12-12 16:57:20 +01:00
paxcut bfa9788099
impr: Various fixes and improvements to the pattern editor (#2559)
- fixed crash when utf8 chars were present in text editor
- fixed unable to scroll when cursor at line 1
- removed dependencies on thext editor that were not being used.

I had to go back to the old code (old for me) and fit in the changes
that were applied to the new code.That was only possible by
incorporating some of the new structural differences to the text editor.
This created new bugs and crashes that I ve have fixed but there may be
ones that I couldn't find in the very small amount of time I could spend
testing so that this commit wouldn't be delayed. If more crashes are
found due to the mixing of old and new code they should be resolved when
the new code is brought in.
2025-12-12 16:27:26 +01:00
WerWolv de25ce7fbb feat: Add support for custom inspector edit widgets 2025-12-12 13:15:16 +01:00
WerWolv 21e61bfce6 fix: Extended ASCII display being enabled by default 2025-12-12 13:14:49 +01:00
WerWolv 82e168c438 build: Update libwolv 2025-12-12 13:14:27 +01:00
WerWolv 48583a2b6e build: Go back to WiX 4 again for ARM64 support 2025-12-11 23:41:37 +01:00
WerWolv 0db0982fa7 build: Update dependencies 2025-12-11 23:15:13 +01:00
WerWolv 6a28ce9e4b fix: Wrong variable access 2025-12-11 21:32:18 +01:00
WerWolv 1db79f6117 feat: Add command line arguments to process tooltips, exclude kthreads on Linux
Fixes #2558
2025-12-11 17:09:26 +01:00
WerWolv f234103320 fix: Off-by-one of starts of process memory regions 2025-12-11 16:27:36 +01:00
WerWolv 45c382a19a fix: Auto backup localization key names 2025-12-11 16:27:13 +01:00
WerWolv fb7ef61d06 fix: Crash when canceling creation of SSH provider
Fixes #2557
2025-12-11 16:24:43 +01:00
WerWolv 2586645d02 build: Force-set REINSTALLMODE=amus for WiX installer 2025-12-11 12:34:29 +01:00
WerWolv e23cb5509d build: Use WiX 3 for packaging 2025-12-11 11:13:26 +01:00
WerWolv 5cbd53ae7a build: Fix version stripping issues 2025-12-09 21:58:29 +01:00
WerWolv a4ee590875 build: Only build the version stripper on mingw 2025-12-09 21:31:50 +01:00
WerWolv 495608ed7c build: Force-remove all version information from libwinpthread 2025-12-09 21:25:35 +01:00
WerWolv 4d10d9a195 build: Manually set FILEVERSION of libwinpthread to 0.0.0.0
#2550
2025-12-08 23:54:33 +01:00
WerWolv 4914a34dd9 build: Fix WiX patch 2025-12-08 21:26:18 +01:00
WerWolv ab0fb3131d fix: Reset selected row after checking it 2025-12-08 21:10:49 +01:00
WerWolv 7922d3b3cb build: Try to force-overwrite libwinpthread 2025-12-08 18:17:33 +01:00
WerWolv 6427f53b5a feat: Add endian option to Sum hash 2025-12-07 22:18:34 +01:00
WerWolv 994df0a3a4 feat: Add shortcut to directly search for the selected bytes 2025-12-07 22:17:57 +01:00
WerWolv e6eee55810 fix: Editing of WString, String16, String32 in data inspector 2025-12-07 21:44:33 +01:00
WerWolv 855e4c4913 feat: Add option to create auto backups of files before they're modified 2025-12-07 21:37:14 +01:00
WerWolv c2e07bf7b2 feat: Added data inspector shortcut to toggle endianness 2025-12-07 20:58:30 +01:00
WerWolv 77b9e3eac8 impr: Allow Esc to clear editing and selected state in data inspector 2025-12-07 20:58:17 +01:00
WerWolv 790487eea6 impr: If there's multiple foreground highlighting providers, only evaluate until a color is found 2025-12-07 20:48:16 +01:00
WerWolv eb83354179 feat: Add option to automatically apply found pattern when a provider is opened 2025-12-07 20:47:33 +01:00
WerWolv 9ba8754f97 build: Remove file version from main executable to make msi installer not skip it 2025-12-07 17:57:21 +01:00
WerWolv 84346119b3 fix: Missing includes 2025-12-07 17:22:26 +01:00
WerWolv 2b3abd06db build: Fix deb package referring to incorrect md4c library package
Fixes #2548
2025-12-07 16:25:15 +01:00
WerWolv 8267aad79e feat: Add new Command Line data source 2025-12-07 16:24:36 +01:00
WerWolv 3f9ce561b9 fix: Post-pone file opening till everything has been initialized 2025-12-07 14:00:10 +01:00
WerWolv 347fc3ed9f impr: Show proper error message if nethost header can't be found 2025-12-07 13:35:08 +01:00
WerWolv 0488c996e9 impr: Make icons look slightly nicer at low resolutions 2025-12-07 11:48:14 +01:00
WerWolv 37bfd97d93 git: Make release CI more reliable 2025-12-07 10:36:03 +01:00
WerWolv c8652b0576 Merge branch 'feature/code-signing' 2025-12-07 00:01:54 +01:00
WerWolv 1208d2eb5e git: Fix issues with the release CI 2025-12-07 00:01:40 +01:00
WerWolv ab34fed0c5 build: Bump version to 1.39.0.WIP 2025-12-07 00:01:22 +01:00
WerWolv 0906e5f9cf git: Remove test signing, get release signing ready 2025-12-06 23:59:56 +01:00
WerWolv 47b1c603b3 git: Remove signpath parameters 2025-12-06 23:59:56 +01:00
WerWolv 4bda321e7a git: Fix version string 2025-12-06 23:59:56 +01:00
WerWolv 691ff11fbc git: Added Windows code signing 2025-12-06 23:59:56 +01:00
127 changed files with 3024 additions and 1188 deletions

View File

@ -95,7 +95,6 @@ jobs:
-DIMHEX_GENERATE_PDBS=ON \
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
-DCPACK_WIX_VERSION="4" \
-DCPACK_WIX_ROOT="$(echo "$USERPROFILE" | tr '\\' '/')/.dotnet/tools" \
..
@ -166,6 +165,7 @@ jobs:
- name: ⬆️ Upload Windows Installer
uses: actions/upload-artifact@v4
id: upload-installer
with:
if-no-files-found: error
name: Windows Installer ${{ matrix.architecture_name }}
@ -279,7 +279,6 @@ jobs:
-DIMHEX_COMMIT_HASH_LONG="$env:GITHUB_SHA" `
-DIMHEX_COMMIT_BRANCH="$($env:GITHUB_REF -replace '.*/', '')" `
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" `
-DCPACK_WIX_VERSION="4" `
-DCPACK_WIX_ROOT="$($env:USERPROFILE -replace '\\','/')/.dotnet/tools" `
.

View File

@ -82,6 +82,43 @@ jobs:
git fetch --tags --recurse-submodules=no
git log nightly..origin/master --oneline --no-merges --pretty=format:'* %s' >> changelog.md
- name: ⬆️ Upload Unsigned x86_64 Windows Installer
if: false
uses: actions/upload-artifact@v4
id: upload-installer-x86_64
with:
if-no-files-found: error
name: Windows Installer ${{ matrix.architecture_name }}
path: |
imhex-*-x86_64.msi
- name: ⬆️ Upload Unsigned ARM64 Windows Installer
if: false
uses: actions/upload-artifact@v4
id: upload-installer-arm64
with:
if-no-files-found: error
name: Windows Installer ${{ matrix.architecture_name }}
path: |
imhex-*-arm64.msi
- name: 🗑️ Delete unsigned installers
if: false
run: |
rm imhex-*.msi
- name: 🗝️ Sign Installer
if: false
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'
project-slug: 'ImHex'
signing-policy-slug: 'release-signing'
github-artifact-id: '${{ steps.upload-installer.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: '.'
- name: 📦 Update Pre-Release
if: ${{ steps.check_commits.outputs.should_run == 'true' }}
run: |

View File

@ -7,6 +7,12 @@ on:
release:
types: [published]
workflow_dispatch:
inputs:
commit_hash:
type: string
description: 'The commit hash to build (defaults to the latest commit on the default branch)'
required: false
default: ''
jobs:
release-update-repos:
@ -41,6 +47,7 @@ jobs:
tag: ImHex-v${{ env.IMHEX_VERSION }}
repo: PatternLanguage
token: ${{ secrets.RELEASE_TOKEN }}
skipIfReleaseExists: true
- name: 🎫 Create ImHex-Patterns release
uses: ncipollo/release-action@v1
@ -51,6 +58,7 @@ jobs:
tag: ImHex-v${{ env.IMHEX_VERSION }}
repo: ImHex-Patterns
token: ${{ secrets.RELEASE_TOKEN }}
skipIfReleaseExists: true
- name: 🎫 Create imhex-download-sdk release
uses: ncipollo/release-action@v1
@ -61,11 +69,13 @@ jobs:
tag: v${{ env.IMHEX_VERSION }}
repo: imhex-download-sdk
token: ${{ secrets.RELEASE_TOKEN }}
skipIfReleaseExists: true
release-upload-artifacts:
runs-on: ubuntu-24.04
name: Release Upload Artifacts
outputs:
IMHEX_VERSION: ${{ steps.verify_version.outputs.IMHEX_VERSION }}
steps:
- name: 🧰 Checkout
uses: actions/checkout@v4
@ -74,6 +84,7 @@ jobs:
submodules: recursive
- name: 📜 Verify version and set version variable
id: verify_version
run: |
set -x
project_version=`cat ImHex/VERSION`
@ -85,6 +96,7 @@ jobs:
fi
echo "IMHEX_VERSION=$project_version" >> $GITHUB_ENV
echo "IMHEX_VERSION=$project_version" >> $GITHUB_OUTPUT
- name: 🗜️ Create tarball from sources with dependencies
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
@ -97,6 +109,7 @@ jobs:
branch: ${{ github.event.release.target_commitish }}
workflow_conclusion: success
skip_unpack: true
commit: ${{ github.event.inputs.commit_hash }}
- name: 🗜️ Unzip files when needed
run: |
@ -115,25 +128,80 @@ jobs:
- name: 🟩 Rename artifacts when needed
run: |
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip
mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip
mv "Windows Portable x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-x86_64.zip || true
mv "Windows Portable arm64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-arm64.zip || true
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip || true
mv "ImHex Web.zip" imhex-${{ env.IMHEX_VERSION }}-Web.zip || true
rm artifact.tar || true
- name: ⬆️ Upload Unsigned x86_64 Windows Installer
uses: actions/upload-artifact@v4
id: upload-installer-x86_64
with:
if-no-files-found: error
name: Windows Installer ${{ matrix.architecture_name }}
path: |
imhex-*-x86_64.msi
- name: ⬆️ Upload Unsigned ARM64 Windows Installer
uses: actions/upload-artifact@v4
id: upload-installer-arm64
with:
if-no-files-found: error
name: Windows Installer ${{ matrix.architecture_name }}
path: |
imhex-*-arm64.msi
- name: 🗑️ Delete unsigned installers
run: |
rm imhex-*.msi
- name: 🗝️ Sign x86_64 Installer
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'
project-slug: 'ImHex'
signing-policy-slug: 'release-signing'
github-artifact-id: '${{ steps.upload-installer-x86_64.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: '.'
- name: 🗝️ Sign ARM64 Installer
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 'f605a0e8-86cd-411c-bb6f-e05025afcc33'
project-slug: 'ImHex'
signing-policy-slug: 'release-signing'
github-artifact-id: '${{ steps.upload-installer-arm64.outputs.artifact-id }}'
wait-for-completion: true
output-artifact-directory: '.'
- name: ⬆️ Upload everything to release
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with:
files: '*'
release-update-aur:
name: Release update AUR package
needs: release-upload-artifacts
runs-on: ubuntu-24.04
steps:
- name: ⬇️ Download artifacts
run: |
tagname=${GITHUB_REF#refs/tags/}
version=${tagname#v}
wget https://github.com/WerWolv/ImHex/releases/download/${tagname}/imhex-${version}-ArchLinux-x86_64.pkg.tar.zst
- name: ✒️ Prepare PKGBUILD
run: |
set -x
cp ImHex/dist/Arch/PKGBUILD .
hash=`md5sum imhex-${{ env.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`
hash=`md5sum imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-ArchLinux-x86_64.pkg.tar.zst | cut -d ' ' -f 1`
sed -i 's/%version%/${{ env.IMHEX_VERSION }}/g' PKGBUILD
sed -i 's/%version%/${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}/g' PKGBUILD
sed -i "s/(SKIP)/($hash)/g" PKGBUILD
- name: ⬆️ Publish AUR package
@ -149,7 +217,7 @@ jobs:
commit_username: iTrooz
commit_email: itrooz@protonmail.com
ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
commit_message: Bump to version ${{ env.IMHEX_VERSION }}
commit_message: Bump to version ${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}
ssh_keyscan_types: rsa,ecdsa,ed25519
release-update-winget:
@ -161,6 +229,7 @@ jobs:
shell: pwsh
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
- name: ⬆️ Update winget manifest
shell: pwsh
env:
@ -193,7 +262,7 @@ jobs:
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
with:
snap: imhex-${{ env.IMHEX_VERSION }}-x86_64.snap
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-x86_64.snap
release: stable
- name: ⬆️ Publish arm64 Snap package
@ -202,5 +271,5 @@ jobs:
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
with:
snap: imhex-${{ env.IMHEX_VERSION }}-arm64.snap
snap: imhex-${{ needs.release-upload-artifacts.outputs.IMHEX_VERSION }}-arm64.snap
release: stable

View File

@ -1,33 +1,38 @@
cmake_minimum_required(VERSION 3.25)
# Options
option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins (Linux only)" OFF)
## General
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON )
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF)
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
option(IMHEX_BUILD_HARDENING "Enable hardening flags for build" ON )
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. (Windows and MacOS only)" OFF)
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" ON )
option(IMHEX_ENABLE_PLUGIN_TESTS "Enable building plugin tests" ON )
option(IMHEX_ENABLE_IMGUI_TEST_ENGINE "Enable the ImGui Test Engine" OFF)
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON )
option(IMHEX_ENABLE_CXX_MODULES "Enable C++20 Module compilation. Testing only!" OFF)
option(IMHEX_ENABLE_CPPCHECK "Enable cppcheck static analysis" OFF)
option(IMHEX_BUNDLE_PLUGIN_SDK "Enable bundling of Plugin SDK into install package" ON )
## Testing
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" ON )
option(IMHEX_ENABLE_IMGUI_TEST_ENGINE "Enable the ImGui Test Engine" OFF)
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
## Debug info
option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON )
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
## Plugins
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
option(IMHEX_ENABLE_PLUGIN_TESTS "Enable building plugin tests" ON )
option(IMHEX_INCLUDE_PLUGINS "Semicolon-separated list of plugins to include in the build (empty = build all)" "" )
option(IMHEX_EXCLUDE_PLUGINS "Semicolon-separated list of plugins to exclude from the build" "" )
set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}")
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")

View File

@ -1 +1 @@
1.38.0
1.39.0.WIP

View File

@ -175,15 +175,11 @@ macro(detectOS)
endif()
include(GNUInstallDirs)
if(IMHEX_PLUGINS_IN_SHARE)
set(PLUGINS_INSTALL_LOCATION "share/imhex/plugins")
else()
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
set(PLUGINS_INSTALL_LOCATION "${CMAKE_INSTALL_LIBDIR}/imhex/plugins")
# Add System plugin location for plugins to be loaded from
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
endif()
# Add System plugin location for plugins to be loaded from
# IMPORTANT: This does not work for Sandboxed or portable builds such as the Flatpak or AppImage release
add_compile_definitions(SYSTEM_PLUGINS_LOCATION="${CMAKE_INSTALL_FULL_LIBDIR}/imhex")
else ()
message(FATAL_ERROR "Unknown / unsupported system!")
@ -205,11 +201,14 @@ macro(configurePackingResources)
set(CPACK_GENERATOR "WIX")
set(CPACK_PACKAGE_NAME "ImHex")
set(CPACK_PACKAGE_VENDOR "WerWolv")
set(CPACK_WIX_VERSION 4)
set(CPACK_WIX_UPGRADE_GUID "05000E99-9659-42FD-A1CF-05C554B39285")
set(CPACK_WIX_PRODUCT_ICON "${PROJECT_SOURCE_DIR}/resources/dist/windows/icon.ico")
set(CPACK_WIX_UI_BANNER "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_banner.png")
set(CPACK_WIX_UI_DIALOG "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_dialog.png")
set(CPACK_WIX_CULTURES "en-US;de-DE;ja-JP;it-IT;pt-BR;zh-CN;zh-TW;ru-RU")
set(CPACK_WIX_PATCH_FILE "${PROJECT_SOURCE_DIR}/resources/dist/windows/wix_patch.xml")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "ImHex")
set_property(INSTALL "$<TARGET_FILE_NAME:main>"
PROPERTY CPACK_START_MENU_SHORTCUTS "ImHex"
@ -312,7 +311,7 @@ macro(createPackage)
POST_EXCLUDE_REGEXES ".*system32/.*\\.dll"
)
if(_c_deps_FILENAMES AND NOT _c_deps STREQUAL "")
if(_c_deps_FILENAMES AND _c_deps AND NOT (_c_deps STREQUAL ""))
message(WARNING "Conflicting dependencies for library: \"${_c_deps}\"!")
endif()
@ -587,7 +586,9 @@ endfunction()
macro(detectBundledPlugins)
file(GLOB PLUGINS_DIRS "plugins/*")
if (NOT DEFINED IMHEX_INCLUDE_PLUGINS)
if (IMHEX_INCLUDE_PLUGINS)
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
else()
foreach(PLUGIN_DIR ${PLUGINS_DIRS})
if (EXISTS "${PLUGIN_DIR}/CMakeLists.txt")
get_filename_component(PLUGIN_NAME ${PLUGIN_DIR} NAME)
@ -596,8 +597,6 @@ macro(detectBundledPlugins)
endif ()
endif()
endforeach()
else()
set(PLUGINS ${IMHEX_INCLUDE_PLUGINS})
endif()
foreach(PLUGIN_NAME ${PLUGINS})
@ -608,9 +607,13 @@ macro(detectBundledPlugins)
message(FATAL_ERROR "No bundled plugins enabled")
endif()
if (NOT ("builtin" IN_LIST PLUGINS))
message(FATAL_ERROR "The 'builtin' plugin is required for ImHex to work!")
endif ()
set(REQUIRED_PLUGINS builtin fonts ui)
foreach(PLUGIN ${REQUIRED_PLUGINS})
list(FIND PLUGINS ${PLUGIN} PLUGIN_INDEX)
if (PLUGIN_INDEX EQUAL -1)
message(FATAL_ERROR "Required plugin '${PLUGIN}' is not enabled!")
endif()
endforeach()
endmacro()
macro(setVariableInParent variable value)

View File

@ -15,8 +15,8 @@ AppDir:
- "{{ARCHITECTURE_PACKAGE}}"
allow_unauthenticated: true
sources:
- sourceline: 'deb [arch=amd64] http://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'
- sourceline: 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'
- sourceline: 'deb [arch=amd64] https://us.archive.ubuntu.com/ubuntu/ noble main restricted universe multiverse'
- sourceline: 'deb [arch=arm64] https://ports.ubuntu.com/ubuntu-ports/ noble main restricted universe multiverse'
include:
- libgdk-pixbuf2.0-0
- libgdk-pixbuf2.0-common

View File

@ -30,9 +30,9 @@ ARG LTO=ON
ARG BUILD_TYPE=RelWithDebInfo
ARG GIT_COMMIT_HASH
ARG GIT_BRANCH
ARG ARCHITECTURE_PACKAGE
ARG ARCHITECTURE_FILE_NAME
ARG ARCHITECTURE_APPIMAGE_BUILDER
ARG ARCHITECTURE_PACKAGE=x86_64
ARG ARCHITECTURE_FILE_NAME=amd64
ARG ARCHITECTURE_APPIMAGE_BUILDER=x86_64
WORKDIR /build
# Ubuntu sh doesnt support string substitution
@ -42,16 +42,18 @@ RUN <<EOF
# Prepare ImHex build
set -xe
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
-DIMHEX_ENABLE_LTO=${LTO} \
-DIMHEX_PLUGINS_IN_SHARE=ON \
CC=gcc-14 CXX=g++-14 cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
-DIMHEX_ENABLE_LTO=${LTO} \
-DIMHEX_BUNDLE_PLUGIN_SDK=OFF \
`# To prevent using a libdir with an architecture-specific name` \
-DCMAKE_INSTALL_LIBDIR="lib" \
/imhex
EOF

View File

@ -4,7 +4,7 @@ Section: editors
Priority: optional
Architecture: amd64
License: GNU GPL-2
Depends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, md4c
Depends: libfontconfig1, libglfw3 | libglfw3-wayland, libmagic1, libmbedtls14, libfreetype6, libopengl0, libdbus-1-3, xdg-desktop-portal, libssh2-1, libmd4c0
Maintainer: WerWolv <hey@werwolv.net>
Description: ImHex Hex Editor
A Hex Editor for Reverse Engineers, Programmers and

4
dist/ImHex.run.xml vendored
View File

@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ImHex" type="CMakeRunConfiguration" factoryName="Application" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentBuildDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="ImHex" TARGET_NAME="imhex_all" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="ImHex" RUN_TARGET_NAME="main">
<configuration default="false" name="ImHex" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS=".ninja_log" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" WORKING_DIR="file://$CMakeCurrentBuildDir$" PASS_PARENT_ENVS_2="true" PROJECT_NAME="ImHex" TARGET_NAME="imhex_all" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="ImHex" RUN_TARGET_NAME="main">
<envs>
<env name="NO_DEBUG_BANNER" value="1" />
</envs>
@ -8,6 +8,6 @@
</method>
</configuration>
<configuration default="false" name="CMake Debug" type="CMakeListConfigurationType" factoryName="CMakeListConfigurationFactory">
<method v="2" />
<method v="2" />
</configuration>
</component>

12
dist/web/Dockerfile vendored
View File

@ -2,7 +2,7 @@ FROM emscripten/emsdk:4.0.21 AS build
# Used to invalidate layer cache but not mount cache
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
ARG UNIQUEKEY 1
ARG UNIQUEKEY=1
RUN apt update
RUN apt install -y git ccache autoconf automake libtool pkg-config ninja-build
@ -12,13 +12,13 @@ RUN <<EOF
# Note: we are a patch on the libmagic port
set -xe
git clone https://github.com/microsoft/vcpkg /vcpkg
git -C /vcpkg pull
git clone --depth 1 https://github.com/microsoft/vcpkg /vcpkg
/vcpkg/bootstrap-vcpkg.sh
sed -i 's/vcpkg_install_make(${EXTRA_ARGS})/vcpkg_install_make(${EXTRA_ARGS} SUBPATH src)/g' /vcpkg/ports/libmagic/portfile.cmake
EOF
# Patch vcpkg build instructions to add -pthread
# Patch vcpkg build instructions to add -pthread flag
# Even dependencies must be built with -pthread to be able to use USE_PTHREADS=1
RUN <<EOF
set -xe
@ -50,6 +50,7 @@ ENV CCACHE_DIR=/cache/ccache
RUN mkdir /build
WORKDIR /build
ARG BUILD_TYPE=Release
RUN --mount=type=cache,target=/cache \
--mount=type=bind,source=.,target=/imhex <<EOF
@ -70,7 +71,7 @@ ccache -zs
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
-DLIBROMFS_COMPRESS_RESOURCES=OFF \
-DIMHEX_ENABLE_PLUGIN_TESTS=OFF \
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_TYPE=${BUILD_TYPE}
ninja -j $JOBS
@ -106,3 +107,4 @@ COPY --from=build [ \
FROM nginx
COPY --from=raw . /usr/share/nginx/html
RUN chmod -R 755 /usr/share/nginx/html

View File

@ -1,5 +1,4 @@
# docker compose -f dist/web/compose.yml up --build
version: '3'
services:
imhex_web:
image: imhex_web:latest

@ -1 +1 @@
Subproject commit 5be84628dc4d7da6281c943888497f8b886b7f5a
Subproject commit b0c9416568475a784838e3af8b0021a542e47cd7

@ -1 +1 @@
Subproject commit 33057885fdb8802245260e8b8de4eecf1819dc60
Subproject commit a06da46a3cf1694ccc785d21a37a2dca08deb56a

View File

@ -57,6 +57,9 @@ set(LIBIMHEX_SOURCES
source/ui/toast.cpp
source/ui/banner.cpp
source/mcp/client.cpp
source/mcp/server.cpp
source/subcommands/subcommands.cpp
)

View File

@ -7,6 +7,8 @@
#include <map>
#include <string>
#include <hex/mcp/server.hpp>
EXPORT_MODULE namespace hex {
/* Network Communication Interface Registry. Allows adding new communication interface endpoints */
@ -22,4 +24,19 @@ EXPORT_MODULE namespace hex {
}
namespace ContentRegistry::MCP {
namespace impl {
mcp::Server& getMcpServerInstance();
void setEnabled(bool enabled);
}
bool isEnabled();
bool isConnected();
void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function);
}
}

View File

@ -8,6 +8,7 @@
#include <optional>
#include <string>
#include <vector>
#include <bit>
EXPORT_MODULE namespace hex {
@ -22,8 +23,10 @@ EXPORT_MODULE namespace hex {
namespace impl {
struct DoNotUseThisByItselfTag {};
using DisplayFunction = std::function<std::string()>;
using EditingFunction = std::function<std::vector<u8>(std::string, std::endian)>;
using EditingFunction = std::function<std::optional<std::vector<u8>>(std::string&, std::endian, DoNotUseThisByItselfTag)>;
using GeneratorFunction = std::function<DisplayFunction(const std::vector<u8> &, std::endian, NumberDisplayStyle)>;
struct Entry {
@ -38,6 +41,35 @@ EXPORT_MODULE namespace hex {
}
namespace EditWidget {
class Widget {
public:
using Function = std::function<std::vector<u8>(const std::string&, std::endian)>;
explicit Widget(const Function &function) : m_function(function) {}
virtual ~Widget() = default;
virtual std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) = 0;
std::optional<std::vector<u8>> operator()(std::string &value, std::endian endian, impl::DoNotUseThisByItselfTag) {
return draw(value, endian);
}
std::vector<u8> getBytes(const std::string &value, std::endian endian) const {
return m_function(value, endian);
}
private:
Function m_function;
};
struct TextInput : Widget {
explicit TextInput(const Function &function) : Widget(function) {}
std::optional<std::vector<u8>> draw(std::string &value, std::endian endian) override;
};
}
/**
* @brief Adds a new entry to the data inspector
* @param unlocalizedName The unlocalized name of the entry

View File

@ -19,7 +19,7 @@ EXPORT_MODULE namespace hex {
void addProviderName(const UnlocalizedString &unlocalizedName, const char *icon);
using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
using ProviderCreationFunction = std::function<std::shared_ptr<prv::Provider>()>;
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
struct Entry {

View File

@ -239,6 +239,14 @@ EXPORT_MODULE namespace hex {
nlohmann::json store() override { return {}; }
};
class Spacer : public Widget {
public:
bool draw(const std::string &name) override;
void load(const nlohmann::json &) override {}
nlohmann::json store() override { return {}; }
};
}
namespace impl {

View File

@ -8,7 +8,7 @@ namespace hex {
/**
* @brief Creates a provider from its unlocalized name, and add it to the provider list
*/
EVENT_DEF(RequestCreateProvider, std::string, bool, bool, hex::prv::Provider **);
EVENT_DEF(RequestCreateProvider, std::string, bool, bool, std::shared_ptr<hex::prv::Provider> *);
/**
* @brief Move the data from all PerProvider instances from one provider to another

View File

@ -86,7 +86,7 @@ EXPORT_MODULE namespace hex {
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
* @param select Whether to select the provider after adding it
*/
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
/**
* @brief Creates a new provider and adds it to the list of providers
@ -111,7 +111,7 @@ EXPORT_MODULE namespace hex {
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
* @param select Whether to select the provider after adding it
*/
prv::Provider* createProvider(
std::shared_ptr<prv::Provider> createProvider(
const UnlocalizedString &unlocalizedName,
bool skipLoadInterface = false,
bool select = true

View File

@ -10,6 +10,7 @@
#include <condition_variable>
#include <source_location>
#include <thread>
#include <hex/trace/exceptions.hpp>
EXPORT_MODULE namespace hex {
@ -94,7 +95,12 @@ EXPORT_MODULE namespace hex {
std::atomic_flag m_hadException;
std::string m_exceptionMessage;
struct TaskInterruptor { virtual ~TaskInterruptor() = default; };
struct TaskInterruptor {
TaskInterruptor() {
trace::disableExceptionCaptureForCurrentThread();
}
virtual ~TaskInterruptor() = default;
};
friend class TaskHolder;
friend class TaskManager;

View File

@ -22,6 +22,8 @@ EXPORT_MODULE namespace hex {
Right = 8
};
using DrawFunction = std::function<void()>;
struct Tutorial {
Tutorial() = delete;
Tutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) :
@ -101,6 +103,7 @@ EXPORT_MODULE namespace hex {
std::vector<Highlight> m_highlights;
std::optional<Message> m_message;
std::function<void()> m_onAppear, m_onComplete;
DrawFunction m_drawFunction;
};
Step& addStep();
@ -166,6 +169,8 @@ EXPORT_MODULE namespace hex {
*/
static void reset();
static void setRenderer(std::function<DrawFunction(const std::string &)> renderer);
private:
TutorialManager() = delete;

View File

@ -14,6 +14,10 @@
static_assert(false, "Debug variables are only intended for use during development.");
#endif
namespace hex::trace {
struct StackTraceResult;
}
namespace hex::dbg {
namespace impl {
@ -47,4 +51,6 @@ namespace hex::dbg {
bool debugModeEnabled();
void setDebugModeEnabled(bool enabled);
void printStackTrace(const trace::StackTraceResult &stackTrace);
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <iostream>
namespace hex::mcp {
class Client {
public:
Client() = default;
~Client() = default;
int run(std::istream &input, std::ostream &output);
};
}

View File

@ -0,0 +1,38 @@
#pragma once
#include <functional>
#include <nlohmann/json.hpp>
#include <wolv/net/socket_server.hpp>
namespace hex::mcp {
class Server {
public:
constexpr static auto McpInternalPort = 19743;
Server();
~Server();
void listen();
void shutdown();
void disconnect();
bool isConnected();
void addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function);
private:
nlohmann::json handleInitialize();
void handleNotifications(const std::string &method, const nlohmann::json &params);
struct Primitive {
nlohmann::json capabilities;
std::function<nlohmann::json(const nlohmann::json &params)> function;
};
std::map<std::string, std::map<std::string, Primitive>> m_primitives;
wolv::net::SocketServer m_server;
bool m_connected = false;
};
}

View File

@ -72,6 +72,21 @@ namespace hex::prv {
[[nodiscard]] virtual std::vector<Description> getDataDescription() const = 0;
};
class IProviderDataBackupable {
public:
explicit IProviderDataBackupable(Provider *provider);
virtual ~IProviderDataBackupable() = default;
void createBackupIfNeeded(const std::fs::path &inputFilePath);
private:
Provider *m_provider = nullptr;
bool m_backupCreated = false;
bool m_shouldCreateBackups = true;
u64 m_maxSize;
std::string m_backupExtension;
};
/**
* @brief Represent the data source for a tab in the UI
*/

View File

@ -319,6 +319,7 @@ namespace ImGuiExt {
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size = ImVec2(0, 0), ImVec2 iconOffset = ImVec2(0, 0));
bool DimmedIconToggle(const char *icon, bool *v);
bool DimmedIconToggle(const char *iconOn, const char *iconOff, bool *v);
bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size = ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight()));
void TextOverlay(const char *text, ImVec2 pos, float maxWidth = -1);

View File

@ -14,7 +14,6 @@
#include <map>
#include <string>
#include <hex/api/tutorial_manager.hpp>
namespace hex {
@ -27,7 +26,7 @@ namespace hex {
* @brief Draws the view
* @note Do not override this method. Override drawContent() instead
*/
virtual void draw() = 0;
virtual void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) = 0;
/**
* @brief Draws the content of the view
@ -126,6 +125,7 @@ namespace hex {
class Window;
class Special;
class Floating;
class Scrolling;
class Modal;
class FullScreen;
@ -153,16 +153,10 @@ namespace hex {
*/
virtual void drawHelpText() = 0;
void draw() final {
if (this->shouldDraw()) {
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });
this->drawContent();
}
ImGui::End();
}
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) override;
virtual bool allowScroll() const {
return false;
}
};
@ -174,12 +168,7 @@ namespace hex {
public:
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
void draw() final {
if (this->shouldDraw()) {
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
this->drawContent();
}
}
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
};
/**
@ -189,7 +178,24 @@ namespace hex {
public:
explicit Floating(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; }
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
};
/**
* @brief A view that draws all its content at once without any scrolling being done by the window itself
*/
class View::Scrolling : public View::Window {
public:
explicit Scrolling(UnlocalizedString unlocalizedName, const char *icon) : Window(std::move(unlocalizedName), icon) {}
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
bool allowScroll() const final {
return true;
}
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
};
@ -200,24 +206,7 @@ namespace hex {
public:
explicit Modal(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
void draw() final {
if (this->shouldDraw()) {
if (this->getWindowOpenState())
ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
if (ImGui::BeginPopupModal(title.c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | this->getWindowFlags())) {
this->drawContent();
ImGui::EndPopup();
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
this->getWindowOpenState() = false;
}
}
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
[[nodiscard]] virtual bool hasCloseButton() const { return true; }
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
@ -227,10 +216,7 @@ namespace hex {
public:
explicit FullScreen() : View("FullScreen", "") {}
void draw() final {
this->drawContent();
this->drawAlwaysVisibleContent();
}
void draw(ImGuiWindowFlags extraFlags = ImGuiWindowFlags_None) final;
};
}

View File

@ -594,6 +594,12 @@ namespace hex {
return false;
}
bool Spacer::draw(const std::string& name) {
std::ignore = name;
ImGui::NewLine();
return false;
}
}
@ -864,6 +870,18 @@ namespace hex {
}
namespace EditWidget {
std::optional<std::vector<u8>> TextInput::draw(std::string &value, std::endian endian) {
if (ImGui::InputText("##InspectorLineEditing", value,
ImGuiInputTextFlags_EnterReturnsTrue |
ImGuiInputTextFlags_AutoSelectAll)) {
return getBytes(value, endian);
}
return std::nullopt;
}
}
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction) {
log::debug("Registered new data inspector format: {}", unlocalizedName.get());
@ -1101,13 +1119,13 @@ namespace hex {
namespace impl {
void add(const std::string &typeName, ProviderCreationFunction creationFunction) {
(void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, prv::Provider **provider) {
(void)RequestCreateProvider::subscribe([expectedName = typeName, creationFunction](const std::string &name, bool skipLoadInterface, bool selectProvider, std::shared_ptr<prv::Provider> *provider) {
if (name != expectedName) return;
auto newProvider = creationFunction();
if (provider != nullptr) {
*provider = newProvider.get();
*provider = newProvider;
ImHexApi::Provider::add(std::move(newProvider), skipLoadInterface, selectProvider);
}
});
@ -1395,6 +1413,40 @@ namespace hex {
}
namespace ContentRegistry::MCP {
namespace impl {
mcp::Server& getMcpServerInstance() {
static AutoReset<std::unique_ptr<mcp::Server>> server;
if (*server == nullptr)
server = std::make_unique<mcp::Server>();
return **server;
}
static bool s_mcpEnabled = false;
void setEnabled(bool enabled) {
s_mcpEnabled = enabled;
}
}
bool isEnabled() {
return impl::s_mcpEnabled;
}
bool isConnected() {
return impl::getMcpServerInstance().isConnected();
}
void registerTool(std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function) {
impl::getMcpServerInstance().addPrimitive("tools", capabilities, function);
}
}
namespace ContentRegistry::Experiments {
namespace impl {

View File

@ -294,8 +294,8 @@ namespace hex {
namespace ImHexApi::Provider {
static i64 s_currentProvider = -1;
static AutoReset<std::vector<std::unique_ptr<prv::Provider>>> s_providers;
static AutoReset<std::map<prv::Provider*, std::unique_ptr<prv::Provider>>> s_providersToRemove;
static AutoReset<std::vector<std::shared_ptr<prv::Provider>>> s_providers;
static AutoReset<std::map<prv::Provider*, std::shared_ptr<prv::Provider>>> s_providersToRemove;
namespace impl {
@ -382,7 +382,7 @@ namespace hex {
});
}
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
void add(std::shared_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
std::scoped_lock lock(impl::s_providerMutex);
if (TaskManager::getRunningTaskCount() > 0)
@ -491,8 +491,8 @@ namespace hex {
});
}
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
prv::Provider* result = nullptr;
std::shared_ptr<prv::Provider> createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface, bool select) {
std::shared_ptr<prv::Provider> result = nullptr;
RequestCreateProvider::post(unlocalizedName, skipLoadInterface, select, &result);
return result;

View File

@ -104,9 +104,21 @@ namespace hex {
for (const auto &entry : json.items()) {
auto value = entry.value().get<std::string>();
// Skip empty values
if (value.empty())
continue;
// Handle references to files
if (value.starts_with("#@")) {
try {
value = path.callback(value.substr(2));
} catch (std::exception &e) {
log::error("Failed to load localization file reference '{}': {}", entry.key(), e.what());
continue;
}
}
localizations.try_emplace(LangConst::hash(entry.key()), std::move(value));
}
} catch (std::exception &e) {

View File

@ -7,6 +7,8 @@
#include <ranges>
#include <jthread.hpp>
#include <hex/helpers/debugging.hpp>
#include <hex/trace/exceptions.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
@ -310,6 +312,8 @@ namespace hex {
}
try {
trace::enableExceptionCaptureForCurrentThread();
// Set the thread name to the name of the task
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
@ -323,15 +327,21 @@ namespace hex {
} catch (const std::exception &e) {
log::error("Exception in task '{}': {}", task->m_unlocalizedName.get(), e.what());
dbg::printStackTrace(trace::getStackTrace());
// Handle the task throwing an uncaught exception
task->exception(e.what());
} catch (...) {
log::error("Exception in task '{}'", task->m_unlocalizedName.get());
dbg::printStackTrace(trace::getStackTrace());
// Handle the task throwing an uncaught exception of unknown type
task->exception("Unknown Exception");
}
trace::disableExceptionCaptureForCurrentThread();
s_currentTask = nullptr;
task->finish();
}

View File

@ -32,6 +32,8 @@ namespace hex {
ImGuiID s_activeHelpId;
bool s_helpHoverActive = false;
AutoReset<std::function<std::function<void()>(const std::string &)>> s_renderer;
class IDStack {
public:
@ -126,6 +128,17 @@ namespace hex {
}
}
});
if (*s_renderer == nullptr) {
*s_renderer = [](const std::string &message) {
return [message] {
ImGui::PushTextWrapPos(300_scaled);
ImGui::TextUnformatted(message.c_str());
ImGui::PopTextWrapPos();
ImGui::NewLine();
};
};
}
}
const std::map<std::string, TutorialManager::Tutorial>& TutorialManager::getTutorials() {
@ -333,30 +346,29 @@ namespace hex {
ImGui::SetNextWindowPos(position, ImGuiCond_Always, pivot);
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
if (ImGui::Begin("##TutorialMessage", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::SetNextWindowSize(ImVec2(300_scaled, 0));
if (ImGui::Begin(message->unlocalizedTitle.empty() ? "##TutorialMessage" : Lang(message->unlocalizedTitle), nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoFocusOnAppearing)) {
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
if (!message->unlocalizedTitle.empty())
ImGuiExt::Header(Lang(message->unlocalizedTitle), true);
auto &step = s_currentTutorial->second.m_currentStep;
if (!message->unlocalizedMessage.empty()) {
ImGui::PushTextWrapPos(300_scaled);
ImGui::TextUnformatted(Lang(message->unlocalizedMessage));
ImGui::PopTextWrapPos();
step->m_drawFunction();
ImGui::NewLine();
ImGui::NewLine();
}
ImGui::BeginDisabled(s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_steps.begin());
if (ImGui::ArrowButton("Backwards", ImGuiDir_Left)) {
ImGui::BeginDisabled(step == s_currentTutorial->second.m_steps.begin());
if (ImGuiExt::DimmedArrowButton("Backwards", ImGuiDir_Left)) {
s_currentTutorial->second.m_currentStep->advance(-1);
}
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(!message->allowSkip && s_currentTutorial->second.m_currentStep == s_currentTutorial->second.m_latestStep);
if (ImGui::ArrowButton("Forwards", ImGuiDir_Right)) {
s_currentTutorial->second.m_currentStep->advance(1);
ImGui::SetCursorPosX(ImGui::GetWindowWidth() - ImGui::GetFrameHeight() - ImGui::GetStyle().WindowPadding.x);
ImGui::BeginDisabled(!message->allowSkip && step == s_currentTutorial->second.m_latestStep);
if (ImGuiExt::DimmedArrowButton("Forwards", ImGuiDir_Right)) {
step->advance(1);
}
ImGui::EndDisabled();
}
@ -387,6 +399,10 @@ namespace hex {
s_highlightDisplays->clear();
}
void TutorialManager::setRenderer(std::function<DrawFunction(const std::string &)> renderer) {
s_renderer = std::move(renderer);
}
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
auto &newStep = m_steps.emplace_back(this);
m_currentStep = m_steps.end();
@ -402,6 +418,9 @@ namespace hex {
return;
m_currentStep->addHighlights();
if (m_currentStep->m_message.has_value())
m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_currentStep->m_message->unlocalizedMessage));
}
void TutorialManager::Tutorial::Step::addHighlights() const {
@ -426,8 +445,12 @@ namespace hex {
std::advance(m_parent->m_latestStep, steps);
std::advance(m_parent->m_currentStep, steps);
if (m_parent->m_currentStep != m_parent->m_steps.end())
if (m_parent->m_currentStep != m_parent->m_steps.end()) {
m_parent->m_currentStep->addHighlights();
if (m_message.has_value())
m_parent->m_currentStep->m_drawFunction = (*s_renderer)(Lang(m_parent->m_currentStep->m_message->unlocalizedMessage));
}
else
s_currentTutorial = s_tutorials->end();
}

View File

@ -1,4 +1,6 @@
#include <hex/helpers/debugging.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/trace/stacktrace.hpp>
namespace hex::dbg {
@ -21,4 +23,23 @@ namespace hex::dbg {
s_debugMode = enabled;
}
[[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString) {
log::error("Assertion failed: {} at {}:{} => {}", exprString, file, line, function);
const auto stackTrace = trace::getStackTrace();
dbg::printStackTrace(stackTrace);
std::abort();
}
void printStackTrace(const trace::StackTraceResult &stackTrace) {
log::fatal("Printing stacktrace using implementation '{}'", stackTrace.implementationName);
for (const auto &stackFrame : stackTrace.stackFrames) {
if (stackFrame.line == 0)
log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
else
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
}
}
}

View File

@ -97,6 +97,13 @@ namespace hex::paths {
}
static std::vector<std::fs::path> getPluginPaths() {
// If running from an AppImage, only allow loaded plugins from inside it
#if defined(OS_LINUX)
if(const char* appdir = std::getenv("APPDIR")) { // check for AppImage environment
return {std::string(appdir) + "/usr/lib/imhex"};
}
#endif
std::vector<std::fs::path> paths = getDataPaths(true);
// Add the system plugin directory to the path if one was provided at compile time

View File

@ -12,6 +12,7 @@
#include <mutex>
#include <chrono>
#include <fmt/chrono.h>
#include <hex/helpers/debugging.hpp>
#if defined(OS_WINDOWS)
#include <Windows.h>
@ -149,14 +150,6 @@ namespace hex::log {
);
}
void assertionHandler(const char* exprString, const char* file, int line) {
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
#if defined (DEBUG)
std::abort();
#endif
}
namespace color {
fmt::color debug() { return fmt::color::medium_sea_green; }

View File

@ -0,0 +1,38 @@
#include <hex/mcp/client.hpp>
#include <hex/mcp/server.hpp>
#include <hex.hpp>
#include <string>
#include <cstdlib>
#include <fmt/format.h>
#include <hex/helpers/logger.hpp>
#include <nlohmann/json.hpp>
#include <wolv/net/socket_client.hpp>
namespace hex::mcp {
int Client::run(std::istream &input, std::ostream &output) {
wolv::net::SocketClient client(wolv::net::SocketClient::Type::TCP, true);
client.connect("127.0.0.1", Server::McpInternalPort);
if (!client.isConnected()) {
log::resumeLogging();
log::error("Cannot connect to ImHex. Do you have an instance running and is the MCP server enabled?");
return EXIT_FAILURE;
}
while (true) {
std::string request;
std::getline(input, request);
client.writeString(request);
auto response = client.readString();
if (!response.empty() && response.front() != 0x00)
output << response << std::endl;
}
return EXIT_SUCCESS;
}
}

View File

@ -0,0 +1,226 @@
#include <hex/mcp/server.hpp>
#include <string>
#include <fmt/format.h>
#include <hex/helpers/logger.hpp>
#include <nlohmann/json.hpp>
#include <wolv/net/socket_client.hpp>
#include <hex/api/imhex_api/system.hpp>
namespace hex::mcp {
class JsonRpc {
public:
explicit JsonRpc(const std::string &request) : m_request(request) { }
struct MethodNotFoundException : std::exception {};
struct InvalidParametersException : std::exception {};
std::optional<std::string> execute(auto callback) {
try {
auto requestJson = nlohmann::json::parse(m_request);
if (requestJson.is_array()) {
return handleBatchedMessages(requestJson, callback).transform([](const auto &response) { return response.dump(); });
} else {
return handleMessage(requestJson, callback).transform([](const auto &response) { return response.dump(); });
}
} catch (const MethodNotFoundException &) {
return createErrorMessage(ErrorCode::MethodNotFound, "Method not found").dump();
} catch (const InvalidParametersException &) {
return createErrorMessage(ErrorCode::InvalidParams, "Invalid params").dump();
} catch (const nlohmann::json::parse_error &) {
return createErrorMessage(ErrorCode::ParseError, "Parse error").dump();
} catch (const std::exception &e) {
return createErrorMessage(ErrorCode::InternalError, e.what()).dump();
}
}
private:
std::optional<nlohmann::json> handleMessage(const nlohmann::json &request, auto callback) {
// Validate JSON-RPC request
if (!request.contains("jsonrpc") || request["jsonrpc"] != "2.0" ||
!request.contains("method") || !request["method"].is_string()) {
m_id = request.contains("id") ? std::optional(request["id"].get<int>()) : std::nullopt;
return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request").dump();
}
m_id = request.contains("id") ? std::optional(request["id"].get<int>()) : std::nullopt;
// Execute the method
auto result = callback(request["method"].get<std::string>(), request.value("params", nlohmann::json::object()));
if (!m_id.has_value())
return std::nullopt;
return createResponseMessage(result);
}
std::optional<nlohmann::json> handleBatchedMessages(const nlohmann::json &request, auto callback) {
if (!request.is_array()) {
return createErrorMessage(ErrorCode::InvalidRequest, "Invalid Request").dump();
}
nlohmann::json responses = nlohmann::json::array();
for (const auto &message : request) {
auto response = handleMessage(message, callback);
if (response.has_value())
responses.push_back(*response);
}
if (responses.empty())
return std::nullopt;
return responses.dump();
}
enum class ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
};
nlohmann::json createDefaultMessage() {
nlohmann::json message;
message["jsonrpc"] = "2.0";
if (m_id.has_value())
message["id"] = m_id.value();
else
message["id"] = nullptr;
return message;
}
nlohmann::json createErrorMessage(ErrorCode code, const std::string &message) {
auto json = createDefaultMessage();
json["error"] = {
{ "code", int(code) },
{ "message", message }
};
return json;
}
nlohmann::json createResponseMessage(const nlohmann::json &result) {
auto json = createDefaultMessage();
json["result"] = result;
return json;
}
private:
std::string m_request;
std::optional<int> m_id;
};
Server::Server() : m_server(McpInternalPort, 1024, 1, true) {
}
Server::~Server() {
this->shutdown();
}
void Server::listen() {
m_server.accept([this](auto, const std::vector<u8> &data) -> std::vector<u8> {
std::string request(data.begin(), data.end());
log::debug("MCP ----> {}", request);
JsonRpc rpc(request);
auto response = rpc.execute([this](const std::string &method, const nlohmann::json &params) -> nlohmann::json {
if (method == "initialize") {
return handleInitialize();
} else if (method.starts_with("notifications/")) {
handleNotifications(method.substr(14), params);
return {};
} else if (method.ends_with("/list")) {
auto primitiveName = method.substr(0, method.size() - 5);
if (m_primitives.contains(primitiveName)) {
nlohmann::json capabilitiesList = nlohmann::json::array();
for (const auto &[name, primitive] : m_primitives[primitiveName]) {
capabilitiesList.push_back(primitive.capabilities);
}
nlohmann::json result;
result[primitiveName] = capabilitiesList;
return result;
}
} else if (method.ends_with("/call")) {
auto primitive = method.substr(0, method.size() - 5);
if (auto primitiveIt = m_primitives.find(primitive); primitiveIt != m_primitives.end()) {
auto name = params.value("name", "");
if (auto functionIt = primitiveIt->second.find(name); functionIt != primitiveIt->second.end()) {
return functionIt->second.function(params.value("arguments", nlohmann::json::object()));
}
}
}
throw JsonRpc::MethodNotFoundException();
});
log::debug("MCP <---- {}", response.value_or("<Nothing>"));
if (response.has_value())
return { response->begin(), response->end() };
else
return std::vector<u8>{ 0x00 };
}, [this](auto) {
log::info("MCP client disconnected");
m_connected = false;
}, true);
}
void Server::shutdown() {
m_server.shutdown();
}
void Server::disconnect() {
m_server.disconnectClients();
}
void Server::addPrimitive(std::string type, std::string_view capabilities, std::function<nlohmann::json(const nlohmann::json &params)> function) {
auto json = nlohmann::json::parse(capabilities);
auto name = json["name"].get<std::string>();
m_primitives[type][name] = {
json,
function
};
}
nlohmann::json Server::handleInitialize() {
constexpr static auto ServerName = "ImHex";
constexpr static auto ProtocolVersion = "2025-06-18";
return {
{ "protocolVersion", ProtocolVersion },
{
"capabilities",
{
{ "tools", nlohmann::json::object() },
},
},
{
"serverInfo", {
{ "name", ServerName },
{ "version", ImHexApi::System::getImHexVersion().get() }
}
}
};
}
void Server::handleNotifications(const std::string &method, [[maybe_unused]] const nlohmann::json &params) {
if (method == "initialized") {
m_connected = true;
}
}
bool Server::isConnected() {
return m_connected;
}
}

View File

@ -7,12 +7,14 @@
#include <cmath>
#include <cstring>
#include <optional>
#include <hex/api/content_registry/settings.hpp>
#include <hex/helpers/magic.hpp>
#include <wolv/io/file.hpp>
#include <wolv/literals.hpp>
#include <nlohmann/json.hpp>
#include <wolv/utils/string.hpp>
namespace hex::prv {
@ -24,6 +26,28 @@ namespace hex::prv {
}
IProviderDataBackupable::IProviderDataBackupable(Provider* provider) : m_provider(provider) {
m_shouldCreateBackups = ContentRegistry::Settings::read<bool>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.enable", true);
m_maxSize = ContentRegistry::Settings::read<u32>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.max_size", 1_MiB);
m_backupExtension = ContentRegistry::Settings::read<std::string>("hex.builtin.setting.general", "hex.builtin.setting.general.backups.file_backup.extension", ".bak");
}
void IProviderDataBackupable::createBackupIfNeeded(const std::fs::path &inputFilePath) {
if (!m_shouldCreateBackups || m_backupCreated)
return;
if (m_provider->getActualSize() > m_maxSize)
return;
const std::fs::path backupFilePath = wolv::util::toUTF8String(inputFilePath) + m_backupExtension;
if (wolv::io::fs::copyFile(inputFilePath, backupFilePath, std::fs::copy_options::overwrite_existing)) {
if (wolv::io::fs::exists(backupFilePath)) {
m_backupCreated = true;
log::info("Created backup of provider data at '{}'", backupFilePath.string());
}
}
}
Provider::Provider() : m_undoRedoStack(this), m_id(s_idCounter++) {

View File

@ -1218,6 +1218,21 @@ namespace ImGuiExt {
return res;
}
bool DimmedArrowButton(const char *id, ImGuiDir dir, ImVec2 size) {
PushStyleColor(ImGuiCol_ButtonHovered, GetCustomColorU32(ImGuiCustomCol_DescButtonHovered));
PushStyleColor(ImGuiCol_Button, GetCustomColorU32(ImGuiCustomCol_DescButton));
PushStyleColor(ImGuiCol_Text, GetColorU32(ImGuiCol_ButtonActive));
PushStyleColor(ImGuiCol_ButtonActive, GetCustomColorU32(ImGuiCustomCol_DescButtonActive));
PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.5 * hex::ImHexApi::System::getGlobalScale());
bool res = ArrowButtonEx(id, dir, size);
PopStyleColor(4);
PopStyleVar(1);
return res;
}
bool DimmedButtonToggle(const char *icon, bool *v, ImVec2 size, ImVec2 iconOffset) {
bool pushed = false;
bool toggled = false;

View File

@ -1,8 +1,10 @@
#include <hex/ui/view.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/api/imhex_api/provider.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/api/tutorial_manager.hpp>
#include <hex/providers/provider.hpp>
#include <imgui.h>
@ -116,4 +118,63 @@ namespace hex {
}
void View::Window::draw(ImGuiWindowFlags extraFlags) {
if (this->shouldDraw()) {
if (!allowScroll())
extraFlags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
if (ImGui::Begin(title.c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) {
TutorialManager::setLastItemInteractiveHelpPopup([this]{ this->drawHelpText(); });
this->drawContent();
}
ImGui::End();
}
}
void View::Special::draw(ImGuiWindowFlags extraFlags) {
std::ignore = extraFlags;
if (this->shouldDraw()) {
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
this->drawContent();
}
}
void View::Floating::draw(ImGuiWindowFlags extraFlags) {
Window::draw(extraFlags | ImGuiWindowFlags_NoDocking);
}
void View::Scrolling::draw(ImGuiWindowFlags extraFlags) {
Window::draw(extraFlags);
}
void View::Modal::draw(ImGuiWindowFlags extraFlags) {
if (this->shouldDraw()) {
if (this->getWindowOpenState())
ImGui::OpenPopup(View::toWindowName(this->getUnlocalizedName()).c_str());
ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
ImGui::SetNextWindowSizeConstraints(this->getMinSize(), this->getMaxSize());
const auto title = fmt::format("{} {}", this->getIcon(), View::toWindowName(this->getUnlocalizedName()));
if (ImGui::BeginPopupModal(title.c_str(), this->hasCloseButton() ? &this->getWindowOpenState() : nullptr, ImGuiWindowFlags_NoCollapse | extraFlags | this->getWindowFlags())) {
this->drawContent();
ImGui::EndPopup();
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
this->getWindowOpenState() = false;
}
}
void View::FullScreen::draw(ImGuiWindowFlags extraFlags) {
std::ignore = extraFlags;
this->drawContent();
this->drawAlwaysVisibleContent();
}
}

View File

@ -25,10 +25,17 @@
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
namespace hex::log::impl {
void assertionHandler(const char* expr_str, const char* file, int line);
namespace hex::dbg {
[[noreturn]] void assertionHandler(const char* file, int line, const char *function, const char* exprString);
}
#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::log::impl::assertionHandler(#_EXPR, __FILE__, __LINE__); } } while(0)
#if defined(__PRETTY_FUNCTION__)
#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __PRETTY_FUNCTION__, #_EXPR); } } while(0)
#elif defined(__FUNCSIG__)
#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCSIG__, #_EXPR); } } while(0)
#else
#define IM_ASSERT(_EXPR) do { if (!(_EXPR)) [[unlikely]] { hex::dbg::assertionHandler(__FILE__, __LINE__, __FUNCTION__, #_EXPR); } } while(0)
#endif
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.

View File

@ -81,6 +81,9 @@ if (IMHEX_TRACE_EXCEPTIONS)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
target_link_options(tracing ${LIBIMHEX_LIBRARY_TYPE_PUBLIC} "-Wl,--wrap=__cxa_throw")
target_compile_definitions(tracing ${LIBIMHEX_LIBRARY_TYPE_PRIVATE} HEX_WRAP_CXA_THROW)
target_link_options(tracing ${LIBIMHEX_LIBRARY_TYPE_PUBLIC} "-Wl,--wrap=_ZSt21__glibcxx_assert_failPKciS0_S0_")
target_compile_definitions(tracing ${LIBIMHEX_LIBRARY_TYPE_PRIVATE} HEX_WRAP_GLIBCXX_ASSERT_FAIL)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# Not supported currently
endif()

View File

@ -6,7 +6,12 @@
namespace hex::trace {
using AssertionHandler = void(*)(const char* file, int line, const char *function, const char* exprString);
std::optional<StackTraceResult> getLastExceptionStackTrace();
void setAssertionHandler(AssertionHandler handler);
void enableExceptionCaptureForCurrentThread();
void disableExceptionCaptureForCurrentThread();
}

View File

@ -2,8 +2,9 @@
namespace hex::trace {
static std::optional<StackTraceResult> s_lastExceptionStackTrace;
static thread_local std::optional<StackTraceResult> s_lastExceptionStackTrace;
static thread_local bool s_threadExceptionCaptureEnabled = false;
static AssertionHandler s_assertionHandler = nullptr;
std::optional<StackTraceResult> getLastExceptionStackTrace() {
if (!s_lastExceptionStackTrace.has_value())
@ -15,18 +16,26 @@ namespace hex::trace {
return result;
}
void setAssertionHandler(AssertionHandler handler) {
s_assertionHandler = handler;
}
void enableExceptionCaptureForCurrentThread() {
s_threadExceptionCaptureEnabled = true;
}
void disableExceptionCaptureForCurrentThread() {
s_threadExceptionCaptureEnabled = false;
}
}
#if defined(HEX_WRAP_CXA_THROW)
extern "C" {
[[noreturn]] void __real___cxa_throw(void* thrownException, void* type, void (*destructor)(void*));
[[noreturn]] void __wrap___cxa_throw(void* thrownException, void* type, void (*destructor)(void*)) {
[[noreturn]] void __real___cxa_throw(void* thrownException, std::type_info* type, void (*destructor)(void*));
[[noreturn]] void __wrap___cxa_throw(void* thrownException, std::type_info* type, void (*destructor)(void*)) {
if (hex::trace::s_threadExceptionCaptureEnabled)
hex::trace::s_lastExceptionStackTrace = hex::trace::getStackTrace();
@ -35,4 +44,23 @@ namespace hex::trace {
}
#endif
#if defined(HEX_WRAP_GLIBCXX_ASSERT_FAIL)
extern "C" {
[[noreturn]] void __real__ZSt21__glibcxx_assert_failPKciS0_S0_(const char* file, int line, const char* function, const char* condition);
[[noreturn]] void __wrap__ZSt21__glibcxx_assert_failPKciS0_S0_(const char* file, int line, const char* function, const char* condition) {
if (hex::trace::s_assertionHandler != nullptr) {
hex::trace::s_assertionHandler(file, line, function, condition);
} else {
__real__ZSt21__glibcxx_assert_failPKciS0_S0_(file, line, function, condition);
}
std::abort();
}
}
#endif

View File

@ -16,6 +16,7 @@
#include <csignal>
#include <exception>
#include <typeinfo>
#include <hex/helpers/debugging.hpp>
#include <hex/helpers/utils.hpp>
#if defined(IMGUI_TEST_ENGINE)
@ -71,23 +72,12 @@ namespace hex::crash {
log::warn("Could not write crash.json file!");
}
static void printStackTrace() {
auto stackTraceResult = trace::getStackTrace();
log::fatal("Printing stacktrace using implementation '{}'", stackTraceResult.implementationName);
for (const auto &stackFrame : stackTraceResult.stackFrames) {
if (stackFrame.line == 0)
log::fatal(" ({}) | {}", stackFrame.file, stackFrame.function);
else
log::fatal(" ({}:{}) | {}", stackFrame.file, stackFrame.line, stackFrame.function);
}
}
static void callCrashHandlers(const std::string &msg) {
// Call the crash callback
crashCallback(msg);
// Print the stacktrace to the console or log file
printStackTrace();
dbg::printStackTrace(trace::getStackTrace());
// Flush all streams
std::fflush(stdout);
@ -188,6 +178,7 @@ namespace hex::crash {
// Setup functions to handle signals, uncaught exception, or similar stuff that will crash ImHex
void setupCrashHandlers() {
trace::initialize();
trace::setAssertionHandler(dbg::assertionHandler);
// Register signal handlers
{

View File

@ -68,6 +68,7 @@ add_imhex_plugin(
source/content/providers/base64_provider.cpp
source/content/providers/view_provider.cpp
source/content/providers/udp_provider.cpp
source/content/providers/command_provider.cpp
source/content/tools/ascii_table.cpp
source/content/tools/base_converter.cpp

View File

@ -30,6 +30,7 @@ namespace hex::plugin::builtin {
void handleValidatePluginCommand(const std::vector<std::string> &args);
void handleSaveEditorCommand(const std::vector<std::string> &args);
void handleFileInfoCommand(const std::vector<std::string> &args);
void handleMCPCommand(const std::vector<std::string> &args);
void registerCommandForwarders();

View File

@ -3,7 +3,6 @@
#include <content/views/view_hex_editor.hpp>
#include <hex/api/localization_manager.hpp>
#include <ui/text_editor.hpp>
#include <string>
namespace hex::plugin::builtin {
@ -21,6 +20,5 @@ namespace hex::plugin::builtin {
private:
std::string m_decodedString;
ui::TextEditor m_editor;
};
}

View File

@ -0,0 +1,54 @@
#pragma once
#include <hex/providers/provider.hpp>
#include <wolv/net/socket_client.hpp>
#include <fonts/vscode_icons.hpp>
#include <hex/providers/cached_provider.hpp>
namespace hex::plugin::builtin {
class CommandProvider : public prv::CachedProvider,
public prv::IProviderLoadInterface {
public:
CommandProvider();
~CommandProvider() override = default;
[[nodiscard]] bool isAvailable() const override;
[[nodiscard]] bool isReadable() const override;
[[nodiscard]] bool isWritable() const override;
[[nodiscard]] bool isResizable() const override;
[[nodiscard]] bool isSavable() const override;
void readFromSource(u64 offset, void *buffer, size_t size) override;
void writeToSource(u64 offset, const void *buffer, size_t size) override;
[[nodiscard]] u64 getSourceSize() const override;
void save() override;
[[nodiscard]] std::string getName() const override;
[[nodiscard]] bool open() override;
void close() override;
bool drawLoadInterface() override;
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] UnlocalizedString getTypeName() const override {
return "hex.builtin.provider.command";
}
[[nodiscard]] const char* getIcon() const override {
return ICON_VS_TERMINAL_CMD;
}
protected:
std::string m_name;
std::string m_readCommand, m_writeCommand, m_sizeCommand, m_resizeCommand, m_saveCommand;
bool m_open = false;
};
}

View File

@ -13,9 +13,10 @@ namespace hex::plugin::builtin {
class FileProvider : public prv::Provider,
public prv::IProviderDataDescription,
public prv::IProviderFilePicker,
public prv::IProviderMenuItems {
public prv::IProviderMenuItems,
public prv::IProviderDataBackupable {
public:
FileProvider() = default;
FileProvider() : IProviderDataBackupable(this) {}
~FileProvider() override = default;
[[nodiscard]] bool isAvailable() const override;

View File

@ -86,6 +86,7 @@ namespace hex::plugin::builtin {
struct Process {
u32 id;
std::string name;
std::string commandLine;
ImGuiExt::Texture icon;
};

View File

@ -3,8 +3,6 @@
#include <hex/ui/view.hpp>
#include <hex/api/imhex_api/bookmarks.hpp>
#include <ui/text_editor.hpp>
#include <list>
#include <ui/markdown.hpp>

View File

@ -57,10 +57,6 @@ namespace hex::plugin::builtin {
void reloadCustomNodes();
void updateNodePositions();
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override {
return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
}
std::vector<Workspace*> &getWorkspaceStack() { return *m_workspaceStack; }
private:

View File

@ -13,7 +13,7 @@ namespace hex::plugin::builtin {
void drawContent() override;
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override {
return ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
return ImGuiWindowFlags_NoNavInputs;
}
bool shouldDefaultFocus() const override { return true; }

View File

@ -27,7 +27,7 @@ namespace hex::plugin::builtin {
}
ImGuiWindowFlags getWindowFlags() const override {
return View::Floating::getWindowFlags() | ImGuiWindowFlags_NoResize;
return ImGuiWindowFlags_NoResize;
}
private:

View File

@ -7,7 +7,7 @@
namespace hex::plugin::builtin {
class ViewInformation : public View::Window {
class ViewInformation : public View::Scrolling {
public:
explicit ViewInformation();
~ViewInformation() override = default;

View File

@ -66,9 +66,6 @@ namespace hex::plugin::builtin {
}
void drawContent() override;
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override {
return ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
}
void setPopupWindowHeight(u32 height) { m_popupWindowHeight = height; }
u32 getPopupWindowHeight() const { return m_popupWindowHeight; }
@ -140,7 +137,8 @@ namespace hex::plugin::builtin {
std::atomic<bool> m_dangerousFunctionCalled = false;
std::atomic<DangerousFunctionPerms> m_dangerousFunctionsAllowed = DangerousFunctionPerms::Ask;
bool m_autoLoadPatterns = true;
bool m_suggestSupportedPatterns = true;
bool m_autoApplyPatterns = false;
PerProvider<ui::VisualizerDrawer> m_visualizerDrawer;
bool m_tooltipJustOpened = false;
@ -152,12 +150,12 @@ namespace hex::plugin::builtin {
std::mutex m_logMutex;
PerProvider<ui::TextEditor::Coordinates> m_cursorPosition;
PerProvider<ImVec2> m_scroll;
PerProvider<ImVec2> m_consoleScroll;
PerProvider<ui::TextEditor::Coordinates> m_consoleCursorPosition;
PerProvider<bool> m_cursorNeedsUpdate;
PerProvider<bool> m_consoleCursorNeedsUpdate;
PerProvider<ui::TextEditor::Selection> m_selection;
PerProvider<ui::TextEditor::Selection> m_consoleSelection;
PerProvider<ui::TextEditor::Range> m_selection;
PerProvider<ui::TextEditor::Range> m_consoleSelection;
PerProvider<size_t> m_consoleLongestLineLength;
PerProvider<ui::TextEditor::Breakpoints> m_breakpoints;
PerProvider<std::optional<pl::core::err::PatternLanguageError>> m_lastEvaluationError;

View File

@ -7,7 +7,7 @@
namespace hex::plugin::builtin {
class ViewTools : public View::Window {
class ViewTools : public View::Scrolling {
public:
ViewTools();
~ViewTools() override = default;

View File

@ -26,7 +26,7 @@ namespace hex::plugin::builtin {
}
ImGuiWindowFlags getWindowFlags() const override {
return Floating::getWindowFlags() | ImGuiWindowFlags_NoResize;
return ImGuiWindowFlags_NoResize;
}
private:

View File

@ -444,10 +444,10 @@
"hex.builtin.setting.folders.description": "Gib zusätzliche Orderpfade an, in welchen Pattern, Scripts, Yara Rules und anderes gesucht wird",
"hex.builtin.setting.folders.remove_folder": "Ausgewählter Ordner von Liste entfernen",
"hex.builtin.setting.general": "Allgemein",
"hex.builtin.setting.general.auto_backup_time": "Periodisches Projekt Backup",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Alle {0}min {1}s",
"hex.builtin.setting.general.auto_backup_time.format.simple": "Alle {0}s",
"hex.builtin.setting.general.auto_load_patterns": "Automatisches Laden unterstützter Pattern",
"hex.builtin.setting.general.backups.auto_backup_time": "Periodisches Projekt Backup",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Alle {0}min {1}s",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Alle {0}s",
"hex.builtin.setting.general.auto_apply_patterns": "Automatisches Laden unterstützter Pattern",
"hex.builtin.setting.general.network": "Netzwerk",
"hex.builtin.setting.general.network_interface": "Netzwerk Interface Aktivieren",
"hex.builtin.setting.general.patterns": "Patterns",

View File

@ -54,6 +54,7 @@
"hex.builtin.achievement.misc.download_from_store.name": "There's an app for that",
"hex.builtin.achievement.misc.download_from_store.desc": "Download any item from the Content Store",
"hex.builtin.background_service.network_interface": "Network Interface",
"hex.builtin.setting.general.mcp_server": "MCP Server support",
"hex.builtin.background_service.auto_backup": "Auto Backup",
"hex.builtin.command.calc.desc": "Calculator",
"hex.builtin.command.convert.desc": "Unit conversion",
@ -123,6 +124,7 @@
"hex.builtin.layouts.none.restore_default": "Restore default layout",
"hex.builtin.menu.edit": "Edit",
"hex.builtin.menu.edit.bookmark.create": "Create Bookmark",
"hex.builtin.menu.edit.find.find_selection": "Find occurrences",
"hex.builtin.view.hex_editor.menu.edit.redo": "Redo",
"hex.builtin.view.hex_editor.menu.edit.undo": "Undo",
"hex.builtin.menu.extras": "Extras",
@ -406,7 +408,7 @@
"hex.builtin.popup.exit_application.title": "Exit Application?",
"hex.builtin.popup.waiting_for_tasks.title": "Waiting for Tasks",
"hex.builtin.popup.crash_recover.title": "Crash recovery",
"hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and advert a crash",
"hex.builtin.popup.crash_recover.message": "An exception was thrown, but ImHex was able to catch it and avert a crash",
"hex.builtin.popup.foreground_task.title": "Please Wait...",
"hex.builtin.popup.blocking_task.title": "Running Task",
"hex.builtin.popup.blocking_task.desc": "A task is currently executing.",
@ -420,6 +422,16 @@
"hex.builtin.provider.tooltip.show_more": "Hold SHIFT for more information",
"hex.builtin.provider.error.open": "Failed to open data provider: {}",
"hex.builtin.provider.base64": "Base64 File",
"hex.builtin.provider.command": "Terminal Command",
"hex.builtin.provider.command.name": "Command {0}",
"hex.builtin.provider.command.load.name": "Name",
"hex.builtin.provider.command.load.hint": "Enter commands to be executed for specific functions.\n\nThe {address} and {size} placeholders will be replaced with the respective value",
"hex.builtin.provider.command.load.read_command": "Read Data Command",
"hex.builtin.provider.command.load.write_command": "Write Data Command",
"hex.builtin.provider.command.load.size_command": "Get Data Size Command",
"hex.builtin.provider.command.load.resize_command": "Resize Data Command",
"hex.builtin.provider.command.load.save_command": "Save Data Command",
"hex.builtin.provider.command.optional": "Optional",
"hex.builtin.provider.disk": "Raw Disk",
"hex.builtin.provider.disk.disk_size": "Disk Size",
"hex.builtin.provider.disk.elevation": "Accessing raw disks most likely requires elevated privileges",
@ -483,12 +495,17 @@
"hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more",
"hex.builtin.setting.folders.remove_folder": "Remove currently selected folder from list",
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.backups": "Backups",
"hex.builtin.setting.general.backups.auto_backup_time": "Periodically backup project",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Every {0}s",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Every {0}m {1}s",
"hex.builtin.setting.general.backups.file_backup.enable": "Backup data sources before modification if possible",
"hex.builtin.setting.general.backups.file_backup.max_size": "Max file size for file backups",
"hex.builtin.setting.general.backups.file_backup.extension": "Backup file extension",
"hex.builtin.setting.general.patterns": "Patterns",
"hex.builtin.setting.general.network": "Network",
"hex.builtin.setting.general.auto_backup_time": "Periodically backup project",
"hex.builtin.setting.general.auto_backup_time.format.simple": "Every {0}s",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Every {0}m {1}s",
"hex.builtin.setting.general.auto_load_patterns": "Auto-load supported pattern",
"hex.builtin.setting.general.auto_apply_patterns": "Auto-load supported pattern",
"hex.builtin.setting.general.suggest_patterns": "Suggest patterns based on loaded data",
"hex.builtin.setting.general.server_contact": "Enable update checks and usage statistics",
"hex.builtin.setting.general.max_mem_file_size": "Max file size to load into RAM",
"hex.builtin.setting.general.max_mem_file_size.desc": "Small files are loaded into memory to prevent them from being modified directly on disk.\n\nIncreasing this size allows larger files to be loaded into memory before ImHex resorts to streaming in data from disk.",
@ -750,6 +767,7 @@
"hex.builtin.view.data_inspector.table.value": "Value",
"hex.builtin.view.data_inspector.custom_row.title": "Adding custom rows",
"hex.builtin.view.data_inspector.custom_row.hint": "It's possible to define custom data inspector rows by placing pattern language scripts in the <ImHex>/scripts/inspectors folder.\n\nCheck the documentation for more information.",
"hex.builtin.view.data_inspector.toggle_endianness": "Toggle Endianness",
"hex.builtin.view.data_processor.continuous_evaluation": "Continuous Evaluation",
"hex.builtin.view.data_processor.help_text": "Right click to add a new node",
"hex.builtin.view.data_processor.menu.file.load_processor": "Load data processor...",
@ -1031,6 +1049,9 @@
"hex.builtin.view.pattern_data.virtual_files.no_virtual_files": "Visualize regions of data as a virtual folder structure by adding them using the hex::core::add_virtual_file function.",
"hex.builtin.view.pattern_editor.no_env_vars": "The content of Environment Variables created here can be accessed from Pattern scripts using the std::env function.",
"hex.builtin.view.pattern_editor.no_results": "no results",
"hex.builtin.view.pattern_editor.match_case_tooltip": "Match Case Alt-C",
"hex.builtin.view.pattern_editor.whole_word_tooltip": "Whole Word Alt-W",
"hex.builtin.view.pattern_editor.regex_tooltip": "Regex Alt-R",
"hex.builtin.view.pattern_editor.of": "of",
"hex.builtin.view.pattern_editor.open_pattern": "Open pattern",
"hex.builtin.view.pattern_editor.replace_hint": "Replace",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "Especifica rutas de búsqueda adicionales para patterns, scripts, Yara Rules y más",
"hex.builtin.setting.folders.remove_folder": "Eliminar la carpeta seleccionada de la lista",
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "Cargar automáticamente patterns soportados",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "Cargar automáticamente patterns soportados",
"hex.builtin.setting.general.network": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.patterns": "",

View File

@ -464,10 +464,10 @@
"hex.builtin.setting.general": "Général",
"hex.builtin.setting.general.patterns": "Modèles",
"hex.builtin.setting.general.network": "Réseau",
"hex.builtin.setting.general.auto_backup_time": "Sauvegarder périodiquement le projet",
"hex.builtin.setting.general.auto_backup_time.format.simple": "Toutes les {0}s",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Toutes les {0}m {1}s",
"hex.builtin.setting.general.auto_load_patterns": "Charger automatiquement les modèles pris en charge",
"hex.builtin.setting.general.backups.auto_backup_time": "Sauvegarder périodiquement le projet",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Toutes les {0}s",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Toutes les {0}m {1}s",
"hex.builtin.setting.general.auto_apply_patterns": "Charger automatiquement les modèles pris en charge",
"hex.builtin.setting.general.server_contact": "Activer les vérifications de mise à jour et les statistiques d'utilisation",
"hex.builtin.setting.general.max_mem_file_size": "Taille maximale du fichier à charger en mémoire",
"hex.builtin.setting.general.max_mem_file_size.desc": "Les petits fichiers sont chargés en mémoire pour éviter qu'ils ne soient modifiés directement sur le disque.\n\nL'augmentation de cette taille permet de charger des fichiers plus volumineux en mémoire avqu'ImHex ne se mette à diffuser les données depuis le disque.",

View File

@ -438,10 +438,10 @@
"hex.builtin.setting.general": "Általános",
"hex.builtin.setting.general.patterns": "Sablonok",
"hex.builtin.setting.general.network": "Hálózat",
"hex.builtin.setting.general.auto_backup_time": "A projekt automatikus mentése időközönként",
"hex.builtin.setting.general.auto_backup_time.format.simple": "Minden {0}mp",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Minden {0}p {1}mp",
"hex.builtin.setting.general.auto_load_patterns": "Támogatott sablonok automatikus betöltése",
"hex.builtin.setting.general.backups.auto_backup_time": "A projekt automatikus mentése időközönként",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Minden {0}mp",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Minden {0}p {1}mp",
"hex.builtin.setting.general.auto_apply_patterns": "Támogatott sablonok automatikus betöltése",
"hex.builtin.setting.general.server_contact": "Frissítések ellenőrzésének és használati statisztikák gyűjtésének engedélyezése",
"hex.builtin.setting.general.network_interface": "Hálózati interfész engedélyezése",
"hex.builtin.setting.general.save_recent_providers": "Nemrég használt források mentése",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "",
"hex.builtin.setting.folders.remove_folder": "",
"hex.builtin.setting.general": "Generali",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "Auto-caricamento del pattern supportato",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "Auto-caricamento del pattern supportato",
"hex.builtin.setting.general.network": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.patterns": "",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "パターン、スクリプト、ルールなどのための検索パスを指定して追加できます。",
"hex.builtin.setting.folders.remove_folder": "選択中のフォルダをリストから消去",
"hex.builtin.setting.general": "基本",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "対応するパターンを自動で読み込む",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "対応するパターンを自動で読み込む",
"hex.builtin.setting.general.network": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.patterns": "",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "패턴, 스크립트, YARA 규칙 등에 대한 추가 검색 경로를 지정합니다",
"hex.builtin.setting.folders.remove_folder": "목록에서 현재 선택된 폴더 제거",
"hex.builtin.setting.general": "일반",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "지원하는 패턴 자동으로 불러오기",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "지원하는 패턴 자동으로 불러오기",
"hex.builtin.setting.general.network": "네트워크",
"hex.builtin.setting.general.network_interface": "네트워크 인터페이스 사용",
"hex.builtin.setting.general.patterns": "패턴",

View File

@ -465,10 +465,10 @@
"hex.builtin.setting.general": "Ogólne",
"hex.builtin.setting.general.patterns": "Pattern",
"hex.builtin.setting.general.network": "Sieć",
"hex.builtin.setting.general.auto_backup_time": "Okresowe tworzenie kopii zapasowej projektu",
"hex.builtin.setting.general.auto_backup_time.format.simple": "Co {0}s",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Co {0}m {1}s",
"hex.builtin.setting.general.auto_load_patterns": "Automatycznie wczytaj obsługiwany pattern",
"hex.builtin.setting.general.backups.auto_backup_time": "Okresowe tworzenie kopii zapasowej projektu",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "Co {0}s",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Co {0}m {1}s",
"hex.builtin.setting.general.auto_apply_patterns": "Automatycznie wczytaj obsługiwany pattern",
"hex.builtin.setting.general.server_contact": "Włącz sprawdzanie aktualizacji i statystyki użytkowania",
"hex.builtin.setting.general.max_mem_file_size": "Maksymalny rozmiar pliku do wczytania do RAM",
"hex.builtin.setting.general.max_mem_file_size.desc": "Małe pliki są wczytywane do pamięci aby zapobiec ich bezpośredniej modyfikacji na dysku.\n\nZwiększenie tego rozmiaru pozwala większym plikom być wczytanym do pamięci zanim ImHex ucieknie się do strumieniowania danych z dysku.",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "Especifique caminhos de pesquisa adicionais para padrões, scripts, regras Yara e muito mais",
"hex.builtin.setting.folders.remove_folder": "Remover a pasta atualmente selecionada da lista",
"hex.builtin.setting.general": "General",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "Padrão compatível com carregamento automático",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "Padrão compatível com carregamento automático",
"hex.builtin.setting.general.network": "",
"hex.builtin.setting.general.network_interface": "",
"hex.builtin.setting.general.patterns": "",

View File

@ -453,10 +453,10 @@
"hex.builtin.setting.general": "Основное",
"hex.builtin.setting.general.patterns": "Шаблоны",
"hex.builtin.setting.general.network": "Сеть",
"hex.builtin.setting.general.auto_backup_time": "Делать резервные копии каждые",
"hex.builtin.setting.general.auto_backup_time.format.simple": "{0} секунд",
"hex.builtin.setting.general.auto_backup_time.format.extended": "{0} минут {1} секунд",
"hex.builtin.setting.general.auto_load_patterns": "Автоматически подгружать распознанные шаблоны",
"hex.builtin.setting.general.backups.auto_backup_time": "Делать резервные копии каждые",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "{0} секунд",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "{0} минут {1} секунд",
"hex.builtin.setting.general.auto_apply_patterns": "Автоматически подгружать распознанные шаблоны",
"hex.builtin.setting.general.server_contact": "Включить проверку обновлений и статистики использования",
"hex.builtin.setting.general.max_mem_file_size": "Макс. размер файла для сохранения в RAM",
"hex.builtin.setting.general.max_mem_file_size.desc": "Маленькие файлы загружаются в оперативную память, чтобы не сохранять изменения сразу на диск.\n\nУвеличение этого параметра позволит ImHex загружать более объёмные файлы в память.",

View File

@ -423,9 +423,9 @@
"hex.builtin.setting.folders.remove_folder": "Видалити поточну вибрану папку зі списку",
"hex.builtin.setting.general": "Основне",
"hex.builtin.setting.general.patterns": "Шаблони",
"hex.builtin.setting.general.auto_backup_time.format.simple": "кожні {0} секунд",
"hex.builtin.setting.general.auto_backup_time.format.extended": "Кожні {0} хвилин {1} секунд",
"hex.builtin.setting.general.auto_load_patterns": "Автоматично завантажувати розпізнані шаблони",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "кожні {0} секунд",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "Кожні {0} хвилин {1} секунд",
"hex.builtin.setting.general.auto_apply_patterns": "Автоматично завантажувати розпізнані шаблони",
"hex.builtin.setting.general.server_contact": "Увімкнути перевірку оновлень та статистику використання",
"hex.builtin.setting.general.network_interface": "Увімкнути мережевий інтерфейс",
"hex.builtin.setting.general.pattern_data_max_filter_items": "Максимальна кількість відфільтрованих елементів шаблону",
@ -585,7 +585,7 @@
"hex.builtin.provider.process_memory.macos_limitations": "macOS не дозволяє належним чином зчитувати пам'ять з інших процесів, навіть під час роботи як root. Якщо ввімкнено захист цілісності системи (SIP), він працює лише для програм, які не підписані або мають право \"Отримати дозвіл на завдання\", яке зазвичай застосовується лише до програм, скомпільованих вами самостійно.",
"hex.builtin.setting.experiments.description": "Експерименти — це функції, які ще знаходяться в стадії розробки і можуть працювати некоректно.\n\nНе соромтеся їх випробовувати та повідомляти про будь-які проблеми, з якими ви стикаєтеся!",
"hex.builtin.setting.general.network": "Мережа",
"hex.builtin.setting.general.auto_backup_time": "Робити резервні копії проекту",
"hex.builtin.setting.general.backups.auto_backup_time": "Робити резервні копії проекту",
"hex.builtin.setting.general.max_mem_file_size": "Максимальний розмір файлу для завантаження в RAM",
"hex.builtin.setting.general.max_mem_file_size.desc": "Невеликі файли завантажуються в пам'ять, щоб запобігти їх безпосередній модифікації на диску.\n\nЗбільшення цього розміру дозволяє завантажувати в пам'ять більші файли, перш ніж ImHex вдається до потокової передачі даних із диску.",
"hex.builtin.setting.general.sync_pattern_source": "Синхронізація вихідного коду шаблону між відкритими джерелами даних",

View File

@ -467,10 +467,10 @@
"hex.builtin.setting.general": "通用",
"hex.builtin.setting.general.patterns": "模式",
"hex.builtin.setting.general.network": "网络",
"hex.builtin.setting.general.auto_backup_time": "定期备份项目",
"hex.builtin.setting.general.auto_backup_time.format.simple": "每 {0}秒",
"hex.builtin.setting.general.auto_backup_time.format.extended": "每 {0}分 {1}秒",
"hex.builtin.setting.general.auto_load_patterns": "自动加载支持的模式",
"hex.builtin.setting.general.backups.auto_backup_time": "定期备份项目",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "每 {0}秒",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "每 {0}分 {1}秒",
"hex.builtin.setting.general.auto_apply_patterns": "自动加载支持的模式",
"hex.builtin.setting.general.server_contact": "启用更新检查和使用统计",
"hex.builtin.setting.general.max_mem_file_size": "要加载到内存中的文件的最大大小",
"hex.builtin.setting.general.max_mem_file_size.desc": "小文件会被加载到内存中,以防止直接在磁盘上修改它们。\n\n增大这个大小可以让更大的文件在ImHex从磁盘读取数据之前被加载到内存中。",

View File

@ -443,10 +443,10 @@
"hex.builtin.setting.folders.description": "Specify additional search paths for patterns, scripts, Yara rules and more",
"hex.builtin.setting.folders.remove_folder": "從列表中移除目前選擇的資料夾",
"hex.builtin.setting.general": "一般",
"hex.builtin.setting.general.auto_backup_time": "",
"hex.builtin.setting.general.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_load_patterns": "自動載入支援的模式",
"hex.builtin.setting.general.backups.auto_backup_time": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.extended": "",
"hex.builtin.setting.general.backups.auto_backup_time.format.simple": "",
"hex.builtin.setting.general.auto_apply_patterns": "自動載入支援的模式",
"hex.builtin.setting.general.network": "",
"hex.builtin.setting.general.network_interface": "啟用網路介面",
"hex.builtin.setting.general.patterns": "",

View File

@ -17,6 +17,8 @@
#include <fmt/chrono.h>
#include <nlohmann/json.hpp>
#include <romfs/romfs.hpp>
#include <toasts/toast_notification.hpp>
namespace hex::plugin::builtin {
@ -103,6 +105,16 @@ namespace hex::plugin::builtin {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
void handleMCPServer() {
if (!ContentRegistry::MCP::isEnabled()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
ContentRegistry::MCP::impl::getMcpServerInstance().disconnect();
return;
}
ContentRegistry::MCP::impl::getMcpServerInstance().listen();
}
}
void registerBackgroundServices() {
@ -110,12 +122,17 @@ namespace hex::plugin::builtin {
s_networkInterfaceServiceEnabled = value.get<bool>(false);
});
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.auto_backup_time", [](const ContentRegistry::Settings::SettingsValue &value) {
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.mcp_server", [](const ContentRegistry::Settings::SettingsValue &value) {
ContentRegistry::MCP::impl::setEnabled(value.get<bool>(false));
});
ContentRegistry::Settings::onChange("hex.builtin.setting.general", "hex.builtin.setting.general.backups.auto_backup_time", [](const ContentRegistry::Settings::SettingsValue &value) {
s_autoBackupTime = value.get<int>(0) * 30;
});
ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.network_interface", handleNetworkInterfaceService);
ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.auto_backup", handleAutoBackup);
ContentRegistry::BackgroundServices::registerService("hex.builtin.background_service.mcp", handleMCPServer);
EventProviderDirtied::subscribe([](prv::Provider *) {
s_dataDirty = true;

View File

@ -1,4 +1,6 @@
#include <iostream>
#include <content/command_line_interface.hpp>
#include <hex/mcp/client.hpp>
#include <hex/api/imhex_api/system.hpp>
#include <hex/api/imhex_api/hex_editor.hpp>
@ -530,6 +532,14 @@ namespace hex::plugin::builtin {
ContentRegistry::Views::setFullScreenView<ViewFullScreenFileInfo>(path);
}
void handleMCPCommand(const std::vector<std::string> &) {
mcp::Client client;
auto result = client.run(std::cin, std::cout);
std::fprintf(stderr, "MCP Client disconnected!\n");
std::exit(result);
}
void registerCommandForwarders() {
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){

View File

@ -287,23 +287,23 @@ namespace hex::plugin::builtin {
"@",
"hex.builtin.command.goto.desc",
[](auto input) {
wolv::math_eval::MathEvaluator<long double> evaluator;
wolv::math_eval::MathEvaluator<i64> evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
std::optional<long double> result = evaluator.evaluate(input);
const auto result = evaluator.evaluate(input);
if (result.has_value())
return fmt::format("hex.builtin.command.goto.result"_lang, result.value());
return fmt::format("hex.builtin.command.goto.result"_lang, static_cast<u64>(result.value()));
else if (evaluator.hasError())
return fmt::format("Error: {}", *evaluator.getLastError());
else
return std::string("???");
}, [](auto input) -> std::optional<std::string> {
wolv::math_eval::MathEvaluator<long double> evaluator;
wolv::math_eval::MathEvaluator<i64> evaluator;
evaluator.registerStandardVariables();
evaluator.registerStandardFunctions();
std::optional<long double> result = evaluator.evaluate(input);
const auto result = evaluator.evaluate(input);
if (result.has_value()) {
ImHexApi::HexEditor::setSelection(result.value(), 1);
}

View File

@ -33,54 +33,60 @@ namespace hex::plugin::builtin {
};
template<std::unsigned_integral T, size_t Size = sizeof(T)>
static std::vector<u8> stringToUnsigned(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) {
const auto result = wolv::util::from_chars<u64>(value).value_or(0);
if (result > std::numeric_limits<T>::max()) return {};
static ContentRegistry::DataInspector::impl::EditingFunction stringToUnsigned() requires(sizeof(T) <= sizeof(u64)) {
return ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
const auto result = wolv::util::from_chars<u64>(value).value_or(0);
if (result > std::numeric_limits<T>::max()) return {};
std::vector<u8> bytes(Size, 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
std::vector<u8> bytes(Size, 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
return bytes;
});
}
template<std::signed_integral T, size_t Size = sizeof(T)>
static std::vector<u8> stringToSigned(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) {
const auto result = wolv::util::from_chars<i64>(value).value_or(0);
if (result > std::numeric_limits<T>::max() || result < std::numeric_limits<T>::min()) return {};
static ContentRegistry::DataInspector::impl::EditingFunction stringToSigned() requires(sizeof(T) <= sizeof(u64)) {
return ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
const auto result = wolv::util::from_chars<i64>(value).value_or(0);
if (result > std::numeric_limits<T>::max() || result < std::numeric_limits<T>::min()) return {};
std::vector<u8> bytes(Size, 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
std::vector<u8> bytes(Size, 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
return bytes;
});
}
template<std::floating_point T>
static std::vector<u8> stringToFloat(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(long double)) {
const T result = wolv::util::from_chars<double>(value).value_or(0);
static ContentRegistry::DataInspector::impl::EditingFunction stringToFloat() requires(sizeof(T) <= sizeof(long double)) {
return ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
const T result = wolv::util::from_chars<double>(value).value_or(0);
std::vector<u8> bytes(sizeof(T), 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
std::vector<u8> bytes(sizeof(T), 0x00);
std::memcpy(bytes.data(), &result, bytes.size());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
if (endian != std::endian::native)
std::reverse(bytes.begin(), bytes.end());
return bytes;
return bytes;
});
}
template<std::integral T, size_t Size = sizeof(T)>
static std::vector<u8> stringToInteger(const std::string &value, std::endian endian) requires(sizeof(T) <= sizeof(u64)) {
static ContentRegistry::DataInspector::impl::EditingFunction stringToInteger() requires(sizeof(T) <= sizeof(u64)) {
if constexpr (std::unsigned_integral<T>)
return stringToUnsigned<T, Size>(value, endian);
return stringToUnsigned<T, Size>();
else if constexpr (std::signed_integral<T>)
return stringToSigned<T, Size>(value, endian);
return stringToSigned<T, Size>();
else
return {};
static_assert("Unsupported type for stringToInteger");
}
template<std::unsigned_integral T, size_t Size = sizeof(T)>
@ -154,7 +160,8 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted(binary.c_str());
return binary;
};
}, [](const std::string &value, std::endian endian) -> std::vector<u8> {
},
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
std::string binary = value;
@ -167,69 +174,69 @@ namespace hex::plugin::builtin {
return { result.value() };
else
return { };
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u8", sizeof(u8),
drawString<u8>(integerToString<u8>),
stringToInteger<u8>
stringToInteger<u8>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i8", sizeof(i8),
drawString<i8>(integerToString<i8>),
stringToInteger<i8>
stringToInteger<i8>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u16", sizeof(u16),
drawString<u16>(integerToString<u16>),
stringToInteger<u16>
stringToInteger<u16>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i16", sizeof(i16),
drawString<i16>(integerToString<i16>),
stringToInteger<i16>
stringToInteger<i16>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u24", 3,
drawString<u32, 3>(integerToString<u32, 3>),
stringToInteger<u32, 3>
stringToInteger<u32, 3>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i24", 3,
drawString<i32, 3>(integerToString<i32, 3>),
stringToInteger<i32, 3>
stringToInteger<i32, 3>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u32", sizeof(u32),
drawString<u32>(integerToString<u32>),
stringToInteger<u32>
stringToInteger<u32>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i32", sizeof(i32),
drawString<i32>(integerToString<i32>),
stringToInteger<i32>
stringToInteger<i32>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u48", 6,
drawString<u64, 6>(integerToString<u64, 6>),
stringToInteger<u64, 6>
stringToInteger<u64, 6>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i48", 6,
drawString<i64, 6>(integerToString<i64, 6>),
stringToInteger<i64, 6>
stringToInteger<i64, 6>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.u64", sizeof(u64),
drawString<u64>(integerToString<u64>),
stringToInteger<u64>
stringToInteger<u64>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.i64", sizeof(i64),
drawString<i64>(integerToString<i64>),
stringToInteger<i64>
stringToInteger<i64>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.float16", sizeof(u16),
@ -255,7 +262,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format(fmt::runtime(formatString), hex::changeEndianness(result, endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<float>
stringToFloat<float>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.double", sizeof(double),
@ -268,7 +275,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format(fmt::runtime(formatString), hex::changeEndianness(result, endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<double>
stringToFloat<double>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.long_double", sizeof(long double),
@ -281,7 +288,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format(fmt::runtime(formatString), hex::changeEndianness(result, endian));
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
stringToFloat<long double>
stringToFloat<long double>()
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.bfloat16", sizeof(u16),
@ -359,11 +366,11 @@ namespace hex::plugin::builtin {
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
return hex::crypt::encodeSleb128(wolv::util::from_chars<i64>(value).value_or(0));
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.uleb128", 1, (sizeof(u128) * 8 / 7) + 1,
@ -376,11 +383,11 @@ namespace hex::plugin::builtin {
return [value] { ImGui::TextUnformatted(value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
return hex::crypt::encodeUleb128(wolv::util::from_chars<u64>(value).value_or(0));
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.bool", sizeof(bool),
@ -411,13 +418,13 @@ namespace hex::plugin::builtin {
auto value = makePrintable(*reinterpret_cast<char8_t *>(buffer.data()));
return [value] { ImGuiExt::TextFormatted("'{0}'", value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
if (value.length() > 1) return { };
return { u8(value[0]) };
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.wide", sizeof(wchar_t),
@ -432,7 +439,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format("{0}", c <= 255 ? makePrintable(c) : wolv::util::wstringToUtf8(std::wstring(&c, 1)).value_or("???"));
return [value] { ImGuiExt::TextFormatted("L'{0}'", value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::vector<u8> bytes;
auto wideString = wolv::util::utf8ToWstring(value);
if (!wideString.has_value())
@ -445,7 +452,7 @@ namespace hex::plugin::builtin {
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.char16", sizeof(char16_t),
@ -460,7 +467,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format("{0}", c <= 255 ? makePrintable(c) : wolv::util::utf16ToUtf8(std::u16string(&c, 1)).value_or("???"));
return [value] { ImGuiExt::TextFormatted("u'{0}'", value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::vector<u8> bytes;
auto wideString = wolv::util::utf8ToUtf16(value);
if (!wideString.has_value())
@ -473,7 +480,7 @@ namespace hex::plugin::builtin {
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.char32", sizeof(char32_t),
@ -488,7 +495,7 @@ namespace hex::plugin::builtin {
auto value = fmt::format("{0}", c <= 255 ? makePrintable(c) : wolv::util::utf32ToUtf8(std::u32string(&c, 1)).value_or("???"));
return [value] { ImGuiExt::TextFormatted("U'{0}'", value.c_str()); return value; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::vector<u8> bytes;
auto wideString = wolv::util::utf8ToUtf32(value);
if (!wideString.has_value())
@ -501,7 +508,7 @@ namespace hex::plugin::builtin {
std::reverse(bytes.begin(), bytes.end());
return bytes;
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.utf8", sizeof(char8_t) * 4,
@ -551,11 +558,11 @@ namespace hex::plugin::builtin {
return [value, copyValue] { ImGuiExt::TextFormatted("\"{0}\"", value.c_str()); return copyValue; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
return hex::decodeByteString(value);
}
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.wstring", sizeof(wchar_t),
@ -588,11 +595,20 @@ namespace hex::plugin::builtin {
return [value, copyValue] { ImGuiExt::TextFormatted("L\"{0}\"", value.c_str()); return copyValue; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
auto utf8 = hex::decodeByteString(value);
auto wstring = wolv::util::utf8ToWstring({ utf8.begin(), utf8.end() });
if (!wstring.has_value())
return {};
return hex::decodeByteString(value);
}
for (auto &c : wstring.value()) {
c = hex::changeEndianness(c, endian);
}
std::vector<u8> bytes(wstring->size() * sizeof(wchar_t), 0x00);
std::memcpy(bytes.data(), wstring->data(), bytes.size());
return bytes;
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.string16", sizeof(char16_t),
@ -625,11 +641,20 @@ namespace hex::plugin::builtin {
return [value, copyValue] { ImGuiExt::TextFormatted("u\"{0}\"", value.c_str()); return copyValue; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
auto utf8 = hex::decodeByteString(value);
auto utf16 = wolv::util::utf8ToUtf16({ utf8.begin(), utf8.end() });
if (!utf16.has_value())
return {};
return hex::decodeByteString(value);
}
for (auto &c : utf16.value()) {
c = hex::changeEndianness(c, endian);
}
std::vector<u8> bytes(utf16->size() * sizeof(char16_t), 0x00);
std::memcpy(bytes.data(), utf16->data(), bytes.size());
return bytes;
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.string32", sizeof(char32_t),
@ -662,11 +687,20 @@ namespace hex::plugin::builtin {
return [value, copyValue] { ImGuiExt::TextFormatted("U\"{0}\"", value.c_str()); return copyValue; };
},
[](const std::string &value, std::endian endian) -> std::vector<u8> {
std::ignore = endian;
ContentRegistry::DataInspector::EditWidget::TextInput([](const std::string &value, std::endian endian) -> std::vector<u8> {
auto utf8 = hex::decodeByteString(value);
auto utf32 = wolv::util::utf8ToUtf32({ utf8.begin(), utf8.end() });
if (!utf32.has_value())
return {};
return hex::decodeByteString(value);
}
for (auto &c : utf32.value()) {
c = hex::changeEndianness(c, endian);
}
std::vector<u8> bytes(utf32->size() * sizeof(char32_t), 0x00);
std::memcpy(bytes.data(), utf32->data(), bytes.size());
return bytes;
})
);
ContentRegistry::DataInspector::add("hex.builtin.inspector.custom_encoding", 1, [encodingFile = EncodingFile()](const std::vector<u8> &, std::endian, Style) mutable {

View File

@ -40,30 +40,32 @@
namespace hex::plugin::builtin {
static void openFile(const std::fs::path &path) {
if (path.extension() == ".hexproj") {
if (!ProjectFile::load(path)) {
ui::ToastError::open(fmt::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
}
TaskManager::doLater([path] {
if (path.extension() == ".hexproj") {
if (!ProjectFile::load(path)) {
ui::ToastError::open(fmt::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
}
return;
}
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
return;
}
EventProviderOpened::post(fileProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
ImHexApi::Provider::setCurrentProvider(provider);
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider.get()); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
return;
}
glfwRequestWindowAttention(ImHexApi::System::getMainWindowHandle());
glfwFocusWindow(ImHexApi::System::getMainWindowHandle());
}
EventProviderOpened::post(fileProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
ImHexApi::Provider::setCurrentProvider(provider.get());
glfwRequestWindowAttention(ImHexApi::System::getMainWindowHandle());
glfwFocusWindow(ImHexApi::System::getMainWindowHandle());
}
});
}
void registerEventHandlers() {
@ -182,9 +184,9 @@ namespace hex::plugin::builtin {
if (name == "Create File") {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
hex::ImHexApi::Provider::remove(newProvider);
hex::ImHexApi::Provider::remove(newProvider.get());
else
EventProviderOpened::post(newProvider);
EventProviderOpened::post(newProvider.get());
} else if (name == "Open File") {
fs::openFileBrowser(fs::DialogMode::Open, { }, [](const auto &path) {
if (path.extension() == ".hexproj") {
@ -195,9 +197,8 @@ namespace hex::plugin::builtin {
}
}
auto newProvider = static_cast<FileProvider*>(
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
);
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
auto newProvider = static_cast<FileProvider*>(provider.get());
if (newProvider == nullptr)
return;

View File

@ -378,9 +378,9 @@ namespace hex::plugin::builtin {
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.create_file" }, ICON_VS_FILE, 1050, CTRLCMD + Keys::N + AllowWhileTyping + ShowOnWelcomeScreen, [] {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.mem_file", true);
if (newProvider != nullptr && !newProvider->open())
hex::ImHexApi::Provider::remove(newProvider);
hex::ImHexApi::Provider::remove(newProvider.get());
else
EventProviderOpened::post(newProvider);
EventProviderOpened::post(newProvider.get());
}, noRunningTasks, ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name"));
/* Open File */

View File

@ -8,8 +8,6 @@
#include <popups/popup_file_chooser.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {
PopupDecodedString::PopupDecodedString(std::string decodedString) : m_decodedString(std::move(decodedString)) {

View File

@ -13,7 +13,6 @@
#include <fonts/vscode_icons.hpp>
#include <ui/text_editor.hpp>
#include <wolv/literals.hpp>
using namespace wolv::literals;

View File

@ -18,8 +18,6 @@
#include <popups/popup_file_chooser.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {
PerProvider<std::string> PopupFind::s_inputString;

View File

@ -3,8 +3,6 @@
#include <hex/api/content_registry/settings.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {
PopupPasteBehaviour::PopupPasteBehaviour(const Region &selection, const std::function<void(const Region &selection, bool selectionCheck)> &pasteCallback)

View File

@ -6,8 +6,6 @@
#include <content/differing_byte_searcher.hpp>
#include <ui/text_editor.hpp>
namespace hex::plugin::builtin {
PopupRemove::PopupRemove(u64 address, size_t size) : m_address(address), m_size(size) {}

View File

@ -11,15 +11,16 @@
#include <content/providers/process_memory_provider.hpp>
#include <content/providers/base64_provider.hpp>
#include <content/providers/udp_provider.hpp>
#include <popups/popup_notification.hpp>
#include <content/providers/command_provider.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/helpers/fmt.hpp>
#include <nlohmann/json.hpp>
#include <popups/popup_notification.hpp>
#include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <wolv/utils/guards.hpp>
namespace hex::plugin::builtin {
@ -31,6 +32,7 @@ namespace hex::plugin::builtin {
#if !defined(OS_WEB)
ContentRegistry::Provider::add<DiskProvider>();
ContentRegistry::Provider::add<UDPProvider>();
ContentRegistry::Provider::add<CommandProvider>();
#endif
ContentRegistry::Provider::add<GDBProvider>();
ContentRegistry::Provider::add<IntelHexProvider>();
@ -87,13 +89,13 @@ namespace hex::plugin::builtin {
newProvider->loadSettings(providerSettings.at("settings"));
loaded = true;
} catch (const std::exception &e){
providerWarnings[newProvider] = e.what();
providerWarnings[newProvider.get()] = e.what();
}
if (loaded) {
if (!newProvider->open() || !newProvider->isAvailable() || !newProvider->isReadable()) {
providerWarnings[newProvider] = newProvider->getErrorMessage();
providerWarnings[newProvider.get()] = newProvider->getErrorMessage();
} else {
EventProviderOpened::post(newProvider);
EventProviderOpened::post(newProvider.get());
}
}
}

View File

@ -0,0 +1,293 @@
#if !defined(OS_WEB)
#include "content/providers/command_provider.hpp"
#include <chrono>
#include <imgui.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <hex/helpers/fmt.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/helpers/logger.hpp>
#include <nlohmann/json.hpp>
#include <wolv/utils/charconv.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#endif
namespace hex::plugin::builtin {
using namespace std::chrono_literals;
CommandProvider::CommandProvider() { }
bool CommandProvider::isAvailable() const {
return m_open;
}
bool CommandProvider::isReadable() const {
return true;
}
bool CommandProvider::isWritable() const {
return !m_writeCommand.empty();
}
bool CommandProvider::isResizable() const {
return !m_resizeCommand.empty();
}
bool CommandProvider::isSavable() const {
return !m_saveCommand.empty();
}
static std::vector<u8> executeCommand(const std::string &command, std::span<const u8> stdinData = {}) {
std::vector<u8> output;
#if defined(_WIN32)
HANDLE hStdinRead = nullptr, hStdinWrite = nullptr;
HANDLE hStdoutRead = nullptr, hStdoutWrite = nullptr;
SECURITY_ATTRIBUTES sa{};
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) {
log::error("CreatePipe(stdout) failed");
return {};
}
if (!SetHandleInformation(hStdoutRead, HANDLE_FLAG_INHERIT, 0)) {
log::error("SetHandleInformation(stdout) failed");
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
return {};
}
if (!CreatePipe(&hStdinRead, &hStdinWrite, &sa, 0)) {
log::error("CreatePipe(stdin) failed");
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
return {};
}
if (!SetHandleInformation(hStdinWrite, HANDLE_FLAG_INHERIT, 0)) {
log::error("SetHandleInformation(stdin) failed");
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
return {};
}
STARTUPINFOW si{};
si.cb = sizeof(si);
si.hStdOutput = si.hStdError = hStdoutWrite;
si.hStdInput = hStdinRead;
si.dwFlags = STARTF_USESTDHANDLES;
PROCESS_INFORMATION pi{};
// UTF-16 conversion
auto wcmd = wolv::util::utf8ToWstring(command);
if (!CreateProcessW(
nullptr, wcmd->data(),
nullptr, nullptr,
TRUE,
0,
nullptr, nullptr,
&si, &pi
)) {
log::error("CreateProcessW failed");
CloseHandle(hStdoutRead); CloseHandle(hStdoutWrite);
CloseHandle(hStdinRead); CloseHandle(hStdinWrite);
return {};
}
CloseHandle(hStdoutWrite);
CloseHandle(hStdinRead);
// Write stdin
if (!stdinData.empty()) {
DWORD written = 0;
WriteFile(hStdinWrite, stdinData.data(),
(DWORD)stdinData.size(), &written, nullptr);
}
CloseHandle(hStdinWrite);
// Read stdout
u8 buffer[4096];
DWORD bytesRead = 0;
while (ReadFile(hStdoutRead, buffer, sizeof(buffer), &bytesRead, nullptr) && bytesRead > 0) {
output.insert(output.end(), buffer, buffer + bytesRead);
}
CloseHandle(hStdoutRead);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return output;
#else
int stdinPipe[2];
int stdoutPipe[2];
if (pipe(stdinPipe) != 0 || pipe(stdoutPipe) != 0) {
log::error("pipe() failed");
return {};
}
pid_t pid = fork();
if (pid < 0) {
log::error("fork() failed");
return {};
}
if (pid == 0) {
// Child
dup2(stdinPipe[0], STDIN_FILENO);
dup2(stdoutPipe[1], STDOUT_FILENO);
dup2(stdoutPipe[1], STDERR_FILENO);
close(stdinPipe[1]);
close(stdoutPipe[0]);
execl("/bin/sh", "sh", "-c", command.c_str(), (char*)nullptr);
_exit(127);
}
// Parent
close(stdinPipe[0]);
close(stdoutPipe[1]);
if (!stdinData.empty()) {
std::ignore = write(stdinPipe[1], stdinData.data(), stdinData.size());
}
close(stdinPipe[1]);
u8 buffer[4096];
while (true) {
ssize_t n = read(stdoutPipe[0], buffer, sizeof(buffer));
if (n <= 0) break;
output.insert(output.end(), buffer, buffer + n);
}
close(stdoutPipe[0]);
waitpid(pid, nullptr, 0);
return output;
#endif
}
static std::string executeCommandString(const std::string &command) {
auto output = executeCommand(command);
return std::string(output.begin(), output.end());
}
void CommandProvider::readFromSource(u64 offset, void *buffer, size_t size) {
auto output = executeCommand(
fmt::format(fmt::runtime(m_readCommand),
fmt::arg("address", offset),
fmt::arg("size", size)
)
);
if (output.size() < size) {
std::memcpy(buffer, output.data(), output.size());
std::memset(static_cast<u8*>(buffer) + output.size(), 0, size - output.size());
} else {
std::memcpy(buffer, output.data(), size);
}
}
void CommandProvider::writeToSource(u64 offset, const void *buffer, size_t size) {
if (m_writeCommand.empty())
return;
std::ignore = executeCommand(
fmt::format(fmt::runtime(m_writeCommand),
fmt::arg("address", offset),
fmt::arg("size", size)
),
{ static_cast<const u8*>(buffer), size }
);
}
void CommandProvider::save() {
Provider::save();
std::ignore = executeCommand(m_saveCommand);
}
u64 CommandProvider::getSourceSize() const {
if (m_sizeCommand.empty())
return std::numeric_limits<u32>::max();
const auto output = executeCommandString(m_sizeCommand);
return wolv::util::from_chars<u64>(output).value_or(0);
}
std::string CommandProvider::getName() const {
return fmt::format("hex.builtin.provider.command.name"_lang, m_name);
}
bool CommandProvider::open() {
m_open = true;
return true;
}
void CommandProvider::close() {
}
bool CommandProvider::drawLoadInterface() {
ImGui::InputText("hex.builtin.provider.command.load.name"_lang, m_name);
ImGui::Separator();
ImGui::NewLine();
ImGui::InputText("hex.builtin.provider.command.load.read_command"_lang, m_readCommand);
ImGui::InputTextWithHint("hex.builtin.provider.command.load.write_command"_lang, "hex.builtin.provider.command.optional"_lang, m_writeCommand);
ImGui::InputTextWithHint("hex.builtin.provider.command.load.size_command"_lang, "hex.builtin.provider.command.optional"_lang, m_sizeCommand);
ImGui::InputTextWithHint("hex.builtin.provider.command.load.resize_command"_lang, "hex.builtin.provider.command.optional"_lang, m_resizeCommand);
ImGui::InputTextWithHint("hex.builtin.provider.command.load.save_command"_lang, "hex.builtin.provider.command.optional"_lang, m_saveCommand);
ImGuiExt::HelpHover("hex.builtin.provider.command.load.hint"_lang, ICON_VS_INFO);
return !m_name.empty() && !m_readCommand.empty();
}
void CommandProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
m_readCommand = settings.value("read", "");
m_writeCommand = settings.value("write", "");
m_resizeCommand = settings.value("resize", "");
m_sizeCommand = settings.value("size", "");
m_saveCommand = settings.value("save", "");
m_name = settings.value("name", "");
}
nlohmann::json CommandProvider::storeSettings(nlohmann::json settings) const {
settings["read"] = m_readCommand;
settings["write"] = m_writeCommand;
settings["resize"] = m_resizeCommand;
settings["size"] = m_sizeCommand;
settings["save"] = m_saveCommand;
settings["name"] = m_name;
return Provider::storeSettings(settings);
}
}
#endif

View File

@ -70,13 +70,16 @@ namespace hex::plugin::builtin {
if (m_loadedIntoMemory)
std::memcpy(m_data.data() + offset, buffer, size);
else
else {
this->createBackupIfNeeded(m_file.getPath());
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
}
}
void FileProvider::save() {
if (m_loadedIntoMemory) {
m_ignoreNextChangeEvent = true;
this->createBackupIfNeeded(m_file.getPath());
m_file.open();
m_file.writeVectorAtomic(0x00, m_data);
m_file.setSize(m_data.size());
@ -113,8 +116,10 @@ namespace hex::plugin::builtin {
void FileProvider::resizeRaw(u64 newSize) {
if (m_loadedIntoMemory)
m_data.resize(newSize);
else
else {
this->createBackupIfNeeded(m_file.getPath());
m_file.setSize(newSize);
}
m_fileSize = newSize;
}

View File

@ -51,16 +51,16 @@ namespace hex::plugin::builtin {
auto newProvider = hex::ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto fileProvider = dynamic_cast<FileProvider*>(newProvider); fileProvider != nullptr) {
if (auto fileProvider = dynamic_cast<FileProvider*>(newProvider.get()); fileProvider != nullptr) {
fileProvider->setPath(path);
if (!fileProvider->open()) {
ImHexApi::Provider::remove(newProvider);
ImHexApi::Provider::remove(newProvider.get());
} else {
MovePerProviderData::post(this, fileProvider);
fileProvider->markDirty(false);
EventProviderOpened::post(newProvider);
EventProviderOpened::post(newProvider.get());
ImHexApi::Provider::remove(this, true);
}
}

View File

@ -5,6 +5,7 @@
#if defined(OS_WINDOWS)
#include <windows.h>
#include <winternl.h>
#include <psapi.h>
#include <shellapi.h>
#elif defined(OS_MACOS)
@ -30,9 +31,77 @@
#include <wolv/io/fs.hpp>
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
#include <wolv/literals.hpp>
namespace hex::plugin::builtin {
using namespace wolv::literals;
#if defined(OS_WINDOWS)
using NtQueryInformationProcessFunc = NTSTATUS (NTAPI*)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
std::string getProcessCommandLine(HANDLE processHandle) {
// Get NtQueryInformationProcess function
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
if (!ntdll) return "";
auto funcNtQueryInformationProcess = (NtQueryInformationProcessFunc)
(void*)GetProcAddress(ntdll, "NtQueryInformationProcess");
if (!funcNtQueryInformationProcess) return "";
// Query for PROCESS_BASIC_INFORMATION to get PEB address
PROCESS_BASIC_INFORMATION pbi = {};
ULONG len = 0;
NTSTATUS status = funcNtQueryInformationProcess(
processHandle,
ProcessBasicInformation,
&pbi,
sizeof(pbi),
&len
);
if (status != 0 || !pbi.PebBaseAddress) return "";
// Read PEB to get ProcessParameters address
PEB peb = {};
SIZE_T bytesRead = 0;
if (!ReadProcessMemory(processHandle, pbi.PebBaseAddress, &peb, sizeof(peb), &bytesRead))
return "";
// Read RTL_USER_PROCESS_PARAMETERS
RTL_USER_PROCESS_PARAMETERS params = {};
if (!ReadProcessMemory(processHandle, peb.ProcessParameters, &params, sizeof(params), &bytesRead))
return "";
// Read the command line string
std::vector<wchar_t> cmdLine(params.CommandLine.Length / sizeof(wchar_t) + 1, 0);
if (!ReadProcessMemory(processHandle, params.CommandLine.Buffer, cmdLine.data(),
params.CommandLine.Length, &bytesRead))
return "";
// Convert wide string to narrow string
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, cmdLine.data(), -1, nullptr, 0, nullptr, nullptr);
if (sizeNeeded <= 0) return "";
std::string result(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, cmdLine.data(), -1, &result[0], sizeNeeded, nullptr, nullptr);
// Remove trailing null terminator
if (!result.empty() && result.back() == '\0')
result.pop_back();
return result;
}
#endif
bool ProcessMemoryProvider::open() {
if (m_selectedProcess == nullptr)
return false;
@ -125,7 +194,7 @@ namespace hex::plugin::builtin {
for (const auto &memoryRegion : m_memoryRegions) {
if (address < memoryRegion.region.getStartAddress())
return { Region { lastRegion.getEndAddress() + 1LLU, memoryRegion.region.getStartAddress() - lastRegion.getEndAddress() }, false };
return { Region { lastRegion.getEndAddress(), memoryRegion.region.getStartAddress() - lastRegion.getEndAddress() }, false };
lastRegion = memoryRegion.region;
}
@ -202,7 +271,7 @@ namespace hex::plugin::builtin {
}
}
m_processes.push_back({ u32(processId), processName, std::move(texture) });
m_processes.push_back({ u32(processId), processName, getProcessCommandLine(processHandle), std::move(texture) });
}
#elif defined(OS_MACOS)
std::array<pid_t, 2048> pids;
@ -213,7 +282,7 @@ namespace hex::plugin::builtin {
const auto result = proc_pidinfo(pids[i], PROC_PIDTBSDINFO, 0,
&proc, PROC_PIDTBSDINFO_SIZE);
if (result == PROC_PIDTBSDINFO_SIZE) {
m_processes.emplace_back(pids[i], proc.pbi_name, ImGuiExt::Texture());
m_processes.emplace_back(pids[i], proc.pbi_name, proc.pbi_comm, ImGuiExt::Texture());
}
}
#elif defined(OS_LINUX)
@ -228,13 +297,40 @@ namespace hex::plugin::builtin {
continue; // not a PID
}
wolv::io::File file(path /"cmdline", wolv::io::File::Mode::Read);
// Parse status file
wolv::io::File file(path / "status", wolv::io::File::Mode::Read);
std::map<std::string, std::string> statusInfo;
if (file.isValid()) {
const auto statusContent = file.readString(1_MiB);
for (const auto &line : wolv::util::splitString(statusContent, "\n")) {
const auto delimiterPos = line.find(':');
if (delimiterPos != std::string::npos) {
const auto key = line.substr(0, delimiterPos);
const auto value = line.substr(delimiterPos + 1);
statusInfo[key] = wolv::util::trim(value);
}
}
}
// Skip kernel threads
if (statusInfo.contains("Kthread") && statusInfo["Kthread"] == "1")
continue;
// Parse process name
std::string processName;
if (statusInfo.contains("Name")) {
processName = statusInfo["Name"];
}
wolv::io::File cmdlineFile(path / "cmdline", wolv::io::File::Mode::Read);
if (!file.isValid())
continue;
std::string processName = file.readString(0xF'FFFF);
auto commandLine = cmdlineFile.readString(1_MiB);
if (processName.empty())
processName = commandLine;
m_processes.emplace_back(processId, processName, ImGuiExt::Texture());
m_processes.emplace_back(processId, processName, commandLine, ImGuiExt::Texture());
}
#endif
}
@ -265,7 +361,11 @@ namespace hex::plugin::builtin {
ImGui::TableNextColumn();
auto height = ImGui::GetTextLineHeight();
ImGui::Image(process->icon, { height, height });
if (process->icon.isValid()) {
ImGui::Image(process->icon, { height, height });
} else {
ImGui::Dummy({ height, height });
}
ImGui::TableNextColumn();
ImGuiExt::TextFormatted("{}", process->id);
@ -274,6 +374,15 @@ namespace hex::plugin::builtin {
if (ImGui::Selectable(process->name.c_str(), m_selectedProcess != nullptr && process->id == m_selectedProcess->id, ImGuiSelectableFlags_SpanAllColumns))
m_selectedProcess = process;
if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal)) {
if (ImGui::BeginTooltip()) {
ImGui::PushTextWrapPos(200_scaled);
ImGui::TextWrapped("%s", process->commandLine.c_str());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
ImGui::PopID();
}

View File

@ -279,16 +279,16 @@ namespace hex::plugin::builtin::recent {
return;
}
auto *provider = ImHexApi::Provider::createProvider(recentEntry.type, true);
auto provider = ImHexApi::Provider::createProvider(recentEntry.type, true);
if (provider != nullptr) {
provider->loadSettings(recentEntry.data);
TaskManager::createBlockingTask("hex.builtin.provider.opening", TaskManager::NoProgress, [provider]() {
if (!provider->open() || !provider->isAvailable()) {
ui::ToastError::open(fmt::format("hex.builtin.provider.error.open"_lang, provider->getErrorMessage()));
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider.get()); });
} else {
TaskManager::doLater([provider]{ EventProviderOpened::post(provider); });
TaskManager::doLater([provider]{ EventProviderOpened::post(provider.get()); });
}
});

View File

@ -227,9 +227,9 @@ namespace hex::plugin::builtin {
if (value == 0)
return "hex.ui.common.off"_lang;
else if (value < 60)
return fmt::format("hex.builtin.setting.general.auto_backup_time.format.simple"_lang, value);
return fmt::format("hex.builtin.setting.general.backups.auto_backup_time.format.simple"_lang, value);
else
return fmt::format("hex.builtin.setting.general.auto_backup_time.format.extended"_lang, value / 60, value % 60);
return fmt::format("hex.builtin.setting.general.backups.auto_backup_time.format.extended"_lang, value / 60, value % 60);
}();
if (ImGui::SliderInt(name.data(), &m_value, 0, (30 * 60) / 30, format.c_str(), ImGuiSliderFlags_AlwaysClamp | ImGuiSliderFlags_NoInput)) {
@ -756,20 +756,38 @@ namespace hex::plugin::builtin {
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.show_tips", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "", "hex.builtin.setting.general.save_recent_providers", true);
ContentRegistry::Settings::add<AutoBackupWidget>("hex.builtin.setting.general", "", "hex.builtin.setting.general.auto_backup_time");
ContentRegistry::Settings::add<Widgets::SliderDataSize>("hex.builtin.setting.general", "", "hex.builtin.setting.general.max_mem_file_size", 512_MiB, 0_bytes, 32_GiB, 1_MiB)
.setTooltip("hex.builtin.setting.general.max_mem_file_size.desc");
ContentRegistry::Settings::add<Widgets::SliderInteger>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.pattern_data_max_filter_items", 128, 32, 1024);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.auto_load_patterns", true);
auto suggestPatterns = ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.suggest_patterns", true);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.auto_apply_patterns", false).setEnabledCallback([=] {
return static_cast<Widgets::Checkbox&>(suggestPatterns.getWidget()).isChecked();
});
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.patterns", "hex.builtin.setting.general.sync_pattern_source", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.network_interface", false);
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.mcp_server", false);
#if !defined(OS_WEB)
ContentRegistry::Settings::add<ServerContactWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.server_contact");
ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.network", "hex.builtin.setting.general.upload_crash_logs", true);
#endif
ContentRegistry::Settings::add<AutoBackupWidget>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.auto_backup_time");
ContentRegistry::Settings::add<Widgets::Spacer>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.spacer");
auto fileBackupEnabledWidget = ContentRegistry::Settings::add<Widgets::Checkbox>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.enable", true);
ContentRegistry::Settings::add<Widgets::SliderDataSize>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.max_size", 512_MiB, 0_bytes, 32_GiB, 1_MiB)
.setEnabledCallback([=] {
return static_cast<Widgets::Checkbox&>(fileBackupEnabledWidget.getWidget()).isChecked();
});
ContentRegistry::Settings::add<Widgets::TextBox>("hex.builtin.setting.general", "hex.builtin.setting.general.backups", "hex.builtin.setting.general.backups.file_backup.extension", ".bak")
.setEnabledCallback([=] {
return static_cast<Widgets::Checkbox&>(fileBackupEnabledWidget.getWidget()).isChecked();
});
}
/* Interface */

View File

@ -1247,7 +1247,7 @@ namespace hex::plugin::builtin {
if (isLocationValid(error.getLocation())) {
auto key = ui::TextEditor::Coordinates(error.getLocation().line, error.getLocation().column);
if (!errorMarkers.contains(key) || errorMarkers[key].first < error.getLocation().length)
if (!errorMarkers.contains(key) || errorMarkers[key].first < (i32) error.getLocation().length)
errorMarkers[key] = std::make_pair(error.getLocation().length, processMessage(error.getMessage()));
}
}

View File

@ -1,9 +1,18 @@
#include <hex/api/tutorial_manager.hpp>
#include <ui/markdown.hpp>
namespace hex::plugin::builtin {
void registerIntroductionTutorial();
void registerTutorials() {
TutorialManager::setRenderer([](const std::string &message) {
return [markdown = std::make_shared<ui::Markdown>(message)] {
markdown->draw();
};
});
registerIntroductionTutorial();
}
}
}

View File

@ -3,6 +3,7 @@
#include <hex/api/imhex_api/system.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <hex/api/tutorial_manager.hpp>
#include <hex/api/events/events_provider.hpp>
#include <hex/api/events/events_gui.hpp>
@ -28,6 +29,7 @@
#include <csignal>
#include <fonts/tabler_icons.hpp>
#include <hex/api/content_registry/communication_interface.hpp>
namespace hex::plugin::builtin {
@ -238,6 +240,20 @@ namespace hex::plugin::builtin {
});
}
ContentRegistry::UserInterface::addFooterItem([] {
if (ContentRegistry::MCP::isConnected()) {
ImGui::PushStyleColor(ImGuiCol_Text, ImGuiExt::GetCustomColorU32(ImGuiCustomCol_Highlight));
} else {
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetColorU32(ImGuiCol_TextDisabled));
}
if (ContentRegistry::MCP::isEnabled()) {
ImGui::TextUnformatted(ICON_VS_MCP);
}
ImGui::PopStyleColor();
});
if (dbg::debugModeEnabled()) {
ContentRegistry::UserInterface::addFooterItem([] {
static float framerate = 0;

View File

@ -20,6 +20,7 @@
#include <wolv/io/file.hpp>
#include <wolv/utils/guards.hpp>
#include "imgui_internal.h"
namespace hex::plugin::builtin {
@ -389,7 +390,7 @@ namespace hex::plugin::builtin {
auto provider = ImHexApi::Provider::get();
TaskManager::doLater([region, provider, name]{
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true);
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider); viewProvider != nullptr) {
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider.get()); viewProvider != nullptr) {
viewProvider->setProvider(region.getStartAddress(), region.getSize(), provider);
viewProvider->setName(fmt::format("'{}' View", name));

View File

@ -21,6 +21,7 @@
#include <ranges>
#include <fonts/tabler_icons.hpp>
#include <ui/widgets.hpp>
namespace hex::plugin::builtin {
@ -56,6 +57,12 @@ namespace hex::plugin::builtin {
auto filterValues = value.get<std::vector<std::string>>({});
m_hiddenValues = std::set(filterValues.begin(), filterValues.end());
});
ShortcutManager::addShortcut(this, CTRLCMD + Keys::E, "hex.builtin.view.data_inspector.toggle_endianness", [this] {
if (m_endian == std::endian::little) m_endian = std::endian::big;
else m_endian = std::endian::little;
m_shouldInvalidate = true;
});
}
ViewDataInspector::~ViewDataInspector() {
@ -191,8 +198,7 @@ namespace hex::plugin::builtin {
// Set up the editing function if a write formatter is available
std::optional<ContentRegistry::DataInspector::impl::EditingFunction> editingFunction;
if (!pattern->getWriteFormatterFunction().empty()) {
editingFunction = [&pattern](const std::string &value,
std::endian) -> std::vector<u8> {
editingFunction = ContentRegistry::DataInspector::EditWidget::TextInput([&pattern](const std::string &value, std::endian) -> std::vector<u8> {
try {
pattern->setValue(value);
} catch (const pl::core::err::EvaluatorError::Exception &error) {
@ -201,7 +207,7 @@ namespace hex::plugin::builtin {
}
return {};
};
});
}
try {
@ -434,13 +440,17 @@ namespace hex::plugin::builtin {
ImGui::SameLine();
// Handle copying the value to the clipboard when clicking the row
if (ImGui::Selectable("##InspectorLine", m_selectedEntryName == entry.unlocalizedName, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
if (ImGui::Selectable("##InspectorLine", m_selectedEntryName == entry.unlocalizedName, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_AllowDoubleClick)) {
m_selectedEntryName = entry.unlocalizedName;
if (auto selection = ImHexApi::HexEditor::getSelection(); selection.has_value()) {
ImHexApi::HexEditor::setSelection(Region { selection->getStartAddress(), entry.requiredSize });
}
}
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
m_selectedEntryName.reset();
}
// Enter editing mode when double-clicking the row
const bool editable = entry.editingFunction.has_value() && m_selectedProvider->isWritable();
if (ImGui::IsItemHovered()) {
@ -465,73 +475,47 @@ namespace hex::plugin::builtin {
}
ImGui::EndPopup();
}
} else {
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
entry.editing = false;
}
return;
// Handle editing mode
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::SetNextItemWidth(-1);
ImGui::SetKeyboardFocusHere();
// Draw editing widget and capture edited value
auto bytes = (*entry.editingFunction)(m_editingValue, m_endian, {});
if (bytes.has_value()) {
if (m_invert)
std::ranges::transform(*bytes, bytes->begin(), [](auto byte) { return byte ^ 0xFF; });
// Write those bytes to the selected provider at the current address
m_selectedProvider->write(m_startAddress, bytes->data(), bytes->size());
// Disable editing mode
m_editingValue.clear();
entry.editing = false;
// Reload all inspector rows
m_shouldInvalidate = true;
}
ImGui::PopStyleVar();
// Disable editing mode when clicking outside the input text box
if (!ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
m_editingValue.clear();
entry.editing = false;
}
}
// Handle editing mode
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));
ImGui::SetNextItemWidth(-1);
ImGui::SetKeyboardFocusHere();
// Draw input text box
if (ImGui::InputText("##InspectorLineEditing", m_editingValue,
ImGuiInputTextFlags_EnterReturnsTrue |
ImGuiInputTextFlags_AutoSelectAll)) {
// Turn the entered value into bytes
auto bytes = entry.editingFunction.value()(m_editingValue, m_endian);
if (m_invert)
std::ranges::transform(bytes, bytes.begin(), [](auto byte) { return byte ^ 0xFF; });
// Write those bytes to the selected provider at the current address
m_selectedProvider->write(m_startAddress, bytes.data(), bytes.size());
// Disable editing mode
m_editingValue.clear();
entry.editing = false;
// Reload all inspector rows
m_shouldInvalidate = true;
}
ImGui::PopStyleVar();
// Disable editing mode when clicking outside the input text box
if (!ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
m_editingValue.clear();
entry.editing = false;
}
}
void ViewDataInspector::drawEndianSetting() {
int selection = [this] {
switch (m_endian) {
default:
case std::endian::little:
return 0;
case std::endian::big:
return 1;
}
}();
std::array options = {
fmt::format("{}: {}", "hex.ui.common.endian"_lang, "hex.ui.common.little"_lang),
fmt::format("{}: {}", "hex.ui.common.endian"_lang, "hex.ui.common.big"_lang)
};
if (ImGui::SliderInt("##endian", &selection, 0, options.size() - 1, options[selection].c_str(), ImGuiSliderFlags_NoInput)) {
if (ui::endiannessSlider(m_endian)) {
m_shouldInvalidate = true;
switch (selection) {
default:
case 0:
m_endian = std::endian::little;
break;
case 1:
m_endian = std::endian::big;
break;
}
}
}

View File

@ -3,6 +3,7 @@
#include <hex/api/achievement_manager.hpp>
#include <hex/api/imhex_api/hex_editor.hpp>
#include <hex/api/events/events_interaction.hpp>
#include <hex/api/content_registry/user_interface.hpp>
#include <hex/trace/stacktrace.hpp>
#include <hex/providers/buffered_reader.hpp>
@ -18,6 +19,7 @@
#include <boost/regex.hpp>
#include <content/helpers/constants.hpp>
#include <hex/helpers/crypto.hpp>
#include <hex/helpers/default_paths.hpp>
#include <toasts/toast_notification.hpp>
@ -113,6 +115,32 @@ namespace hex::plugin::builtin {
for (auto &occurrence : *m_sortedOccurrences)
occurrence.selected = true;
});
/* Find Selection */
ContentRegistry::UserInterface::addMenuItem({ "hex.builtin.menu.edit", "hex.builtin.menu.edit.find.find_selection" }, ICON_VS_SEARCH_SPARKLE, 1950, CTRLCMD + SHIFT + Keys::F, [&] {
auto selection = ImHexApi::HexEditor::getSelection();
if (!selection.has_value())
return;
std::string sequence;
{
std::vector<u8> buffer(selection->getSize());
selection->getProvider()->read(selection->getStartAddress(), buffer.data(), buffer.size());
sequence = hex::crypt::encode16(buffer);
}
m_searchSettings.mode = SearchSettings::Mode::BinaryPattern;
m_searchSettings.region = { selection->getProvider()->getBaseAddress(), selection->getProvider()->getActualSize() };
m_searchSettings.binaryPattern = {
.input = sequence,
.pattern = hex::BinaryPattern(sequence),
.alignment = 1
};
this->runSearch();
this->bringToFront();
}, []{ return ImHexApi::Provider::isValid() && ImHexApi::HexEditor::isSelectionValid(); },
ContentRegistry::Views::getViewByName("hex.builtin.view.hex_editor.name"));
}
template<typename Type, typename StorageType>
@ -701,6 +729,14 @@ namespace hex::plugin::builtin {
m_settingsCollapsed.get(provider) = !m_foundOccurrences->empty();
});
});
m_decodeSettings = m_searchSettings;
m_foundOccurrences->clear();
m_sortedOccurrences->clear();
m_occurrenceTree->clear();
m_lastSelectedOccurrence = nullptr;
EventHighlightingChanged::post();
}
std::string ViewFind::decodeValue(prv::Provider *provider, const Occurrence &occurrence, size_t maxBytes) const {
@ -1079,14 +1115,6 @@ namespace hex::plugin::builtin {
{
if (ImGuiExt::DimmedIconButton(ICON_VS_SEARCH, ImGui::GetStyleColorVec4(ImGuiCol_Text))) {
this->runSearch();
m_decodeSettings = m_searchSettings;
m_foundOccurrences->clear();
m_sortedOccurrences->clear();
m_occurrenceTree->clear();
m_lastSelectedOccurrence = nullptr;
EventHighlightingChanged::post();
}
ImGui::SetItemTooltip("%s", "hex.builtin.view.find.search"_lang.get());
}
@ -1187,7 +1215,7 @@ namespace hex::plugin::builtin {
ImGui::TableSetupScrollFreeze(0, 1);
ImGui::TableSetupColumn("hex.ui.common.offset"_lang, 0, -1, ImGui::GetID("offset"));
ImGui::TableSetupColumn("hex.ui.common.size"_lang, 0, -1, ImGui::GetID("size"));
ImGui::TableSetupColumn("hex.ui.common.value"_lang, 0, -1, ImGui::GetID("value"));
ImGui::TableSetupColumn("hex.ui.common.value"_lang, ImGuiTableColumnFlags_WidthStretch, -1, ImGui::GetID("value"));
auto sortSpecs = ImGui::TableGetSortSpecs();
@ -1197,22 +1225,22 @@ namespace hex::plugin::builtin {
}
if (sortSpecs->SpecsDirty) {
std::sort(currOccurrences.begin(), currOccurrences.end(), [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool {
std::ranges::stable_sort(currOccurrences, [this, &sortSpecs, provider](const Occurrence &left, const Occurrence &right) -> bool {
if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("offset")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.region.getStartAddress() > right.region.getStartAddress();
else
return left.region.getStartAddress() < right.region.getStartAddress();
else
return left.region.getStartAddress() > right.region.getStartAddress();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("size")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return left.region.getSize() > right.region.getSize();
else
return left.region.getSize() < right.region.getSize();
else
return left.region.getSize() > right.region.getSize();
} else if (sortSpecs->Specs->ColumnUserID == ImGui::GetID("value")) {
if (sortSpecs->Specs->SortDirection == ImGuiSortDirection_Ascending)
return this->decodeValue(provider, left) > this->decodeValue(provider, right);
else
return this->decodeValue(provider, left) < this->decodeValue(provider, right);
else
return this->decodeValue(provider, left) > this->decodeValue(provider, right);
}
return false;

View File

@ -43,7 +43,6 @@
#include <content/popups/hex_editor/popup_hex_editor_find.hpp>
#include <pl/patterns/pattern.hpp>
#include <hex/helpers/menu_items.hpp>
#include <ui/text_editor.hpp>
#include <wolv/literals.hpp>
using namespace std::literals::string_literals;
@ -60,8 +59,10 @@ namespace hex::plugin::builtin {
std::optional<color_t> result;
for (const auto &[id, callback] : ImHexApi::HexEditor::impl::getForegroundHighlightingFunctions()) {
if (auto color = callback(address, data, size, result.has_value()); color.has_value())
if (auto color = callback(address, data, size, result.has_value()); color.has_value()) {
result = color;
break;
}
}
if (!result.has_value()) {
@ -95,7 +96,7 @@ namespace hex::plugin::builtin {
});
ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.show_extended_ascii", [this](const ContentRegistry::Settings::SettingsValue &value) {
m_hexEditor.enableShowExtendedAscii(value.get<bool>(true));
m_hexEditor.enableShowExtendedAscii(value.get<bool>(false));
});
ContentRegistry::Settings::onChange("hex.builtin.setting.hex_editor", "hex.builtin.setting.hex_editor.minimap", [this](const ContentRegistry::Settings::SettingsValue &value) {
@ -1167,7 +1168,7 @@ namespace hex::plugin::builtin {
auto selection = ImHexApi::HexEditor::getSelection();
auto newProvider = ImHexApi::Provider::createProvider("hex.builtin.provider.view", true);
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider); viewProvider != nullptr) {
if (auto *viewProvider = dynamic_cast<ViewProvider*>(newProvider.get()); viewProvider != nullptr) {
viewProvider->setProvider(selection->getStartAddress(), selection->getSize(), selection->getProvider());
if (viewProvider->open())
EventProviderOpened::post(viewProvider);

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