Compare commits

...

133 Commits

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

---------

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

---------

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

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


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

Main infrastructure done by @WerWolv

---------

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

View File

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

View File

@@ -2,7 +2,9 @@ name: Build for the web
on:
push:
branches: ["*"]
branches:
- 'master'
- 'releases/**'
pull_request:
workflow_dispatch:

View File

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

View File

@@ -2,9 +2,13 @@ name: "Unit Tests"
on:
push:
branches: [ master ]
branches:
- 'master'
- 'releases/**'
pull_request:
branches: [ master ]
branches:
- 'master'
- 'releases/**'
workflow_dispatch:
jobs:
@@ -49,6 +53,7 @@ jobs:
cd build
CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=Debug \
-DIMHEX_ENABLE_UNIT_TESTS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \

View File

@@ -19,6 +19,8 @@ option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build
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" OFF)
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
# Basic compiler and cmake configurations
set(CMAKE_CXX_STANDARD 23)
@@ -38,6 +40,9 @@ project(imhex
HOMEPAGE_URL "https://imhex.werwolv.net"
)
# Add ImHex sources
add_custom_target(imhex_all ALL)
# Make sure project is configured correctly
setDefaultBuiltTypeIfUnset()
detectBadClone()
@@ -54,17 +59,19 @@ configurePackingResources()
setUninstallTarget()
addBundledLibraries()
# Add ImHex sources
add_custom_target(imhex_all ALL)
add_subdirectory(lib/libimhex)
add_subdirectory(main)
addPluginDirectories()
# Add unit tests
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
if (IMHEX_ENABLE_UNIT_TESTS)
enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
endif ()
# Configure more resources that will be added to the install package
createPackage()
generatePDBs()
generateSDKDirectory()
generateSDKDirectory()
# Handle package generation
createPackage()

View File

@@ -46,6 +46,9 @@ function(addDefineToSource SOURCE DEFINE)
APPEND
PROPERTY COMPILE_DEFINITIONS "${DEFINE}"
)
# Disable precompiled headers for this file
set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endfunction()
# Detect current OS / System
@@ -67,6 +70,9 @@ macro(detectOS)
add_compile_definitions(OS_WEB)
elseif (UNIX AND NOT APPLE)
add_compile_definitions(OS_LINUX)
if (BSD AND BSD STREQUAL "FreeBSD")
add_compile_definitions(OS_FREEBSD)
endif()
include(GNUInstallDirs)
if(IMHEX_PLUGINS_IN_SHARE)
@@ -86,6 +92,8 @@ macro(detectOS)
endmacro()
macro(configurePackingResources)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if (WIN32)
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(APPLICATION_TYPE WIN32)
@@ -139,9 +147,7 @@ macro(configurePackingResources)
endif()
endmacro()
macro(createPackage)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
macro(addPluginDirectories)
file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}")
@@ -172,9 +178,9 @@ macro(createPackage)
add_dependencies(imhex_all ${plugin})
endif ()
endforeach()
endmacro()
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
macro(createPackage)
if (WIN32)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".")
@@ -211,7 +217,6 @@ macro(createPackage)
endforeach()
]])
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./")
elseif(UNIX AND NOT APPLE)
@@ -222,7 +227,6 @@ macro(createPackage)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./share/imhex")
# install AppStream file
@@ -243,17 +247,15 @@ macro(createPackage)
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main> || true)
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".")
install(FILES $<TARGET_FILE:main> DESTINATION "${IMHEX_BUNDLE_PATH}")
install(FILES $<TARGET_FILE:updater> DESTINATION "${IMHEX_BUNDLE_PATH}")
# Update library references to make the bundle portable
postprocess_bundle(imhex_all main)
@@ -262,15 +264,17 @@ macro(createPackage)
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist")
set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist")
if (IMHEX_RESIGN_BUNDLE)
message(STATUS "Resigning bundle...")
find_program(CODESIGN_PATH codesign)
if (CODESIGN_PATH)
add_custom_command(TARGET imhex_all POST_BUILD COMMAND "codesign" ARGS "--force" "--deep" "--sign" "-" "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}")
install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")")
install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)")
endif()
endif()
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
endif()
else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -503,7 +507,7 @@ macro(setupCompilerFlags target)
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
# Disable some warnings
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations")
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations -Wno-unknown-pragmas")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
@@ -611,6 +615,7 @@ macro(addBundledLibraries)
endif()
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL "" FORCE)
set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL "" FORCE)
if (WIN32)
set(LIBPL_SHARED_LIBRARY ON CACHE BOOL "" FORCE)
@@ -653,20 +658,20 @@ macro(addBundledLibraries)
if (${Backtrace_FOUND})
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
if (Backtrace_HEADER STREQUAL "execinfo.h")
if (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_EXECINFO)
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_BACKTRACE)
endif ()
elseif (Backtrace_HEADER STREQUAL "execinfo.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_EXECINFO)
endif()
endif()
endif ()
endif ()
endif()
endif()
endmacro()
function(enableUnityBuild TARGET)
@@ -732,7 +737,7 @@ function(generateSDKDirectory)
if (WIN32)
set(SDK_PATH "./sdk")
elseif (APPLE)
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk")
set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
else()
set(SDK_PATH "share/imhex/sdk")
endif()
@@ -758,4 +763,19 @@ endfunction()
function(addIncludesFromLibrary target library)
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} PRIVATE ${library_include_dirs})
endfunction()
function(precompileHeaders target includeFolder)
if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS)
return()
endif()
file(GLOB_RECURSE TARGET_INCLUDES "${includeFolder}/**/*.hpp")
set(SYSTEM_INCLUDES "<algorithm>;<array>;<atomic>;<chrono>;<cmath>;<cstddef>;<cstdint>;<cstdio>;<cstdlib>;<cstring>;<exception>;<filesystem>;<functional>;<iterator>;<limits>;<list>;<map>;<memory>;<optional>;<ranges>;<set>;<stdexcept>;<string>;<string_view>;<thread>;<tuple>;<type_traits>;<unordered_map>;<unordered_set>;<utility>;<variant>;<vector>")
set(INCLUDES "${SYSTEM_INCLUDES};${TARGET_INCLUDES}")
string(REPLACE ">" "$<ANGLE-R>" INCLUDES "${INCLUDES}")
target_precompile_headers(${target}
PUBLIC
"$<$<COMPILE_LANGUAGE:CXX>:${INCLUDES}>"
)
endfunction()

View File

@@ -1,4 +1,4 @@
find_file(libyara.a YARA_LIBRARIES)
find_library(YARA_LIBRARIES NAMES yara)
find_file(yara.h YARA_INCLUDE_DIRS)
mark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS)

View File

@@ -36,8 +36,12 @@ macro(add_imhex_plugin)
# Add include directories and link libraries
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${IMHEX_PLUGIN_LIBRARIES} ${FMT_LIBRARIES} imgui_all_includes libwolv)
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES})
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv)
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)
precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include")
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
@@ -88,6 +92,12 @@ macro(add_imhex_plugin)
elseif (UNIX)
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_ORIGIN ON INSTALL_RPATH "$ORIGIN/")
endif()
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests)
target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}-tests")
endif()
endmacro()
macro(add_romfs_resource input output)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

6
dist/rpm/imhex.spec vendored
View File

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

View File

@@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
source/helpers/tar.cpp
source/helpers/debugging.cpp
source/test/tests.cpp
source/providers/provider.cpp
source/providers/memory_provider.cpp
source/providers/undo/stack.cpp
@@ -77,37 +79,6 @@ else()
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
endif()
enableUnityBuild(libimhex)
setupCompilerFlags(libimhex)
include(GenerateExportHeader)
generate_export_header(libimhex)
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
if (NOT EMSCRIPTEN)
# curl is only used in non-emscripten builds
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
endif()
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
if (WIN32)
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
elseif (APPLE)
find_library(FOUNDATION NAMES Foundation)
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
endif ()
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
endif()
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)
set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}")
@@ -142,4 +113,41 @@ endif ()
addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"")
add_dependencies(imhex_all libimhex)
enableUnityBuild(libimhex)
setupCompilerFlags(libimhex)
include(GenerateExportHeader)
generate_export_header(libimhex)
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
if (NOT EMSCRIPTEN)
# curl is only used in non-emscripten builds
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
endif()
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
if (WIN32)
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
elseif (APPLE)
find_library(FOUNDATION NAMES Foundation)
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
endif ()
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
precompileHeaders(libimhex "${CMAKE_CURRENT_SOURCE_DIR}/include")
endif()
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES})
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
add_dependencies(imhex_all libimhex)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})

View File

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

View File

@@ -3,6 +3,7 @@
#include <hex.hpp>
#include <list>
#include <mutex>
#include <map>
#include <string_view>
#include <functional>
@@ -22,7 +23,7 @@
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, function); } \
static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
static void post(auto &&...args) noexcept { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
static void post(auto &&...args) { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
};
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
@@ -71,11 +72,12 @@ namespace hex {
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
void operator()(Params... params) const noexcept {
void operator()(std::string_view eventName, Params... params) const {
try {
m_func(params...);
} catch (const std::exception &e) {
log::error("An exception occurred while handling event: {}", e.what());
log::error("An exception occurred while handling event {}: {}", eventName, e.what());
throw;
}
}
@@ -172,12 +174,12 @@ namespace hex {
* @param args Arguments to pass to the event
*/
template<impl::EventType E>
static void post(auto &&...args) noexcept {
static void post(auto && ...args) {
std::scoped_lock lock(getEventMutex());
for (const auto &[id, event] : getEvents()) {
if (id == E::Id) {
(*static_cast<E *const>(event.get()))(std::forward<decltype(args)>(args)...);
(*static_cast<E *const>(event.get()))(wolv::type::getTypeName<E>(), std::forward<decltype(args)>(args)...);
}
}
@@ -308,4 +310,9 @@ namespace hex {
* The 'from' provider should not have any per provider data after this, and should be immediately deleted
*/
EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *);
/**
* Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash
*/
EVENT_DEF(EventCrashRecovered, const std::exception &);
}

View File

@@ -664,6 +664,12 @@ namespace hex {
*/
std::optional<InitialWindowProperties> getInitialWindowProperties();
/**
* @brief Gets the module handle of libimhex
* @return Module handle
*/
void* getLibImHexModuleHandle();
}
/**
@@ -698,6 +704,7 @@ namespace hex {
std::vector<GlyphRange> glyphRanges;
Offset offset;
u32 flags;
std::optional<u32> defaultSize;
};
namespace impl {
@@ -716,8 +723,8 @@ namespace hex {
GlyphRange range(const char *glyphBegin, const char *glyphEnd);
GlyphRange range(u32 codepointBegin, u32 codepointEnd);
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
constexpr static float DefaultFontSize = 13.0;

View File

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

View File

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

View File

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

View File

@@ -1,72 +1,76 @@
#pragma once
#include <future>
#if defined(OS_WEB)
#include <emscripten/fetch.h>
#include <future>
namespace hex {
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
#include <emscripten/fetch.h>
// Execute the request
auto result = this->executeImpl<T>(response);
namespace hex {
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
// Write the result to the file
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
// Execute the request
auto result = this->executeImpl<T>(response);
return result;
});
}
// Write the result to the file
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
hex::unused(path, mimeName);
throw std::logic_error("Not implemented");
}
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
hex::unused(data, mimeName, fileName);
throw std::logic_error("Not implemented");
}
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
return std::async(std::launch::async, [this] {
std::vector<u8> responseData;
return this->executeImpl<T>(responseData);
});
}
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
strcpy(m_attr.requestMethod, m_method.c_str());
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
if (!m_body.empty()) {
m_attr.requestData = m_body.c_str();
m_attr.requestDataSize = m_body.size();
return result;
});
}
std::vector<const char*> headers;
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
headers.push_back(it->first.c_str());
headers.push_back(it->second.c_str());
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
hex::unused(path, mimeName);
throw std::logic_error("Not implemented");
}
headers.push_back(nullptr);
m_attr.requestHeaders = headers.data();
// Send request
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
hex::unused(data, mimeName, fileName);
throw std::logic_error("Not implemented");
}
data.resize(fetch->numBytes);
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
return std::async(std::launch::async, [this] {
std::vector<u8> responseData;
return this->executeImpl<T>(responseData);
});
}
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
strcpy(m_attr.requestMethod, m_method.c_str());
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
if (!m_body.empty()) {
m_attr.requestData = m_body.c_str();
m_attr.requestDataSize = m_body.size();
}
std::vector<const char*> headers;
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
headers.push_back(it->first.c_str());
headers.push_back(it->second.c_str());
}
headers.push_back(nullptr);
m_attr.requestHeaders = headers.data();
// Send request
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
data.resize(fetch->numBytes);
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
return Result<T>(fetch->status, { data.begin(), data.end() });
}
return Result<T>(fetch->status, { data.begin(), data.end() });
}
}
#endif

View File

@@ -1,145 +1,149 @@
#pragma once
#include <string>
#include <future>
#if !defined(OS_WEB)
#include <curl/curl.h>
#include <string>
#include <future>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp>
#include <curl/curl.h>
#include <wolv/utils/string.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex {
#include <wolv/utils/string.hpp>
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
namespace hex {
wolv::io::File file(path, wolv::io::File::Mode::Create);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
return this->executeImpl<T>(response);
});
}
wolv::io::File file(path, wolv::io::File::Mode::Create);
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
return std::async(std::launch::async, [this, path, mimeName]{
auto fileName = wolv::util::toUTF8String(path.filename());
curl_mime *mime = curl_mime_init(m_curl);
curl_mimepart *part = curl_mime_addpart(mime);
wolv::io::File file(path, wolv::io::File::Mode::Read);
curl_mime_data_cb(part, file.getSize(),
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
auto handle = static_cast<FILE*>(arg);
return fread(buffer, size, nitems, handle);
},
[](void *arg, curl_off_t offset, int origin) -> int {
auto handle = static_cast<FILE*>(arg);
if (fseek(handle, offset, origin) != 0)
return CURL_SEEKFUNC_CANTSEEK;
else
return CURL_SEEKFUNC_OK;
},
[](void *arg) {
auto handle = static_cast<FILE*>(arg);
fclose(handle);
},
file.getHandle());
curl_mime_filename(part, fileName.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
curl_mime *mime = curl_mime_init(m_curl);
curl_mimepart *part = curl_mime_addpart(mime);
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
curl_mime_filename(part, fileNameStr.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
return std::async(std::launch::async, [this] {
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
setDefaultConfig();
if (!m_body.empty()) {
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
return this->executeImpl<T>(response);
});
}
curl_slist *headers = nullptr;
headers = curl_slist_append(headers, "Cache-Control: no-cache");
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
return std::async(std::launch::async, [this, path, mimeName]{
auto fileName = wolv::util::toUTF8String(path.filename());
for (auto &[key, value] : m_headers) {
std::string header = hex::format("{}: {}", key, value);
headers = curl_slist_append(headers, header.c_str());
curl_mime *mime = curl_mime_init(m_curl);
curl_mimepart *part = curl_mime_addpart(mime);
wolv::io::File file(path, wolv::io::File::Mode::Read);
curl_mime_data_cb(part, file.getSize(),
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
auto handle = static_cast<FILE*>(arg);
return fread(buffer, size, nitems, handle);
},
[](void *arg, curl_off_t offset, int origin) -> int {
auto handle = static_cast<FILE*>(arg);
if (fseek(handle, offset, origin) != 0)
return CURL_SEEKFUNC_CANTSEEK;
else
return CURL_SEEKFUNC_OK;
},
[](void *arg) {
auto handle = static_cast<FILE*>(arg);
fclose(handle);
},
file.getHandle());
curl_mime_filename(part, fileName.c_str());
curl_mime_name(part, mimeName.c_str());
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
{
std::scoped_lock lock(m_transmissionMutex);
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
curl_mime *mime = curl_mime_init(m_curl);
curl_mimepart *part = curl_mime_addpart(mime);
auto result = curl_easy_perform(m_curl);
if (result != CURLE_OK){
char *url = nullptr;
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
checkProxyErrors();
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
curl_mime_filename(part, fileNameStr.c_str());
curl_mime_name(part, mimeName.c_str());
return { };
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
template<typename T>
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
return std::async(std::launch::async, [this] {
std::vector<u8> responseData;
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
return this->executeImpl<T>(responseData);
});
}
template<typename T>
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
setDefaultConfig();
if (!m_body.empty()) {
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
}
curl_slist *headers = nullptr;
headers = curl_slist_append(headers, "Cache-Control: no-cache");
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
for (auto &[key, value] : m_headers) {
std::string header = hex::format("{}: {}", key, value);
headers = curl_slist_append(headers, header.c_str());
}
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
{
std::scoped_lock lock(m_transmissionMutex);
auto result = curl_easy_perform(m_curl);
if (result != CURLE_OK){
char *url = nullptr;
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
checkProxyErrors();
return { };
}
}
long statusCode = 0;
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
return Result<T>(statusCode, { data.begin(), data.end() });
}
long statusCode = 0;
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
return Result<T>(statusCode, { data.begin(), data.end() });
}
}
#endif

View File

@@ -8,6 +8,7 @@
#include <fmt/color.h>
#include <wolv/io/file.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::log {
@@ -21,6 +22,7 @@ namespace hex::log {
[[nodiscard]] std::recursive_mutex& getLoggerMutex();
[[nodiscard]] bool isLoggingSuspended();
[[nodiscard]] bool isDebugLoggingEnabled();
struct LogEntry {
std::string project;
@@ -40,13 +42,15 @@ namespace hex::log {
std::scoped_lock lock(getLoggerMutex());
auto dest = getDestination();
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
try {
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message);
fflush(dest);
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message);
fflush(dest);
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
} catch (const std::exception&) { }
}
namespace color {
@@ -63,13 +67,14 @@ namespace hex::log {
void suspendLogging();
void resumeLogging();
void enableDebugLogging();
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
#if defined(DEBUG)
if (impl::isDebugLoggingEnabled()) [[unlikely]] {
hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
#else
} else {
impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
#endif
}
}
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
@@ -88,23 +93,26 @@ namespace hex::log {
hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
}
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::getLoggerMutex());
auto dest = impl::getDestination();
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}", message);
fflush(dest);
try {
auto dest = impl::getDestination();
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}", message);
fflush(dest);
} catch (const std::exception&) { }
}
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::getLoggerMutex());
auto dest = impl::getDestination();
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message);
fflush(dest);
try {
auto dest = impl::getDestination();
auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message);
fflush(dest);
} catch (const std::exception&) { }
}
}

View File

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

View File

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

View File

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

View File

@@ -74,7 +74,7 @@ namespace hex {
return m_data | std::views::values;
}
void setOnCreateCallback(std::function<void(const prv::Provider *, T&)> callback) {
void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {
m_onCreateCallback = std::move(callback);
}
@@ -84,7 +84,7 @@ namespace hex {
auto [it, inserted] = m_data.emplace(provider, T());
auto &[key, value] = *it;
if (m_onCreateCallback)
m_onCreateCallback(key, value);
m_onCreateCallback(provider, value);
});
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
@@ -121,7 +121,7 @@ namespace hex {
private:
std::map<const prv::Provider *, T> m_data;
std::function<void(const prv::Provider *, T&)> m_onCreateCallback;
std::function<void(prv::Provider *, T&)> m_onCreateCallback;
};
}

View File

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

View File

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

View File

@@ -533,6 +533,11 @@ namespace hex {
pl::PatternLanguage& getRuntime() {
static PerProvider<pl::PatternLanguage> runtime;
AT_FIRST_TIME {
runtime.setOnCreateCallback([](prv::Provider *provider, pl::PatternLanguage &runtime) {
configureRuntime(runtime, provider);
});
};
return *runtime;
}

View File

@@ -75,7 +75,11 @@ namespace hex {
static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
void setCurrentSelection(const std::optional<ProviderRegion> &region) {
*s_currentSelection = region;
if (region == Region::Invalid()) {
clearSelection();
} else {
*s_currentSelection = region;
}
}
static PerProvider<std::optional<Region>> s_hoveredRegion;
@@ -599,6 +603,11 @@ namespace hex {
return impl::s_initialWindowProperties;
}
void* getLibImHexModuleHandle() {
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
}
const std::map<std::string, std::string>& getInitArguments() {
return *impl::s_initArguments;
}
@@ -658,7 +667,11 @@ namespace hex {
#if defined(OS_WINDOWS)
return "Windows";
#elif defined(OS_LINUX)
return "Linux";
#if defined(OS_FREEBSD)
return "FreeBSD";
#else
return "Linux";
#endif
#elif defined(OS_MACOS)
return "macOS";
#elif defined(OS_WEB)
@@ -915,7 +928,7 @@ namespace hex {
};
}
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
wolv::io::File fontFile(path, wolv::io::File::Mode::Read);
if (!fontFile.isValid()) {
log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path));
@@ -927,17 +940,19 @@ namespace hex {
fontFile.readVector(),
glyphRanges,
offset,
flags
flags,
defaultSize
});
}
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
impl::s_fonts->emplace_back(Font {
name,
{ data.begin(), data.end() },
glyphRanges,
offset,
flags
flags,
defaultSize
});
}

View File

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

View File

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

View File

@@ -412,13 +412,15 @@ namespace hex {
void TaskManager::runDeferredCalls() {
std::scoped_lock lock(s_deferredCallsMutex);
for (const auto &call : s_deferredCalls)
call();
for (const auto &[location, call] : s_onceDeferredCalls)
call();
s_deferredCalls.clear();
s_onceDeferredCalls.clear();
while (!s_deferredCalls.empty()) {
auto callback = s_deferredCalls.front();
s_deferredCalls.pop_front();
callback();
}
while (!s_onceDeferredCalls.empty()) {
auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());
node.mapped()();
}
}
void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {

View File

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

View File

@@ -13,7 +13,11 @@
#include <shellapi.h>
#elif defined(OS_LINUX) || defined(OS_WEB)
#include <xdg.hpp>
# if defined(OS_FREEBSD)
#include <sys/syslimits.h>
# else
#include <limits.h>
# endif
#endif
#if defined(OS_WEB)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,8 @@ else ()
target_link_libraries(main PRIVATE pthread)
endif ()
precompileHeaders(main ${CMAKE_CURRENT_SOURCE_DIR}/include)
if (APPLE)
add_compile_definitions(GL_SILENCE_DEPRECATION)
endif ()

View File

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

View File

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

View File

@@ -238,7 +238,7 @@ namespace hex::init {
FrameResult WindowSplash::fullFrame() {
glfwSetWindowSize(m_window, 640_scaled, 400_scaled);
glfwSetWindowSize(m_window, 640, 400);
centerWindow(m_window);
glfwPollEvents();
@@ -248,23 +248,21 @@ namespace hex::init {
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
auto scale = ImHexApi::System::getGlobalScale();
// Draw the splash screen background
auto drawList = ImGui::GetBackgroundDrawList();
{
// Draw the splash screen background
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize() * scale);
drawList->AddImage(this->splashBackgroundTexture, ImVec2(0, 0), this->splashBackgroundTexture.getSize());
{
// Function to highlight a given number of bytes at a position in the splash screen
const auto highlightBytes = [&](ImVec2 start, size_t count, ImColor color, float opacity) {
// Dimensions and number of bytes that are drawn. Taken from the splash screen image
const auto hexSize = ImVec2(29, 18) * scale;
const auto hexSpacing = ImVec2(17.4, 15) * scale;
const auto hexStart = ImVec2(27, 127) * scale;
const auto hexSize = ImVec2(29, 18);
const auto hexSpacing = ImVec2(17.4, 15);
const auto hexStart = ImVec2(27, 127);
constexpr auto HexCount = ImVec2(13, 7);
@@ -312,10 +310,10 @@ namespace hex::init {
this->progressLerp += (m_progress - this->progressLerp) * 0.1F;
// Draw the splash screen foreground
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize() * scale);
drawList->AddImage(this->splashTextTexture, ImVec2(0, 0), this->splashTextTexture.getSize());
// Draw the "copyright" notice
drawList->AddText(ImVec2(35, 85) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
drawList->AddText(ImVec2(35, 85), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("WerWolv\n2020 - {0}", &__DATE__[7]).c_str());
// Draw version information
// In debug builds, also display the current commit hash and branch
@@ -325,18 +323,18 @@ namespace hex::init {
const static auto VersionInfo = hex::format("{0}", ImHexApi::System::getImHexVersion());
#endif
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x * scale - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105 * scale), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
drawList->AddText(ImVec2((this->splashBackgroundTexture.getSize().x - ImGui::CalcTextSize(VersionInfo.c_str()).x) / 2, 105), ImColor(0xFF, 0xFF, 0xFF, 0xFF), VersionInfo.c_str());
}
// Draw the task progress bar
{
std::lock_guard guard(m_progressMutex);
const auto progressBackgroundStart = ImVec2(99, 357) * scale;
const auto progressBackgroundSize = ImVec2(442, 30) * scale;
const auto progressBackgroundStart = ImVec2(99, 357);
const auto progressBackgroundSize = ImVec2(442, 30);
const auto progressStart = progressBackgroundStart + ImVec2(0, 20) * scale;
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10 * scale);
const auto progressStart = progressBackgroundStart + ImVec2(0, 20);
const auto progressSize = ImVec2(progressBackgroundSize.x * m_progress, 10);
// Draw progress bar
drawList->AddRectFilled(progressStart, progressStart + progressSize, 0xD0FFFFFF);
@@ -344,7 +342,7 @@ namespace hex::init {
// Draw task names separated by | characters
if (!m_currTaskNames.empty()) {
drawList->PushClipRect(progressBackgroundStart, progressBackgroundStart + progressBackgroundSize, true);
drawList->AddText(progressStart + ImVec2(5, -20) * scale, ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
drawList->AddText(progressStart + ImVec2(5, -20), ImColor(0xFF, 0xFF, 0xFF, 0xFF), hex::format("{}", fmt::join(m_currTaskNames, " | ")).c_str());
drawList->PopClipRect();
}
}
@@ -492,7 +490,7 @@ namespace hex::init {
ImFontConfig cfg;
cfg.OversampleH = cfg.OversampleV = 1, cfg.PixelSnapH = true;
cfg.SizePixels = 13.0_scaled;
cfg.SizePixels = ImHexApi::Fonts::DefaultFontSize;
io.Fonts->AddFontDefault(&cfg);
std::uint8_t *px;

View File

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

View File

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

View File

@@ -126,6 +126,7 @@ namespace hex {
throw;
} catch (const std::exception &e) {
log::fatal("Unhandled exception: {}", e.what());
EventCrashRecovered::post(e);
} catch (...) {
log::fatal("Unhandled exception: Unknown exception");
}
@@ -133,26 +134,47 @@ namespace hex {
void errorRecoverLogCallback(void*, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
std::string message;
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
std::vsnprintf(message.data(), message.size(), fmt, args);
message.resize(message.size() - 1);
va_start(args, fmt);
message.resize(std::vsnprintf(nullptr, 0, fmt, args));
va_end(args);
va_start(args, fmt);
std::vsnprintf(message.data(), message.size(), fmt, args);
va_end(args);
message.resize(message.size() - 1);
log::error("{}", message);
}
void Window::fullFrame() {
static u32 crashWatchdog = 0;
try {
this->frameBegin();
this->frame();
this->frameEnd();
// Feed the watchdog
crashWatchdog = 0;
} catch (...) {
// If an exception keeps being thrown, abort the application after 10 frames
// This is done to avoid the application getting stuck in an infinite loop of exceptions
crashWatchdog += 1;
if (crashWatchdog > 10) {
log::fatal("Crash watchdog triggered, aborting");
std::abort();
}
// Try to recover from the exception by bringing ImGui back into a working state
ImGui::ErrorCheckEndFrameRecover(errorRecoverLogCallback, nullptr);
ImGui::EndFrame();
ImGui::UpdatePlatformWindows();
// Handle the exception
handleException();
}
}
@@ -164,13 +186,11 @@ namespace hex {
// Determine if the application should be in long sleep mode
bool shouldLongSleep = !m_unlockFrameRate;
// Wait 5 frames before actually enabling the long sleep mode to make animations not stutter
constexpr static auto LongSleepTimeout = 5;
static i32 lockTimeout = 0;
static double lockTimeout = 0;
if (!shouldLongSleep) {
lockTimeout = LongSleepTimeout;
lockTimeout = 0.05;
} else if (lockTimeout > 0) {
lockTimeout -= 1;
lockTimeout -= m_lastFrameTime;
}
if (shouldLongSleep && lockTimeout > 0)
@@ -192,6 +212,7 @@ namespace hex {
constexpr static auto LongSleepFPS = 5.0;
const double timeout = std::max(0.0, (1.0 / LongSleepFPS) - (glfwGetTime() - m_lastStartFrameTime));
glfwPollEvents();
glfwWaitEventsTimeout(timeout);
} else {
glfwPollEvents();
@@ -214,14 +235,28 @@ namespace hex {
// Limit frame rate
// If the target FPS are below 15, use the monitor refresh rate, if it's above 200, don't limit the frame rate
const auto targetFPS = ImHexApi::System::getTargetFPS();
if (targetFPS < 15) {
glfwSwapInterval(1);
} else if (targetFPS > 200) {
glfwSwapInterval(0);
auto targetFPS = ImHexApi::System::getTargetFPS();
if (targetFPS >= 200) {
// Let it rip
} else {
// If the target frame rate is below 15, use the current monitor's refresh rate
if (targetFPS < 15) {
// Fall back to 60 FPS if the monitor refresh rate cannot be determined
targetFPS = 60;
if (auto monitor = glfwGetWindowMonitor(m_window); monitor != nullptr) {
if (auto videoMode = glfwGetVideoMode(monitor); videoMode != nullptr) {
targetFPS = videoMode->refreshRate;
}
}
}
// Sleep if we're not in long sleep mode
if (!shouldLongSleep) {
glfwSwapInterval(0);
// If anything goes wrong with these checks, make sure that we're sleeping for at least 1ms
std::this_thread::sleep_for(std::chrono::milliseconds(1));
// Sleep for the remaining time if the frame rate is above the target frame rate
const auto frameTime = glfwGetTime() - m_lastStartFrameTime;
const auto targetFrameTime = 1.0 / targetFPS;
if (frameTime < targetFrameTime) {
@@ -567,29 +602,54 @@ namespace hex {
// Finalize ImGui frame
ImGui::Render();
// Hash the draw data to determine if anything changed on the screen
// Compare the previous frame buffer to the current one to determine if the window content has changed
// If not, there's no point in sending the draw data off to the GPU and swapping buffers
// NOTE: For anybody looking at this code and thinking "why not just hash the buffer and compare the hashes",
// the reason is that hashing the buffer is significantly slower than just comparing the buffers directly.
// The buffer might become quite large if there's a lot of vertices on the screen but it's still usually less than
// 10MB (out of which only the active portion needs to actually be compared) which is worth the ~60x speedup.
bool shouldRender = false;
{
u32 drawDataHash = 0;
static u32 previousDrawDataHash = 0;
static std::vector<u8> previousVtxData;
static size_t previousVtxDataSize = 0;
size_t offset = 0;
size_t vtxDataSize = 0;
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
auto drawData = viewPort->DrawData;
for (int n = 0; n < drawData->CmdListsCount; n++) {
const ImDrawList *cmdList = drawData->CmdLists[n];
drawDataHash = ImHashData(cmdList->VtxBuffer.Data, cmdList->VtxBuffer.Size * sizeof(ImDrawVert), drawDataHash);
vtxDataSize += drawData->CmdLists[n]->VtxBuffer.size() * sizeof(ImDrawVert);
}
}
for (const auto viewPort : ImGui::GetPlatformIO().Viewports) {
auto drawData = viewPort->DrawData;
for (int n = 0; n < drawData->CmdListsCount; n++) {
const ImDrawList *cmdList = drawData->CmdLists[n];
drawDataHash = ImHashData(cmdList->IdxBuffer.Data, cmdList->IdxBuffer.Size * sizeof(ImDrawIdx), drawDataHash);
if (vtxDataSize == previousVtxDataSize) {
shouldRender = shouldRender || std::memcmp(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) != 0;
} else {
shouldRender = true;
}
if (previousVtxData.size() < offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert)) {
previousVtxData.resize(offset + cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
}
std::memcpy(previousVtxData.data() + offset, cmdList->VtxBuffer.Data, cmdList->VtxBuffer.size() * sizeof(ImDrawVert));
offset += cmdList->VtxBuffer.size() * sizeof(ImDrawVert);
}
}
shouldRender = drawDataHash != previousDrawDataHash;
previousDrawDataHash = drawDataHash;
previousVtxDataSize = vtxDataSize;
}
GLFWwindow *backupContext = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backupContext);
if (shouldRender) {
int displayWidth, displayHeight;
glfwGetFramebufferSize(m_window, &displayWidth, &displayHeight);
@@ -603,11 +663,6 @@ namespace hex {
m_unlockFrameRate = true;
}
GLFWwindow *backupContext = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backupContext);
// Process layout load requests
// NOTE: This needs to be done before a new frame is started, otherwise ImGui won't handle docking correctly
LayoutManager::process();
@@ -675,7 +730,9 @@ namespace hex {
glfwSetWindowOpacity(m_window, 1.0F);
glfwMakeContextCurrent(m_window);
glfwSwapInterval(1);
// Disable VSync. Not like any graphics driver actually cares
glfwSwapInterval(0);
// Center window
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
@@ -927,6 +984,8 @@ namespace hex {
void Window::exitImGui() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImNodes::DestroyContext();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}

View File

@@ -61,6 +61,8 @@ std::optional<std::fs::path> downloadUpdate(const std::string &url) {
return std::nullopt;
}
hex::log::info("Writing update to file: {}", file.getPath().string());
// Write the downloaded update data to the file
file.writeVector(data);
@@ -97,7 +99,7 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
};
constexpr static auto UpdateHandlers = {
UpdateHandler { "win-msi", ".msi", "msiexec /fa {} /passive" },
UpdateHandler { "win-msi", ".msi", "msiexec /passive /package {}" },
UpdateHandler { "macos-dmg", ".dmg", "hdiutil attach {}" },
UpdateHandler { "linux-deb-22.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
UpdateHandler { "linux-deb-23.04", ".deb", "sudo apt update && sudo apt install -y --fix-broken {}" },
@@ -108,10 +110,14 @@ int installUpdate(const std::string &type, std::fs::path updatePath) {
// Rename the update file to the correct extension
const auto originalPath = updatePath;
updatePath.replace_extension(handler.extension);
hex::log::info("Moving update package from {} to {}", originalPath.string(), updatePath.string());
std::fs::rename(originalPath, updatePath);
// Install the update using the correct command
hex::startProgram(hex::format(handler.command, updatePath.string()));
const auto command = hex::format(handler.command, updatePath.string());
hex::log::info("Starting update process with command: '{}'", command);
hex::startProgram(command);
return EXIT_SUCCESS;
}
@@ -163,6 +169,8 @@ int main(int argc, char **argv) {
if (!updatePath.has_value())
return EXIT_FAILURE;
hex::log::info("Downloaded update successfully");
// Install the update
return installUpdate(updateType, *updatePath);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,46 @@
#pragma once
#include <hex/ui/popup.hpp>
#include <hex/api/localization_manager.hpp>
#include <llvm/Demangle/Demangle.h>
#include <string>
namespace hex::plugin::builtin {
class PopupCrashRecovered : public Popup<PopupCrashRecovered> {
public:
PopupCrashRecovered(const std::exception &e)
: hex::Popup<PopupCrashRecovered>("hex.builtin.popup.crash_recover.title", false),
m_errorType(typeid(e).name()),
m_errorMessage(e.what()) { }
void drawContent() override {
ImGuiExt::TextFormattedWrapped("hex.builtin.popup.crash_recover.message"_lang);
ImGuiExt::TextFormattedWrapped(hex::format("Error: {}: {}", llvm::itaniumDemangle(this->m_errorType), this->m_errorMessage));
if (ImGui::Button("hex.ui.common.okay"_lang)) {
this->close();
}
}
[[nodiscard]] ImGuiWindowFlags getFlags() const override {
return ImGuiWindowFlags_AlwaysAutoResize;
}
[[nodiscard]] ImVec2 getMinSize() const override {
return scaled({ 400, 100 });
}
[[nodiscard]] ImVec2 getMaxSize() const override {
return scaled({ 600, 300 });
}
private:
std::string m_errorType, m_errorMessage;
};
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#pragma once
#if defined(OS_WINDOWS) || defined (OS_LINUX)
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
#include <hex/providers/provider.hpp>
#include <hex/api/localization_manager.hpp>

View File

@@ -46,11 +46,13 @@ namespace hex::plugin::builtin {
return m_provider->isSavable();
}
[[nodiscard]] bool isSavableAsRecent() const override { return false; }
void save() override {
m_provider->save();
}
[[nodiscard]] bool open() override { return true; }
[[nodiscard]] bool open() override { return m_provider != this; }
void close() override { }
void resizeRaw(u64 newSize) override {
@@ -101,8 +103,31 @@ namespace hex::plugin::builtin {
return m_provider->getDataDescription();
}
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
void loadSettings(const nlohmann::json &settings) override {
Provider::loadSettings(settings);
auto id = settings.at("id").get<u64>();
m_startAddress = settings.at("start_address").get<u64>();
m_size = settings.at("size").get<size_t>();
const auto &providers = ImHexApi::Provider::getProviders();
auto provider = std::ranges::find_if(providers, [id](const prv::Provider *provider) {
return provider->getID() == id;
});
if (provider == providers.end())
return;
m_provider = *provider;
}
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override {
settings["id"] = m_provider->getID();
settings["start_address"] = m_startAddress;
settings["size"] = m_size;
return Provider::storeSettings(settings);
}
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.view";

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

@@ -21,10 +21,19 @@
#include <popups/popup_question.hpp>
#include <content/popups/popup_tasks_waiting.hpp>
#include <content/popups/popup_unsaved_changes.hpp>
#include <content/popups/popup_crash_recovered.hpp>
namespace hex::plugin::builtin {
static void openFile(const std::fs::path &path) {
if (path.extension() == ".hexproj") {
if (!ProjectFile::load(path)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
} else {
return;
}
}
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<FileProvider*>(provider); fileProvider != nullptr) {
fileProvider->setPath(path);
@@ -33,6 +42,7 @@ namespace hex::plugin::builtin {
TaskManager::doLater([provider] { ImHexApi::Provider::remove(provider); });
} else {
EventProviderOpened::post(fileProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
}
}
}
@@ -40,6 +50,10 @@ namespace hex::plugin::builtin {
void registerEventHandlers() {
static bool imhexClosing = false;
EventCrashRecovered::subscribe([](const std::exception &e) {
PopupCrashRecovered::open(e);
});
EventWindowClosing::subscribe([](GLFWwindow *window) {
imhexClosing = false;
if (ImHexApi::Provider::isDirty() && !imhexClosing) {
@@ -91,9 +105,6 @@ namespace hex::plugin::builtin {
EventProviderOpened::subscribe([](hex::prv::Provider *provider) {
if (provider != nullptr && ImHexApi::Provider::get() == provider) {
RequestUpdateWindowTitle::post();
if (!provider->isWritable())
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
}
});
@@ -111,23 +122,24 @@ namespace hex::plugin::builtin {
if (path.extension() == ".hexproj") {
if (!ProjectFile::load(path)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(path)));
}
} else {
auto newProvider = static_cast<FileProvider*>(
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
);
if (newProvider == nullptr)
return;
newProvider->setPath(path);
if (!newProvider->open()) {
hex::ImHexApi::Provider::remove(newProvider);
} else {
EventProviderOpened::post(newProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
return;
}
}
auto newProvider = static_cast<FileProvider*>(
ImHexApi::Provider::createProvider("hex.builtin.provider.file", true)
);
if (newProvider == nullptr)
return;
newProvider->setPath(path);
if (!newProvider->open()) {
hex::ImHexApi::Provider::remove(newProvider);
} else {
EventProviderOpened::post(newProvider);
AchievementManager::unlockAchievement("hex.builtin.achievement.starting_out", "hex.builtin.achievement.starting_out.open_file.name");
}
}, {}, true);
} else if (name == "Open Project") {
@@ -239,6 +251,8 @@ namespace hex::plugin::builtin {
if (ImGui::IsPopupOpen("", ImGuiPopupFlags_AnyPopup))
return;
if (ImGui::IsAnyItemHovered())
return;
static ImGuiWindow *lastFocusedWindow = nullptr;

View File

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

View File

@@ -278,6 +278,13 @@ namespace hex::plugin::builtin {
// Disable merge mode for this font but retain the rest of the configuration
cfg.MergeMode = false;
auto size = fontSize;
if (font.defaultSize.has_value())
size = font.defaultSize.value() * ImHexApi::System::getGlobalScale();
cfg.SizePixels = size;
ON_SCOPE_EXIT { cfg.MergeMode = true; };
// Construct a range that only contains the first glyph of the font

View File

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

View File

@@ -348,6 +348,8 @@ namespace hex::plugin::builtin {
// Draw deny button
ImGui::SetCursorPosX(buttonPos(2));
if (ImGui::Button("hex.ui.common.deny"_lang, buttonSize)) {
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.server_contact", 0);
ContentRegistry::Settings::write<int>("hex.builtin.setting.general", "hex.builtin.setting.general.upload_crash_logs", 0);
page += 1;
}

View File

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

View File

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

View File

@@ -28,7 +28,9 @@
#elif defined(OS_LINUX)
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
#if !defined(OS_FREEBSD)
#include <linux/fs.h>
#endif
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
@@ -41,8 +43,11 @@
#include <sys/disk.h>
#endif
#if defined(OS_LINUX)
#define lseek lseek64
#if defined(OS_LINUX) && !defined(OS_FREEBSD)
#define lseek lseek64
#elif defined(OS_FREEBSD)
#include <sys/disk.h>
#define DEFAULT_SECTOR_SIZE 512
#endif
namespace hex::plugin::builtin {
@@ -83,6 +88,12 @@ namespace hex::plugin::builtin {
return -1;
return 0;
}
#elif defined(OS_FREEBSD) && defined(DIOCGSECTORSIZE)
int blkdev_get_sector_size(int fd, int *sector_size) {
if (ioctl(fd, DIOCGSECTORSIZE, sector_size) < 0)
return -1;
return 0;
}
#else
int blkdev_get_sector_size(int fd, int *sector_size) {
(void)fd;
@@ -97,6 +108,12 @@ namespace hex::plugin::builtin {
return -1;
return 0;
}
#elif defined(OS_FREEBSD) && defined(DIOCGMEDIASIZE)
int blkdev_get_size(int fd, u64 *bytes) {
if (ioctl(fd, DIOCGMEDIASIZE, bytes) < 0)
return -1;
return 0;
}
#else
int blkdev_get_size(int fd, u64 *bytes) {
struct stat st;

View File

@@ -1,13 +1,13 @@
#include "content/providers/file_provider.hpp"
#include "content/providers/memory_file_provider.hpp"
#include <cstring>
#include <hex/api/imhex_api.hpp>
#include <hex/api/localization_manager.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/task_manager.hpp>
#include <toasts/toast_notification.hpp>
#include <hex/helpers/utils.hpp>
#include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h>
@@ -15,6 +15,8 @@
#include <wolv/utils/string.hpp>
#include <nlohmann/json.hpp>
#include <cstring>
#include <popups/popup_question.hpp>
#if defined(OS_WINDOWS)
#include <windows.h>
@@ -41,25 +43,38 @@ namespace hex::plugin::builtin {
}
bool FileProvider::isSavable() const {
return m_undoRedoStack.canUndo();
return m_loadedIntoMemory;
}
void FileProvider::readRaw(u64 offset, void *buffer, size_t size) {
if (m_fileSize == 0 || (offset + size) > m_fileSize || buffer == nullptr || size == 0)
return;
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
if (m_loadedIntoMemory)
std::memcpy(buffer, m_data.data() + offset, size);
else
m_file.readBufferAtomic(offset, static_cast<u8*>(buffer), size);
}
void FileProvider::writeRaw(u64 offset, const void *buffer, size_t size) {
if ((offset + size) > this->getActualSize() || buffer == nullptr || size == 0)
return;
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
if (m_loadedIntoMemory)
std::memcpy(m_data.data() + offset, buffer, size);
else
m_file.writeBufferAtomic(offset, static_cast<const u8*>(buffer), size);
}
void FileProvider::save() {
m_file.flush();
if (m_loadedIntoMemory) {
m_ignoreNextChangeEvent = true;
m_file.open();
m_file.writeVectorAtomic(0x00, m_data);
m_file.setSize(m_data.size());
} else {
m_file.flush();
}
#if defined(OS_WINDOWS)
FILETIME ft;
@@ -74,6 +89,9 @@ namespace hex::plugin::builtin {
}
#endif
if (m_loadedIntoMemory)
m_file.close();
Provider::save();
}
@@ -85,51 +103,12 @@ namespace hex::plugin::builtin {
}
void FileProvider::resizeRaw(u64 newSize) {
m_file.setSize(newSize);
}
if (m_loadedIntoMemory)
m_data.resize(newSize);
else
m_file.setSize(newSize);
void FileProvider::insertRaw(u64 offset, u64 size) {
auto oldSize = this->getActualSize();
this->resizeRaw(oldSize + size);
std::vector<u8> buffer(0x1000);
const std::vector<u8> zeroBuffer(0x1000);
auto position = oldSize;
while (position > offset) {
const auto readSize = std::min<size_t>(position - offset, buffer.size());
position -= readSize;
this->readRaw(position, buffer.data(), readSize);
this->writeRaw(position, zeroBuffer.data(), readSize);
this->writeRaw(position + size, buffer.data(), readSize);
}
}
void FileProvider::removeRaw(u64 offset, u64 size) {
if (offset > this->getActualSize() || size == 0)
return;
if ((offset + size) > this->getActualSize())
size = this->getActualSize() - offset;
auto oldSize = this->getActualSize();
std::vector<u8> buffer(0x1000);
const auto newSize = oldSize - size;
auto position = offset;
while (position < newSize) {
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
this->readRaw(position + size, buffer.data(), readSize);
this->writeRaw(position, buffer.data(), readSize);
position += readSize;
}
this->resizeRaw(newSize);
m_fileSize = newSize;
}
u64 FileProvider::getActualSize() const {
@@ -207,7 +186,7 @@ namespace hex::plugin::builtin {
m_readable = true;
m_writable = true;
if (!std::fs::exists(m_path)) {
if (!wolv::io::fs::exists(m_path)) {
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(ENOENT)));
return false;
}
@@ -222,6 +201,8 @@ namespace hex::plugin::builtin {
this->setErrorMessage(hex::format("hex.builtin.provider.file.error.open"_lang, m_path.string(), ::strerror(errno)));
return false;
}
ui::ToastInfo::open("hex.builtin.popup.error.read_only"_lang);
}
m_file = std::move(file);
@@ -242,12 +223,36 @@ namespace hex::plugin::builtin {
}
}
if (m_writable) {
if (m_fileSize < MaxMemoryFileSize) {
m_data = m_file.readVectorAtomic(0x00, m_fileSize);
if (!m_data.empty()) {
m_changeTracker = wolv::io::ChangeTracker(m_file);
m_changeTracker.startTracking([this]{ this->handleFileChange(); });
m_file.close();
m_loadedIntoMemory = true;
}
} else {
m_writable = false;
ui::PopupQuestion::open("hex.builtin.provider.file.too_large"_lang,
[this] {
m_writable = false;
},
[this] {
m_writable = true;
RequestUpdateWindowTitle::post();
});
}
}
return true;
}
void FileProvider::close() {
m_file.close();
m_data.clear();
s_openedFiles.erase(this);
m_changeTracker.stopTracking();
}
void FileProvider::loadSettings(const nlohmann::json &settings) {
@@ -328,4 +333,19 @@ namespace hex::plugin::builtin {
}
}
void FileProvider::handleFileChange() {
if (m_ignoreNextChangeEvent) {
m_ignoreNextChangeEvent = false;
return;
}
ui::PopupQuestion::open("hex.builtin.provider.file.reload_changes"_lang, [this] {
this->close();
(void)this->open();
getUndoStack().reapply();
},
[]{});
}
}

View File

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

View File

@@ -1,4 +1,4 @@
#if defined(OS_WINDOWS) || defined (OS_LINUX)
#if defined(OS_WINDOWS) || (defined(OS_LINUX) && !defined(OS_FREEBSD))
#include <content/providers/process_memory_provider.hpp>

View File

@@ -219,7 +219,9 @@ namespace hex::plugin::builtin::recent {
void loadRecentEntry(const RecentEntry &recentEntry) {
if (recentEntry.type == "project") {
std::fs::path projectPath = recentEntry.data["path"].get<std::string>();
ProjectFile::load(projectPath);
if (!ProjectFile::load(projectPath)) {
ui::ToastError::open(hex::format("hex.builtin.popup.error.project.load"_lang, wolv::util::toUTF8String(projectPath)));
}
return;
}
auto *provider = ImHexApi::Provider::createProvider(recentEntry.type, true);

View File

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

View File

@@ -235,32 +235,20 @@ namespace hex::plugin::builtin {
ImGui::NewLine();
}
void ViewAbout::drawContributorPage() {
struct Contributor {
const char *name;
const char *description;
const char *link;
bool mainContributor;
};
constexpr static std::array Contributors = {
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/Nowilltolife", true },
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
};
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
ImGui::NewLine();
struct Contributor {
const char *name;
const char *description;
const char *link;
bool mainContributor;
};
static void drawContributorTable(const char *title, const auto &contributors) {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2());
ImGuiExt::BeginSubWindow("Contributors", ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
ImGuiExt::BeginSubWindow(title, ImVec2(ImGui::GetContentRegionAvail().x, 0), ImGuiChildFlags_AutoResizeX);
ImGui::PopStyleVar();
{
if (ImGui::BeginTable("Contributors", 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
for (const auto &contributor : Contributors) {
if (ImGui::BeginTable(title, 1, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
for (const auto &contributor : contributors) {
ImGui::TableNextRow();
if (contributor.mainContributor) {
ImGui::TableSetBgColor(ImGuiTableBgTarget_RowBg0, ImGui::GetColorU32(ImGuiCol_PlotHistogram) & 0x1FFFFFFF);
@@ -270,10 +258,11 @@ namespace hex::plugin::builtin {
if (ImGuiExt::Hyperlink(contributor.name))
hex::openWebpage(contributor.link);
ImGui::Indent();
ImGui::TextUnformatted(contributor.description);
ImGui::Unindent();
if (contributor.description[0] != '\0') {
ImGui::Indent();
ImGui::TextUnformatted(contributor.description);
ImGui::Unindent();
}
}
ImGui::EndTable();
@@ -282,6 +271,36 @@ namespace hex::plugin::builtin {
ImGuiExt::EndSubWindow();
}
void ViewAbout::drawContributorPage() {
constexpr static std::array Contributors = {
Contributor { "iTrooz", "A huge amount of help maintaining ImHex and the CI", "https://github.com/iTrooz", true },
Contributor { "jumanji144", "A ton of help with the Pattern Language, API and usage stats", "https://github.com/jumanji144", true },
Contributor { "AxCut", "A ton of great pattern language improvements and help with the issue tracker", "https://github.com/paxcut", false },
Contributor { "Mary", "Porting ImHex to macOS originally", "https://github.com/marysaka", false },
Contributor { "Roblabla", "Adding the MSI Windows installer", "https://github.com/roblabla", false },
Contributor { "jam1garner", "Adding support for Rust plugins", "https://github.com/jam1garner", false },
Contributor { "All other amazing contributors", "Being part of the community, opening issues, PRs and donating", "https://github.com/WerWolv/ImHex/graphs/contributors", false }
};
constexpr static std::array Testers = {
Contributor { "Nemoumbra", "Breaking my code literal seconds after I push it", "https://github.com/Nemoumbra", true },
Contributor { "Berylskid", "", "https://github.com/Berylskid", false },
Contributor { "Jan Polak", "", "https://github.com/polak-jan", false },
Contributor { "Ken-Kaneki", "", "https://github.com/loneicewolf", false },
Contributor { "Everybody who has reported issues", "Helping me find bugs and improve the software", "https://github.com/WerWolv/ImHex/issues", false }
};
ImGuiExt::TextFormattedWrapped("These amazing people have contributed some incredible things to ImHex in the past.\nConsider opening a PR on the Git Repository to take your place among them!");
ImGui::NewLine();
drawContributorTable("Contributors", Contributors);
ImGui::NewLine();
ImGuiExt::TextFormattedWrapped("All of these great people made ImHex work much much smoother.\nConsider joining our Tester team to help making ImHex better for everyone!");
ImGui::NewLine();
drawContributorTable("Testers", Testers);
}
void ViewAbout::drawLibraryCreditsPage() {
struct Library {
const char *name;

View File

@@ -313,8 +313,8 @@ namespace hex::plugin::builtin {
u64 droppedBookmarkId = *static_cast<const u64*>(payload->Data);
// Find the correct bookmark with that id
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmark) {
return bookmark.entry.id == droppedBookmarkId;
auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmarkItem) {
return bookmarkItem.entry.id == droppedBookmarkId;
});
// Swap the two bookmarks
@@ -412,16 +412,25 @@ namespace hex::plugin::builtin {
u64 end = region.getEndAddress();
if (!locked) {
bool updated = false;
ImGui::PushItemWidth(100_scaled);
ImGuiExt::InputHexadecimal("##begin", &begin);
if (ImGuiExt::InputHexadecimal("##begin", &begin))
updated = true;
ImGui::SameLine(0, 0);
ImGui::TextUnformatted(" - ");
ImGui::SameLine(0, 0);
ImGuiExt::InputHexadecimal("##end", &end);
if (ImGuiExt::InputHexadecimal("##end", &end))
updated = true;
ImGui::PopItemWidth();
if (end > begin)
if (updated && end > begin) {
region = Region(begin, end - begin + 1);
EventHighlightingChanged::post();
}
} else {
ImGuiExt::TextFormatted("0x{:02X} - 0x{:02X}", begin, end);
}

View File

@@ -29,7 +29,7 @@ namespace hex::plugin::builtin {
m_validBytes = 0;
m_selectedProvider = nullptr;
} else {
m_validBytes = u64(region.getProvider()->getActualSize() - region.address);
m_validBytes = u64((region.getProvider()->getBaseAddress() + region.getProvider()->getActualSize()) - region.address);
m_startAddress = region.address;
m_selectedProvider = region.getProvider();
}

View File

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

View File

@@ -1050,7 +1050,7 @@ namespace hex::plugin::builtin {
ImGui::SetClipboardText(
callback(
provider,
selection->getStartAddress() + provider->getBaseAddress() + provider->getCurrentPageAddress(),
selection->getStartAddress(),
selection->size
).c_str()
);

View File

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

View File

@@ -3,13 +3,11 @@
#include <hex/api/content_registry.hpp>
#include <hex/api/project_file_manager.hpp>
#include <hex/api/achievement_manager.hpp>
#include <pl/patterns/pattern.hpp>
#include <pl/core/preprocessor.hpp>
#include <pl/core/parser.hpp>
#include <pl/core/ast/ast_node_variable_decl.hpp>
#include <pl/core/ast/ast_node_type_decl.hpp>
#include <pl/core/ast/ast_node_builtin_type.hpp>
#include <hex/helpers/fs.hpp>
@@ -22,11 +20,9 @@
#include <hex/helpers/fmt.hpp>
#include <fmt/chrono.h>
#include <popups/popup_file_chooser.hpp>
#include <popups/popup_question.hpp>
#include <toasts/toast_notification.hpp>
#include <nlohmann/json.hpp>
#include <chrono>
#include <wolv/io/file.hpp>
@@ -34,6 +30,8 @@
#include <wolv/utils/guards.hpp>
#include <wolv/utils/lock.hpp>
#include <content/global_actions.hpp>
namespace hex::plugin::builtin {
using namespace hex::literals;
@@ -88,6 +86,9 @@ namespace hex::plugin::builtin {
langDef.mAutoIndentation = true;
langDef.mPreprocChar = '#';
langDef.mGlobalDocComment = "/*!";
langDef.mDocComment = "/**";
langDef.mName = "Pattern Language";
initialized = true;
@@ -133,6 +134,9 @@ namespace hex::plugin::builtin {
}
static void drawVirtualFileTree(const std::vector<const ViewPatternEditor::VirtualFile*> &virtualFiles, u32 level = 0) {
ImGui::PushID(level + 1);
ON_SCOPE_EXIT { ImGui::PopID(); };
std::map<std::string, std::vector<const ViewPatternEditor::VirtualFile*>> currFolderEntries;
for (const auto &file : virtualFiles) {
const auto &path = file->path;
@@ -146,8 +150,7 @@ namespace hex::plugin::builtin {
ImGui::SameLine();
ImGui::TreeNodeEx(currSegment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen);
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left) && ImGui::IsItemHovered()) {
ImHexApi::Provider::add<prv::MemoryProvider>(file->data, wolv::util::toUTF8String(file->path.filename()));
}
@@ -157,6 +160,7 @@ namespace hex::plugin::builtin {
currFolderEntries[currSegment].emplace_back(file);
}
int id = 1;
for (const auto &[segment, entries] : currFolderEntries) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
@@ -167,11 +171,16 @@ namespace hex::plugin::builtin {
ImGui::TextUnformatted(ICON_VS_FOLDER);
}
ImGui::PushID(id);
ImGui::SameLine();
if (ImGui::TreeNodeEx(segment.c_str(), ImGuiTreeNodeFlags_SpanFullWidth)) {
drawVirtualFileTree(entries, level + 1);
ImGui::TreePop();
}
ImGui::PopID();
id += 1;
}
}
@@ -228,6 +237,14 @@ namespace hex::plugin::builtin {
bool clickedMenuFind = false;
bool clickedMenuReplace = false;
if (ImGui::BeginPopup("##pattern_editor_context_menu")) {
// no shortcut for this
if (ImGui::MenuItem("hex.builtin.menu.file.import.pattern_file"_lang, nullptr, false))
importPatternFile();
if (ImGui::MenuItem("hex.builtin.menu.file.export.pattern_file"_lang, nullptr, false))
exportPatternFile();
ImGui::Separator();
const bool hasSelection = m_textEditor.HasSelection();
if (ImGui::MenuItem("hex.builtin.view.hex_editor.menu.edit.cut"_lang, Shortcut(CTRLCMD + Keys::X).toString().c_str(), false, hasSelection)) {
m_textEditor.Cut();
@@ -298,6 +315,12 @@ namespace hex::plugin::builtin {
m_replaceMode = true;
openFindPopup = true;
}
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::saveProject();
if (ImGui::IsKeyPressed(ImGuiKey_O, false) && ImGui::GetIO().KeyAlt)
hex::plugin::builtin::openProject();
if (ImGui::IsKeyPressed(ImGuiKey_S, false) && ImGui::GetIO().KeyAlt && ImGui::GetIO().KeyShift)
hex::plugin::builtin::saveProjectAs();
}
static std::string findWord;
@@ -677,6 +700,8 @@ namespace hex::plugin::builtin {
if (altCPressed)
matchCase = !matchCase;
findReplaceHandler->SetMatchCase(&m_textEditor,matchCase);
position = findReplaceHandler->FindPosition(&m_textEditor,m_textEditor.GetCursorPosition(), true);
count = findReplaceHandler->GetMatches().size();
updateCount = true;
requestFocusFind = true;
}
@@ -1158,7 +1183,7 @@ namespace hex::plugin::builtin {
for (const auto &file : virtualFiles)
virtualFilePointers.emplace_back(&file);
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg, size)) {
if (ImGui::BeginTable("Virtual File Tree", 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY, size)) {
ImGui::TableSetupColumn("##path", ImGuiTableColumnFlags_WidthStretch);
drawVirtualFileTree(virtualFilePointers);
@@ -1277,7 +1302,8 @@ namespace hex::plugin::builtin {
if (!m_lastCompileError->empty()) {
for (const auto &error : *m_lastCompileError) {
errorMarkers[error.getLocation().line] = processMessage(error.getMessage());
if (error.getLocation().source->source == pl::api::Source::DefaultSource)
errorMarkers[error.getLocation().line] = processMessage(error.getMessage());
}
}
@@ -1305,7 +1331,7 @@ namespace hex::plugin::builtin {
pl::PatternLanguage runtime;
ContentRegistry::PatternLanguage::configureRuntime(runtime, provider);
auto mimeType = magic::getMIMEType(provider, true);
auto mimeType = magic::getMIMEType(provider, 0, 100_KiB, true);
bool foundCorrectType = false;
runtime.addPragma("MIME", [&mimeType, &foundCorrectType](const pl::PatternLanguage &runtime, const std::string &value) {
@@ -1392,12 +1418,8 @@ namespace hex::plugin::builtin {
if (!file.isValid())
continue;
auto &preprocessor = runtime.getInternals().preprocessor;
pl::api::Source source(file.readString());
auto ret = preprocessor->preprocess(&runtime, &source);
if (ret.hasErrs()) {
auto result = runtime.preprocessString(file.readString(), pl::api::Source::DefaultSource);
if (!result.has_value()) {
log::warn("Failed to preprocess file {} during MIME analysis", entry.path().string());
}
@@ -1542,6 +1564,7 @@ namespace hex::plugin::builtin {
m_sectionWindowDrawer.clear();
m_consoleEditor.SetText("");
m_virtualFiles->clear();
m_accessHistory = {};
m_accessHistoryIndex = 0;
@@ -1681,12 +1704,8 @@ namespace hex::plugin::builtin {
if (newProvider != nullptr)
m_textEditor.SetText(m_sourceCode.get(newProvider));
});
EventProviderClosed::subscribe(this, [this](prv::Provider *) {
if (ImHexApi::Provider::getProviders().empty()) {
else
m_textEditor.SetText("");
}
});
RequestAddVirtualFile::subscribe(this, [this](const std::fs::path &path, const std::vector<u8> &data, Region region) {
@@ -1731,39 +1750,11 @@ namespace hex::plugin::builtin {
void ViewPatternEditor::registerMenuItems() {
/* Import Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.import", "hex.builtin.menu.file.import.pattern" }, ICON_VS_FILE_CODE, 4050, Shortcut::None,
[this] {
auto provider = ImHexApi::Provider::get();
const auto basePaths = fs::getDefaultPaths(fs::ImHexPath::Patterns);
std::vector<std::fs::path> paths;
for (const auto &imhexPath : basePaths) {
if (!wolv::io::fs::exists(imhexPath)) continue;
std::error_code error;
for (auto &entry : std::fs::recursive_directory_iterator(imhexPath, error)) {
if (entry.is_regular_file() && entry.path().extension() == ".hexpat") {
paths.push_back(entry.path());
}
}
}
ui::PopupFileChooser::open(basePaths, paths, std::vector<hex::fs::ItemFilter>{ { "Pattern File", "hexpat" } }, false,
[this, provider](const std::fs::path &path) {
this->loadPatternFile(path, provider);
AchievementManager::unlockAchievement("hex.builtin.achievement.patterns", "hex.builtin.achievement.patterns.load_existing.name");
});
}, ImHexApi::Provider::isValid);
importPatternFile, ImHexApi::Provider::isValid);
/* Export Pattern */
ContentRegistry::Interface::addMenuItem({ "hex.builtin.menu.file", "hex.builtin.menu.file.export", "hex.builtin.menu.file.export.pattern" }, ICON_VS_FILE_CODE, 7050, Shortcut::None,
[this] {
fs::openFileBrowser(fs::DialogMode::Save, { {"Pattern", "hexpat"} },
[this](const auto &path) {
wolv::io::File file(path, wolv::io::File::Mode::Create);
file.writeString(wolv::util::trim(m_textEditor.GetText()));
});
}, [this] {
exportPatternFile, [this] {
return !wolv::util::trim(m_textEditor.GetText()).empty() && ImHexApi::Provider::isValid();
}
);

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