mirror of https://github.com/WerWolv/ImHex
Compare commits
73 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
baa3329e7f | |
|
|
e696d384c2 | |
|
|
932c281223 | |
|
|
858fe0384e | |
|
|
e904cd749f | |
|
|
6b16f39be4 | |
|
|
021c7e5fdb | |
|
|
c161a5c71b | |
|
|
76ccdbccea | |
|
|
553ee89787 | |
|
|
cb6247b16e | |
|
|
cfac7ff0ba | |
|
|
49bbe7dc77 | |
|
|
07b6fa0e2e | |
|
|
67396f2009 | |
|
|
a20ff87cc9 | |
|
|
e02e57a729 | |
|
|
225dc53795 | |
|
|
e7404376db | |
|
|
d6aec341fe | |
|
|
3a3c2fb204 | |
|
|
e9b5cdbccf | |
|
|
3f30e63d95 | |
|
|
f9c6866c7b | |
|
|
388dccfd9f | |
|
|
1676342e28 | |
|
|
62732de227 | |
|
|
63e777c84c | |
|
|
ab95cdf3e5 | |
|
|
827b5b01dd | |
|
|
bfa9788099 | |
|
|
de25ce7fbb | |
|
|
21e61bfce6 | |
|
|
82e168c438 | |
|
|
48583a2b6e | |
|
|
0db0982fa7 | |
|
|
6a28ce9e4b | |
|
|
1db79f6117 | |
|
|
f234103320 | |
|
|
45c382a19a | |
|
|
fb7ef61d06 | |
|
|
2586645d02 | |
|
|
e23cb5509d | |
|
|
5cbd53ae7a | |
|
|
a4ee590875 | |
|
|
495608ed7c | |
|
|
4d10d9a195 | |
|
|
4914a34dd9 | |
|
|
ab0fb3131d | |
|
|
7922d3b3cb | |
|
|
6427f53b5a | |
|
|
994df0a3a4 | |
|
|
e6eee55810 | |
|
|
855e4c4913 | |
|
|
c2e07bf7b2 | |
|
|
77b9e3eac8 | |
|
|
790487eea6 | |
|
|
eb83354179 | |
|
|
9ba8754f97 | |
|
|
84346119b3 | |
|
|
2b3abd06db | |
|
|
8267aad79e | |
|
|
3f9ce561b9 | |
|
|
347fc3ed9f | |
|
|
0488c996e9 | |
|
|
37bfd97d93 | |
|
|
c8652b0576 | |
|
|
1208d2eb5e | |
|
|
ab34fed0c5 | |
|
|
0906e5f9cf | |
|
|
47b1c603b3 | |
|
|
4bda321e7a | |
|
|
691ff11fbc |
|
|
@ -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" `
|
||||
.
|
||||
|
||||
|
|
|
|||
|
|
@ -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: |
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ¶ms)> function);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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 ¶ms)> function);
|
||||
|
||||
private:
|
||||
nlohmann::json handleInitialize();
|
||||
void handleNotifications(const std::string &method, const nlohmann::json ¶ms);
|
||||
|
||||
struct Primitive {
|
||||
nlohmann::json capabilities;
|
||||
std::function<nlohmann::json(const nlohmann::json ¶ms)> function;
|
||||
};
|
||||
|
||||
std::map<std::string, std::map<std::string, Primitive>> m_primitives;
|
||||
|
||||
wolv::net::SocketServer m_server;
|
||||
bool m_connected = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ¶ms)> function) {
|
||||
impl::getMcpServerInstance().addPrimitive("tools", capabilities, function);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ContentRegistry::Experiments {
|
||||
|
||||
namespace impl {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 ¶ms) -> 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 ¶ms)> 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 ¶ms) {
|
||||
if (method == "initialized") {
|
||||
m_connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Server::isConnected() {
|
||||
return m_connected;
|
||||
}
|
||||
}
|
||||
|
|
@ -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++) {
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ namespace hex::plugin::builtin {
|
|||
struct Process {
|
||||
u32 id;
|
||||
std::string name;
|
||||
std::string commandLine;
|
||||
ImGuiExt::Texture icon;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace hex::plugin::builtin {
|
|||
}
|
||||
|
||||
ImGuiWindowFlags getWindowFlags() const override {
|
||||
return View::Floating::getWindowFlags() | ImGuiWindowFlags_NoResize;
|
||||
return ImGuiWindowFlags_NoResize;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewInformation : public View::Window {
|
||||
class ViewInformation : public View::Scrolling {
|
||||
public:
|
||||
explicit ViewInformation();
|
||||
~ViewInformation() override = default;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
namespace hex::plugin::builtin {
|
||||
|
||||
class ViewTools : public View::Window {
|
||||
class ViewTools : public View::Scrolling {
|
||||
public:
|
||||
ViewTools();
|
||||
~ViewTools() override = default;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace hex::plugin::builtin {
|
|||
}
|
||||
|
||||
ImGuiWindowFlags getWindowFlags() const override {
|
||||
return Floating::getWindowFlags() | ImGuiWindowFlags_NoResize;
|
||||
return ImGuiWindowFlags_NoResize;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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": "패턴",
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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 загружать более объёмные файлы в память.",
|
||||
|
|
|
|||
|
|
@ -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": "Синхронізація вихідного коду шаблону між відкритими джерелами даних",
|
||||
|
|
|
|||
|
|
@ -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从磁盘读取数据之前被加载到内存中。",
|
||||
|
|
|
|||
|
|
@ -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": "",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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){
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
#include <fonts/vscode_icons.hpp>
|
||||
|
||||
#include <ui/text_editor.hpp>
|
||||
#include <wolv/literals.hpp>
|
||||
|
||||
using namespace wolv::literals;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ¶ms, 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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()); });
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue