Compare commits

..

422 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
WerWolv
66e0f54d0e build: Bumped version to 1.33.0 2024-02-25 21:54:48 +01:00
WerWolv
cadc9cecf1 build: Updated dependencies 2024-02-25 21:21:34 +01:00
WerWolv
491e2dfe56 fix: Parent hover setting name being wrong 2024-02-25 14:38:26 +01:00
WerWolv
29c7b342eb impr: Make file chooser filter bar be focused by default 2024-02-25 14:34:09 +01:00
WerWolv
ea601a7d03 feat: Added option to highlight pattern parents in the hex editor when hovering 2024-02-25 14:30:56 +01:00
WerWolv
dac45659c0 patterns: Updated pattern language 2024-02-25 12:35:09 +01:00
WerWolv
4f72c60eb0 fix: Text editors highlighting doc comments when they shouldn't 2024-02-25 12:35:01 +01:00
WerWolv
7434fdec6f impr: Move advanced analysis yara rules to the patterns repo 2024-02-25 11:32:05 +01:00
WerWolv
c4f3ea901a feat: Added more yara detection rules for languages, compilers and envs 2024-02-25 11:20:35 +01:00
WerWolv
58ad9f2ca8 impr: Small code layout improvements for diagrams 2024-02-25 11:20:04 +01:00
WerWolv
3fa06cc7c0 build: Remove compression from sdk again 2024-02-25 01:57:42 +01:00
WerWolv
d551e39a1c build: Handle install prefix being absolute when installing sdk package 2024-02-25 01:27:48 +01:00
WerWolv
afede0ff9c fix: Yara rule matching not working properly with non-zero base addresses 2024-02-25 00:16:14 +01:00
WerWolv
d51b065723 fix: Yara information task not being interruptable 2024-02-25 00:09:06 +01:00
WerWolv
752a2d2e8d fix: Magic information querying being wrong with non-zero base address 2024-02-24 23:53:34 +01:00
WerWolv
b57eaca365 build: Try to fix sdk packaging 2024-02-24 23:41:22 +01:00
WerWolv
859574c014 impr: .0f -> .0F 2024-02-24 23:34:29 +01:00
WerWolv
0505b123a0 impr: Remove weird spinner from data information view 2024-02-24 23:34:18 +01:00
WerWolv
00b3d7809c fix: Various issues with the digram and layered distribution diagrams 2024-02-24 23:23:53 +01:00
Nik
ec69849749 feat: Added font picker to the settings (#1570) 2024-02-24 22:46:52 +01:00
WerWolv
8bf7aa9ceb build: Respect DISTDIR env var when packing SDK 2024-02-24 22:46:16 +01:00
WerWolv
ca0b6f2c6d fix: Digram sample size being too low and filter options inverted 2024-02-24 19:04:58 +01:00
WerWolv
5182a61fcc fix: Interacting with popups that extend past the edge of the window 2024-02-24 18:59:01 +01:00
WerWolv
2f7b949bd1 impr: Split out digram and layered distribution in their own section 2024-02-24 18:54:35 +01:00
WerWolv
4100e48fe2 impr: Render digram and layered distribution to texture to improve frame rate 2024-02-24 18:07:20 +01:00
WerWolv
132b211796 impr: Add interactive tutorials button to the welcome screen 2024-02-24 16:10:05 +01:00
WerWolv
0d4d8efe4e build: Compress SDK package 2024-02-24 16:09:48 +01:00
WerWolv
393bea6d4b impr: Don't pass unique_ptr by const reference 2024-02-24 15:06:28 +01:00
WerWolv
b2edb0441a build: Make external plugins build again 2024-02-24 15:06:10 +01:00
WerWolv
9d02379583 impr: Don't allow opening the same file twice
Fixes #1569
2024-02-24 12:06:06 +01:00
WerWolv
3c365d65a4 impr: Nicer looking logger output colors 2024-02-24 11:28:47 +01:00
WerWolv
2049852a80 impr: More consistent naming for information sections 2024-02-24 10:06:56 +01:00
WerWolv
be6a7490fd build: Better formatting for main CMake file 2024-02-24 10:06:21 +01:00
WerWolv
222d0b74d0 patterns: Updated pattern language 2024-02-24 09:37:55 +01:00
WerWolv
cdde0dedc8 impr: Make crash restore popup not show up if there's nothing to restore 2024-02-24 00:37:17 +01:00
WerWolv
6b14facd29 impr: Cleanup task progress handling 2024-02-23 22:09:57 +01:00
WerWolv
a7d6a4968e fix: Certain language strings not loading correctly 2024-02-23 20:33:39 +01:00
WerWolv
2173707925 fix: Building with older libyara versions 2024-02-23 20:20:27 +01:00
WerWolv
75c03d56d2 fix: Opening files on macOS through the Open with... option
Fixes #1070
2024-02-23 20:17:33 +01:00
WerWolv
85ec807417 fix: Localization issue with script loader 2024-02-23 18:57:29 +01:00
WerWolv
e7df0d201f fix: Byte type distribution and entropy slider not updating 2024-02-23 18:44:41 +01:00
WerWolv
026713750d impr: Implement better string limiting algorithm 2024-02-23 18:32:12 +01:00
WerWolv
d19d62b1fc impr: Remember find popup input per provider
Closes #1567
2024-02-23 18:31:58 +01:00
WerWolv
1f9d0181c9 impr: Allow opening multiple files at once
Closes #1566
2024-02-23 18:03:37 +01:00
WerWolv
4d91e7f347 impr: Handle read-only files more gracefully 2024-02-23 17:59:37 +01:00
WerWolv
9bfdfa149e feat: Added search bar to file chooser popup, allow for custom naming functions 2024-02-23 17:52:42 +01:00
WerWolv
214e542da4 impr: Make sure lots of yara matches doesn't lag out the editor 2024-02-23 17:49:20 +01:00
WerWolv
daf74347a3 fix: Try to make menu bar always appear 2024-02-23 17:48:56 +01:00
WerWolv
61fd327aa1 fix: Focus restoring not working correctly 2024-02-23 17:48:37 +01:00
WerWolv
91dcfefc5c fix: Crash when recovering from a thrown exception in the main thread 2024-02-23 17:48:23 +01:00
WerWolv
8059f22a32 impr: Make sure that highlight invalidation doesn't happen more than once per frame 2024-02-23 17:47:59 +01:00
WerWolv
a271658154 impr: Added once execution and task progress increment helpers 2024-02-23 17:47:40 +01:00
WerWolv
e6854d6a6a fix: String limiting slicing unicode characters 2024-02-22 23:44:49 +01:00
WerWolv
1ede41c778 fix: Compile issues using GCC 2024-02-22 23:11:59 +01:00
WerWolv
ed905aa0ff build: Updated ImGui to v1.90.4 2024-02-22 22:32:18 +01:00
WerWolv
e28b72e356 feat: Added a basic C++ and MSVC detector yara signature 2024-02-22 21:31:53 +01:00
WerWolv
daf007fae7 fix: Added missing translations 2024-02-22 21:31:26 +01:00
WerWolv
684c339309 tests: Ignore files in plugins folder when checking languages 2024-02-22 21:31:05 +01:00
WerWolv
3a44b840be impr: Restructure yara rule handling again 2024-02-22 20:49:21 +01:00
WerWolv
5db041adb7 impr: Make sure crashes during logging cannot cause a deadlock 2024-02-22 20:48:35 +01:00
WerWolv
ce704a7d92 git: Update Linux and macOS build instructions to use Ninja 2024-02-22 20:48:15 +01:00
WerWolv
0a9dca5be7 impr: Save data information view settings to projects 2024-02-21 23:21:24 +01:00
WerWolv
5ccb7a7b9a feat: Added simple yara data analyzer 2024-02-21 23:17:12 +01:00
WerWolv
4bd24a4ffe impr: Refactor Yara view 2024-02-21 22:08:26 +01:00
WerWolv
56e7c15064 impr: Refactor and modularize data information view 2024-02-21 00:06:52 +01:00
WerWolv
a2ffac9424 impr: Reorder achievement save routine to never accidentally clear the file 2024-02-20 00:10:05 +01:00
WerWolv
ca35c90cbb fix: Main menu not appearing for the first 200ms 2024-02-19 22:23:17 +01:00
WerWolv
74d59705ad fix: Issues where files are only being partially overwritten instead of truncated 2024-02-19 22:22:59 +01:00
WerWolv
adc51d3773 fix: Crash when restarting ImHex more than once 2024-02-19 22:06:46 +01:00
WerWolv
218946d5de fix: Crash when opening the interface settings tab 2024-02-18 22:45:54 +01:00
Nik
6b32fcef0f git: Remove ugly underlines between banners in readme 2024-02-18 22:32:11 +01:00
WerWolv
016d47b9d7 web: Make sure settings are loaded correctly 2024-02-18 14:02:21 +01:00
WerWolv
1bf1a56b01 git: Add more visible download banners to readme 2024-02-18 13:52:07 +01:00
WerWolv
0413302470 feat: Added setting to disable command palette button 2024-02-18 11:38:22 +01:00
WerWolv
01c934f53a impr: Rework setting change listeners 2024-02-18 11:29:18 +01:00
WerWolv
c1aac6c85e fix: Data inspector showing double negative signs sometimes
Fixes #1534
2024-02-18 10:06:51 +01:00
WerWolv
ed292a1e7a impr: Implement basic exception catching in main thread 2024-02-18 02:12:57 +01:00
WerWolv
61b164a183 feat: Added option to export table in find view 2024-02-17 21:42:41 +01:00
WerWolv
d196169bea fix: Additional folder paths not being loaded from config correctly 2024-02-17 21:30:23 +01:00
WerWolv
a2284a5143 fix: Crash when specifying invalid hash parameters
Fixes #1555
2024-02-17 21:28:30 +01:00
WerWolv
7486468537 fix: Localization issue with custom CRCs 2024-02-17 21:26:31 +01:00
Nik
e475e763db build: Hopefully fix macOS x86 build issues (#1556)
#1109
2024-02-17 21:01:57 +01:00
WerWolv
dfdd06b24c impr: Set name of background service thread 2024-02-15 22:10:11 +01:00
WerWolv
510ed25509 fix: Crash when saving a new file to disk 2024-02-15 22:09:58 +01:00
WerWolv
9f2f01c17d fix: Modification highlighting not working correctly when provider data was moved 2024-02-15 22:09:37 +01:00
WerWolv
12ba05385b fix: Saving not removing red highlighting 2024-02-15 21:54:41 +01:00
WerWolv
f113a2befe build: Only do dynamic linking of libpl on Windows 2024-02-15 16:10:16 +01:00
WerWolv
cc8a1ccc8b git: Try signing x86 macOS build _again_ before packaging
#1500
2024-02-15 15:40:58 +01:00
WerWolv
2f88994c37 fix: Data inspector not updating correctly when data has changed
Fixes #1545
2024-02-15 13:01:34 +01:00
WerWolv
e016c8a702 fix: Double clicking pattern data row not selecting the editing textbox
Fixes #1546
2024-02-15 13:01:03 +01:00
WerWolv
982d367b11 patterns: Updated pattern language 2024-02-15 11:54:59 +01:00
Sten Feldman
d70f7422b7 feat: Support Copy/Paste on WASM build outside the application border (#1542)
### Problem description
WASM build does not support copy/paste beyond the application. Meaning,
there's no practical way of sending text back and forth across the
application border.

There are lengthy threads why this is a technical challenge in
WASM/Browser world, e.g:
- https://github.com/pthom/hello_imgui/issues/3
- https://github.com/emscripten-core/emscripten/pull/19510

### Implementation description
Implements a workaround solution as Header only C++ library, as proposed
and implemented at:
https://github.com/Armchair-Software/emscripten-browser-clipboard

Maybe there are cleaner ways of achieving the functionality. Definitely
would like to have some discussion around this. 👀

ℹ️ The proposed PR "works for me" on Windows, using CTRL-C/V shortcuts
to copy text from and to the application. On MacOS the system shortcut
to Paste is different from what ImHex has defined. This results in
system Paste shortcut of command-V triggering the browser callback to
synchronise the application clipboard, but no actual Paste takes place
within ImHex.

If there would be a clean way to trigger the paste command, that would
be wonderful (or get the context and references to write the data to the
cursor, but I was unable to find a clean solution). The only proposed
solutions in the referenced threads were about triggering paste event
internally via Key events. This seemed wonky 🙃 , so is not currently
implemented. At the moment the paste on MacOS is command+V followed by
control+V.

### Additional things
This is definitely a stopgap solution before the ImGui and Emscripten
take a more proper solution in enabling Copy/Paste outside the
application borders. However, I feel like this is a must have capability
to make the WASM build more useful, not just for trying out ImHex.

Cheers! 🍻

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2024-02-13 20:30:18 +01:00
reggie
56b2e09b01 build: Fix zstd not being linked in correctly (#1544)
Prior to this, at least on Linux/MacOS/etc, I guess it was not possible
to compile with Zstd included for `hex::dec::zstd_decompress()`:


![image](https://github.com/WerWolv/ImHex/assets/56618074/008dbb3d-eeaf-4f49-a918-4751ec69f00c)

Every other target lib for the decompression plugin would compile,
**except for** Zstd. Additionally, the target name `zstd` caused CMake
to not be able to find `Find(ZSTD).cmake` due to case-sensitivity
differences between Windows and Unix-based file paths, of course.
With that said, I'm not too sure if this will break building w/ Zstd on
Windows... Hopefully not 😄
2024-02-13 20:22:28 +01:00
WerWolv
ee3d6ec24b feat: Made sum hash calculation more useful 2024-02-13 20:20:48 +01:00
WerWolv
03beca1099 fix: Broken editing view column rendering in pattern drawer 2024-02-12 19:40:21 +01:00
WerWolv
588f8af966 impr: Remove separator line in view menu if fullscreen and always on top are disabled 2024-02-11 23:02:34 +01:00
WerWolv
eee5e9cd7f fix: Missing translation string in pattern drawer 2024-02-11 23:00:17 +01:00
WerWolv
27a78a00fd patterns: Updated pattern language 2024-02-11 20:44:21 +01:00
WerWolv
0aae605ac4 fix: Processing and drawing of diffs 2024-02-11 19:29:02 +01:00
WerWolv
5cfcca0bc4 feat: Allow switching off borderless window mode 2024-02-11 14:12:14 +01:00
WerWolv
daf4e5cad6 refactor: More cleanup of the main file 2024-02-11 13:46:06 +01:00
WerWolv
27b5d13733 impr: Store default setting when setting couldn't be loaded 2024-02-11 11:44:58 +01:00
WerWolv
5d405b4d10 build: Updated GLFW and OpenGL3 ImGui Backends 2024-02-11 11:44:44 +01:00
WerWolv
4519e24297 impr: Make corner icon stick out a bit less 2024-02-11 11:21:06 +01:00
WerWolv
2fd81c7ffd impr: Better UI for the main about screen page 2024-02-11 00:35:10 +01:00
WerWolv
106c35344b fix: Fade in and out of language text in oobe screen being broken 2024-02-11 00:18:20 +01:00
WerWolv
12f64e5fde build: Updated ImGui to the latest release 2024-02-11 00:11:56 +01:00
Nik
bcbcb1f23c impr: Various web build improvements, API cleanup (#1541) 2024-02-10 23:31:05 +01:00
WerWolv
4b20e35fd2 fix: macOS window missing decoration after restart 2024-02-10 10:17:15 +01:00
iTrooz
f332963c75 build: remove IMHEX_COMMIT_HASH_SHORT cmake flag (#1539) 2024-02-10 01:40:33 +00:00
WerWolv
a5f6756659 impr: Hide window immediately after render loop finishes 2024-02-09 19:42:47 +01:00
WerWolv
ebe0276141 impr: Add small gap between window frame and title bar buttons 2024-02-09 19:39:26 +01:00
WerWolv
9d47ba9031 impr: Cleanup pattern drawer, added comment column 2024-02-09 18:37:05 +01:00
iTrooz
09dd25dc0a fix: Remove debug package created by makepkg (#1537) 2024-02-08 15:49:28 +00:00
WerWolv
dfc249135f build: Make plugin RPATH on Linux point to the plugins folder 2024-02-07 21:51:59 +01:00
WerWolv
c3d755a3e2 fix: Decoding of Windows error messages being broken 2024-02-07 21:51:37 +01:00
WerWolv
1195d2f2e4 patterns: Updated pattern language 2024-02-05 18:49:51 +01:00
WerWolv
b05f478207 patterns: Updated pattern language 2024-02-04 21:45:53 +01:00
WerWolv
1c28d4f610 fix: Error message formatting 2024-02-04 20:59:33 +01:00
WerWolv
77baf6f522 fix: Native error message formatting on Windows 2024-02-04 20:21:16 +01:00
WerWolv
f583df6c7d impr: Improve overwriting individual characters in the hex editor editing mode 2024-02-04 17:19:08 +01:00
WerWolv
4093fadcae fix: Revert Windows title bar changes since window wasn't draggable anymore 2024-02-04 14:59:33 +01:00
WerWolv
16adacb722 fix: Footer height being wrong 2024-02-04 14:18:20 +01:00
Nik
5e5714baeb patterns: Merged in new pattern language refactor (#1533) 2024-02-04 14:03:42 +01:00
WerWolv
f60986de8e fix: Window title bar not looking correct on Windows 2024-02-04 13:55:15 +01:00
WerWolv
a6f4d0cdec impr: Unfocus ImGui windows when main window loses focus 2024-02-03 23:56:08 +01:00
WerWolv
64d147bf96 fix: Window footer being way too narrow 2024-02-03 22:43:06 +01:00
WerWolv
fe3facfc95 impr: Implement borderless window mode for macOS 2024-02-03 22:39:31 +01:00
WerWolv
3555fc01c5 impr: Allow closing menus by clicking on title bar on Windows 2024-02-03 20:19:45 +01:00
WerWolv
ab02cb0a19 build: Bundle libpl into rpm packages 2024-02-03 19:59:58 +01:00
WerWolv
24815c0370 fix: libpl not being installed correctly 2024-02-03 14:43:41 +01:00
WerWolv
a03e8dd879 impr: Harden settings system to not crash ImHex when having outdated configs
Fixes #1514
2024-02-03 12:16:36 +01:00
WerWolv
2ea0bbe5ca fix: Window title not always updating correctly 2024-02-03 11:29:04 +01:00
WerWolv
21e1d01394 patterns: Updated pattern language 2024-02-03 11:14:08 +01:00
WerWolv
ab842cbd73 build: Fix pattern language shared library ending up in the wrong folder on Linux 2024-02-03 10:41:11 +01:00
WerWolv
e864c1aaac fix: Crash due to libmagic bug when using MAGIC_COMPRESS with magic_buffer
#1529
2024-02-02 23:22:46 +01:00
WerWolv
a80f9e9ca7 build: Don't re-sign macOS bundle unless requested 2024-02-01 23:56:14 +01:00
WerWolv
959a404e1c fix: Don't pass nullptrs to libmagic
#1485
2024-02-01 22:35:24 +01:00
iTrooz
7546655695 fix: Fix detected wasm file size being wrong (#1525) 2024-02-01 14:03:33 +00:00
WerWolv
be0e50f983 web: Fix progress bar overflowing 2024-02-01 12:40:37 +01:00
WerWolv
baaf84298c impr: Disable console color output in web version 2024-02-01 12:09:43 +01:00
WerWolv
dfeb9dbc84 web: Allow setting language through URL parameters 2024-02-01 11:57:26 +01:00
WerWolv
4fd4b3dfad fix: Using cli arguments with statically linked plugins not working 2024-02-01 11:40:27 +01:00
WerWolv
7709f4e307 feat: Allow setting language through the command line 2024-02-01 10:58:45 +01:00
WerWolv
26eec66f89 web: Make progress bar prettier 2024-02-01 10:18:54 +01:00
iTrooz
e3c3233b3a feat: Add progress bar to web version (#1523) 2024-01-31 21:15:23 +00:00
WerWolv
f0a44e54d0 feat: Add export selection to file option 2024-01-31 22:07:08 +01:00
WerWolv
7a6ee756b8 patterns: Updated pattern language 2024-01-31 15:40:29 +01:00
WerWolv
944b3a5b6c fix: Selection valid check not working correctly 2024-01-31 15:31:51 +01:00
WerWolv
4a9bac3cd5 fix: Crash when opening file picker 2024-01-31 15:31:23 +01:00
WerWolv
2e630a48f7 build: Downgrade and lock emsdk version for now 2024-01-31 14:36:36 +01:00
iTrooz
baf1e88689 doc: fix --build flag in web docker compose (#1522) 2024-01-31 11:19:26 +00:00
WerWolv
80953a2286 feat: Allow jumping from hex editor to patterns and from patterns to source line 2024-01-31 11:26:22 +01:00
WerWolv
6393bfda37 build: Updated libwolv 2024-01-30 23:36:52 +01:00
WerWolv
d0bdfcd20b git: Update more github actions 2024-01-30 22:31:04 +01:00
WerWolv
e7b615c7e7 fix: Memory leak in event manager 2024-01-30 22:00:42 +01:00
WerWolv
2fd370f4ae tests: Clear events after tests ran 2024-01-30 21:24:26 +01:00
WerWolv
6b86ef3015 fix: Missing include 2024-01-30 21:20:53 +01:00
WerWolv
10f6aa3e4e fix: Default magic database not being bundled correctly 2024-01-30 21:19:43 +01:00
WerWolv
d5365fbf0c git: Update more actions due to NodeJS 16 deprecation 2024-01-30 21:19:25 +01:00
WerWolv
66d4034a4e build: Fix issues with large Windows builds 2024-01-30 21:19:04 +01:00
WerWolv
f4d1049be4 fix: More build errors 2024-01-30 16:32:48 +01:00
WerWolv
c2ff6f4e6b fix: Missing includes 2024-01-30 15:50:00 +01:00
WerWolv
79834b9566 impr: Added language selection to oobe screen 2024-01-30 14:57:36 +01:00
WerWolv
5459be46a4 patterns: Updated pattern language 2024-01-30 12:18:03 +01:00
WerWolv
d584edf546 impr: Improve situation where ImHex crashes on exit when resources aren't cleared properly 2024-01-30 11:21:34 +01:00
WerWolv
0cba735eb3 impr: Discard wayland platform errors 2024-01-30 00:47:02 +01:00
WerWolv
62978e5d34 feat: Added setting to always show provider tabs 2024-01-30 00:11:45 +01:00
WerWolv
3ea32212d7 feat: Added ASCII count minimap visualizer 2024-01-30 00:00:51 +01:00
WerWolv
ba6373daa4 fix: Moving Hex Editor cursor downwards jumping entire page at once 2024-01-29 23:29:18 +01:00
WerWolv
18b717594f fix: Rendering issues with Hex Editor Minimap 2024-01-29 23:28:53 +01:00
WerWolv
76e304c34e build: Fix various issues with linking, clang and unity builds 2024-01-29 22:57:39 +01:00
WerWolv
2e74a78f46 build: Improve unity builds 2024-01-29 21:18:32 +01:00
WerWolv
6a146e239a fix: Workspaces not being exported correctly 2024-01-29 20:50:00 +01:00
WerWolv
cecb8b8d31 fix: Linking issues and menu bar not appearing sometimes 2024-01-29 15:44:18 +01:00
Nik
339541a56f impr: Restructure various components much better (#1520) 2024-01-28 22:14:59 +01:00
WerWolv
6709087760 git: Update actions to latest versions 2024-01-28 22:10:37 +01:00
WerWolv
069544eb93 feat: Added Minimap to Hex Editor 2024-01-28 15:28:55 +01:00
iTrooz
17f769c40d feat: separate cmake configure steps + remove CMakeCache cache (#1519) 2024-01-28 11:40:12 +00:00
WerWolv
ba20790ed2 build: Make libpl not being linked twice 2024-01-28 01:09:26 +01:00
WerWolv
cb3bace15e lang: Update German translation 2024-01-27 22:16:50 +01:00
WerWolv
7c6f4d7bff feat: Added many new Hash algorithms 2024-01-27 20:24:53 +01:00
WerWolv
f0a56b4201 fix: Pattern code not being parsed when loaded from project 2024-01-27 17:33:15 +01:00
WerWolv
d2a26017d7 fix: Build error due to mismatching types 2024-01-27 16:56:18 +01:00
WerWolv
61048757e6 impr: Allow custom encodings with only single-byte characters to be displayed immediately 2024-01-27 16:49:31 +01:00
WerWolv
25b4745997 impr: Make zlib decompress function take in a windowSize parameter 2024-01-27 16:12:02 +01:00
WerWolv
7fec97561b git: Updated build instructions for macOS 2024-01-27 15:05:53 +01:00
paxcut
1957d6f432 feat: Added find-replace to pattern editor (#1465) 2024-01-27 14:52:20 +01:00
WerWolv
823881f7f1 fix: Invalid parameters passed to task progress bar formatter 2024-01-27 14:15:08 +01:00
PerikiyoXD
11f75f72ee feat: Add search options for string encoding and endianness (#1490)
Added search options for string encoding (UTF-8, UTF-16, UTF-32) and
endianness (Little, Big) in the hex editor. This enhancement allows
users to customize the search process based on different string
encodings and byte orders.

Affected files:
- `plugins/builtin/romfs/lang/de_DE.json`
- `plugins/builtin/romfs/lang/en_US.json`
- `plugins/builtin/romfs/lang/es_ES.json`
- `plugins/builtin/romfs/lang/it_IT.json`
- `plugins/builtin/romfs/lang/ja_JP.json`
- `plugins/builtin/romfs/lang/ko_KR.json`
- `plugins/builtin/romfs/lang/pt_BR.json`
- `plugins/builtin/romfs/lang/zh_CN.json`
- `plugins/builtin/romfs/lang/zh_TW.json`
- `plugins/builtin/source/content/views/view_hex_editor.cpp`

Resolves: #1325

---------

Co-authored-by: Nik <werwolv98@gmail.com>
2024-01-27 14:13:41 +01:00
WerWolv
5747b72a41 fix: Exit hex editor editing mode when right clicking
Fixes #1499
2024-01-27 13:41:38 +01:00
WerWolv
44510b5b64 impr: Make task progress not display -100% when in no progress state 2024-01-27 11:02:14 +01:00
WerWolv
6a1a991c08 fix: Pattern settings resetting at re-evaluation 2024-01-27 10:49:40 +01:00
WerWolv
33637e92b5 impr: Make changing pattern settings re-evaluate the code 2024-01-26 22:08:46 +01:00
WerWolv
4c06fd2fb8 impr: Allow plugins to be loaded in debug builds 2024-01-26 21:11:56 +01:00
WerWolv
f5c529b2b3 build: Define DEBUG macro again only in debug builds 2024-01-26 21:11:42 +01:00
iTrooz
b7349e42c7 feat: Allow users to open multiple files with the same name in the web version + make web Dockerfile able to run web server by itself (#1518) 2024-01-26 18:52:05 +00:00
iTrooz
a4d6932ed8 fix: remove TODO + show error message when reading/write in process provider (#1516) 2024-01-26 18:44:52 +00:00
WerWolv
d23d382038 impr: Disable the initial oobe screen in the web version 2024-01-26 16:54:27 +01:00
WerWolv
bde476dfb7 build: Make sure libraries are correctly linked in external plugins 2024-01-26 12:36:07 +01:00
Nik
6ae86ce906 build: Get rid of pkgconfig as much as possible (#1517) 2024-01-26 12:13:22 +01:00
WerWolv
b2121b25c1 fix: Set windows drop handler effect to copy 2024-01-26 00:08:15 +01:00
WerWolv
a08afcf11a impr: Add fallback file drop handler to Windows 2024-01-25 23:53:41 +01:00
WerWolv
919110b024 impr: Make data information view have per-provider state 2024-01-25 22:49:56 +01:00
WerWolv
9c25a1609e feat: Add more magic information to the information view 2024-01-25 22:49:22 +01:00
iTrooz
d86bf44e39 git: Add script to check localized texts occurrences in code (#1511)
This script will be executed on every CI run as part of tests, and will
ensure no unlocalised strings are present in the code

Note that texts without the `_lang` suffix will not be checked, e.g.
96fe608d60/plugins/builtin/source/content/views/view_provider_settings.cpp (L10)
2024-01-25 21:23:03 +01:00
iTrooz
e61ee528ff fix: Fix theme detection on Linux (#1512) 2024-01-25 19:46:12 +00:00
iTrooz
82b56613e9 fix: fix build with edlib (#1513) 2024-01-25 19:25:51 +00:00
WerWolv
78723887e1 build: Updated dependencies 2024-01-25 11:33:34 +01:00
WerWolv
920b403ee3 build: Fixed libimhex not finding nlohmann json on some platforms 2024-01-25 11:05:02 +01:00
WerWolv
390c1469b1 build: Fix rpath issues on macOS 2024-01-24 22:30:40 +01:00
WerWolv
b605c463a1 impr: Further improve interfacing with external plugins 2024-01-22 23:35:00 +01:00
WerWolv
00491c8d90 git: Put libimhex and the ui plugin library under LGPLv2.1 2024-01-22 19:05:04 +01:00
WerWolv
cc3a9f1e81 build: Fix finding .NET runtime when there's multiple installed versions 2024-01-22 14:59:23 +01:00
WerWolv
60e7362f4e feat: Allow extra plugin folders to be specified with the --plugins cli option 2024-01-22 12:53:07 +01:00
WerWolv
f2bab005d0 fix: Make screenshots on initial launch screen behave better in web version 2024-01-22 08:25:29 +01:00
WerWolv
7068a883ed feat: Added basic byte sum hash 2024-01-21 23:31:53 +01:00
WerWolv
3783ec6a23 impr: Save settings of all hashes to disk 2024-01-21 23:31:44 +01:00
WerWolv
0aaa02b347 fix: Diffing plugin not registering localization strings correctly 2024-01-21 23:30:05 +01:00
WerWolv
1af3bf5da7 fix: Crash on exit 2024-01-21 21:39:55 +01:00
WerWolv
d5a57564fe fix: Build issues due to unused variables 2024-01-21 21:39:50 +01:00
WerWolv
2d92858193 feat: Allow switching to other providers through command palette 2024-01-21 21:29:03 +01:00
WerWolv
f666751f5f build: Disable edlib unit tests 2024-01-21 19:55:10 +01:00
Nik
58603ed12a impr: Better experience when first starting ImHex (#1510) 2024-01-21 18:39:51 +01:00
Nik
d005b5d2d9 feat: Allow toolbar icons to be modified (#1509) 2024-01-21 18:39:32 +01:00
Nik
a13b5bf8c0 feat: Implement Myers' diffing algorithm (#1508) 2024-01-21 18:39:13 +01:00
iTrooz
9d351317b8 impr: Do not mark memory providers as dirty when creating them (#1506)
This is in an effort to avoid unnecessary popups
2024-01-21 18:38:58 +01:00
WerWolv
f29d784e13 build: Update .NET SDK version on Fedora 2024-01-21 18:04:18 +01:00
WerWolv
2e582e3a45 fix: Ambiguous conversion to UnlocalizedString 2024-01-21 14:31:19 +01:00
WerWolv
566147dfae fix: Popups not always appearing when starting ImHex 2024-01-21 14:22:08 +01:00
WerWolv
3e5967c5a7 fix: Dropping files onto ImHex opening them twice 2024-01-20 21:03:46 +01:00
WerWolv
52fda5aeb7 fix: Build issue on AlmaLinux due to missing include 2024-01-20 10:35:51 +01:00
WerWolv
a657a23aaa fix: Crash when hex editor view got too small 2024-01-20 10:33:11 +01:00
WerWolv
b32055290a fix: Assertion when there's too many hex editor rows on the screen 2024-01-20 10:32:31 +01:00
Ada
cc97c0e525 fix: Segfault when launching ImHex with file arguments (#1501)
d511080814 introduced a regression where
running imhex with arguments (i.e. `imhex blob1.bin blob2.bin`) will
segfault due to a null pointer dereference.

### Implementation description
This patch updates `getSubCommands` to follow the same control flow as
`getFeatures`, where if the function returns a null pointer, it will
return gracefully rather than crash.
2024-01-18 11:11:06 +01:00
WerWolv
ffb324f685 patterns: Updated pattern language 2024-01-16 00:46:01 +01:00
WerWolv
ad53a0bf4c build: Make PDB generation slightly better 2024-01-16 00:31:46 +01:00
WerWolv
2cc07f0e73 build: Add option to not generate PDB files in release builds 2024-01-15 23:33:31 +01:00
WerWolv
b3d3794e1d patterns: Updated pattern language 2024-01-15 21:10:44 +01:00
WerWolv
82a3017629 feat: Added always on top and fullscreen mode toggle 2024-01-15 20:52:08 +01:00
WerWolv
d511080814 impr: Make plugin features and subcommands work in statically linked builds 2024-01-13 00:34:13 +01:00
WerWolv
db1373d572 fix: Compile error due to types not being constexpr everywhere 2024-01-13 00:33:55 +01:00
WerWolv
ea7483f9a7 impr: Get rid of the concept of built-in plugins
#1489
2024-01-12 23:03:13 +01:00
WerWolv
7441720a88 fix: Invalid signedness in comparison 2024-01-12 18:33:18 +01:00
WerWolv
0d0dd7d57c fix: Make syncing pattern code not erase pattern code as often 2024-01-11 21:56:54 +01:00
WerWolv
e5c7e52d72 fix: Don't show info banner if there's nothing to show 2024-01-11 20:11:52 +01:00
WerWolv
72f4331703 feat: Added virtual files to the pattern language 2024-01-11 20:11:22 +01:00
WerWolv
5f02320e8e impr: Make drag n drop overlay localizable 2024-01-11 10:20:39 +01:00
WerWolv
3449525ead feat: Added drag-n-drop overlay for windows 2024-01-10 23:46:50 +01:00
WerWolv
1c17ec5599 fix: Avoid heap allocations in thread local storage 2024-01-10 20:13:53 +01:00
WerWolv
6611c865f7 fix: Missing <codecvt> include
Thanks to pxd
2024-01-10 19:48:26 +01:00
WerWolv
016c93e795 build: Update xdgpp submodule to rehosted github mirror 2024-01-10 15:38:27 +01:00
WerWolv
f518bdadbd fix: Properly clear thread-local thread name 2024-01-09 21:46:54 +01:00
WerWolv
b408baf254 impr: Make window management tools work better 2024-01-09 17:24:27 +01:00
WerWolv
58441634d6 fix: Provider information in information view always showing info from current provider 2024-01-09 16:48:49 +01:00
WerWolv
d5c8021b41 fix: Use a thread-local storage for thread names instead 2024-01-09 16:16:20 +01:00
WerWolv
929b5176ce impr: Fallback to old thread name API when new one isn't available 2024-01-09 13:43:34 +01:00
WerWolv
179a65ed8b fix: Crash on exit 2024-01-09 11:49:49 +01:00
WerWolv
dd9a2e1818 fix: Errors being thrown during library init 2024-01-09 11:38:56 +01:00
WerWolv
0b5656dcc4 fix: Manually initialize library plugins 2024-01-09 11:31:56 +01:00
WerWolv
037d77f28e impr: Improve plugin unload logging 2024-01-09 10:54:53 +01:00
WerWolv
301e95b708 impr: Move plugin unload logging to plugins 2024-01-09 10:53:50 +01:00
WerWolv
874bac7de2 impr: Include thread name in log 2024-01-09 10:39:06 +01:00
WerWolv
4668f429fb fix: RGBA8 node not setting outputs correctly, added buffer output 2024-01-09 09:49:07 +01:00
WerWolv
d43f25ec70 impr: Remove spaces from fill command input 2024-01-09 01:26:47 +01:00
WerWolv
bdee628360 fix: Views not being openable 2024-01-09 00:24:17 +01:00
WerWolv
a35530f63b fix: Menu bar being hidden by default in the web version 2024-01-08 23:41:28 +01:00
WerWolv
21d6c1326c fix: Invalid address bound check for jump-to option
Fixes #1487
2024-01-08 22:34:42 +01:00
WerWolv
bfafc692db impr: Added icons to all menu items 2024-01-08 21:51:48 +01:00
WerWolv
3a068b9719 impr: Use ImGui's built-in drag n drop support for bookmarks 2024-01-08 10:56:53 +01:00
WerWolv
9530100455 fix: Texture interpreting raw data as structured images 2024-01-08 09:39:01 +01:00
WerWolv
cab329556c impr: Make sure plugins are only loaded once 2024-01-07 18:45:17 +01:00
WerWolv
1c5d6cbe94 impr: Added error reporting for plugin unloading 2024-01-07 16:07:53 +01:00
WerWolv
576bc80716 impr: Use full windows to draw tutorial popups 2024-01-06 22:54:18 +01:00
Anton Samokhvalov
8042aa39bf fix: ODR issues in plugin bundle generation (#1486)
fix possible ODR - multiple classes with same name in different .cpp
files
2024-01-06 22:13:35 +01:00
WerWolv
409b3ccd6c fix: Popups not appearing at launch sometimes and crashes when exiting 2024-01-06 17:38:55 +01:00
WerWolv
c89d19cd27 impr: Make file provider use atomic file IO instead of memory mappings 2024-01-06 16:09:28 +01:00
WerWolv
f9ab16049b fix: Make sure plugins are unloaded in opposite load order 2024-01-06 16:09:05 +01:00
WerWolv
09300c209f build: Updated libfmt 2024-01-06 09:36:53 +01:00
Nik
d199e0fa0f git: Updated plugin template link 2024-01-05 22:19:05 +01:00
WerWolv
89cab26888 build: Use Arch Docker file to its own folder 2024-01-05 21:37:24 +01:00
WerWolv
31b900a71c git: Make a imhex-download-sdk release when a new ImHex version is released 2024-01-05 21:35:02 +01:00
WerWolv
76a58cd843 fix: Updater not renaming file correctly 2024-01-05 18:12:46 +01:00
WerWolv
dbdc900e94 build: Don't update pacman packages on msys2 2024-01-05 14:52:07 +01:00
WerWolv
e3ec4ebd21 build: Make msys2 deps install script use pacboy 2024-01-05 14:47:30 +01:00
WerWolv
2ab529a3f5 build: Make macos ARM build only require macOS 12.1 as well 2024-01-05 13:52:53 +01:00
Nik
a82fdcca4b build: Remove manual macOS code signing again 2024-01-05 12:39:19 +01:00
Nik
0627b36bf6 git: Fixed rest of the screenshots in the readme 2024-01-04 22:39:28 +01:00
Nik
e8a5b5ab9c git: Fixed screenshots in readme 2024-01-04 22:37:31 +01:00
470 changed files with 139499 additions and 91091 deletions

View File

@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -26,14 +26,14 @@ jobs:
languages: 'cpp' languages: 'cpp'
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
with: with:
key: ${{ runner.os }}-analysis-build-${{ github.run_id }} key: ${{ runner.os }}-analysis-build-${{ github.run_id }}
restore-keys: ${{ runner.os }}-analysis-build restore-keys: ${{ runner.os }}-analysis-build
max-size: 50M max-size: 50M
- name: 📜 Restore CMakeCache - name: 📜 Restore CMakeCache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: | path: |
build/CMakeCache.txt build/CMakeCache.txt

View File

@@ -2,7 +2,9 @@ name: Build
on: on:
push: push:
branches: ["*"] branches:
- 'master'
- 'releases/**'
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
@@ -22,46 +24,30 @@ jobs:
CCACHE_DIR: "${{ github.workspace }}/.ccache" CCACHE_DIR: "${{ github.workspace }}/.ccache"
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
id: cache-ccache id: cache-ccache
with: with:
key: ${{ runner.os }}-ccache-${{ github.run_id }} key: ${{ runner.os }}-ccache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-ccache restore-keys: ${{ runner.os }}-ccache
max-size: 1G max-size: 1G
- name: 📜 Restore CMakeCache
uses: actions/cache@v3
with:
path: |
build/CMakeCache.txt
key: ${{ runner.os }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
- name: 🟦 Install msys2 - name: 🟦 Install msys2
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
with: with:
msystem: mingw64 msystem: mingw64
pacboy: >-
gcc:p - name: ⬇️ Install dependencies
lld:p run: |
cmake:p set -x
ccache:p dist/get_deps_msys2.sh
glfw:p
file:p
mbedtls:p
freetype:p
dlfcn:p
libbacktrace:p
ninja:p
curl-winssl:p
capstone:p
- name: ⬇️ Install .NET - name: ⬇️ Install .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: '8.0.100' dotnet-version: '8.0.100'
@@ -70,24 +56,29 @@ jobs:
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV
# Windows cmake build # Windows cmake build
- name: 🛠️ Build - name: 🛠️ Configure CMake
run: | run: |
set -x set -x
mkdir -p build mkdir -p build
cd build cd build
cmake -G "Ninja" \ cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DCMAKE_INSTALL_PREFIX="$PWD/install" \ -DCMAKE_INSTALL_PREFIX="$PWD/install" \
-DIMHEX_GENERATE_PACKAGE=ON \ -DIMHEX_GENERATE_PACKAGE=ON \
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \ -DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DUSE_SYSTEM_CAPSTONE=ON \
-DUSE_SYSTEM_CAPSTONE=ON \ -DIMHEX_GENERATE_PDBS=ON \
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \ -DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
.. ..
- name: 🛠️ Build
run: |
cd build
ninja install ninja install
cpack cpack
mv ImHex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi mv ImHex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi
@@ -144,7 +135,7 @@ jobs:
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -153,19 +144,12 @@ jobs:
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
with: with:
key: ${{ runner.os }}${{ matrix.suffix }}-ccache-${{ github.run_id }} key: ${{ runner.os }}${{ matrix.suffix }}-ccache-${{ github.run_id }}
restore-keys: ${{ runner.os }}${{ matrix.suffix }}-ccache restore-keys: ${{ runner.os }}${{ matrix.suffix }}-ccache
max-size: 1G max-size: 1G
- name: 📜 Restore CMakeCache
uses: actions/cache@v3
with:
path: |
build/CMakeCache.txt
key: ${{ runner.os }}-${{ matrix.suffix }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
- name: ⬇️ Install dependencies - name: ⬇️ Install dependencies
run: | run: |
set -x set -x
@@ -179,13 +163,13 @@ jobs:
brew install glfw brew install glfw
- name: ⬇️ Install .NET - name: ⬇️ Install .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: '8.0.100' dotnet-version: '8.0.100'
- name: 🧰 Checkout glfw - name: 🧰 Checkout glfw
if: ${{matrix.custom_glfw}} if: ${{matrix.custom_glfw}}
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
repository: glfw/glfw repository: glfw/glfw
path: glfw path: glfw
@@ -212,7 +196,7 @@ jobs:
ninja install ninja install
# MacOS cmake build # MacOS cmake build
- name: 🛠️ Build - name: 🛠️ Configure CMake
run: | run: |
set -x set -x
mkdir -p build mkdir -p build
@@ -229,20 +213,45 @@ jobs:
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
.. ..
ninja package
- name: 🛠️ Build
run: cd build && ninja install
- name: ✒️ Fix Signature
run: |
set -x
cd build/install
mv imhex.app ImHex.app
codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app
- name: 📁 Fix permissions
run: |
set -x
cd build/install
chmod -R 755 ImHex.app/
- name: 📦 Create DMG
run: |
set -x
mkdir bundle
mv build/install/ImHex.app bundle
cd bundle
ln -s /Applications Applications
cd ..
hdiutil create -volname "ImHex" -srcfolder bundle -ov -format UDZO imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64.dmg
- name: ⬆️ Upload DMG - name: ⬆️ Upload DMG
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
if-no-files-found: error if-no-files-found: error
name: macOS DMG${{matrix.suffix}} x86_64 name: macOS DMG${{matrix.suffix}} x86_64
path: build/*.dmg path: ./*.dmg
macos-arm64-build: macos-arm64-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@@ -251,18 +260,18 @@ jobs:
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }} IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📁 Restore docker /cache - name: 📁 Restore docker /cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: cache path: cache
key: build-macos-arm64-cache key: build-macos-arm64-cache
- name: 🐳 Inject /cache into docker - name: 🐳 Inject /cache into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2 # Doesn't work with a MacOS runner ? uses: reproducible-containers/buildkit-cache-dance@v2
with: with:
cache-source: cache cache-source: cache
cache-target: /cache cache-target: /cache
@@ -302,15 +311,15 @@ jobs:
path: out path: out
- name: 🗑️ Delete artifact - name: 🗑️ Delete artifact
uses: geekyeggo/delete-artifact@v4 uses: geekyeggo/delete-artifact@v5
with: with:
token: ${{ secrets.GITHUB_TOKEN }}
name: macos_arm64_intermediate name: macos_arm64_intermediate
- name: ✒️ Fix Signature - name: ✒️ Fix Signature
run: | run: |
set -x set -x
cd out cd out
mv imhex.app ImHex.app
codesign --remove-signature ImHex.app codesign --remove-signature ImHex.app
codesign --force --deep --sign - ImHex.app codesign --force --deep --sign - ImHex.app
@@ -360,36 +369,29 @@ jobs:
run: apt update && apt install -y git curl run: apt update && apt install -y git curl
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
with: with:
key: Ubuntu-${{matrix.release_num}}-ccache-${{ github.run_id }} key: Ubuntu-${{matrix.release_num}}-ccache-${{ github.run_id }}
restore-keys: Ubuntu-${{matrix.release_num}}-ccache restore-keys: Ubuntu-${{matrix.release_num}}-ccache
max-size: 1G max-size: 1G
- name: 📜 Restore CMakeCache
uses: actions/cache@v3
with:
path: |
build/CMakeCache.txt
key: Ubuntu-${{matrix.release_num}}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
- name: ⬇️ Install dependencies - name: ⬇️ Install dependencies
run: | run: |
apt update apt update
bash dist/get_deps_debian.sh bash dist/get_deps_debian.sh
- name: ⬇️ Install .NET - name: ⬇️ Install .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: '8.0.100' dotnet-version: '8.0.100'
# Ubuntu cmake build # Ubuntu cmake build
- name: 🛠️ Build - name: 🛠️ Configure CMake
shell: bash shell: bash
run: | run: |
set -x set -x
@@ -402,14 +404,15 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DIMHEX_ENABLE_LTO=ON \ -DIMHEX_ENABLE_LTO=ON \
-DIMHEX_USE_GTK_FILE_PICKER=ON \ -DIMHEX_USE_GTK_FILE_PICKER=ON \
-DDOTNET_EXECUTABLE="dotnet" \ -DDOTNET_EXECUTABLE="dotnet" \
.. ..
DESTDIR=DebDir ninja install
- name: 🛠️ Build
run: cd build && DESTDIR=DebDir ninja install
- name: 📜 Set version variable - name: 📜 Set version variable
run: | run: |
@@ -434,19 +437,19 @@ jobs:
name: ⬇️ AppImage name: ⬇️ AppImage
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📁 Restore docker /cache - name: 📁 Restore docker /cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: cache path: cache
key: appimage-ccache-${{ github.run_id }} key: appimage-ccache-${{ github.run_id }}
restore-keys: appimage-cache restore-keys: appimage-cache
- name: 🐳 Inject /cache into docker - name: 🐳 Inject /cache into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2 uses: reproducible-containers/buildkit-cache-dance@v2
with: with:
cache-source: cache cache-source: cache
cache-target: /cache cache-target: /cache
@@ -489,7 +492,7 @@ jobs:
pacman -Syu git ccache --noconfirm pacman -Syu git ccache --noconfirm
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -498,26 +501,19 @@ jobs:
dist/get_deps_archlinux.sh --noconfirm dist/get_deps_archlinux.sh --noconfirm
- name: ⬇️ Install .NET - name: ⬇️ Install .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: '8.0.100' dotnet-version: '8.0.100'
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
with: with:
key: archlinux-ccache-${{ github.run_id }} key: archlinux-ccache-${{ github.run_id }}
restore-keys: archlinux-ccache restore-keys: archlinux-ccache
max-size: 1G max-size: 1G
- name: 📜 Restore CMakeCache
uses: actions/cache@v3
with:
path: |
build/CMakeCache.txt
key: archlinux-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
# ArchLinux cmake build # ArchLinux cmake build
- name: 🛠️ Build - name: 🛠️ Configure CMake
run: | run: |
set -x set -x
mkdir -p build mkdir -p build
@@ -532,13 +528,14 @@ jobs:
-DUSE_SYSTEM_NLOHMANN_JSON=ON \ -DUSE_SYSTEM_NLOHMANN_JSON=ON \
-DUSE_SYSTEM_CAPSTONE=OFF \ -DUSE_SYSTEM_CAPSTONE=OFF \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \ -DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \ -DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
-DIMHEX_ENABLE_LTO=ON \ -DIMHEX_ENABLE_LTO=ON \
-DIMHEX_USE_GTK_FILE_PICKER=ON \ -DIMHEX_USE_GTK_FILE_PICKER=ON \
.. ..
DESTDIR=installDir ninja install
- name: 🛠️ Build
run: cd build && DESTDIR=installDir ninja install
- name: 📜 Set version variable - name: 📜 Set version variable
run: | run: |
@@ -565,6 +562,7 @@ jobs:
# Replace the old file # Replace the old file
rm imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst rm imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
rm *imhex-bin-debug* # rm debug package which is created for some reason
mv *.pkg.tar.zst imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst mv *.pkg.tar.zst imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
- name: ⬆️ Upload imhex-archlinux.pkg.tar.zst - name: ⬆️ Upload imhex-archlinux.pkg.tar.zst
@@ -610,13 +608,13 @@ jobs:
run: dnf install --disablerepo="*" --enablerepo="fedora" git-core -y run: dnf install --disablerepo="*" --enablerepo="fedora" git-core -y
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
path: ImHex path: ImHex
submodules: recursive submodules: recursive
- name: 📜 Setup DNF Cache - name: 📜 Setup DNF Cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: /var/cache/dnf path: /var/cache/dnf
key: ${{ matrix.mock_release }}-dnf-${{ github.run_id }} key: ${{ matrix.mock_release }}-dnf-${{ github.run_id }}
@@ -632,12 +630,12 @@ jobs:
ccache ccache
- name: ⬇️ Install .NET - name: ⬇️ Install .NET
uses: actions/setup-dotnet@v3 uses: actions/setup-dotnet@v4
with: with:
dotnet-version: '8.0.100' dotnet-version: '8.0.100'
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2.5 uses: hendrikmuhs/ccache-action@v1
with: with:
key: ${{ matrix.mock_release }}-rpm-${{ github.run_id }} key: ${{ matrix.mock_release }}-rpm-${{ github.run_id }}
restore-keys: ${{ matrix.mock_release }}-rpm restore-keys: ${{ matrix.mock_release }}-rpm
@@ -653,11 +651,11 @@ jobs:
- name: ✒️ Modify spec file - name: ✒️ Modify spec file
run: | run: |
sed -i \ sed -i \
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/g' \ -e 's/Version: VERSION$/Version: ${{env.IMHEX_VERSION}}/g' \
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \ -e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \ -e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
-e '/BuildRequires: cmake/a BuildRequires: git-core' \ -e '/BuildRequires: cmake/a BuildRequires: git-core' \
-e '/%files/a %{_datadir}/%{name}/' \ -e '/%files/a %{_datadir}/%{name}/' \
$GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec $GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec
- name: 📜 Fix ccache on EL9 - name: 📜 Fix ccache on EL9
@@ -678,7 +676,7 @@ jobs:
EOT EOT
- name: 📜 Setup Mock Cache - name: 📜 Setup Mock Cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: /var/cache/mock path: /var/cache/mock
key: ${{ matrix.mock_release }}-mock-${{ github.run_id }} key: ${{ matrix.mock_release }}-mock-${{ github.run_id }}

View File

@@ -2,7 +2,9 @@ name: Build for the web
on: on:
push: push:
branches: ["*"] branches:
- 'master'
- 'releases/**'
pull_request: pull_request:
workflow_dispatch: workflow_dispatch:
@@ -21,25 +23,25 @@ jobs:
name: 🌍 WebAssembly name: 🌍 WebAssembly
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📁 Restore docker /cache - name: 📁 Restore docker /cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: cache path: cache
key: web-cmakecache-${{ hashFiles('**/CMakeLists.txt') }} key: web-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
- name: 🐳 Inject /cache into docker - name: 🐳 Inject /cache into docker
uses: reproducible-containers/buildkit-cache-dance@v2.1.2 uses: reproducible-containers/buildkit-cache-dance@v2
with: with:
cache-source: cache cache-source: cache
cache-target: /cache cache-target: /cache
- name: 🛠️ Build using docker - name: 🛠️ Build using docker
run: | run: |
docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out --target raw
- name: 🔨 Fix permissions - name: 🔨 Fix permissions
run: | run: |

View File

@@ -15,7 +15,7 @@ jobs:
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
path: ImHex path: ImHex
@@ -52,13 +52,23 @@ jobs:
repo: ImHex-Patterns repo: ImHex-Patterns
token: ${{ secrets.RELEASE_TOKEN }} token: ${{ secrets.RELEASE_TOKEN }}
- name: 🎫 Create imhex-download-sdk release
uses: ncipollo/release-action@v1
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
if: "${{ env.RELEASE_TOKEN != '' }}"
with:
tag: v${{ env.IMHEX_VERSION }}
repo: imhex-download-sdk
token: ${{ secrets.RELEASE_TOKEN }}
release-upload-artifacts: release-upload-artifacts:
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Release Upload Artifacts name: Release Upload Artifacts
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
path: ImHex path: ImHex
submodules: recursive submodules: recursive
@@ -80,7 +90,7 @@ jobs:
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
- name: ⬇️ Download artifacts from latest workflow - name: ⬇️ Download artifacts from latest workflow
uses: dawidd6/action-download-artifact@v2 uses: dawidd6/action-download-artifact@v3
with: with:
github_token: ${{secrets.GITHUB_TOKEN}} github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build.yml workflow: build.yml
@@ -109,7 +119,7 @@ jobs:
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
- name: ⬆️ Upload everything to release - name: ⬆️ Upload everything to release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
with: with:
files: '*' files: '*'

View File

@@ -2,9 +2,13 @@ name: "Unit Tests"
on: on:
push: push:
branches: [ master ] branches:
- 'master'
- 'releases/**'
pull_request: pull_request:
branches: [ master ] branches:
- 'master'
- 'releases/**'
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -18,12 +22,12 @@ jobs:
steps: steps:
- name: 🧰 Checkout - name: 🧰 Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
- name: 📜 Setup ccache - name: 📜 Setup ccache
uses: hendrikmuhs/ccache-action@v1.2 uses: hendrikmuhs/ccache-action@v1
with: with:
key: ${{ runner.os }}-tests-build-${{ github.run_id }} key: ${{ runner.os }}-tests-build-${{ github.run_id }}
restore-keys: ${{ runner.os }}-tests-build restore-keys: ${{ runner.os }}-tests-build
@@ -31,7 +35,7 @@ jobs:
- name: 📜 Restore CMakeCache - name: 📜 Restore CMakeCache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: | path: |
build/CMakeCache.txt build/CMakeCache.txt
@@ -49,6 +53,7 @@ jobs:
cd build cd build
CC=gcc-12 CXX=g++-12 cmake \ CC=gcc-12 CXX=g++-12 cmake \
-DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_BUILD_TYPE=Debug \
-DIMHEX_ENABLE_UNIT_TESTS=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \ -DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \
@@ -61,3 +66,17 @@ jobs:
run: | run: |
cd build cd build
ctest --output-on-failure ctest --output-on-failure
langs:
name: 🧪 Langs
runs-on: ubuntu-22.04
steps:
- name: 🧰 Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Check langs
run:
python3 tests/check_langs.py

5
.gitmodules vendored
View File

@@ -8,7 +8,7 @@
ignore = dirty ignore = dirty
[submodule "lib/third_party/xdgpp"] [submodule "lib/third_party/xdgpp"]
path = lib/third_party/xdgpp path = lib/third_party/xdgpp
url = https://git.sr.ht/~danyspin97/xdgpp url = https://github.com/WerWolv/xdgpp
ignore = dirty ignore = dirty
[submodule "lib/third_party/fmt"] [submodule "lib/third_party/fmt"]
path = lib/third_party/fmt path = lib/third_party/fmt
@@ -36,3 +36,6 @@
[submodule "lib/third_party/HashLibPlus"] [submodule "lib/third_party/HashLibPlus"]
path = lib/third_party/HashLibPlus path = lib/third_party/HashLibPlus
url = https://github.com/WerWolv/HashLibPlus url = https://github.com/WerWolv/HashLibPlus
[submodule "lib/third_party/edlib"]
path = lib/third_party/edlib
url = https://github.com/Martinsos/edlib

View File

@@ -1,26 +1,31 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
# Options # Options
option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins" OFF) option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins (Linux only)" OFF)
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON) option(IMHEX_STRIP_RELEASE "Strip the release builds" ON )
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF) option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF) option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF) option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF) option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals" OFF) option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF)
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF) option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON) option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF) option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF) option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON) option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF) option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. Only usable on Windows and MacOS" OFF) option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. (Windows and MacOS only)" OFF)
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF) option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" OFF)
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
# Basic compiler and cmake configurations # Basic compiler and cmake configurations
set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD 23)
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON) set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR}) set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}")
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules") set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake") include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake")
@@ -29,11 +34,15 @@ loadVersion(IMHEX_VERSION)
setVariableInParent(IMHEX_VERSION ${IMHEX_VERSION}) setVariableInParent(IMHEX_VERSION ${IMHEX_VERSION})
configureCMake() configureCMake()
project(imhex project(imhex
LANGUAGES C CXX VERSION ${IMHEX_VERSION} LANGUAGES C CXX
DESCRIPTION "The ImHex Hex Editor" VERSION ${IMHEX_VERSION}
HOMEPAGE_URL "https://imhex.werwolv.net" DESCRIPTION "The ImHex Hex Editor"
HOMEPAGE_URL "https://imhex.werwolv.net"
) )
# Add ImHex sources
add_custom_target(imhex_all ALL)
# Make sure project is configured correctly # Make sure project is configured correctly
setDefaultBuiltTypeIfUnset() setDefaultBuiltTypeIfUnset()
detectBadClone() detectBadClone()
@@ -50,17 +59,19 @@ configurePackingResources()
setUninstallTarget() setUninstallTarget()
addBundledLibraries() addBundledLibraries()
# Add ImHex sources
add_custom_target(imhex_all ALL)
add_subdirectory(lib/libimhex) add_subdirectory(lib/libimhex)
add_subdirectory(main) add_subdirectory(main)
addPluginDirectories()
# Add unit tests # Add unit tests
enable_testing() if (IMHEX_ENABLE_UNIT_TESTS)
add_subdirectory(tests EXCLUDE_FROM_ALL) enable_testing()
add_subdirectory(tests EXCLUDE_FROM_ALL)
endif ()
# Configure more resources that will be added to the install package # Configure more resources that will be added to the install package
createPackage()
generatePDBs() generatePDBs()
generateSDKDirectory() generateSDKDirectory()
# Handle package generation
createPackage()

View File

@@ -10,33 +10,19 @@
<p align="center">A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</p> <p align="center">A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</p>
<p align="center"> <p align="center">
<a title="'Build' workflow Status" href="https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild"> <a title="'Build' workflow Status" href="https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild"><img alt="'Build' workflow Status" src="https://img.shields.io/github/actions/workflow/status/WerWolv/ImHex/build.yml?longCache=true&style=for-the-badge&label=Build&logoColor=fff&logo=GitHub%20Actions&branch=master"></a>
<img alt="'Build' workflow Status" src="https://img.shields.io/github/actions/workflow/status/WerWolv/ImHex/build.yml?longCache=true&style=for-the-badge&label=Build&logoColor=fff&logo=GitHub%20Actions&branch=master"> <a title="Discord Server" href="https://discord.gg/X63jZ36xBY"><img alt="Discord Server" src="https://img.shields.io/discord/789833418631675954?label=Discord&logo=Discord&logoColor=fff&style=for-the-badge"></a>
</a> <a title="Total Downloads" href="https://github.com/WerWolv/ImHex/releases/latest"><img alt="Total Downloads" src="https://img.shields.io/github/downloads/WerWolv/ImHex/total?longCache=true&style=for-the-badge&label=Downloads&logoColor=fff&logo=GitHub"></a>
<a title="Discord Server" href="https://discord.gg/X63jZ36xBY"> <a title="Code Quality" href="https://www.codefactor.io/repository/github/werwolv/imhex"><img alt="Code Quality" src="https://img.shields.io/codefactor/grade/github/WerWolv/ImHex?longCache=true&style=for-the-badge&label=Code%20Quality&logoColor=fff&logo=CodeFactor&branch=master"></a>
<img alt="Discord Server" src="https://img.shields.io/discord/789833418631675954?label=Discord&logo=Discord&logoColor=fff&style=for-the-badge"> <a title="Translation" href="https://weblate.werwolv.net/projects/imhex/"><img alt="Translation" src="https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge"></a>
</a> <a title="Plugins" href="https://github.com/WerWolv/ImHex/blob/master/PLUGINS.md"><img alt="Plugins" src="https://img.shields.io/badge/Plugins-Supported-brightgreen?logo=stackedit&logoColor=%23FFFFFF&style=for-the-badge"></a>
<a title="Total Downloads" href="https://github.com/WerWolv/ImHex/releases/latest">
<img alt="Total Downloads" src="https://img.shields.io/github/downloads/WerWolv/ImHex/total?longCache=true&style=for-the-badge&label=Downloads&logoColor=fff&logo=GitHub">
</a>
<a title="Code Quality" href="https://www.codefactor.io/repository/github/werwolv/imhex">
<img alt="Code Quality" src="https://img.shields.io/codefactor/grade/github/WerWolv/ImHex?longCache=true&style=for-the-badge&label=Code%20Quality&logoColor=fff&logo=CodeFactor&branch=master">
</a>
<a title="Translation" href="https://weblate.werwolv.net/projects/imhex/">
<img alt="Translation" src="https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge">
</a>
<a title="Documentation" href="https://imhex.werwolv.net/docs">
<img alt="Documentation" src="https://img.shields.io/badge/Docs-Available-brightgreen?logo=gitbook&logoColor=%23FFFFFF&style=for-the-badge">
</a>
<a title="Plugins" href="https://github.com/WerWolv/ImHex/blob/master/PLUGINS.md">
<img alt="Plugins" src="https://img.shields.io/badge/Plugins-Supported-brightgreen?logo=stackedit&logoColor=%23FFFFFF&style=for-the-badge">
</a>
</p> </p>
<p align="center"> <p align="center">
<a title="Use the Web version of ImHex right in your browser!" href="https://web.imhex.werwolv.net"> <a title="Download the latest version of ImHex" href="https://imhex.download"><img alt="Download the latest version of ImHex!" src="resources/dist/common/get_release_banner.png"></a>
<img alt="Use the Web version of ImHex right in your browser!" src="resources/dist/common/try_online_banner.png"> <a title="Download the latest nightly pre-release version of ImHex" href="https://imhex.download/#nightly"><img alt="Download the latest nightly pre-release version of ImHex" src="resources/dist/common/get_nightly_banner.png"></a>
</a> <a title="Use the Web version of ImHex right in your browser!" href="https://web.imhex.werwolv.net"><img alt="Use the Web version of ImHex right in your browser!" src="resources/dist/common/try_online_banner.png"></a>
<a title="Read the documentation of ImHex!" href="https://docs.werwolv.net"><img alt="Read the documentation of ImHex!" src="resources/dist/common/read_docs_banner.png"></a>
</p> </p>
## Supporting ## Supporting
@@ -44,22 +30,21 @@
If you like my work, please consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot! If you like my work, please consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot!
<p align="center"> <p align="center">
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /> </a> <a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /></a>
<a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /> </a> <a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /></a>
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /> </a> <a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /></a>
</p> </p>
## Screenshots ## Screenshots
![Hex editor, patterns and data information](https://github.com/WerWolv/ImHex/assets/10835354/4f358238-2d27-41aa-9015-a2c6cc3708cf)
![Hex editor, patterns and data information](https://private-user-images.githubusercontent.com/10835354/290512928-ae20c3ce-4c02-4579-9471-640f43fd6bad.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyNDk0NTksIm5iZiI6MTcwMzI0OTE1OSwicGF0aCI6Ii8xMDgzNTM1NC8yOTA1MTI5MjgtYWUyMGMzY2UtNGMwMi00NTc5LTk0NzEtNjQwZjQzZmQ2YmFkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIyVDEyNDU1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTFmN2RhYmNlYWVjYzI0YWQxNjE0YjI1NDNjMTQ2MGFlODhkMmYzMTVkY2Y3MzNlYzUzYTM3N2IwNWE4OGY2YTAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.RbVnFXxuJS0xUoWbdgqCHtd-BopaFCyDypAfgkzGezU) ![Bookmarks, disassembler and data processor](https://github.com/WerWolv/ImHex/assets/10835354/183bc2cc-2439-4ded-b4c5-b140e19fc92f)
![Bookmarks, disassembler and data processor](https://user-images.githubusercontent.com/10835354/139717323-1f8c9d52-f7eb-4f43-9f11-097ac728ed6c.png)
<details> <details>
<summary><strong>More Screenshots</strong></summary> <summary><strong>More Screenshots</strong></summary>
![Data Processor decrypting some data and displaying it as an image](https://private-user-images.githubusercontent.com/10835354/290514353-d7f53f91-09e9-46c4-a720-e979c1c4e820.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyNDk0NTksIm5iZiI6MTcwMzI0OTE1OSwicGF0aCI6Ii8xMDgzNTM1NC8yOTA1MTQzNTMtZDdmNTNmOTEtMDllOS00NmM0LWE3MjAtZTk3OWMxYzRlODIwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIyVDEyNDU1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWE4OGIxNjg0NDcyMWVkYTMzOTAzMTAxMmJlNjE2ZjVkMTVmNzhlMDU1ZWNhODQyZjNjYTgzMzgzYjYyODVlNzQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.Y4VAVgSPBZI3Q1gDehPuoC98pQXVe-z9FUWhvDAV-1g) ![Data Processor decrypting some data and displaying it as an image](https://github.com/WerWolv/ImHex/assets/10835354/d0623081-3094-4840-a8a8-647b38724db8)
![STL Parser written in the Pattern Language visualizing a 3D model](https://private-user-images.githubusercontent.com/10835354/290517253-426d83c5-f6b7-4b69-aa87-05e48ab73e24.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyNDk0NTksIm5iZiI6MTcwMzI0OTE1OSwicGF0aCI6Ii8xMDgzNTM1NC8yOTA1MTcyNTMtNDI2ZDgzYzUtZjZiNy00YjY5LWFhODctMDVlNDhhYjczZTI0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIyVDEyNDU1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWJlMmJmZDYzOTgyZGYxOWQ5MmJhMTMyMzA2YWE3YmU0ODJlY2MwYmQxM2RlODc1MTgwYTQ5ZGYxMWNkY2JlNjAmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.lsJSbAnAWZNXm_bteUWZUM96Nd4qKhk3H3WZtjsFQB4) ![STL Parser written in the Pattern Language visualizing a 3D model](https://github.com/WerWolv/ImHex/assets/10835354/62cbcd18-1c3f-4dd6-a877-2bf2bf4bb2a5)
![Data Information view displaying various stats about the file](https://private-user-images.githubusercontent.com/10835354/290517882-1601cd05-e50b-41f7-8272-cb9af9e6fb81.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMyNDk0NTksIm5iZiI6MTcwMzI0OTE1OSwicGF0aCI6Ii8xMDgzNTM1NC8yOTA1MTc4ODItMTYwMWNkMDUtZTUwYi00MWY3LTgyNzItY2I5YWY5ZTZmYjgxLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjIlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIyVDEyNDU1OVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWRlZjNmNDA4NThlZjFmOTdmYWVlNGIxODQ3MDdmZDkzZGM4ZDEwOWZmNWRiMmM3NjNlODFiMTQ5ZTFhZTRlOTMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.AWm3GajW2KkNMT7AWhhCYca18UbXqZCS3-8RG4YN_ng) ![Data Information view displaying various stats about the file](https://github.com/WerWolv/ImHex/assets/10835354/d4706c01-c258-45c9-80b8-fe7a10d5a1de)
</details> </details>
@@ -365,9 +350,8 @@ See [Contributing](/CONTRIBUTING.md)
## Plugin development ## Plugin development
To develop plugins for ImHex, use one of the following two templates projects to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content. To develop plugins for ImHex, use the following template project to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.
- [C++ Plugin Template](https://github.com/WerWolv/ImHex-Cpp-Plugin-Template) - [ImHex Plugin Template](https://github.com/WerWolv/ImHex-Plugin-Template)
- [Rust Plugin Template](https://github.com/WerWolv/ImHex-Rust-Plugin-Template)
## Credits ## Credits
@@ -393,3 +377,12 @@ To develop plugins for ImHex, use one of the following two templates projects to
- Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended) - Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended)
- Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp) - Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp)
- Thanks to all other groups and organizations whose libraries are used in ImHex - Thanks to all other groups and organizations whose libraries are used in ImHex
### License
The biggest part of ImHex is under the GPLv2-only license.
Notable exceptions to this are the following parts which are under the LGPLv2.1 license:
- **/lib/libimhex**: The library that allows Plugins to interact with ImHex.
- **/plugins/ui**: The UI plugin library that contains some common UI elements that can be used by other plugins
The reason for this is to allow for proprietary plugins to be developed for ImHex.

View File

@@ -1 +1 @@
1.32.2 1.33.0

View File

@@ -22,7 +22,7 @@ macro(addDefines)
add_compile_definitions(NDEBUG) add_compile_definitions(NDEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug") elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug) set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug)
add_compile_definitions(DEBUG _GLIBCXX_DEBUG _GLIBCXX_VERBOSE) add_compile_definitions(DEBUG)
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}) set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})
add_compile_definitions(NDEBUG) add_compile_definitions(NDEBUG)
@@ -31,6 +31,10 @@ macro(addDefines)
add_compile_definitions(NDEBUG) add_compile_definitions(NDEBUG)
endif () endif ()
if (IMHEX_ENABLE_STD_ASSERTS)
add_compile_definitions(_GLIBCXX_DEBUG _GLIBCXX_VERBOSE)
endif()
if (IMHEX_STATIC_LINK_PLUGINS) if (IMHEX_STATIC_LINK_PLUGINS)
add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS) add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS)
endif () endif ()
@@ -42,6 +46,9 @@ function(addDefineToSource SOURCE DEFINE)
APPEND APPEND
PROPERTY COMPILE_DEFINITIONS "${DEFINE}" PROPERTY COMPILE_DEFINITIONS "${DEFINE}"
) )
# Disable precompiled headers for this file
set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
endfunction() endfunction()
# Detect current OS / System # Detect current OS / System
@@ -51,6 +58,7 @@ macro(detectOS)
set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_BINDIR ".")
set(CMAKE_INSTALL_LIBDIR ".") set(CMAKE_INSTALL_LIBDIR ".")
set(PLUGINS_INSTALL_LOCATION "plugins") set(PLUGINS_INSTALL_LOCATION "plugins")
add_compile_definitions(WIN32_LEAN_AND_MEAN)
elseif (APPLE) elseif (APPLE)
add_compile_definitions(OS_MACOS) add_compile_definitions(OS_MACOS)
set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_BINDIR ".")
@@ -62,6 +70,9 @@ macro(detectOS)
add_compile_definitions(OS_WEB) add_compile_definitions(OS_WEB)
elseif (UNIX AND NOT APPLE) elseif (UNIX AND NOT APPLE)
add_compile_definitions(OS_LINUX) add_compile_definitions(OS_LINUX)
if (BSD AND BSD STREQUAL "FreeBSD")
add_compile_definitions(OS_FREEBSD)
endif()
include(GNUInstallDirs) include(GNUInstallDirs)
if(IMHEX_PLUGINS_IN_SHARE) if(IMHEX_PLUGINS_IN_SHARE)
@@ -81,6 +92,8 @@ macro(detectOS)
endmacro() endmacro()
macro(configurePackingResources) macro(configurePackingResources)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
if (WIN32) if (WIN32)
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug")) if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
set(APPLICATION_TYPE WIN32) set(APPLICATION_TYPE WIN32)
@@ -116,7 +129,8 @@ macro(configurePackingResources)
set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in") set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in")
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex") set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${IMHEX_COMMIT_HASH_SHORT}") string(SUBSTRING "${IMHEX_COMMIT_HASH_LONG}" 0 7 COMMIT_HASH_SHORT)
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${COMMIT_HASH_SHORT}")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
string(TIMESTAMP CURR_YEAR "%Y") string(TIMESTAMP CURR_YEAR "%Y")
@@ -133,9 +147,7 @@ macro(configurePackingResources)
endif() endif()
endmacro() endmacro()
macro(createPackage) macro(addPluginDirectories)
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
file(MAKE_DIRECTORY "plugins") file(MAKE_DIRECTORY "plugins")
foreach (plugin IN LISTS PLUGINS) foreach (plugin IN LISTS PLUGINS)
add_subdirectory("plugins/${plugin}") add_subdirectory("plugins/${plugin}")
@@ -166,9 +178,9 @@ macro(createPackage)
add_dependencies(imhex_all ${plugin}) add_dependencies(imhex_all ${plugin})
endif () endif ()
endforeach() endforeach()
endmacro()
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) macro(createPackage)
if (WIN32) if (WIN32)
# Install binaries directly in the prefix, usually C:\Program Files\ImHex. # Install binaries directly in the prefix, usually C:\Program Files\ImHex.
set(CMAKE_INSTALL_BINDIR ".") set(CMAKE_INSTALL_BINDIR ".")
@@ -205,7 +217,6 @@ macro(createPackage)
endforeach() endforeach()
]]) ]])
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./") downloadImHexPatternsFiles("./")
elseif(UNIX AND NOT APPLE) elseif(UNIX AND NOT APPLE)
@@ -216,7 +227,6 @@ macro(createPackage)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
downloadImHexPatternsFiles("./share/imhex") downloadImHexPatternsFiles("./share/imhex")
# install AppStream file # install AppStream file
@@ -237,14 +247,14 @@ macro(createPackage)
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST}) set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
# Fix rpath # Fix rpath
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>) install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins") add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources") add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS") downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources") install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
install(TARGETS main BUNDLE DESTINATION ".") install(TARGETS main BUNDLE DESTINATION ".")
# Update library references to make the bundle portable # Update library references to make the bundle portable
@@ -254,13 +264,17 @@ macro(createPackage)
set(CPACK_GENERATOR "DragNDrop") set(CPACK_GENERATOR "DragNDrop")
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns") set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist") set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist")
# Sign the bundle if (IMHEX_RESIGN_BUNDLE)
find_program(CODESIGN_PATH codesign) find_program(CODESIGN_PATH codesign)
if (CODESIGN_PATH) if (CODESIGN_PATH)
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_BINARY_DIR}/${BUNDLE_NAME}) install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")")
install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)")
endif()
endif() endif()
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
endif() endif()
else() else()
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
@@ -295,6 +309,8 @@ macro(configureCMake)
# Enable C and C++ languages # Enable C and C++ languages
enable_language(C CXX) enable_language(C CXX)
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "Enable position independent code for all targets" FORCE)
# Configure use of recommended build tools # Configure use of recommended build tools
if (IMHEX_USE_DEFAULT_BUILD_SETTINGS) if (IMHEX_USE_DEFAULT_BUILD_SETTINGS)
message(STATUS "Configuring CMake to use recommended build tools...") message(STATUS "Configuring CMake to use recommended build tools...")
@@ -356,6 +372,10 @@ macro(configureCMake)
# display a warning about options being set using set() instead of option(). # display a warning about options being set using set() instead of option().
# Explicitly set the policy to NEW to suppress the warning. # Explicitly set the policy to NEW to suppress the warning.
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Disable deprecated warnings" FORCE)
endmacro() endmacro()
macro(setDefaultBuiltTypeIfUnset) macro(setDefaultBuiltTypeIfUnset)
@@ -475,7 +495,6 @@ endfunction()
macro(setupCompilerFlags target) macro(setupCompilerFlags target)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
# Define strict compilation flags # Define strict compilation flags
if (IMHEX_STRICT_WARNINGS) if (IMHEX_STRICT_WARNINGS)
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wall -Wextra -Wpedantic -Werror") set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wall -Wextra -Wpedantic -Werror")
@@ -488,8 +507,10 @@ macro(setupCompilerFlags target)
set(IMHEX_CXX_FLAGS "-fexceptions -frtti") set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
# Disable some warnings # 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")
if (IMHEX_ENABLE_UNITY_BUILD AND WIN32) if (IMHEX_ENABLE_UNITY_BUILD AND WIN32)
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wa,-mbig-obj") set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wa,-mbig-obj")
endif () endif ()
@@ -526,8 +547,6 @@ macro(setUninstallTarget)
endmacro() endmacro()
macro(addBundledLibraries) macro(addBundledLibraries)
find_package(PkgConfig REQUIRED)
set(EXTERNAL_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/external") set(EXTERNAL_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/external")
set(THIRD_PARTY_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/third_party") set(THIRD_PARTY_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/third_party")
@@ -535,28 +554,17 @@ macro(addBundledLibraries)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/imgui) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/imgui)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL)
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL) add_subdirectory(${EXTERNAL_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL)
set_property(TARGET libwolv-types PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-utils PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-io PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-hash PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-containers PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-net PROPERTY POSITION_INDEPENDENT_CODE ON)
set_property(TARGET libwolv-math_eval PROPERTY POSITION_INDEPENDENT_CODE ON)
set(XDGPP_INCLUDE_DIRS "${THIRD_PARTY_LIBS_FOLDER}/xdgpp") set(XDGPP_INCLUDE_DIRS "${THIRD_PARTY_LIBS_FOLDER}/xdgpp")
set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "") set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "")
find_package(PkgConfig REQUIRED)
if(NOT USE_SYSTEM_FMT) if(NOT USE_SYSTEM_FMT)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL)
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(FMT_LIBRARIES fmt::fmt-header-only) set(FMT_LIBRARIES fmt::fmt-header-only)
else() else()
find_package(fmt 8.0.0 REQUIRED) find_package(fmt REQUIRED)
set(FMT_LIBRARIES fmt::fmt) set(FMT_LIBRARIES fmt::fmt)
endif() endif()
@@ -568,13 +576,11 @@ macro(addBundledLibraries)
if (NOT EMSCRIPTEN) if (NOT EMSCRIPTEN)
# curl # curl
find_package(PkgConfig REQUIRED) find_package(CURL REQUIRED)
pkg_check_modules(LIBCURL REQUIRED IMPORTED_TARGET libcurl>=7.60.0)
# nfd # nfd
if (NOT USE_SYSTEM_NFD) if (NOT USE_SYSTEM_NFD)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL)
set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
set(NFD_LIBRARIES nfd) set(NFD_LIBRARIES nfd)
else() else()
find_package(nfd) find_package(nfd)
@@ -592,7 +598,6 @@ macro(addBundledLibraries)
if (NOT USE_SYSTEM_LLVM) if (NOT USE_SYSTEM_LLVM)
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL) add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL)
set_target_properties(LLVMDemangle PROPERTIES POSITION_INDEPENDENT_CODE ON)
else() else()
find_package(LLVM REQUIRED Demangle) find_package(LLVM REQUIRED Demangle)
endif() endif()
@@ -609,18 +614,40 @@ macro(addBundledLibraries)
set(JTHREAD_LIBRARIES jthread) set(JTHREAD_LIBRARIES jthread)
endif() endif()
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF) 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)
else()
set(LIBPL_SHARED_LIBRARY OFF CACHE BOOL "" FORCE)
endif()
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL) add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL)
set_target_properties(libpl PROPERTIES POSITION_INDEPENDENT_CODE ON)
if (LIBPL_SHARED_LIBRARY)
install(
TARGETS
libpl
DESTINATION
"${CMAKE_INSTALL_LIBDIR}"
PERMISSIONS
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
)
endif()
if (WIN32)
set_target_properties(
libpl
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
)
endif()
enableUnityBuild(libpl)
find_package(mbedTLS 3.4.0 REQUIRED) find_package(mbedTLS 3.4.0 REQUIRED)
find_package(Magic 5.39 REQUIRED)
pkg_search_module(MAGIC libmagic>=5.39)
if(NOT MAGIC_FOUND)
find_library(MAGIC 5.39 magic REQUIRED)
else()
set(MAGIC_INCLUDE_DIRS ${MAGIC_INCLUDEDIR})
endif()
if (NOT IMHEX_DISABLE_STACKTRACE) if (NOT IMHEX_DISABLE_STACKTRACE)
if (WIN32) if (WIN32)
@@ -631,20 +658,20 @@ macro(addBundledLibraries)
if (${Backtrace_FOUND}) if (${Backtrace_FOUND})
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}") message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
if (Backtrace_HEADER STREQUAL "execinfo.h") if (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY}) set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR}) set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\") add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_EXECINFO)
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
add_compile_definitions(HEX_HAS_BACKTRACE) add_compile_definitions(HEX_HAS_BACKTRACE)
endif () elseif (Backtrace_HEADER STREQUAL "execinfo.h")
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
add_compile_definitions(BACKTRACE_HEADER=<${Backtrace_HEADER}>)
add_compile_definitions(HEX_HAS_EXECINFO)
endif()
endif() endif()
endif () endif()
endif () endif()
endmacro() endmacro()
function(enableUnityBuild TARGET) function(enableUnityBuild TARGET)
@@ -654,6 +681,10 @@ function(enableUnityBuild TARGET)
endfunction() endfunction()
function(generatePDBs) function(generatePDBs)
if (NOT IMHEX_GENERATE_PDBS)
return()
endif ()
if (NOT WIN32 OR CMAKE_BUILD_TYPE STREQUAL "Debug") if (NOT WIN32 OR CMAKE_BUILD_TYPE STREQUAL "Debug")
return() return()
endif () endif ()
@@ -667,7 +698,6 @@ function(generatePDBs)
FetchContent_Populate(cv2pdb) FetchContent_Populate(cv2pdb)
set(PDBS_TO_GENERATE main main-forwarder libimhex ${PLUGINS}) set(PDBS_TO_GENERATE main main-forwarder libimhex ${PLUGINS})
add_custom_target(pdbs)
foreach (PDB ${PDBS_TO_GENERATE}) foreach (PDB ${PDBS_TO_GENERATE})
if (PDB STREQUAL "main") if (PDB STREQUAL "main")
set(GENERATED_PDB imhex) set(GENERATED_PDB imhex)
@@ -679,19 +709,26 @@ function(generatePDBs)
set(GENERATED_PDB plugins/${PDB}) set(GENERATED_PDB plugins/${PDB})
endif () endif ()
if (IMHEX_REPLACE_DWARF_WITH_PDB)
set(PDB_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${GENERATED_PDB})
else ()
set(PDB_OUTPUT_PATH)
endif()
add_custom_target(${PDB}_pdb DEPENDS ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb
WORKING_DIRECTORY ${cv2pdb_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND COMMAND
( (
${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb && ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb &&
${cv2pdb_SOURCE_DIR}/cv2pdb64.exe ${cv2pdb_SOURCE_DIR}/cv2pdb64.exe $<TARGET_FILE:${PDB}> ${PDB_OUTPUT_PATH} &&
$<TARGET_FILE:${PDB}> ${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}
) || (exit 0) ) || (exit 0)
DEPENDS $<TARGET_FILE:${PDB}>
COMMAND_EXPAND_LISTS) COMMAND_EXPAND_LISTS)
target_sources(imhex_all PRIVATE ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb)
install(FILES ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb DESTINATION ".") install(FILES ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb DESTINATION ".")
add_dependencies(imhex_all ${PDB}_pdb)
endforeach () endforeach ()
endfunction() endfunction()
@@ -700,14 +737,16 @@ function(generateSDKDirectory)
if (WIN32) if (WIN32)
set(SDK_PATH "./sdk") set(SDK_PATH "./sdk")
elseif (APPLE) elseif (APPLE)
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk") set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
else() else()
set(SDK_PATH "share/imhex/sdk") set(SDK_PATH "share/imhex/sdk")
endif() endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION "${SDK_PATH}/lib") set(SDK_BUILD_PATH "${CMAKE_BINARY_DIR}/sdk")
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION "${SDK_PATH}/lib" PATTERN "**/source/*" EXCLUDE)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/external DESTINATION "${SDK_PATH}/lib") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/external DESTINATION "${SDK_PATH}/lib")
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION "${SDK_PATH}/lib/third_party") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION "${SDK_PATH}/lib/third_party" PATTERN "**/source/*" EXCLUDE)
if (NOT USE_SYSTEM_FMT) if (NOT USE_SYSTEM_FMT)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION "${SDK_PATH}/lib/third_party") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION "${SDK_PATH}/lib/third_party")
endif() endif()
@@ -715,15 +754,28 @@ function(generateSDKDirectory)
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION "${SDK_PATH}/lib/third_party") install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION "${SDK_PATH}/lib/third_party")
endif() endif()
install(FILES ${CMAKE_SOURCE_DIR}/cmake/modules/ImHexPlugin.cmake DESTINATION "${SDK_PATH}/cmake/modules") install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/modules DESTINATION "${SDK_PATH}/cmake")
install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION "${SDK_PATH}/cmake") install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION "${SDK_PATH}/cmake")
install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION "${SDK_PATH}") install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION "${SDK_PATH}")
install(TARGETS libimhex ARCHIVE DESTINATION "${SDK_PATH}/lib") install(TARGETS libimhex ARCHIVE DESTINATION "${SDK_PATH}/lib")
install(TARGETS libimhex RUNTIME DESTINATION "${SDK_PATH}/lib")
install(TARGETS libimhex LIBRARY DESTINATION "${SDK_PATH}/lib")
endfunction() endfunction()
function(addIncludesFromLibrary target library) function(addIncludesFromLibrary target library)
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES) get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
target_include_directories(${target} PRIVATE ${library_include_dirs}) 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() endfunction()

View File

@@ -0,0 +1,8 @@
find_path(CAPSTONE_INCLUDE_DIR capstone.h PATH_SUFFIXES capstone)
find_library(CAPSTONE_LIBRARY NAMES capstone)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Capstone DEFAULT_MSG CAPSTONE_LIBRARY CAPSTONE_INCLUDE_DIR)
mark_as_advanced(CAPSTONE_INCLUDE_DIR CAPSTONE_LIBRARY)

View File

@@ -49,13 +49,16 @@ endif()
set(CoreClrEmbed_ROOT_PATH "${CORECLR_RUNTIME_ROOT_PATH}") set(CoreClrEmbed_ROOT_PATH "${CORECLR_RUNTIME_ROOT_PATH}")
file(GLOB _CORECLR_HOST_ARCH_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}") file(GLOB _CORECLR_HOST_ARCH_PATH_LIST "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}")
if (_CORECLR_HOST_ARCH_PATH) if (_CORECLR_HOST_ARCH_PATH_LIST)
get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME) foreach(_CORECLR_HOST_ARCH_PATH ${_CORECLR_HOST_ARCH_PATH_LIST})
string(REPLACE "Microsoft.NETCore.App.Host." "" _CORECLR_COMPUTED_ARCH "${_CORECLR_HOST_ARCH_FILENAME}") get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME)
if (_CORECLR_COMPUTED_ARCH) string(REPLACE "Microsoft.NETCore.App.Host." "" _CORECLR_COMPUTED_ARCH "${_CORECLR_HOST_ARCH_FILENAME}")
set(CORECLR_ARCH "${_CORECLR_COMPUTED_ARCH}") if (_CORECLR_COMPUTED_ARCH)
endif() set(CORECLR_ARCH "${_CORECLR_COMPUTED_ARCH}")
break()
endif()
endforeach()
endif() endif()
set(CORECLR_HOST_BASE_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.${CORECLR_ARCH}/${CORECLR_RUNTIME_VERSION_FULL}") set(CORECLR_HOST_BASE_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.${CORECLR_ARCH}/${CORECLR_RUNTIME_VERSION_FULL}")

View File

@@ -0,0 +1,139 @@
#.rst:
# Find GLFW
# ---------
#
# Finds the GLFW library using its cmake config if that exists, otherwise
# falls back to finding it manually. This module defines:
#
# GLFW_FOUND - True if GLFW library is found
# GLFW::GLFW - GLFW imported target
#
# Additionally, in case the config was not found, these variables are defined
# for internal usage:
#
# GLFW_LIBRARY - GLFW library
# GLFW_DLL_DEBUG - GLFW debug DLL on Windows, if found
# GLFW_DLL_RELEASE - GLFW release DLL on Windows, if found
# GLFW_INCLUDE_DIR - Root include dir
#
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
# Copyright © 2016 Jonathan Hale <squareys@googlemail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
# GLFW installs cmake package config files which handles dependencies in case
# GLFW is built statically. Try to find first, quietly, so it doesn't print
# loud messages when it's not found, since that's okay. If the glfw target
# already exists, it means we're using it through a CMake subproject -- don't
# attempt to find the package in that case.
if(NOT TARGET glfw)
find_package(glfw3 CONFIG QUIET)
endif()
# If either a glfw config file was found or we have a subproject, point
# GLFW::GLFW to that and exit -- nothing else to do here.
if(TARGET glfw)
if(NOT TARGET GLFW::GLFW)
# Aliases of (global) targets are only supported in CMake 3.11, so we
# work around it by this. This is easier than fetching all possible
# properties (which are impossible to track of) and then attempting to
# rebuild them into a new target.
add_library(GLFW::GLFW INTERFACE IMPORTED)
set_target_properties(GLFW::GLFW PROPERTIES INTERFACE_LINK_LIBRARIES glfw)
endif()
# Just to make FPHSA print some meaningful location, nothing else
get_target_property(_GLFW_INTERFACE_INCLUDE_DIRECTORIES glfw INTERFACE_INCLUDE_DIRECTORIES)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("GLFW" DEFAULT_MSG
_GLFW_INTERFACE_INCLUDE_DIRECTORIES)
if(CORRADE_TARGET_WINDOWS)
# .dll is in LOCATION, .lib is in IMPLIB. Yay, useful!
get_target_property(GLFW_DLL_DEBUG glfw IMPORTED_LOCATION_DEBUG)
get_target_property(GLFW_DLL_RELEASE glfw IMPORTED_LOCATION_RELEASE)
endif()
return()
endif()
if(CORRADE_TARGET_WINDOWS)
if(MSVC)
if(MSVC_VERSION VERSION_LESS 1910)
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2015)
elseif(MSVC_VERSION VERSION_LESS 1920)
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2017)
elseif(MSVC_VERSION VERSION_LESS 1930)
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2019)
elseif(MSVC_VERSION VERSION_LESS 1940)
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2022)
else()
message(FATAL_ERROR "Unsupported MSVC version")
endif()
elseif(MINGW)
set(_GLFW_LIBRARY_PATH_SUFFIX lib-mingw-w64)
else()
message(FATAL_ERROR "Unsupported compiler")
endif()
endif()
# In case no config file was found, try manually finding the library. Prefer
# the glfw3dll as it's a dynamic library.
find_library(GLFW_LIBRARY
NAMES glfw glfw3dll glfw3
PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})
if(CORRADE_TARGET_WINDOWS AND GLFW_LIBRARY MATCHES "glfw3dll.(lib|a)$")
# TODO: debug?
find_file(GLFW_DLL_RELEASE
NAMES glfw3.dll
PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})
endif()
# Include dir
find_path(GLFW_INCLUDE_DIR
NAMES GLFW/glfw3.h)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args("GLFW" DEFAULT_MSG
GLFW_LIBRARY
GLFW_INCLUDE_DIR)
if(NOT TARGET GLFW::GLFW)
add_library(GLFW::GLFW UNKNOWN IMPORTED)
# Work around BUGGY framework support on macOS
# https://cmake.org/Bug/view.php?id=14105
if(CORRADE_TARGET_APPLE AND GLFW_LIBRARY MATCHES "\\.framework$")
set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}/GLFW)
else()
set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY})
endif()
set_property(TARGET GLFW::GLFW PROPERTY
INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR})
endif()
mark_as_advanced(GLFW_LIBRARY GLFW_INCLUDE_DIR)

View File

@@ -0,0 +1,18 @@
find_path(LIBMAGIC_INCLUDE_DIR magic.h)
find_library(LIBMAGIC_LIBRARY NAMES magic)
if (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
set(LIBMAGIC_FOUND TRUE)
endif (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
find_package_handle_standard_args("libmagic" DEFAULT_MSG
LIBMAGIC_LIBRARY
LIBMAGIC_INCLUDE_DIR
)
mark_as_advanced(
LIBMAGIC_INCLUDE_DIR
LIBMAGIC_LIBRARY
LIBMAGIC_FOUND
)

View File

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

View File

@@ -0,0 +1,41 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# - Try to find Facebook zstd library
# This will define
# ZSTD_FOUND
# ZSTD_INCLUDE_DIR
# ZSTD_LIBRARY
#
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
include(SelectLibraryConfigurations)
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
ZSTD DEFAULT_MSG
ZSTD_LIBRARY ZSTD_INCLUDE_DIR
)
if (ZSTD_FOUND)
message(STATUS "Found Zstd: ${ZSTD_LIBRARY}")
endif()
mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)

View File

@@ -36,8 +36,12 @@ macro(add_imhex_plugin)
# Add include directories and link libraries # Add include directories and link libraries
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES}) 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)
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)
precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include")
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define # Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}") target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
@@ -45,7 +49,6 @@ macro(add_imhex_plugin)
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME}) target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME})
# Enable required compiler flags # Enable required compiler flags
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
enableUnityBuild(${IMHEX_PLUGIN_NAME}) enableUnityBuild(${IMHEX_PLUGIN_NAME})
setupCompilerFlags(${IMHEX_PLUGIN_NAME}) setupCompilerFlags(${IMHEX_PLUGIN_NAME})
@@ -58,11 +61,15 @@ macro(add_imhex_plugin)
SUFFIX ${IMHEX_PLUGIN_SUFFIX} SUFFIX ${IMHEX_PLUGIN_SUFFIX}
) )
# Set rpath of plugin libraries to the plugins folder
if (APPLE)
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES BUILD_RPATH "@executable_path/../Frameworks;@executable_path/plugins")
endif()
# Setup a romfs for the plugin # Setup a romfs for the plugin
list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs) list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs)
set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME}) set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME})
add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs) add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs)
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY}) target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY})
foreach(feature ${IMHEX_PLUGIN_FEATURES}) foreach(feature ${IMHEX_PLUGIN_FEATURES})
@@ -77,11 +84,19 @@ macro(add_imhex_plugin)
if (IMHEX_EXTERNAL_PLUGIN_BUILD) if (IMHEX_EXTERNAL_PLUGIN_BUILD)
install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION ".") install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION ".")
endif()
# Fix rpath # Fix rpath
if (APPLE) if (APPLE)
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks") set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks;@executable_path/plugins")
endif() 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() endif()
endmacro() endmacro()

View File

@@ -14,12 +14,11 @@ if(CMAKE_GENERATOR)
# Being called as include(PostprocessBundle), so define a helper function. # Being called as include(PostprocessBundle), so define a helper function.
set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}") set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
function(postprocess_bundle out_target in_target) function(postprocess_bundle out_target in_target)
add_custom_command(TARGET ${out_target} POST_BUILD
COMMAND ${CMAKE_COMMAND} -DBUNDLE_PATH="$<TARGET_FILE_DIR:${in_target}>/../.." install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})")
-DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_CERTIFICATE_ID}" install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})")
-DEXTRA_BUNDLE_LIBRARY_PATHS="${EXTRA_BUNDLE_LIBRARY_PATHS}" install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})")
-P "${_POSTPROCESS_BUNDLE_MODULE_LOCATION}" install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})
)
endfunction() endfunction()
return() return()
endif() endif()
@@ -36,14 +35,13 @@ message(STATUS "Fixing up application bundle: ${BUNDLE_PATH}")
# Make sure to fix up any included ImHex plugin. # Make sure to fix up any included ImHex plugin.
file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug") file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug")
file(GLOB_RECURSE plugin_libs "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexpluglib")
# BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which # BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which
# makes it sometimes break on libraries that do weird things with @rpath. Specify # makes it sometimes break on libraries that do weird things with @rpath. Specify
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625 # equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
# is fixed and in our minimum CMake version. # is fixed and in our minimum CMake version.
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins") set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins" "${BUNDLE_PATH}/Contents/Frameworks")
message(STATUS "Fixing up application bundle: ${extra_dirs}") message(STATUS "Fixing up application bundle: ${extra_dirs}")
# BundleUtilities is overly verbose, so disable most of its messages # BundleUtilities is overly verbose, so disable most of its messages
@@ -55,7 +53,8 @@ message(STATUS "Fixing up application bundle: ${extra_dirs}")
include(BundleUtilities) include(BundleUtilities)
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
fixup_bundle("${BUNDLE_PATH}" "${plugins};${plugin_libs}" "${extra_dirs}")
fixup_bundle("${BUNDLE_PATH}" "${plugins}" "${extra_dirs}")
if (CODE_SIGN_CERTIFICATE_ID) if (CODE_SIGN_CERTIFICATE_ID)
# Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back. # Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back.
@@ -67,4 +66,13 @@ if (CODE_SIGN_CERTIFICATE_ID)
endif() endif()
# Add a necessary rpath to the imhex binary # Add a necessary rpath to the imhex binary
get_bundle_main_executable("${BUNDLE_PATH}" IMHEX_EXECUTABLE) get_bundle_main_executable("${BUNDLE_PATH}" IMHEX_EXECUTABLE)
file(GLOB_RECURSE plugin_libs "${BUNDLE_PATH}/Contents/MacOS/*.hexpluglib")
foreach(plugin_lib ${plugin_libs})
get_filename_component(plugin_lib_name ${plugin_lib} NAME)
set(plugin_lib_dest "${BUNDLE_PATH}/Contents/MacOS/plugins/${plugin_lib_name}")
configure_file(${plugin_lib} "${plugin_lib_dest}" COPYONLY)
message(STATUS "Copying plugin library: ${plugin_lib} to ${plugin_lib_dest}")
endforeach ()

View File

@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.20)
project(ImHexSDK) project(ImHexSDK)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "" FORCE)
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake") include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake")
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
@@ -22,27 +23,36 @@ add_subdirectory(lib/third_party/imgui EXCLUDE_FROM_ALL)
set(FMT_INSTALL OFF CACHE BOOL "" FORCE) set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
add_subdirectory_if_exists(lib/third_party/fmt) add_subdirectory_if_exists(lib/third_party/fmt)
set(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE) set(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE)
set(FMT_LIBRARIES fmt::fmt-header-only)
add_subdirectory_if_exists(lib/third_party/nlohmann_json) add_subdirectory_if_exists(lib/third_party/nlohmann_json)
set(NLOHMANN_JSON_LIBRARIES nlohmann_json PARENT_SCOPE)
set(NLOHMANN_JSON_LIBRARIES nlohmann_json)
add_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL) add_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL)
set(LIBPL_ENABLE_CLI OFF CACHE BOOL "" FORCE) set(LIBPL_ENABLE_CLI OFF CACHE BOOL "" FORCE)
add_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL) add_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL)
find_package(CURL REQUIRED)
find_package(mbedTLS 3.4.0 REQUIRED)
set(CURL_LIBRARIES ${CURL_LIBRARIES} PARENT_SCOPE)
set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARIES} PARENT_SCOPE)
add_subdirectory(lib/libimhex) add_subdirectory(lib/libimhex)
if (WIN32) if (WIN32)
set_target_properties(libimhex PROPERTIES set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dll" IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.dll"
IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a" IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
elseif (APPLE) elseif (APPLE)
file(GLOB LIBIMHEX_DYLIB "${CMAKE_CURRENT_SOURCE_DIR}/../../Frameworks/libimhex.*.dylib")
set_target_properties(libimhex PROPERTIES set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dylib" IMPORTED_LOCATION "${LIBIMHEX_DYLIB}"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
else() else()
set_target_properties(libimhex PROPERTIES set_target_properties(libimhex PROPERTIES
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.so" IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.so"
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include") INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
endif() endif()

View File

@@ -53,7 +53,6 @@ CC=gcc-12 CXX=g++-12 cmake -G "Ninja" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DIMHEX_PATTERNS_PULL_MASTER=ON \ -DIMHEX_PATTERNS_PULL_MASTER=ON \
-DIMHEX_COMMIT_HASH_SHORT="${GIT_COMMIT_HASH::7}" \
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \ -DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \ -DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
-DIMHEX_ENABLE_LTO=${LTO} \ -DIMHEX_ENABLE_LTO=${LTO} \

View File

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

View File

@@ -9,17 +9,12 @@ On Linux, ImHex is built through regular GCC (or optionally Clang).
cd ImHex cd ImHex
mkdir -p build mkdir -p build
cd build cd build
CC=gcc-12 CXX=g++-12 cmake \ CC=gcc-12 CXX=g++-12 \
cmake -G "Ninja" \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="/usr" \ -DCMAKE_INSTALL_PREFIX="/usr" \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
.. ..
make -j 4 install ninja install
``` ```
All paths follow the XDG Base Directories standard, and can thus be modified All paths follow the XDG Base Directories standard, and can thus be modified

View File

@@ -9,23 +9,14 @@ On macOS, ImHex is built through regular GCC and LLVM clang.
cd ImHex cd ImHex
mkdir -p build mkdir -p build
cd build cd build
CC=$(brew --prefix gcc@12)/bin/gcc-12 \ CC=$(brew --prefix llvm)/bin/clang \
CXX=$(brew --prefix gcc@12)/bin/g++-12 \ CXX=$(brew --prefix llvm)/bin/clang++ \
OBJC=$(brew --prefix llvm)/bin/clang \ OBJC=$(brew --prefix llvm)/bin/clang \
OBJCXX=$(brew --prefix llvm)/bin/clang++ \ OBJCXX=$(brew --prefix llvm)/bin/clang++ \
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \ cmake -G "Ninja" \
MACOSX_DEPLOYMENT_TARGET="10.15" \
cmake \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="./install" \
-DIMHEX_GENERATE_PACKAGE=ON \ -DIMHEX_GENERATE_PACKAGE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
.. ..
make -j4 package ninja install
``` ```
If the build fails while trying to find the macOS libraries, make sure you have
Xcode installed with `xcode-select --install`. Homebrew will also help get the
most recent SDK installed and configured with `brew doctor`.

View File

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

View File

@@ -1,19 +1,20 @@
#!/usr/bin/env sh #!/usr/bin/env sh
pacman -S --needed --noconfirm \ pacman -S --needed --noconfirm pactoys
mingw-w64-x86_64-gcc \ pacboy -S --needed --noconfirm \
mingw-w64-x86_64-lld \ gcc:p \
mingw-w64-x86_64-cmake \ lld:p \
mingw-w64-x86_64-ccache \ cmake:p \
mingw-w64-x86_64-glfw \ ccache:p \
mingw-w64-x86_64-file \ glfw:p \
mingw-w64-x86_64-curl-winssl \ file:p \
mingw-w64-x86_64-mbedtls \ curl-winssl:p \
mingw-w64-x86_64-freetype \ mbedtls:p \
mingw-w64-x86_64-dlfcn \ freetype:p \
mingw-w64-x86_64-ninja \ dlfcn:p \
mingw-w64-x86_64-capstone \ ninja:p \
mingw-w64-x86_64-zlib \ capstone:p \
mingw-w64-x86_64-bzip2 \ zlib:p \
mingw-w64-x86_64-xz \ bzip2:p \
mingw-w64-x86_64-zstd xz:p \
zstd:p

View File

@@ -126,7 +126,7 @@ if [ "$CUSTOM_GLFW" ]; then
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_INSTALL_PREFIX=/vcpkg/installed/arm-osx-mytriplet \ -DCMAKE_INSTALL_PREFIX=/vcpkg/installed/arm-osx-mytriplet \
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \ -DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 \
.. ..
ninja -j $JOBS install ninja -j $JOBS install
@@ -148,7 +148,7 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
`# ccache flags` \ `# ccache flags` \
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
`# MacOS cross-compiling flags` \ `# MacOS cross-compiling flags` \
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \ -DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 \
`# Override compilers for code generators` \ `# Override compilers for code generators` \
-DNATIVE_CMAKE_C_COMPILER=/usr/bin/clang -DNATIVE_CMAKE_CXX_COMPILER=/usr/bin/clang++ \ -DNATIVE_CMAKE_C_COMPILER=/usr/bin/clang -DNATIVE_CMAKE_CXX_COMPILER=/usr/bin/clang++ \
`# Normal ImHex flags` \ `# Normal ImHex flags` \
@@ -170,4 +170,4 @@ EOF
FROM scratch FROM scratch
COPY --from=build /mnt/ImHex/build/install/imhex.app ImHex.app COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app

9
dist/rpm/imhex.spec vendored
View File

@@ -1,5 +1,5 @@
Name: imhex Name: imhex
Version: 1.26.2 Version: VERSION
Release: 0%{?dist} Release: 0%{?dist}
Summary: A hex editor for reverse engineers and programmers Summary: A hex editor for reverse engineers and programmers
@@ -26,7 +26,7 @@ BuildRequires: llvm-devel
BuildRequires: mbedtls-devel BuildRequires: mbedtls-devel
BuildRequires: yara-devel BuildRequires: yara-devel
BuildRequires: nativefiledialog-extended-devel BuildRequires: nativefiledialog-extended-devel
BuildRequires: dotnet-sdk-7.0 BuildRequires: dotnet-sdk-8.0
BuildRequires: libzstd-devel BuildRequires: libzstd-devel
BuildRequires: zlib-devel BuildRequires: zlib-devel
BuildRequires: bzip2-devel BuildRequires: bzip2-devel
@@ -40,7 +40,6 @@ Provides: bundled(capstone) = 5.0-rc2
Provides: bundled(imgui) Provides: bundled(imgui)
Provides: bundled(libromfs) Provides: bundled(libromfs)
Provides: bundled(microtar) Provides: bundled(microtar)
Provides: bundled(libpl)
Provides: bundled(xdgpp) Provides: bundled(xdgpp)
# ftbfs on these arches. armv7hl might compile when capstone 5.x # ftbfs on these arches. armv7hl might compile when capstone 5.x
@@ -96,10 +95,6 @@ CXXFLAGS+=" -std=gnu++2b"
%set_build_flags %set_build_flags
CXXFLAGS+=" -std=gnu++2b" CXXFLAGS+=" -std=gnu++2b"
%endif %endif
# build binaries required for tests
%cmake_build --target unit_tests
%ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)'
# Helpers/*API exclude tests that require network access
%install %install

14
dist/web/Dockerfile vendored
View File

@@ -1,4 +1,4 @@
FROM emscripten/emsdk:latest as build FROM emscripten/emsdk:3.1.51 as build
# Used to invalidate layer cache but not mount cache # Used to invalidate layer cache but not mount cache
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493 # See https://github.com/moby/moby/issues/41715#issuecomment-733976493
@@ -73,10 +73,15 @@ cp /imhex/dist/web/source/* /build
ccache -s ccache -s
EOF EOF
FROM scratch # Create a file dedicated to store wasm size, because I know no way to get the wasm content length if the web server uses compression
# See https://stackoverflow.com/questions/41701849/cannot-modify-accept-encoding-with-fetch https://github.com/AnthumChris/fetch-progress-indicators/issues/13
RUN du -b /build/imhex.wasm | cut -f1 > imhex.wasm.size
FROM scratch as raw
COPY --from=build [ \ COPY --from=build [ \
# ImHex \ # ImHex \
"/build/imhex.wasm", \ "/build/imhex.wasm", \
"/build/imhex.wasm.size", \
"/build/imhex.js", \ "/build/imhex.js", \
"/build/imhex.worker.js", \ "/build/imhex.worker.js", \
\ \
@@ -93,4 +98,7 @@ COPY --from=build [ \
\ \
# Destination \ # Destination \
"./" \ "./" \
] ]
FROM nginx
COPY --from=raw . /usr/share/nginx/html

10
dist/web/compose.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# docker compose -f dist/web/compose.yml up --build
version: '3'
services:
imhex_web:
image: imhex_web:latest
build:
context: ../../ # ImHex folder
dockerfile: ./dist/web/Dockerfile
ports:
- 8080:80

View File

@@ -2,10 +2,12 @@
extern "C" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@(); extern "C" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
namespace {
struct StaticLoad { struct StaticLoad {
StaticLoad() { StaticLoad() {
forceLinkPlugin_@IMHEX_PLUGIN_NAME@(); forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
} }
}; };
}
static StaticLoad staticLoad; static StaticLoad staticLoad;

View File

@@ -61,37 +61,44 @@
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<div id="loading" class="centered"> <div id="loading" class="centered">
<img src="https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.png" id="logo" alt="ImHex Logo"> <img src="https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.png" id="logo" alt="ImHex Logo">
<h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1> <h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1>
<h2>Available both natively and on the web</h2> <h2>Available both natively and on the web</h2>
<h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5> <h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>
<div style="height: 50%"> <div style="height: 50%">
<div style="height: 30%"> </div> <div style="height: 30%"> </div>
<h2 id="not_working"> <h2 id="not_working">
Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a> Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a>
</h2> </h2>
<div style="height: 50%"></div> <div class="progress-bar-container">
</div> <div class="progress progress-moved">
<div class="progress-bar" id="progress-bar-content">
<div class="loading_ripple"> </div>
<div class="lds-ripple"><div></div><div></div></div> </div>
</div>
<div style="height: 10%">
</div>
<div class="footer">
<a href="https://imhex.werwolv.net">Homepage</a>
<p>Made with ♥️ by the ImHex Team</p>
<a href="https://github.com/WerWolv/ImHex">GitHub</a>
</div> </div>
</div> </div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script src="wasm-config.js"></script>
<script async src="imhex.js"></script> <div class="loading_ripple">
<div class="lds-ripple"><div></div><div></div></div>
</div>
<div style="height: 10%">
</div>
<div class="footer">
<a href="https://imhex.werwolv.net">Homepage</a>
<p>Made with ♥️ by the ImHex Team</p>
<a href="https://github.com/WerWolv/ImHex">GitHub</a>
</div>
</div>
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
<script src="wasm-config.js"></script>
<script async src="imhex.js"></script>
</body> </body>
</html> </html>

View File

@@ -5,10 +5,58 @@
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
<url> <url>
<loc>https://web.imhex.werwolv.net/</loc> <loc>https://web.imhex.werwolv.net/</loc>
<lastmod>2023-12-07T22:53:06+00:00</lastmod> <lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url> <priority>1.00</priority>
</url>
<url>
<title>English</title>
<loc>https://web.imhex.werwolv.net?lang=en-US</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
<priority>1.00</priority>
</url>
<url>
<title>Deutsch</title>
<loc>https://web.imhex.werwolv.net?lang=de-DE</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
<priority>1.00</priority>
</url>
<url>
<title>Português</title>
<loc>https://web.imhex.werwolv.net?lang=pt-BR</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>中国</title>
<loc>https://web.imhex.werwolv.net?lang=zh-CN</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>國語</title>
<loc>https://web.imhex.werwolv.net?lang=zh-TW</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>日本語</title>
<loc>https://web.imhex.werwolv.net?lang=ja-JP</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>한국어</title>
<loc>https://web.imhex.werwolv.net?lang=ko-KR</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>Español</title>
<loc>https://web.imhex.werwolv.net?lang=es-ES</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
<url>
<title>Italiano</title>
<loc>https://web.imhex.werwolv.net?lang=it-IT</loc>
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
</url>
</urlset> </urlset>

View File

@@ -17,6 +17,7 @@ body {
margin-right: auto; margin-right: auto;
display: none; display: none;
border: 0 none; border: 0 none;
image-rendering: smooth;
} }
h1, h2, h5 { h1, h2, h5 {
@@ -132,4 +133,45 @@ a:hover {
height: 72px; height: 72px;
opacity: 0; opacity: 0;
} }
}
:root {
--progress: 25%;
}
.progress-bar-container {
margin: 100px auto;
width: 600px;
text-align: center;
}
.progress {
padding: 6px;
border-radius: 30px;
background: rgba(0, 0, 0, 0.25);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25),
0 1px rgba(255, 255, 255, 0.08);
}
.progress-bar {
color: rgba(240, 240, 240, 0.9);
height: 18px;
border-radius: 30px;
font-size: 13px;
font-family: monospace;
font-weight: bold;
text-wrap: avoid;
white-space: nowrap;
overflow: hidden;
background-image: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.2),
rgba(255, 255, 255, 0.0)
);
}
.progress-moved .progress-bar {
width: var(--progress);
background-color: #3864cb;
} }

View File

@@ -1,3 +1,67 @@
let wasmSize = null;
// See comment in dist/web/Dockerfile about imhex.wasm.size
fetch("imhex.wasm.size").then(async (resp) => {
wasmSize = parseInt((await resp.text()).trim());
console.log(`Real WASM binary size is ${wasmSize} bytes`);
});
// Monkeypatch WebAssembly to have a progress bar
// inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)
function monkeyPatch(progressFun) {
const _instantiateStreaming = WebAssembly.instantiateStreaming;
WebAssembly.instantiateStreaming = (response, ...args) => {
// Do not collect wasm content length here see above
const file = response.url.substring(
new URL(response.url).origin.length + 1
);
const reportingResponse = new Response(
new ReadableStream(
{
async start(controller) {
const reader = response.clone().body.getReader();
let loaded = 0;
for (; ;) {
const { done, value } = await reader.read();
if (done) {
if(wasmSize) progressFun(file, wasmSize);
break;
}
loaded += value.byteLength;
progressFun(file, loaded);
controller.enqueue(value);
}
controller.close();
}
},
{
status: response.status,
statusText: response.statusText
}
)
);
for (const pair of response.headers.entries()) {
reportingResponse.headers.set(pair[0], pair[1]);
}
return _instantiateStreaming(reportingResponse, ...args);
}
}
monkeyPatch((file, done) => {
if (!wasmSize) return;
if (done > wasmSize) {
console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);
return;
}
const percent = ((done / wasmSize) * 100).toFixed(0);
const mibNow = (done / 1024**2).toFixed(1);
const mibTotal = (wasmSize / 1024**2).toFixed(1);
let root = document.querySelector(':root');
root.style.setProperty("--progress", `${percent}%`)
document.getElementById("progress-bar-content").innerHTML = `${percent}% &nbsp;[${mibNow} MiB / ${mibTotal} MiB]`;
});
function glfwSetCursorCustom(wnd, shape) { function glfwSetCursorCustom(wnd, shape) {
let body = document.getElementsByTagName("body")[0] let body = document.getElementsByTagName("body")[0]
switch (shape) { switch (shape) {
@@ -57,14 +121,25 @@ var Module = {
})(), })(),
setStatus: function(text) { }, setStatus: function(text) { },
totalDependencies: 0, totalDependencies: 0,
monitorRunDependencies: function(left) { }, monitorRunDependencies: function(left) {
},
instantiateWasm: function(imports, successCallback) { instantiateWasm: function(imports, successCallback) {
imports.env.glfwSetCursor = glfwSetCursorCustom imports.env.glfwSetCursor = glfwSetCursorCustom
imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => successCallback(result.instance, result.module)); instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => {
} successCallback(result.instance, result.module)
});
},
arguments: []
}; };
// Handle passing arguments to the wasm module
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
if (urlParams.has("lang")) {
Module["arguments"].push("--language");
Module["arguments"].push(urlParams.get("lang"));
}
window.addEventListener('resize', js_resizeCanvas, false); window.addEventListener('resize', js_resizeCanvas, false);
function js_resizeCanvas() { function js_resizeCanvas() {

View File

@@ -38,6 +38,8 @@ set(LIBIMHEX_SOURCES
source/helpers/tar.cpp source/helpers/tar.cpp
source/helpers/debugging.cpp source/helpers/debugging.cpp
source/test/tests.cpp
source/providers/provider.cpp source/providers/provider.cpp
source/providers/memory_provider.cpp source/providers/memory_provider.cpp
source/providers/undo/stack.cpp source/providers/undo/stack.cpp
@@ -77,43 +79,9 @@ else()
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}") target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
endif() endif()
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
enableUnityBuild(libimhex)
setupCompilerFlags(libimhex)
include(GenerateExportHeader) if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)
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} ${CURL_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_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_INCLUDE_DIRS})
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${LIBCURL_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 libpl plcli libpl-gen libwolv ${NFD_LIBRARIES} magic dl ${NLOHMANN_JSON_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} ${JTHREAD_LIBRARIES})
target_link_libraries(libimhex PUBLIC ${IMGUI_LIBRARIES})
endif()
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES})
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_HASH_SHORT AND DEFINED IMHEX_COMMIT_BRANCH)
set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}") set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}")
set(GIT_COMMIT_HASH_SHORT "${IMHEX_COMMIT_HASH_SHORT}")
set(GIT_BRANCH "${IMHEX_COMMIT_BRANCH}") set(GIT_BRANCH "${IMHEX_COMMIT_BRANCH}")
else() else()
# Get the current working branch # Get the current working branch
@@ -126,16 +94,6 @@ else()
ERROR_QUIET ERROR_QUIET
) )
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h --abbrev=7
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH_SHORT
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE RESULT_HASH_SHORT
ERROR_QUIET
)
execute_process( execute_process(
COMMAND git log -1 --format=%H COMMAND git log -1 --format=%H
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
@@ -146,12 +104,50 @@ else()
) )
endif () endif ()
if ((NOT GIT_COMMIT_HASH_SHORT STREQUAL "") AND (NOT GIT_COMMIT_HASH_LONG STREQUAL "") AND (NOT GIT_BRANCH STREQUAL "")) if (GIT_COMMIT_HASH_LONG STREQUAL "" OR GIT_BRANCH STREQUAL "")
addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_SHORT=\"${GIT_COMMIT_HASH_SHORT}\"") message(WARNING "Failed to to determine commit hash/branch")
else()
addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_LONG=\"${GIT_COMMIT_HASH_LONG}\"") addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_LONG=\"${GIT_COMMIT_HASH_LONG}\"")
addDefineToSource(source/api/imhex_api.cpp "GIT_BRANCH=\"${GIT_BRANCH}\"") addDefineToSource(source/api/imhex_api.cpp "GIT_BRANCH=\"${GIT_BRANCH}\"")
endif () endif ()
addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"") 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})

502
lib/libimhex/LICENSE Normal file
View File

@@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@@ -331,18 +331,7 @@ namespace hex {
static Achievement& addAchievement(auto && ... args) { static Achievement& addAchievement(auto && ... args) {
auto newAchievement = std::make_unique<T>(std::forward<decltype(args)>(args)...); auto newAchievement = std::make_unique<T>(std::forward<decltype(args)>(args)...);
const auto &category = newAchievement->getUnlocalizedCategory(); return addAchievementImpl(std::move(newAchievement));
const auto &name = newAchievement->getUnlocalizedName();
auto [categoryIter, categoryInserted] = getAchievements().insert({ category, std::unordered_map<std::string, std::unique_ptr<Achievement>>{} });
auto &[categoryKey, achievements] = *categoryIter;
auto [achievementIter, achievementInserted] = achievements.insert({ name, std::move(newAchievement) });
auto &[achievementKey, achievement] = *achievementIter;
achievementAdded();
return *achievement;
} }
/** /**
@@ -371,7 +360,7 @@ namespace hex {
* @brief Returns all registered achievements * @brief Returns all registered achievements
* @return All achievements * @return All achievements
*/ */
static std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>& getAchievements(); static const std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>& getAchievements();
/** /**
* @brief Returns all achievement start nodes * @brief Returns all achievement start nodes
@@ -379,14 +368,14 @@ namespace hex {
* @param rebuild Whether to rebuild the list of start nodes * @param rebuild Whether to rebuild the list of start nodes
* @return All achievement start nodes * @return All achievement start nodes
*/ */
static std::unordered_map<std::string, std::vector<AchievementNode*>>& getAchievementStartNodes(bool rebuild = true); static const std::unordered_map<std::string, std::vector<AchievementNode*>>& getAchievementStartNodes(bool rebuild = true);
/** /**
* @brief Returns all achievement nodes * @brief Returns all achievement nodes
* @param rebuild Whether to rebuild the list of nodes * @param rebuild Whether to rebuild the list of nodes
* @return All achievement nodes * @return All achievement nodes
*/ */
static std::unordered_map<std::string, std::list<AchievementNode>>& getAchievementNodes(bool rebuild = true); static const std::unordered_map<std::string, std::list<AchievementNode>>& getAchievementNodes(bool rebuild = true);
/** /**
* @brief Loads the progress of all achievements from the achievements save file * @brief Loads the progress of all achievements from the achievements save file
@@ -398,11 +387,6 @@ namespace hex {
*/ */
static void storeProgress(); static void storeProgress();
/**
* @brief Removes all registered achievements from the tree
*/
static void clear();
/** /**
* @brief Removes all temporary achievements from the tree * @brief Removes all temporary achievements from the tree
*/ */
@@ -416,6 +400,8 @@ namespace hex {
private: private:
static void achievementAdded(); static void achievementAdded();
static Achievement& addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement);
}; };
} }

View File

@@ -18,11 +18,13 @@
using ImGuiDataType = int; using ImGuiDataType = int;
using ImGuiInputTextFlags = int; using ImGuiInputTextFlags = int;
struct ImColor; struct ImColor;
enum ImGuiCustomCol : int;
namespace hex { namespace hex {
class View; class View;
class Shortcut; class Shortcut;
class Task;
namespace dp { namespace dp {
class Node; class Node;
@@ -139,7 +141,7 @@ namespace hex {
[[nodiscard]] bool isChecked() const { return m_value; } [[nodiscard]] bool isChecked() const { return m_value; }
private: protected:
bool m_value; bool m_value;
}; };
@@ -153,7 +155,7 @@ namespace hex {
[[nodiscard]] i32 getValue() const { return m_value; } [[nodiscard]] i32 getValue() const { return m_value; }
private: protected:
int m_value; int m_value;
i32 m_min, m_max; i32 m_min, m_max;
}; };
@@ -168,7 +170,7 @@ namespace hex {
[[nodiscard]] float getValue() const { return m_value; } [[nodiscard]] float getValue() const { return m_value; }
private: protected:
float m_value; float m_value;
float m_min, m_max; float m_min, m_max;
}; };
@@ -184,7 +186,7 @@ namespace hex {
[[nodiscard]] ImColor getColor() const; [[nodiscard]] ImColor getColor() const;
private: protected:
std::array<float, 4> m_value{}; std::array<float, 4> m_value{};
}; };
@@ -200,7 +202,7 @@ namespace hex {
[[nodiscard]] [[nodiscard]]
const nlohmann::json& getValue() const; const nlohmann::json& getValue() const;
private: protected:
std::vector<std::string> m_items; std::vector<std::string> m_items;
std::vector<nlohmann::json> m_settingsValues; std::vector<nlohmann::json> m_settingsValues;
nlohmann::json m_defaultItem; nlohmann::json m_defaultItem;
@@ -220,7 +222,7 @@ namespace hex {
[[nodiscard]] [[nodiscard]]
const std::string& getValue() const { return m_value; } const std::string& getValue() const { return m_value; }
private: protected:
std::string m_value; std::string m_value;
}; };
@@ -231,12 +233,12 @@ namespace hex {
void load(const nlohmann::json &data) override; void load(const nlohmann::json &data) override;
nlohmann::json store() override; nlohmann::json store() override;
[[nodiscard]] std::fs::path getPath() const { [[nodiscard]] const std::fs::path& getPath() const {
return m_value; return m_path;
} }
private: protected:
std::string m_value; std::fs::path m_path;
}; };
class Label : public Widget { class Label : public Widget {
@@ -271,11 +273,15 @@ namespace hex {
void store(); void store();
void clear(); void clear();
std::vector<Category> &getSettings(); const std::vector<Category>& getSettings();
nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue); nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);
nlohmann::json &getSettingsData(); const nlohmann::json& getSettingsData();
Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget); Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget);
void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception &e);
void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value);
} }
template<std::derived_from<Widgets::Widget> T> template<std::derived_from<Widgets::Widget> T>
@@ -290,8 +296,55 @@ namespace hex {
void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription); void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription);
[[nodiscard]] nlohmann::json read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue); class SettingsValue {
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value); public:
SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}
template<typename T>
T get(std::common_type_t<T> defaultValue) const {
try {
auto result = m_value;
if (result.is_number() && std::same_as<T, bool>)
result = m_value.get<int>() != 0;
if (m_value.is_null())
result = defaultValue;
return result.get<T>();
} catch (const nlohmann::json::exception &e) {
return defaultValue;
}
}
private:
nlohmann::json m_value;
};
template<typename T>
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &defaultValue) {
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
try {
if (setting.is_number() && std::same_as<T, bool>)
setting = setting.template get<int>() != 0;
if (setting.is_null())
setting = defaultValue;
return setting.template get<T>();
} catch (const nlohmann::json::exception &e) {
impl::printSettingReadError(unlocalizedCategory, unlocalizedName, e);
return defaultValue;
}
}
template<typename T>
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &value) {
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);
}
using OnChangeCallback = std::function<void(const SettingsValue &)>;
u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);
} }
/* Command Palette Command Registry. Allows adding of new commands to the command palette */ /* Command Palette Command Registry. Allows adding of new commands to the command palette */
@@ -328,8 +381,8 @@ namespace hex {
DisplayCallback displayCallback; DisplayCallback displayCallback;
}; };
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
std::vector<Handler> &getHandlers(); const std::vector<Handler>& getHandlers();
} }
@@ -384,10 +437,10 @@ namespace hex {
VisualizerFunctionCallback callback; VisualizerFunctionCallback callback;
}; };
std::map<std::string, Visualizer> &getVisualizers(); const std::map<std::string, Visualizer>& getVisualizers();
std::map<std::string, Visualizer> &getInlineVisualizers(); const std::map<std::string, Visualizer>& getInlineVisualizers();
std::map<std::string, pl::api::PragmaHandler> &getPragmas(); const std::map<std::string, pl::api::PragmaHandler>& getPragmas();
std::vector<FunctionDefinition> &getFunctions(); const std::vector<FunctionDefinition>& getFunctions();
} }
@@ -424,7 +477,12 @@ namespace hex {
* @param parameterCount The amount of parameters the function takes * @param parameterCount The amount of parameters the function takes
* @param func The function callback * @param func The function callback
*/ */
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); void addFunction(
const pl::api::Namespace &ns,
const std::string &name,
pl::api::FunctionParameterCount parameterCount,
const pl::api::FunctionCallback &func
);
/** /**
* @brief Adds a new dangerous function to the pattern language * @brief Adds a new dangerous function to the pattern language
@@ -434,7 +492,12 @@ namespace hex {
* @param parameterCount The amount of parameters the function takes * @param parameterCount The amount of parameters the function takes
* @param func The function callback * @param func The function callback
*/ */
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func); void addDangerousFunction(
const pl::api::Namespace &ns,
const std::string &name,
pl::api::FunctionParameterCount parameterCount,
const pl::api::FunctionCallback &func
);
/** /**
* @brief Adds a new visualizer to the pattern language * @brief Adds a new visualizer to the pattern language
@@ -443,7 +506,11 @@ namespace hex {
* @param function The function callback * @param function The function callback
* @param parameterCount The amount of parameters the function takes * @param parameterCount The amount of parameters the function takes
*/ */
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); void addVisualizer(
const std::string &name,
const impl::VisualizerFunctionCallback &function,
pl::api::FunctionParameterCount parameterCount
);
/** /**
* @brief Adds a new inline visualizer to the pattern language * @brief Adds a new inline visualizer to the pattern language
@@ -452,7 +519,11 @@ namespace hex {
* @param function The function callback * @param function The function callback
* @param parameterCount The amount of parameters the function takes * @param parameterCount The amount of parameters the function takes
*/ */
void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount); void addInlineVisualizer(
const std::string &name,
const impl::VisualizerFunctionCallback &function,
pl::api::FunctionParameterCount parameterCount
);
} }
@@ -462,7 +533,7 @@ namespace hex {
namespace impl { namespace impl {
void add(std::unique_ptr<View> &&view); void add(std::unique_ptr<View> &&view);
std::map<std::string, std::unique_ptr<View>> &getEntries(); const std::map<std::string, std::unique_ptr<View>>& getEntries();
} }
@@ -493,12 +564,11 @@ namespace hex {
using Callback = std::function<void()>; using Callback = std::function<void()>;
struct Entry { struct Entry {
std::string name; UnlocalizedString unlocalizedName;
Callback function; Callback function;
bool detached;
}; };
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
} }
@@ -534,7 +604,7 @@ namespace hex {
std::optional<EditingFunction> editingFunction; std::optional<EditingFunction> editingFunction;
}; };
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
} }
@@ -545,7 +615,12 @@ namespace hex {
* @param displayGeneratorFunction The function that will be called to generate the display function * @param displayGeneratorFunction The function that will be called to generate the display function
* @param editingFunction The function that will be called to edit the data * @param editingFunction The function that will be called to edit the data
*/ */
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt); void add(
const UnlocalizedString &unlocalizedName,
size_t requiredSize,
impl::GeneratorFunction displayGeneratorFunction,
std::optional<impl::EditingFunction> editingFunction = std::nullopt
);
/** /**
* @brief Adds a new entry to the data inspector * @brief Adds a new entry to the data inspector
@@ -555,7 +630,14 @@ namespace hex {
* @param displayGeneratorFunction The function that will be called to generate the display function * @param displayGeneratorFunction The function that will be called to generate the display function
* @param editingFunction The function that will be called to edit the data * @param editingFunction The function that will be called to edit the data
*/ */
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt); void add(
const UnlocalizedString &unlocalizedName,
size_t requiredSize,
size_t maxSize,
impl::GeneratorFunction displayGeneratorFunction,
std::optional<impl::EditingFunction> editingFunction = std::nullopt
);
} }
/* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */ /* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */
@@ -573,7 +655,7 @@ namespace hex {
void add(const Entry &entry); void add(const Entry &entry);
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
} }
@@ -616,8 +698,8 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, std::string> &getLanguages(); const std::map<std::string, std::string>& getLanguages();
std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>> &getLanguageDefinitions(); const std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>>& getLanguageDefinitions();
} }
@@ -626,11 +708,19 @@ namespace hex {
/* Interface Registry. Allows adding new items to various interfaces */ /* Interface Registry. Allows adding new items to various interfaces */
namespace Interface { namespace Interface {
struct Icon {
Icon(const char *glyph, ImGuiCustomCol color = ImGuiCustomCol(0x00)) : glyph(glyph), color(color) {}
std::string glyph;
ImGuiCustomCol color;
};
namespace impl { namespace impl {
using DrawCallback = std::function<void()>; using DrawCallback = std::function<void()>;
using MenuCallback = std::function<void()>; using MenuCallback = std::function<void()>;
using EnabledCallback = std::function<bool()>; using EnabledCallback = std::function<bool()>;
using SelectedCallback = std::function<bool()>;
using ClickCallback = std::function<void()>; using ClickCallback = std::function<void()>;
struct MainMenuItem { struct MainMenuItem {
@@ -639,10 +729,13 @@ namespace hex {
struct MenuItem { struct MenuItem {
std::vector<UnlocalizedString> unlocalizedNames; std::vector<UnlocalizedString> unlocalizedNames;
Icon icon;
std::unique_ptr<Shortcut> shortcut; std::unique_ptr<Shortcut> shortcut;
View *view; View *view;
MenuCallback callback; MenuCallback callback;
EnabledCallback enabledCallback; EnabledCallback enabledCallback;
SelectedCallback selectedCallback;
i32 toolbarIndex;
}; };
struct SidebarItem { struct SidebarItem {
@@ -660,14 +753,16 @@ namespace hex {
constexpr static auto SeparatorValue = "$SEPARATOR$"; constexpr static auto SeparatorValue = "$SEPARATOR$";
constexpr static auto SubMenuValue = "$SUBMENU$"; constexpr static auto SubMenuValue = "$SUBMENU$";
std::multimap<u32, MainMenuItem> &getMainMenuItems(); const std::multimap<u32, MainMenuItem>& getMainMenuItems();
std::multimap<u32, MenuItem> &getMenuItems();
std::vector<DrawCallback> &getWelcomeScreenEntries(); const std::multimap<u32, MenuItem>& getMenuItems();
std::vector<DrawCallback> &getFooterItems(); std::multimap<u32, MenuItem>& getMenuItemsMutable();
std::vector<DrawCallback> &getToolbarItems();
std::vector<SidebarItem> &getSidebarItems(); const std::vector<DrawCallback>& getWelcomeScreenEntries();
std::vector<TitleBarButton> &getTitleBarButtons(); const std::vector<DrawCallback>& getFooterItems();
const std::vector<DrawCallback>& getToolbarItems();
const std::vector<SidebarItem>& getSidebarItems();
const std::vector<TitleBarButton>& getTitlebarButtons();
} }
@@ -681,13 +776,63 @@ namespace hex {
/** /**
* @brief Adds a new main menu entry * @brief Adds a new main menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries * @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param icon The icon to use for the entry
* @param priority The priority of the entry. Lower values are displayed first * @param priority The priority of the entry. Lower values are displayed first
* @param shortcut The shortcut to use for the entry * @param shortcut The shortcut to use for the entry
* @param function The function to call when the entry is clicked * @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled * @param enabledCallback The function to call to determine if the entry is enabled
* @param view The view to use for the entry. If nullptr, the shortcut will work globally * @param view The view to use for the entry. If nullptr, the shortcut will work globally
*/ */
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr); void addMenuItem(
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
const Icon &icon,
u32 priority,
const Shortcut &shortcut,
const impl::MenuCallback &function,
const impl::EnabledCallback& enabledCallback, View *view
);
/**
* @brief Adds a new main menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param icon The icon to use for the entry
* @param priority The priority of the entry. Lower values are displayed first
* @param shortcut The shortcut to use for the entry
* @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled
* @param selectedCallback The function to call to determine if the entry is selected
* @param view The view to use for the entry. If nullptr, the shortcut will work globally
*/
void addMenuItem(
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
const Icon &icon,
u32 priority,
const Shortcut &shortcut,
const impl::MenuCallback &function,
const impl::EnabledCallback& enabledCallback = []{ return true; },
const impl::SelectedCallback &selectedCallback = []{ return false; },
View *view = nullptr
);
/**
* @brief Adds a new main menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param priority The priority of the entry. Lower values are displayed first
* @param shortcut The shortcut to use for the entry
* @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled
* @param selectedCallback The function to call to determine if the entry is selected
* @param view The view to use for the entry. If nullptr, the shortcut will work globally
*/
void addMenuItem(
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
u32 priority,
const Shortcut &shortcut,
const impl::MenuCallback &function,
const impl::EnabledCallback& enabledCallback = []{ return true; },
const impl::SelectedCallback &selectedCallback = []{ return false; },
View *view = nullptr
);
/** /**
* @brief Adds a new main menu sub-menu entry * @brief Adds a new main menu sub-menu entry
@@ -696,7 +841,29 @@ namespace hex {
* @param function The function to call when the entry is clicked * @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled * @param enabledCallback The function to call to determine if the entry is enabled
*/ */
void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }); void addMenuItemSubMenu(
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
u32 priority,
const impl::MenuCallback &function,
const impl::EnabledCallback& enabledCallback = []{ return true; }
);
/**
* @brief Adds a new main menu sub-menu entry
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
* @param icon The icon to use for the entry
* @param priority The priority of the entry. Lower values are displayed first
* @param function The function to call when the entry is clicked
* @param enabledCallback The function to call to determine if the entry is enabled
*/
void addMenuItemSubMenu(
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
const char *icon,
u32 priority,
const impl::MenuCallback &function,
const impl::EnabledCallback& enabledCallback = []{ return true; }
);
/** /**
* @brief Adds a new main menu separator * @brief Adds a new main menu separator
@@ -724,13 +891,24 @@ namespace hex {
*/ */
void addToolbarItem(const impl::DrawCallback &function); void addToolbarItem(const impl::DrawCallback &function);
/**
* @brief Adds a menu item to the toolbar
* @param unlocalizedName Unlocalized name of the menu item
* @param color Color of the toolbar icon
*/
void addMenuItemToToolbar(const UnlocalizedString &unlocalizedName, ImGuiCustomCol color);
/** /**
* @brief Adds a new sidebar item * @brief Adds a new sidebar item
* @param icon The icon to use for the item * @param icon The icon to use for the item
* @param function The function to call to draw the item * @param function The function to call to draw the item
* @param enabledCallback The function * @param enabledCallback The function
*/ */
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback = []{ return true; }); void addSidebarItem(
const std::string &icon,
const impl::DrawCallback &function,
const impl::EnabledCallback &enabledCallback = []{ return true; }
);
/** /**
* @brief Adds a new title bar button * @brief Adds a new title bar button
@@ -738,7 +916,11 @@ namespace hex {
* @param unlocalizedTooltip The unlocalized tooltip to use for the button * @param unlocalizedTooltip The unlocalized tooltip to use for the button
* @param function The function to call when the button is clicked * @param function The function to call when the button is clicked
*/ */
void addTitleBarButton(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function); void addTitleBarButton(
const std::string &icon,
const UnlocalizedString &unlocalizedTooltip,
const impl::ClickCallback &function
);
} }
@@ -749,10 +931,10 @@ namespace hex {
void addProviderName(const UnlocalizedString &unlocalizedName); void addProviderName(const UnlocalizedString &unlocalizedName);
using ProviderCreationFunction = prv::Provider*(*)(); using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
void add(const std::string &typeName, ProviderCreationFunction creationFunction); void add(const std::string &typeName, ProviderCreationFunction creationFunction);
std::vector<std::string> &getEntries(); const std::vector<std::string>& getEntries();
} }
@@ -765,8 +947,8 @@ namespace hex {
void add(bool addToList = true) { void add(bool addToList = true) {
auto typeName = T().getTypeName(); auto typeName = T().getTypeName();
impl::add(typeName, [] -> prv::Provider* { impl::add(typeName, [] -> std::unique_ptr<prv::Provider> {
return new T(); return std::make_unique<T>();
}); });
if (addToList) if (addToList)
@@ -786,7 +968,7 @@ namespace hex {
Callback callback; Callback callback;
}; };
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
} }
@@ -811,7 +993,7 @@ namespace hex {
Callback callback; Callback callback;
}; };
std::vector<Entry> &getEntries(); const std::vector<Entry>& getEntries();
} }
@@ -856,11 +1038,19 @@ namespace hex {
u16 m_maxCharsPerCell; u16 m_maxCharsPerCell;
}; };
struct MiniMapVisualizer {
using Callback = std::function<ImColor(const std::vector<u8>&)>;
UnlocalizedString unlocalizedName;
Callback callback;
};
namespace impl { namespace impl {
void addDataVisualizer(std::shared_ptr<DataVisualizer> &&visualizer); void addDataVisualizer(std::shared_ptr<DataVisualizer> &&visualizer);
std::vector<std::shared_ptr<DataVisualizer>> &getVisualizers(); const std::vector<std::shared_ptr<DataVisualizer>>& getVisualizers();
const std::vector<std::shared_ptr<MiniMapVisualizer>>& getMiniMapVisualizers();
} }
@@ -881,6 +1071,63 @@ namespace hex {
*/ */
std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName); std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName);
/**
* @brief Adds a new minimap visualizer
* @param unlocalizedName Unlocalized name of the minimap visualizer
* @param callback The callback that will be called to get the color of a line
*/
void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback);
}
/* Diffing Registry. Allows adding new diffing algorithms */
namespace Diffing {
enum class DifferenceType : u8 {
Match = 0,
Insertion = 1,
Deletion = 2,
Mismatch = 3
};
using DiffTree = wolv::container::IntervalTree<DifferenceType>;
class Algorithm {
public:
explicit Algorithm(UnlocalizedString unlocalizedName, UnlocalizedString unlocalizedDescription)
: m_unlocalizedName(std::move(unlocalizedName)),
m_unlocalizedDescription(std::move(unlocalizedDescription)) { }
virtual ~Algorithm() = default;
virtual std::vector<DiffTree> analyze(prv::Provider *providerA, prv::Provider *providerB) const = 0;
virtual void drawSettings() { }
const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }
private:
UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;
};
namespace impl {
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms();
void addAlgorithm(std::unique_ptr<Algorithm> &&hash);
}
/**
* @brief Adds a new hash
* @tparam T The hash type that extends hex::Hash
* @param args The arguments to pass to the constructor of the hash
*/
template<typename T, typename ... Args>
void addAlgorithm(Args && ... args) {
impl::addAlgorithm(std::make_unique<T>(std::forward<Args>(args)...));
}
} }
/* Hash Registry. Allows adding new hashes to the Hash view */ /* Hash Registry. Allows adding new hashes to the Hash view */
@@ -902,7 +1149,7 @@ namespace hex {
[[nodiscard]] Hash *getType() { return m_type; } [[nodiscard]] Hash *getType() { return m_type; }
[[nodiscard]] const Hash *getType() const { return m_type; } [[nodiscard]] const Hash *getType() const { return m_type; }
[[nodiscard]] const std::string &getName() const { return m_name; } [[nodiscard]] const std::string& getName() const { return m_name; }
const std::vector<u8>& get(const Region& region, prv::Provider *provider) { const std::vector<u8>& get(const Region& region, prv::Provider *provider) {
if (m_cache.empty()) { if (m_cache.empty()) {
@@ -930,7 +1177,7 @@ namespace hex {
[[nodiscard]] virtual nlohmann::json store() const = 0; [[nodiscard]] virtual nlohmann::json store() const = 0;
virtual void load(const nlohmann::json &json) = 0; virtual void load(const nlohmann::json &json) = 0;
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const { [[nodiscard]] const UnlocalizedString& getUnlocalizedName() const {
return m_unlocalizedName; return m_unlocalizedName;
} }
@@ -945,9 +1192,10 @@ namespace hex {
namespace impl { namespace impl {
std::vector<std::unique_ptr<Hash>> &getHashes(); const std::vector<std::unique_ptr<Hash>>& getHashes();
void add(std::unique_ptr<Hash> &&hash); void add(std::unique_ptr<Hash> &&hash);
} }
@@ -981,7 +1229,7 @@ namespace hex {
namespace impl { namespace impl {
using NetworkCallback = std::function<nlohmann::json(const nlohmann::json &)>; using NetworkCallback = std::function<nlohmann::json(const nlohmann::json &)>;
std::map<std::string, NetworkCallback> &getNetworkEndpoints(); const std::map<std::string, NetworkCallback>& getNetworkEndpoints();
} }
void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback); void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback);
@@ -998,15 +1246,21 @@ namespace hex {
bool enabled; bool enabled;
}; };
std::map<std::string, Experiment> &getExperiments(); const std::map<std::string, Experiment>& getExperiments();
} }
void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = ""); void addExperiment(
const std::string &experimentName,
const UnlocalizedString &unlocalizedName,
const UnlocalizedString &unlocalizedDescription = ""
);
void enableExperiement(const std::string &experimentName, bool enabled); void enableExperiement(const std::string &experimentName, bool enabled);
[[nodiscard]] bool isExperimentEnabled(const std::string &experimentName); [[nodiscard]] bool isExperimentEnabled(const std::string &experimentName);
} }
/* Reports Registry. Allows adding new sections to exported reports */
namespace Reports { namespace Reports {
namespace impl { namespace impl {
@@ -1017,13 +1271,80 @@ namespace hex {
Callback callback; Callback callback;
}; };
std::vector<ReportGenerator>& getGenerators(); const std::vector<ReportGenerator>& getGenerators();
} }
void addReportProvider(impl::Callback callback); void addReportProvider(impl::Callback callback);
} }
namespace DataInformation {
class InformationSection {
public:
InformationSection(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "", bool hasSettings = false)
: m_unlocalizedName(unlocalizedName), m_unlocalizedDescription(unlocalizedDescription),
m_hasSettings(hasSettings) { }
virtual ~InformationSection() = default;
[[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
[[nodiscard]] const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }
virtual void process(Task &task, prv::Provider *provider, Region region) = 0;
virtual void reset() = 0;
virtual void drawSettings() { }
virtual void drawContent() = 0;
[[nodiscard]] bool isValid() const { return m_valid; }
void markValid(bool valid = true) { m_valid = valid; }
[[nodiscard]] bool isEnabled() const { return m_enabled; }
void setEnabled(bool enabled) { m_enabled = enabled; }
[[nodiscard]] bool isAnalyzing() const { return m_analyzing; }
void setAnalyzing(bool analyzing) { m_analyzing = analyzing; }
virtual void load(const nlohmann::json &data) {
m_enabled = data.value<bool>("enabled", true);
}
[[nodiscard]] virtual nlohmann::json store() {
nlohmann::json data;
data["enabled"] = m_enabled.load();
return data;
}
[[nodiscard]] bool hasSettings() const { return m_hasSettings; }
private:
UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;
bool m_hasSettings;
std::atomic<bool> m_analyzing = false;
std::atomic<bool> m_valid = false;
std::atomic<bool> m_enabled = true;
};
namespace impl {
using CreateCallback = std::function<std::unique_ptr<InformationSection>()>;
const std::vector<CreateCallback>& getInformationSectionConstructors();
void addInformationSectionCreator(const CreateCallback &callback);
}
template<typename T>
void addInformationSection(auto && ...args) {
impl::addInformationSectionCreator([args...] {
return std::make_unique<T>(std::forward<decltype(args)>(args)...);
});
}
}
} }
} }

View File

@@ -3,12 +3,12 @@
#include <hex.hpp> #include <hex.hpp>
#include <list> #include <list>
#include <mutex>
#include <map> #include <map>
#include <string_view> #include <string_view>
#include <functional> #include <functional>
#include <hex/api/imhex_api.hpp> #include <hex/api/imhex_api.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <wolv/types/type_name.hpp> #include <wolv/types/type_name.hpp>
@@ -23,7 +23,7 @@
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, function); } \ 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(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(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__) #define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
@@ -37,6 +37,7 @@ namespace hex {
class View; class View;
} }
namespace pl::ptrn { class Pattern; }
namespace hex { namespace hex {
@@ -46,7 +47,7 @@ namespace hex {
public: public:
explicit constexpr EventId(const char *eventName) { explicit constexpr EventId(const char *eventName) {
m_hash = 0x811C'9DC5; m_hash = 0x811C'9DC5;
for (auto c : std::string_view(eventName)) { for (const char c : std::string_view(eventName)) {
m_hash = (m_hash >> 5) | (m_hash << 27); m_hash = (m_hash >> 5) | (m_hash << 27);
m_hash ^= c; m_hash ^= c;
} }
@@ -62,6 +63,7 @@ namespace hex {
struct EventBase { struct EventBase {
EventBase() noexcept = default; EventBase() noexcept = default;
virtual ~EventBase() = default;
}; };
template<typename... Params> template<typename... Params>
@@ -70,8 +72,13 @@ namespace hex {
explicit Event(Callback func) noexcept : m_func(std::move(func)) { } 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 {
m_func(params...); try {
m_func(params...);
} catch (const std::exception &e) {
log::error("An exception occurred while handling event {}: {}", eventName, e.what());
throw;
}
} }
private: private:
@@ -118,7 +125,7 @@ namespace hex {
if (getTokenStore().contains(token)) { if (getTokenStore().contains(token)) {
auto&& [begin, end] = getTokenStore().equal_range(token); auto&& [begin, end] = getTokenStore().equal_range(token);
auto eventRegistered = std::any_of(begin, end, [&](auto &item) { const auto eventRegistered = std::any_of(begin, end, [&](auto &item) {
return item.second->first == E::Id; return item.second->first == E::Id;
}); });
if (eventRegistered) { if (eventRegistered) {
@@ -167,16 +174,12 @@ namespace hex {
* @param args Arguments to pass to the event * @param args Arguments to pass to the event
*/ */
template<impl::EventType E> template<impl::EventType E>
static void post(auto &&...args) noexcept { static void post(auto && ...args) {
std::scoped_lock lock(getEventMutex()); std::scoped_lock lock(getEventMutex());
for (const auto &[id, event] : getEvents()) { for (const auto &[id, event] : getEvents()) {
if (id == E::Id) { if (id == E::Id) {
try { (*static_cast<E *const>(event.get()))(wolv::type::getTypeName<E>(), std::forward<decltype(args)>(args)...);
(*static_cast<E *const>(event.get()))(std::forward<decltype(args)>(args)...);
} catch (const std::exception &e) {
log::error("Event '{}' threw {}: {}", wolv::type::getTypeName<decltype(e)>(), wolv::type::getTypeName<E>(), e.what());
}
} }
} }
@@ -210,14 +213,14 @@ namespace hex {
EVENT_DEF(EventImHexStartupFinished); EVENT_DEF(EventImHexStartupFinished);
EVENT_DEF(EventFileLoaded, std::fs::path); EVENT_DEF(EventFileLoaded, std::fs::path);
EVENT_DEF(EventDataChanged); EVENT_DEF(EventDataChanged, prv::Provider *);
EVENT_DEF(EventHighlightingChanged); EVENT_DEF(EventHighlightingChanged);
EVENT_DEF(EventWindowClosing, GLFWwindow *); EVENT_DEF(EventWindowClosing, GLFWwindow *);
EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion); EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion);
EVENT_DEF(EventSettingsChanged);
EVENT_DEF(EventAbnormalTermination, int); EVENT_DEF(EventAbnormalTermination, int);
EVENT_DEF(EventThemeChanged); EVENT_DEF(EventThemeChanged);
EVENT_DEF(EventOSThemeChanged); EVENT_DEF(EventOSThemeChanged);
EVENT_DEF(EventWindowFocused, bool);
/** /**
* @brief Called when the provider is created. * @brief Called when the provider is created.
@@ -241,6 +244,7 @@ namespace hex {
EVENT_DEF(EventProviderDeleted, prv::Provider *); EVENT_DEF(EventProviderDeleted, prv::Provider *);
EVENT_DEF(EventProviderSaved, prv::Provider *); EVENT_DEF(EventProviderSaved, prv::Provider *);
EVENT_DEF(EventWindowInitialized); EVENT_DEF(EventWindowInitialized);
EVENT_DEF(EventWindowDeinitializing, GLFWwindow *);
EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&); EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&);
EVENT_DEF(EventPatchCreated, u64, u8, u8); EVENT_DEF(EventPatchCreated, u64, u8, u8);
EVENT_DEF(EventPatternEvaluating); EVENT_DEF(EventPatternEvaluating);
@@ -252,6 +256,10 @@ namespace hex {
EVENT_DEF(EventAchievementUnlocked, const Achievement&); EVENT_DEF(EventAchievementUnlocked, const Achievement&);
EVENT_DEF(EventSearchBoxClicked, u32); EVENT_DEF(EventSearchBoxClicked, u32);
EVENT_DEF(EventViewOpened, View*); EVENT_DEF(EventViewOpened, View*);
EVENT_DEF(EventFirstLaunch);
EVENT_DEF(EventFileDragged, bool);
EVENT_DEF(EventFileDropped, std::fs::path);
EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*); EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*);
EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64); EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64);
@@ -269,7 +277,9 @@ namespace hex {
EVENT_DEF(RequestAddInitTask, std::string, bool, std::function<bool()>); EVENT_DEF(RequestAddInitTask, std::string, bool, std::function<bool()>);
EVENT_DEF(RequestAddExitTask, std::string, std::function<bool()>); EVENT_DEF(RequestAddExitTask, std::string, std::function<bool()>);
EVENT_DEF(RequestOpenWindow, std::string); EVENT_DEF(RequestOpenWindow, std::string);
EVENT_DEF(RequestSelectionChange, Region); EVENT_DEF(RequestHexEditorSelectionChange, Region);
EVENT_DEF(RequestPatternEditorSelectionChange, u32, u32);
EVENT_DEF(RequestJumpToPattern, const pl::ptrn::Pattern*);
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*); EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*);
EVENT_DEF(RequestRemoveBookmark, u64); EVENT_DEF(RequestRemoveBookmark, u64);
EVENT_DEF(RequestSetPatternLanguageCode, std::string); EVENT_DEF(RequestSetPatternLanguageCode, std::string);
@@ -282,6 +292,7 @@ namespace hex {
EVENT_DEF(RequestOpenFile, std::fs::path); EVENT_DEF(RequestOpenFile, std::fs::path);
EVENT_DEF(RequestChangeTheme, std::string); EVENT_DEF(RequestChangeTheme, std::string);
EVENT_DEF(RequestOpenPopup, std::string); EVENT_DEF(RequestOpenPopup, std::string);
EVENT_DEF(RequestAddVirtualFile, std::fs::path, std::vector<u8>, Region);
/** /**
* @brief Creates a provider from it's unlocalized name, and add it to the provider list * @brief Creates a provider from it's unlocalized name, and add it to the provider list
@@ -299,4 +310,9 @@ namespace hex {
* The 'from' provider should not have any per provider data after this, and should be immediately deleted * The 'from' provider should not have any per provider data after this, and should be immediately deleted
*/ */
EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *); 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

@@ -15,9 +15,14 @@ using ImGuiID = unsigned int;
struct ImVec2; struct ImVec2;
struct ImFontAtlas; struct ImFontAtlas;
struct ImFont; struct ImFont;
struct GLFWwindow;
namespace hex { namespace hex {
namespace impl {
class AutoResetBase;
}
namespace prv { namespace prv {
class Provider; class Provider;
} }
@@ -34,8 +39,8 @@ namespace hex {
Highlighting() = default; Highlighting() = default;
Highlighting(Region region, color_t color); Highlighting(Region region, color_t color);
[[nodiscard]] const Region &getRegion() const { return m_region; } [[nodiscard]] const Region& getRegion() const { return m_region; }
[[nodiscard]] const color_t &getColor() const { return m_color; } [[nodiscard]] const color_t& getColor() const { return m_color; }
private: private:
Region m_region = {}; Region m_region = {};
@@ -47,9 +52,9 @@ namespace hex {
Tooltip() = default; Tooltip() = default;
Tooltip(Region region, std::string value, color_t color); Tooltip(Region region, std::string value, color_t color);
[[nodiscard]] const Region &getRegion() const { return m_region; } [[nodiscard]] const Region& getRegion() const { return m_region; }
[[nodiscard]] const color_t &getColor() const { return m_color; } [[nodiscard]] const color_t& getColor() const { return m_color; }
[[nodiscard]] const std::string &getValue() const { return m_value; } [[nodiscard]] const std::string& getValue() const { return m_value; }
private: private:
Region m_region = {}; Region m_region = {};
@@ -68,15 +73,18 @@ namespace hex {
namespace impl { namespace impl {
using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>; using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>;
using HoveringFunction = std::function<bool(const prv::Provider *, u64, const u8*, size_t)>;
std::map<u32, Highlighting> &getBackgroundHighlights(); const std::map<u32, Highlighting>& getBackgroundHighlights();
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions(); const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions();
std::map<u32, Highlighting> &getForegroundHighlights(); const std::map<u32, Highlighting>& getForegroundHighlights();
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions(); const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions();
std::map<u32, Tooltip> &getTooltips(); const std::map<u32, HoveringFunction>& getHoveringFunctions();
std::map<u32, TooltipFunction> &getTooltipFunctions(); const std::map<u32, Tooltip>& getTooltips();
const std::map<u32, TooltipFunction>& getTooltipFunctions();
void setCurrentSelection(const std::optional<ProviderRegion> &region); void setCurrentSelection(const std::optional<ProviderRegion> &region);
void setHoveredRegion(const prv::Provider *provider, const Region &region);
} }
/** /**
@@ -165,6 +173,19 @@ namespace hex {
*/ */
void removeForegroundHighlightingProvider(u32 id); void removeForegroundHighlightingProvider(u32 id);
/**
* @brief Adds a hovering provider to the Hex Editor using a callback function
* @param function Function that draws the highlighting based on the hovered region
* @return Unique ID used to remove the highlighting again later
*/
u32 addHoverHighlightProvider(const impl::HoveringFunction &function);
/**
* @brief Removes a hovering color highlighting from the Hex Editor
* @param id The ID of the highlighting to remove
*/
void removeHoverHighlightProvider(u32 id);
/** /**
* @brief Checks if there's a valid selection in the Hex Editor right now * @brief Checks if there's a valid selection in the Hex Editor right now
*/ */
@@ -202,6 +223,20 @@ namespace hex {
*/ */
void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr); void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr);
/**
* @brief Adds a virtual file to the list in the Hex Editor
* @param path The path of the file
* @param data The data of the file
* @param region The location of the file in the Hex Editor if available
*/
void addVirtualFile(const std::fs::path &path, std::vector<u8> data, Region region = Region::Invalid());
/**
* @brief Gets the currently hovered cell region in the Hex Editor
* @return
*/
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider);
} }
/* Functions to interact with Bookmarks */ /* Functions to interact with Bookmarks */
@@ -270,13 +305,19 @@ namespace hex {
* @brief Gets a list of all currently loaded data providers * @brief Gets a list of all currently loaded data providers
* @return The currently loaded data providers * @return The currently loaded data providers
*/ */
const std::vector<prv::Provider *> &getProviders(); std::vector<prv::Provider*> getProviders();
/** /**
* @brief Sets the currently selected data provider * @brief Sets the currently selected data provider
* @param index Index of the provider to select * @param index Index of the provider to select
*/ */
void setCurrentProvider(u32 index); void setCurrentProvider(i64 index);
/**
* @brief Sets the currently selected data provider
* @param provider The provider to select
*/
void setCurrentProvider(NonNull<prv::Provider*> provider);
/** /**
* @brief Gets the index of the currently selected data provider * @brief Gets the index of the currently selected data provider
@@ -314,7 +355,7 @@ namespace hex {
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation) * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
* @param select Whether to select the provider after adding it * @param select Whether to select the provider after adding it
*/ */
void add(prv::Provider *provider, bool skipLoadInterface = false, bool select = true); void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
/** /**
* @brief Creates a new provider and adds it to the list of providers * @brief Creates a new provider and adds it to the list of providers
@@ -323,7 +364,7 @@ namespace hex {
*/ */
template<std::derived_from<prv::Provider> T> template<std::derived_from<prv::Provider> T>
void add(auto &&...args) { void add(auto &&...args) {
add(new T(std::forward<decltype(args)>(args)...)); add(std::make_unique<T>(std::forward<decltype(args)>(args)...));
} }
/** /**
@@ -339,42 +380,29 @@ namespace hex {
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation) * @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
* @param select Whether to select the provider after adding it * @param select Whether to select the provider after adding it
*/ */
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface = false, bool select = true); prv::Provider* createProvider(
const UnlocalizedString &unlocalizedName,
bool skipLoadInterface = false,
bool select = true
);
} }
/* Functions to interact with various ImHex system settings */ /* Functions to interact with various ImHex system settings */
namespace System { namespace System {
bool isMainInstance();
namespace impl {
void setMainInstanceStatus(bool status);
void setMainWindowPosition(i32 x, i32 y);
void setMainWindowSize(u32 width, u32 height);
void setMainDockSpaceId(ImGuiID id);
void setGlobalScale(float scale);
void setNativeScale(float scale);
void setBorderlessWindowMode(bool enabled);
void setGPUVendor(const std::string &vendor);
void setPortableVersion(bool enabled);
void addInitArgument(const std::string &key, const std::string &value = { });
void setLastFrameTime(double time);
}
struct ProgramArguments { struct ProgramArguments {
int argc; int argc;
char **argv; char **argv;
char **envp; char **envp;
}; };
struct InitialWindowProperties {
i32 x, y;
u32 width, height;
bool maximized;
};
enum class TaskProgressState { enum class TaskProgressState {
Reset, Reset,
Progress, Progress,
@@ -387,6 +415,35 @@ namespace hex {
Error Error
}; };
namespace impl {
void setMainInstanceStatus(bool status);
void setMainWindowPosition(i32 x, i32 y);
void setMainWindowSize(u32 width, u32 height);
void setMainDockSpaceId(ImGuiID id);
void setMainWindowHandle(GLFWwindow *window);
void setGlobalScale(float scale);
void setNativeScale(float scale);
void setBorderlessWindowMode(bool enabled);
void setMultiWindowMode(bool enabled);
void setInitialWindowProperties(InitialWindowProperties properties);
void setGPUVendor(const std::string &vendor);
void addInitArgument(const std::string &key, const std::string &value = { });
void setLastFrameTime(double time);
bool isWindowResizable();
void addAutoResetObject(hex::impl::AutoResetBase *object);
void cleanup();
}
/** /**
* @brief Closes ImHex * @brief Closes ImHex
* @param noQuestions Whether to skip asking the user for confirmation * @param noQuestions Whether to skip asking the user for confirmation
@@ -451,6 +508,11 @@ namespace hex {
*/ */
ImGuiID getMainDockSpaceId(); ImGuiID getMainDockSpaceId();
/**
* @brief Gets the main window's GLFW window handle
* @return GLFW window handle
*/
GLFWwindow* getMainWindowHandle();
/** /**
* @brief Checks if borderless window mode is enabled currently * @brief Checks if borderless window mode is enabled currently
@@ -458,11 +520,24 @@ namespace hex {
*/ */
bool isBorderlessWindowModeEnabled(); bool isBorderlessWindowModeEnabled();
/**
* @brief Checks if multi-window mode is enabled currently
* @return Whether multi-window mode is enabled
*/
bool isMutliWindowModeEnabled();
/** /**
* @brief Gets the init arguments passed to ImHex from the splash screen * @brief Gets the init arguments passed to ImHex from the splash screen
* @return Init arguments * @return Init arguments
*/ */
std::map<std::string, std::string> &getInitArguments(); const std::map<std::string, std::string>& getInitArguments();
/**
* @brief Gets a init arguments passed to ImHex from the splash screen
* @param key The key of the init argument
* @return Init argument
*/
std::string getInitArgument(const std::string &key);
/** /**
* @brief Sets if ImHex should follow the system theme * @brief Sets if ImHex should follow the system theme
@@ -481,7 +556,7 @@ namespace hex {
* @brief Gets the currently set additional folder paths * @brief Gets the currently set additional folder paths
* @return The currently set additional folder paths * @return The currently set additional folder paths
*/ */
std::vector<std::filesystem::path> &getAdditionalFolderPaths(); const std::vector<std::filesystem::path>& getAdditionalFolderPaths();
/** /**
* @brief Sets the additional folder paths * @brief Sets the additional folder paths
@@ -494,7 +569,7 @@ namespace hex {
* @brief Gets the current GPU vendor * @brief Gets the current GPU vendor
* @return The current GPU vendor * @return The current GPU vendor
*/ */
const std::string &getGPUVendor(); const std::string& getGPUVendor();
/** /**
* @brief Checks if ImHex is running in portable mode * @brief Checks if ImHex is running in portable mode
@@ -557,9 +632,44 @@ namespace hex {
*/ */
bool updateImHex(UpdateType updateType); bool updateImHex(UpdateType updateType);
/**
* @brief Add a new startup task that will be run while ImHex's splash screen is shown
* @param name Name to be shown in the UI
* @param async Whether to run the task asynchronously
* @param function The function to run
*/
void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function); void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function);
/**
* @brief Gets the time the previous frame took
* @return Previous frame time
*/
double getLastFrameTime(); double getLastFrameTime();
/**
* @brief Sets the window resizable
* @param resizable Whether the window should be resizable
*/
void setWindowResizable(bool resizable);
/**
* @brief Checks if this window is the main instance of ImHex
* @return True if this is the main instance, false if another instance is already running
*/
bool isMainInstance();
/**
* @brief Gets the initial window properties
* @return Initial window properties
*/
std::optional<InitialWindowProperties> getInitialWindowProperties();
/**
* @brief Gets the module handle of libimhex
* @return Module handle
*/
void* getLibImHexModuleHandle();
} }
/** /**
@@ -569,11 +679,12 @@ namespace hex {
namespace Messaging { namespace Messaging {
namespace impl { namespace impl {
using MessagingHandler = std::function<void(const std::vector<u8> &)>; using MessagingHandler = std::function<void(const std::vector<u8> &)>;
std::map<std::string, MessagingHandler> &getHandlers(); const std::map<std::string, MessagingHandler>& getHandlers();
void runHandler(const std::string &eventName, const std::vector<u8> &args); void runHandler(const std::string &eventName, const std::vector<u8> &args);
} }
/** /**
@@ -593,11 +704,12 @@ namespace hex {
std::vector<GlyphRange> glyphRanges; std::vector<GlyphRange> glyphRanges;
Offset offset; Offset offset;
u32 flags; u32 flags;
std::optional<u32> defaultSize;
}; };
namespace impl { namespace impl {
std::vector<Font>& getFonts(); const std::vector<Font>& getFonts();
void setCustomFontPath(const std::fs::path &path); void setCustomFontPath(const std::fs::path &path);
void setFontSize(float size); void setFontSize(float size);
@@ -611,8 +723,8 @@ namespace hex {
GlyphRange range(const char *glyphBegin, const char *glyphEnd); GlyphRange range(const char *glyphBegin, const char *glyphEnd);
GlyphRange range(u32 codepointBegin, u32 codepointEnd); 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::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); 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; constexpr static float DefaultFontSize = 13.0;
@@ -623,7 +735,7 @@ namespace hex {
* @brief Gets the current custom font path * @brief Gets the current custom font path
* @return The current custom font path * @return The current custom font path
*/ */
std::filesystem::path &getCustomFontPath(); const std::filesystem::path& getCustomFontPath();
/** /**
* @brief Gets the current font size * @brief Gets the current font size

View File

@@ -4,6 +4,8 @@
#include <string> #include <string>
struct ImGuiTextBuffer;
namespace hex { namespace hex {
class LayoutManager { class LayoutManager {
@@ -13,6 +15,9 @@ namespace hex {
std::fs::path path; std::fs::path path;
}; };
using LoadCallback = std::function<void(std::string_view)>;
using StoreCallback = std::function<void(ImGuiTextBuffer *)>;
/** /**
* @brief Save the current layout * @brief Save the current layout
* @param name Name of the layout * @param name Name of the layout
@@ -41,7 +46,13 @@ namespace hex {
* @brief Get a list of all layouts * @brief Get a list of all layouts
* @return List of all added layouts * @return List of all added layouts
*/ */
static std::vector<Layout> getLayouts(); static const std::vector<Layout> &getLayouts();
/**
* @brief Removes the layout with the given name
* @param name Name of the layout
*/
static void removeLayout(const std::string &name);
/** /**
* @brief Handles loading of layouts if needed * @brief Handles loading of layouts if needed
@@ -76,6 +87,12 @@ namespace hex {
*/ */
static void closeAllViews(); static void closeAllViews();
static void registerLoadCallback(const LoadCallback &callback);
static void registerStoreCallback(const StoreCallback &callback);
static void onStore(ImGuiTextBuffer *buffer);
static void onLoad(std::string_view line);
private: private:
LayoutManager() = default; LayoutManager() = default;
}; };

View File

@@ -3,6 +3,7 @@
#include <map> #include <map>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <vector>
#include <fmt/format.h> #include <fmt/format.h>
@@ -26,6 +27,7 @@ namespace hex {
} }
void loadLanguage(const std::string &language); void loadLanguage(const std::string &language);
std::string getLocalizedString(const std::string &unlocalizedString, const std::string &language = "");
[[nodiscard]] const std::map<std::string, std::string> &getSupportedLanguages(); [[nodiscard]] const std::map<std::string, std::string> &getSupportedLanguages();
[[nodiscard]] const std::string &getFallbackLanguage(); [[nodiscard]] const std::string &getFallbackLanguage();

View File

@@ -1,19 +1,24 @@
#pragma once #pragma once
#include <functional> #include <functional>
#include <list>
#include <span> #include <span>
#include <string> #include <string>
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/auto_reset.hpp>
struct ImGuiContext; struct ImGuiContext;
namespace hex { namespace hex {
struct SubCommand { struct SubCommand {
std::string commandKey; std::string commandLong;
std::string commandDesc; std::string commandShort;
std::string commandDescription;
std::function<void(const std::vector<std::string>&)> callback; std::function<void(const std::vector<std::string>&)> callback;
}; };
@@ -26,22 +31,23 @@ namespace hex {
using InitializePluginFunc = void (*)(); using InitializePluginFunc = void (*)();
using InitializeLibraryFunc = void (*)(); using InitializeLibraryFunc = void (*)();
using GetPluginNameFunc = const char *(*)(); using GetPluginNameFunc = const char *(*)();
using GetLibraryNameFunc = const char *(*)();
using GetPluginAuthorFunc = const char *(*)(); using GetPluginAuthorFunc = const char *(*)();
using GetPluginDescriptionFunc = const char *(*)(); using GetPluginDescriptionFunc = const char *(*)();
using GetCompatibleVersionFunc = const char *(*)(); using GetCompatibleVersionFunc = const char *(*)();
using SetImGuiContextFunc = void (*)(ImGuiContext *); using SetImGuiContextFunc = void (*)(ImGuiContext *);
using IsBuiltinPluginFunc = bool (*)();
using GetSubCommandsFunc = void* (*)(); using GetSubCommandsFunc = void* (*)();
using GetFeaturesFunc = void* (*)(); using GetFeaturesFunc = void* (*)();
InitializePluginFunc initializePluginFunction = nullptr; InitializePluginFunc initializePluginFunction = nullptr;
InitializeLibraryFunc initializeLibraryFunction = nullptr; InitializeLibraryFunc initializeLibraryFunction = nullptr;
GetPluginNameFunc getPluginNameFunction = nullptr; GetPluginNameFunc getPluginNameFunction = nullptr;
GetLibraryNameFunc getLibraryNameFunction = nullptr;
GetPluginAuthorFunc getPluginAuthorFunction = nullptr; GetPluginAuthorFunc getPluginAuthorFunction = nullptr;
GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr; GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr;
GetCompatibleVersionFunc getCompatibleVersionFunction = nullptr; GetCompatibleVersionFunc getCompatibleVersionFunction = nullptr;
SetImGuiContextFunc setImGuiContextFunction = nullptr; SetImGuiContextFunc setImGuiContextFunction = nullptr;
IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr; SetImGuiContextFunc setImGuiContextLibraryFunction = nullptr;
GetSubCommandsFunc getSubCommandsFunction = nullptr; GetSubCommandsFunc getSubCommandsFunction = nullptr;
GetFeaturesFunc getFeaturesFunction = nullptr; GetFeaturesFunc getFeaturesFunction = nullptr;
}; };
@@ -49,7 +55,7 @@ namespace hex {
class Plugin { class Plugin {
public: public:
explicit Plugin(const std::fs::path &path); explicit Plugin(const std::fs::path &path);
explicit Plugin(const PluginFunctions &functions); explicit Plugin(const std::string &name, const PluginFunctions &functions);
Plugin(const Plugin &) = delete; Plugin(const Plugin &) = delete;
Plugin(Plugin &&other) noexcept; Plugin(Plugin &&other) noexcept;
@@ -64,7 +70,6 @@ namespace hex {
[[nodiscard]] std::string getPluginDescription() const; [[nodiscard]] std::string getPluginDescription() const;
[[nodiscard]] std::string getCompatibleVersion() const; [[nodiscard]] std::string getCompatibleVersion() const;
void setImGuiContext(ImGuiContext *ctx) const; void setImGuiContext(ImGuiContext *ctx) const;
[[nodiscard]] bool isBuiltinPlugin() const;
[[nodiscard]] const std::fs::path &getPath() const; [[nodiscard]] const std::fs::path &getPath() const;
@@ -76,11 +81,14 @@ namespace hex {
[[nodiscard]] bool isLibraryPlugin() const; [[nodiscard]] bool isLibraryPlugin() const;
[[nodiscard]] bool wasAddedManually() const;
private: private:
uintptr_t m_handle = 0; uintptr_t m_handle = 0;
std::fs::path m_path; std::fs::path m_path;
mutable bool m_initialized = false; mutable bool m_initialized = false;
bool m_addedManually = false;
PluginFunctions m_functions = {}; PluginFunctions m_functions = {};
@@ -96,14 +104,31 @@ namespace hex {
public: public:
PluginManager() = delete; PluginManager() = delete;
static bool load();
static bool load(const std::fs::path &pluginFolder); static bool load(const std::fs::path &pluginFolder);
static bool loadLibraries();
static bool loadLibraries(const std::fs::path &libraryFolder);
static void unload(); static void unload();
static void reload(); static void reload();
static void initializeNewPlugins();
static void addLoadPath(const std::fs::path &path);
static void addPlugin(PluginFunctions functions); static void addPlugin(const std::string &name, PluginFunctions functions);
static std::vector<Plugin> &getPlugins(); static Plugin* getPlugin(const std::string &name);
static std::vector<std::fs::path> &getPluginPaths(); static const std::list<Plugin>& getPlugins();
static const std::vector<std::fs::path>& getPluginPaths();
static const std::vector<std::fs::path>& getPluginLoadPaths();
static bool isPluginLoaded(const std::fs::path &path);
private:
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

@@ -93,30 +93,26 @@ namespace hex {
* *
* @param handler The handler to register * @param handler The handler to register
*/ */
static void registerHandler(const Handler &handler) { static void registerHandler(const Handler &handler);
getHandlers().push_back(handler);
}
/** /**
* @brief Register a handler for storing and loading per-provider data from a project file * @brief Register a handler for storing and loading per-provider data from a project file
* *
* @param handler The handler to register * @param handler The handler to register
*/ */
static void registerPerProviderHandler(const ProviderHandler &handler) { static void registerPerProviderHandler(const ProviderHandler &handler);
getProviderHandlers().push_back(handler);
}
/** /**
* @brief Get the list of registered handlers * @brief Get the list of registered handlers
* @return List of registered handlers * @return List of registered handlers
*/ */
static std::vector<Handler>& getHandlers(); static const std::vector<Handler>& getHandlers();
/** /**
* @brief Get the list of registered per-provider handlers * @brief Get the list of registered per-provider handlers
* @return List of registered per-provider handlers * @return List of registered per-provider handlers
*/ */
static std::vector<ProviderHandler>& getProviderHandlers(); static const std::vector<ProviderHandler>& getProviderHandlers();
private: private:
ProjectFile() = default; ProjectFile() = default;

View File

@@ -425,7 +425,7 @@ namespace hex {
* @param focused Whether the current view is focused * @param focused Whether the current view is focused
* @param keyCode The key code of the key that was pressed * @param keyCode The key code of the key that was pressed
*/ */
static void process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode); static void process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
/** /**
* @brief Process a key event. This should be called from the main loop. * @brief Process a key event. This should be called from the main loop.

View File

@@ -9,6 +9,7 @@
#include <memory> #include <memory>
#include <list> #include <list>
#include <condition_variable> #include <condition_variable>
#include <source_location>
namespace hex { namespace hex {
@@ -31,7 +32,9 @@ namespace hex {
* @brief Updates the current process value of the task * @brief Updates the current process value of the task
* @param value Current value * @param value Current value
*/ */
void update(u64 value = 0); void update(u64 value);
void update() const;
void increment();
/** /**
* @brief Sets the maximum value of the task * @brief Sets the maximum value of the task
@@ -148,18 +151,42 @@ namespace hex {
*/ */
static void doLater(const std::function<void()> &function); static void doLater(const std::function<void()> &function);
/**
* @brief Creates a new synchronous task that will execute the given function at the start of the next frame
* @param function Function to be executed
* @param location Source location of the function call. This is used to make sure repeated calls to the function at the same location are only executed once
*/
static void doLaterOnce(const std::function<void()> &function, std::source_location location = std::source_location::current());
/** /**
* @brief Creates a callback that will be executed when all tasks are finished * @brief Creates a callback that will be executed when all tasks are finished
* @param function Function to be executed * @param function Function to be executed
*/ */
static void runWhenTasksFinished(const std::function<void()> &function); static void runWhenTasksFinished(const std::function<void()> &function);
/**
* @brief Sets the name of the current thread
* @param name Name of the thread
*/
static void setCurrentThreadName(const std::string &name);
/**
* @brief Gets the name of the current thread
* @return Name of the thread
*/
static std::string getCurrentThreadName();
/**
* @brief Cleans up finished tasks
*/
static void collectGarbage(); static void collectGarbage();
static Task& getCurrentTask();
static size_t getRunningTaskCount(); static size_t getRunningTaskCount();
static size_t getRunningBackgroundTaskCount(); static size_t getRunningBackgroundTaskCount();
static std::list<std::shared_ptr<Task>> &getRunningTasks(); static const std::list<std::shared_ptr<Task>>& getRunningTasks();
static void runDeferredCalls(); static void runDeferredCalls();
private: private:

View File

@@ -78,8 +78,8 @@ namespace hex {
StyleMap styleMap; StyleMap styleMap;
}; };
static std::map<std::string, ThemeHandler>& getThemeHandlers(); static const std::map<std::string, ThemeHandler>& getThemeHandlers();
static std::map<std::string, StyleHandler>& getStyleHandlers(); static const std::map<std::string, StyleHandler>& getStyleHandlers();
private: private:
ThemeManager() = default; ThemeManager() = default;

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <map> #include <map>
#include <string> #include <string>
@@ -12,26 +13,30 @@ namespace hex {
struct Workspace { struct Workspace {
std::string layout; std::string layout;
std::fs::path path; std::fs::path path;
bool builtin;
}; };
static void createWorkspace(const std::string &name, const std::string &layout = ""); static void createWorkspace(const std::string &name, const std::string &layout = "");
static void switchWorkspace(const std::string &name); static void switchWorkspace(const std::string &name);
static void importFromFile(const std::fs::path &path); static void importFromFile(const std::fs::path &path);
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}); static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false);
static const auto& getWorkspaces() { return s_workspaces; } static void removeWorkspace(const std::string &name);
static const auto& getWorkspaces() { return *s_workspaces; }
static const auto& getCurrentWorkspace() { return s_currentWorkspace; } static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
static void reset(); static void reset();
static void reload();
static void process(); static void process();
private: private:
WorkspaceManager() = default; WorkspaceManager() = default;
static std::map<std::string, Workspace> s_workspaces; static AutoReset<std::map<std::string, Workspace>> s_workspaces;
static decltype(s_workspaces)::iterator s_currentWorkspace, s_previousWorkspace; static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace, s_workspaceToRemove;
}; };
} }

View File

@@ -0,0 +1,77 @@
#pragma once
#include <hex/api/event_manager.hpp>
#include <hex/api/imhex_api.hpp>
namespace hex {
namespace impl {
class AutoResetBase {
public:
virtual ~AutoResetBase() = default;
virtual void reset() = 0;
};
}
template<typename T>
class AutoReset : public impl::AutoResetBase {
public:
using Type = T;
AutoReset() {
ImHexApi::System::impl::addAutoResetObject(this);
}
T* operator->() {
return &m_value;
}
const T* operator->() const {
return &m_value;
}
T& operator*() {
return m_value;
}
const T& operator*() const {
return m_value;
}
operator T&() {
return m_value;
}
operator const T&() const {
return m_value;
}
T& operator=(const T &value) {
m_value = value;
return m_value;
}
T& operator=(T &&value) noexcept {
m_value = std::move(value);
return m_value;
}
void reset() override {
if constexpr (requires { m_value.reset(); }) {
m_value.reset();
} else if constexpr (requires { m_value.clear(); }) {
m_value.clear();
} else if constexpr (requires(T t) { t = nullptr; }) {
m_value = nullptr;
} else {
m_value = { };
}
}
private:
T m_value;
};
}

View File

@@ -28,8 +28,9 @@ namespace hex {
EncodingFile& operator=(EncodingFile &&other) noexcept; EncodingFile& operator=(EncodingFile &&other) noexcept;
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<u8> buffer) const; [[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<u8> buffer) const;
[[nodiscard]] size_t getEncodingLengthFor(std::span<u8> buffer) const; [[nodiscard]] u64 getEncodingLengthFor(std::span<u8> buffer) const;
[[nodiscard]] size_t getLongestSequence() const { return m_longestSequence; } [[nodiscard]] u64 getShortestSequence() const { return m_shortestSequence; }
[[nodiscard]] u64 getLongestSequence() const { return m_longestSequence; }
[[nodiscard]] bool valid() const { return m_valid; } [[nodiscard]] bool valid() const { return m_valid; }
@@ -45,7 +46,9 @@ namespace hex {
std::string m_name; std::string m_name;
std::string m_tableContent; std::string m_tableContent;
std::unique_ptr<std::map<size_t, std::map<std::vector<u8>, std::string>>> m_mapping; std::unique_ptr<std::map<size_t, std::map<std::vector<u8>, std::string>>> m_mapping;
size_t m_longestSequence = 0;
u64 m_shortestSequence = std::numeric_limits<u64>::max();
u64 m_longestSequence = std::numeric_limits<u64>::min();
}; };
} }

View File

@@ -37,6 +37,7 @@ namespace hex::fs {
Magic, Magic,
Plugins, Plugins,
Yara, Yara,
YaraAdvancedAnalysis,
Config, Config,
Backups, Backups,
Resources, Resources,

View File

@@ -1,72 +1,76 @@
#pragma once #pragma once
#include <future> #if defined(OS_WEB)
#include <emscripten/fetch.h> #include <future>
namespace hex { #include <emscripten/fetch.h>
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;
// Execute the request namespace hex {
auto result = this->executeImpl<T>(response); 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 // Execute the request
wolv::io::File file(path, wolv::io::File::Mode::Create); auto result = this->executeImpl<T>(response);
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
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> return result;
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();
} }
std::vector<const char*> headers; template<typename T>
for (auto it = m_headers.begin(); it != m_headers.end(); it++) { std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
headers.push_back(it->first.c_str()); hex::unused(path, mimeName);
headers.push_back(it->second.c_str()); throw std::logic_error("Not implemented");
} }
headers.push_back(nullptr);
m_attr.requestHeaders = headers.data();
// Send request template<typename T>
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str()); 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); template<typename T>
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin()); 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 #pragma once
#include <string> #if !defined(OS_WEB)
#include <future>
#include <curl/curl.h> #include <string>
#include <future>
#include <hex/helpers/logger.hpp> #include <curl/curl.h>
#include <hex/helpers/fmt.hpp>
#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> namespace hex {
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
return std::async(std::launch::async, [this, path] {
std::vector<u8> response;
wolv::io::File file(path, wolv::io::File::Mode::Create); template<typename T>
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile); std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file); 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> return this->executeImpl<T>(response);
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());
} }
curl_slist *headers = nullptr; template<typename T>
headers = curl_slist_append(headers, "Cache-Control: no-cache"); std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
ON_SCOPE_EXIT { curl_slist_free_all(headers); }; return std::async(std::launch::async, [this, path, mimeName]{
auto fileName = wolv::util::toUTF8String(path.filename());
for (auto &[key, value] : m_headers) { curl_mime *mime = curl_mime_init(m_curl);
std::string header = hex::format("{}: {}", key, value); curl_mimepart *part = curl_mime_addpart(mime);
headers = curl_slist_append(headers, header.c_str());
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);
{ template<typename T>
std::scoped_lock lock(m_transmissionMutex); 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); curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
if (result != CURLE_OK){ auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
char *url = nullptr; curl_mime_filename(part, fileNameStr.c_str());
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url); curl_mime_name(part, mimeName.c_str());
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
checkProxyErrors();
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 <fmt/color.h>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
#include <hex/helpers/fmt.hpp>
namespace hex::log { namespace hex::log {
@@ -19,7 +20,9 @@ namespace hex::log {
[[maybe_unused]] void redirectToFile(); [[maybe_unused]] void redirectToFile();
[[maybe_unused]] void enableColorPrinting(); [[maybe_unused]] void enableColorPrinting();
extern std::mutex g_loggerMutex; [[nodiscard]] std::recursive_mutex& getLoggerMutex();
[[nodiscard]] bool isLoggingSuspended();
[[nodiscard]] bool isDebugLoggingEnabled();
struct LogEntry { struct LogEntry {
std::string project; std::string project;
@@ -28,65 +31,88 @@ namespace hex::log {
}; };
std::vector<LogEntry>& getLogEntries(); std::vector<LogEntry>& getLogEntries();
void addLogEntry(std::string_view project, std::string_view level, std::string_view message);
[[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName); [[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName);
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto && ... args) { [[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto && ... args) {
std::scoped_lock lock(g_loggerMutex); if (isLoggingSuspended()) [[unlikely]]
return;
std::scoped_lock lock(getLoggerMutex());
auto dest = getDestination(); 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...); auto message = fmt::format(fmt::runtime(fmt), args...);
fmt::print(dest, "{}\n", message); fmt::print(dest, "{}\n", message);
fflush(dest); fflush(dest);
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
} catch (const std::exception&) { }
}
namespace color {
fmt::color debug();
fmt::color info();
fmt::color warn();
fmt::color error();
fmt::color fatal();
getLogEntries().push_back({ IMHEX_PROJECT_NAME, level, std::move(message) });
} }
} }
void suspendLogging();
void resumeLogging();
void enableDebugLogging();
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) { [[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
#if defined(DEBUG) if (impl::isDebugLoggingEnabled()) [[unlikely]] {
hex::log::impl::print(fg(fmt::color::light_green) | fmt::emphasis::bold, "[DEBUG]", fmt, args...); hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
#else } else {
impl::getLogEntries().push_back({ IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...) }); impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
#endif }
} }
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) { [[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
hex::log::impl::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...); hex::log::impl::print(fg(impl::color::info()) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
} }
[[maybe_unused]] void warn(const std::string &fmt, auto && ... args) { [[maybe_unused]] void warn(const std::string &fmt, auto && ... args) {
hex::log::impl::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...); hex::log::impl::print(fg(impl::color::warn()) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
} }
[[maybe_unused]] void error(const std::string &fmt, auto && ... args) { [[maybe_unused]] void error(const std::string &fmt, auto && ... args) {
hex::log::impl::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...); hex::log::impl::print(fg(impl::color::error()) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
} }
[[maybe_unused]] void fatal(const std::string &fmt, auto && ... args) { [[maybe_unused]] void fatal(const std::string &fmt, auto && ... args) {
hex::log::impl::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...); hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
} }
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) { [[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::g_loggerMutex); std::scoped_lock lock(impl::getLoggerMutex());
auto dest = impl::getDestination(); try {
auto message = fmt::format(fmt::runtime(fmt), args...); auto dest = impl::getDestination();
fmt::print(dest, "{}", message); auto message = fmt::format(fmt::runtime(fmt), args...);
fflush(dest); fmt::print(dest, "{}", message);
fflush(dest);
} catch (const std::exception&) { }
} }
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) { [[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
std::scoped_lock lock(impl::g_loggerMutex); std::scoped_lock lock(impl::getLoggerMutex());
auto dest = impl::getDestination(); try {
auto message = fmt::format(fmt::runtime(fmt), args...); auto dest = impl::getDestination();
fmt::print(dest, "{}\n", message); auto message = fmt::format(fmt::runtime(fmt), args...);
fflush(dest); fmt::print(dest, "{}\n", message);
fflush(dest);
} catch (const std::exception&) { }
} }
} }

View File

@@ -16,10 +16,14 @@ namespace hex::magic {
using namespace hex::literals; using namespace hex::literals;
bool compile(); bool compile();
std::string getDescription(const std::vector<u8> &data); std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly = false);
std::string getDescription(prv::Provider *provider, size_t size = 100_KiB); std::string getDescription(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
std::string getMIMEType(const std::vector<u8> &data); std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly = false);
std::string getMIMEType(prv::Provider *provider, size_t size = 100_KiB); std::string getMIMEType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly = false);
std::string getExtensions(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly = false);
std::string getAppleCreatorType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
bool isValidMIMEType(const std::string &mimeType); bool isValidMIMEType(const std::string &mimeType);

View File

@@ -3,6 +3,8 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <concepts>
using u8 = std::uint8_t; using u8 = std::uint8_t;
using u16 = std::uint16_t; using u16 = std::uint16_t;
using u32 = std::uint32_t; using u32 = std::uint32_t;
@@ -61,4 +63,23 @@ namespace hex {
} }
}; };
template<typename T>
concept Pointer = std::is_pointer_v<T>;
template<Pointer T>
struct NonNull {
NonNull(T ptr) : pointer(ptr) { }
NonNull(std::nullptr_t) = delete;
NonNull(std::integral auto) = delete;
NonNull(bool) = delete;
[[nodiscard]] T get() const { return pointer; }
[[nodiscard]] T operator->() const { return pointer; }
[[nodiscard]] T operator*() const { return *pointer; }
[[nodiscard]] operator T() const { return pointer; }
T pointer;
};
} }

View File

@@ -9,8 +9,10 @@
#include <bit> #include <bit>
#include <cstring> #include <cstring>
#include <cctype> #include <cctype>
#include <concepts>
#include <functional> #include <functional>
#include <limits> #include <limits>
#include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
@@ -75,10 +77,14 @@ namespace hex {
int executeCommand(const std::string &command); int executeCommand(const std::string &command);
void openWebpage(std::string url); void openWebpage(std::string url);
extern "C" void registerFont(const char *fontName, const char *fontPath);
const std::map<std::fs::path, std::string>& getFonts();
[[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes); [[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes);
[[nodiscard]] std::vector<u8> decodeByteString(const std::string &string); [[nodiscard]] std::vector<u8> decodeByteString(const std::string &string);
std::wstring utf8ToUtf16(const std::string& utf8); [[nodiscard]] std::wstring utf8ToUtf16(const std::string& utf8);
[[nodiscard]] std::string utf16ToUtf8(const std::wstring& utf16);
[[nodiscard]] constexpr u64 extract(u8 from, u8 to, const std::unsigned_integral auto &value) { [[nodiscard]] constexpr u64 extract(u8 from, u8 to, const std::unsigned_integral auto &value) {
if (from < to) std::swap(from, to); if (from < to) std::swap(from, to);
@@ -293,10 +299,29 @@ namespace hex {
[[nodiscard]] std::optional<std::string> getEnvironmentVariable(const std::string &env); [[nodiscard]] std::optional<std::string> getEnvironmentVariable(const std::string &env);
[[nodiscard]] inline std::string limitStringLength(const std::string &string, size_t maxLength) { [[nodiscard]] inline std::string limitStringLength(const std::string &string, size_t maxLength) {
if (string.length() <= maxLength) // If the string is shorter than the max length, return it as is
if (string.size() < maxLength)
return string; return string;
return string.substr(0, maxLength - 3) + "..."; // If the string is longer than the max length, find the last space before the max length
auto it = string.begin() + maxLength;
while (it != string.begin() && !std::isspace(*it)) --it;
// If there's no space before the max length, just cut the string
if (it == string.begin()) {
it = string.begin() + maxLength;
// Try to find a UTF-8 character boundary
while (it != string.begin() && (*it & 0x80) != 0x00) --it;
++it;
}
// If we still didn't find a valid boundary, just return the string as is
if (it == string.begin())
return string;
// Append
return std::string(string.begin(), it) + "";
} }
[[nodiscard]] std::optional<std::fs::path> getInitialFilePath(); [[nodiscard]] std::optional<std::fs::path> getInitialFilePath();
@@ -304,4 +329,16 @@ namespace hex {
[[nodiscard]] std::string generateHexView(u64 offset, u64 size, prv::Provider *provider); [[nodiscard]] std::string generateHexView(u64 offset, u64 size, prv::Provider *provider);
[[nodiscard]] std::string generateHexView(u64 offset, const std::vector<u8> &data); [[nodiscard]] std::string generateHexView(u64 offset, const std::vector<u8> &data);
[[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

@@ -9,10 +9,12 @@
void errorMessageMacos(const char *message); void errorMessageMacos(const char *message);
void openWebpageMacos(const char *url); void openWebpageMacos(const char *url);
bool isMacosSystemDarkModeEnabled(); bool isMacosSystemDarkModeEnabled();
bool isMacosFullScreenModeEnabled(GLFWwindow *window);
float getBackingScaleFactor(); float getBackingScaleFactor();
void setupMacosWindowStyle(GLFWwindow *window); void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode);
void enumerateFontsMacos();
} }
#endif #endif

View File

@@ -11,6 +11,31 @@
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
#include <wolv/utils/preproc.hpp> #include <wolv/utils/preproc.hpp>
#include <wolv/utils/guards.hpp>
namespace {
struct PluginFunctionHelperInstantiation {};
}
template<typename T>
struct PluginFeatureFunctionHelper {
static void* getFeatures();
};
template<typename T>
struct PluginSubCommandsFunctionHelper {
static void* getSubCommands();
};
template<typename T>
void* PluginFeatureFunctionHelper<T>::getFeatures() {
return nullptr;
}
template<typename T>
void* PluginSubCommandsFunctionHelper<T>::getSubCommands() {
return nullptr;
}
#if defined (IMHEX_STATIC_LINK_PLUGINS) #if defined (IMHEX_STATIC_LINK_PLUGINS)
#define IMHEX_PLUGIN_VISIBILITY_PREFIX static #define IMHEX_PLUGIN_VISIBILITY_PREFIX static
@@ -23,57 +48,65 @@
* Name, Author and Description will be displayed in the in the plugin list on the Welcome screen. * Name, Author and Description will be displayed in the in the plugin list on the Welcome screen.
*/ */
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description) #define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
#define IMHEX_LIBRARY_SETUP() IMHEX_LIBRARY_SETUP_IMPL() #define IMHEX_LIBRARY_SETUP(name) IMHEX_LIBRARY_SETUP_IMPL(name)
#define IMHEX_LIBRARY_SETUP_IMPL() \ #define IMHEX_LIBRARY_SETUP_IMPL(name) \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary(); \ namespace { static struct EXIT_HANDLER { ~EXIT_HANDLER() { hex::log::debug("Unloaded library '{}'", name); } } HANDLER; } \
static auto WOLV_TOKEN_CONCAT(libraryInitializer_, IMHEX_PLUGIN_NAME) = [] { \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)(); \
initializeLibrary(); \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME)() { return name; } \
hex::log::info("Library plugin '{}' initialized successfully", WOLV_STRINGIFY(IMHEX_PLUGIN_NAME)); \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME)(ImGuiContext *ctx) { \
return 0; \ ImGui::SetCurrentContext(ctx); \
}(); \ GImGui = ctx; \
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \ } \
ImGui::SetCurrentContext(ctx); \ extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
GImGui = ctx; \ hex::PluginManager::addPlugin(name, hex::PluginFunctions { \
} \ nullptr, \
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \ WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME), \
hex::PluginManager::addPlugin(hex::PluginFunctions { \ nullptr, \
nullptr, \ WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME), \
initializeLibrary, \ nullptr, \
nullptr, \ nullptr, \
nullptr, \ nullptr, \
nullptr, \ WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME), \
nullptr, \ nullptr, \
setImGuiContext, \ nullptr, \
nullptr, \ nullptr \
nullptr \ }); \
}); \ } \
} \ IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)()
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary()
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \ #define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \ namespace { static struct EXIT_HANDLER { ~EXIT_HANDLER() { hex::log::debug("Unloaded plugin '{}'", name); } } HANDLER; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \ IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
ImGui::SetCurrentContext(ctx); \ IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
GImGui = ctx; \ ImGui::SetCurrentContext(ctx); \
} \ GImGui = ctx; \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \ } \
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \ IMHEX_PLUGIN_VISIBILITY_PREFIX void* getFeatures() { \
hex::PluginManager::addPlugin(hex::PluginFunctions { \ return PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures(); \
initializePlugin, \ } \
nullptr, \ IMHEX_PLUGIN_VISIBILITY_PREFIX void* getSubCommands() { \
getPluginName, \ return PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands(); \
getPluginAuthor, \ } \
getPluginDescription, \ IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \
getCompatibleVersion, \ extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
setImGuiContext, \ hex::PluginManager::addPlugin(name, hex::PluginFunctions { \
nullptr, \ initializePlugin, \
nullptr \ nullptr, \
}); \ getPluginName, \
} \ nullptr, \
getPluginAuthor, \
getPluginDescription, \
getCompatibleVersion, \
setImGuiContext, \
nullptr, \
getSubCommands, \
getFeatures \
}); \
} \
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin() IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin()
/** /**
@@ -86,18 +119,26 @@
*/ */
#define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL() #define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL()
#define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \ #define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \
extern std::vector<hex::SubCommand> g_subCommands; \ extern std::vector<hex::SubCommand> g_subCommands; \
extern "C" [[gnu::visibility("default")]] void* getSubCommands() { \ template<> \
return &g_subCommands; \ struct PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation> { \
} \ static void* getSubCommands(); \
}; \
void* PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands() { \
return &g_subCommands; \
} \
std::vector<hex::SubCommand> g_subCommands std::vector<hex::SubCommand> g_subCommands
#define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature) #define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature)
#define IMHEX_PLUGIN_FEATURES() IMHEX_PLUGIN_FEATURES_IMPL() #define IMHEX_PLUGIN_FEATURES() IMHEX_PLUGIN_FEATURES_IMPL()
#define IMHEX_PLUGIN_FEATURES_IMPL() \ #define IMHEX_PLUGIN_FEATURES_IMPL() \
extern std::vector<hex::Feature> g_features; \ extern std::vector<hex::Feature> g_features; \
extern "C" [[gnu::visibility("default")]] void* getFeatures() { \ template<> \
return &g_features; \ struct PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation> { \
} \ static void* getFeatures(); \
}; \
void* PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures() { \
return &g_features; \
} \
std::vector<hex::Feature> g_features std::vector<hex::Feature> g_features

View File

@@ -11,9 +11,15 @@ namespace hex::prv {
class MemoryProvider : public hex::prv::Provider { class MemoryProvider : public hex::prv::Provider {
public: public:
MemoryProvider() = default; MemoryProvider() = default;
explicit MemoryProvider(std::vector<u8> data) : m_data(std::move(data)) { } explicit MemoryProvider(std::vector<u8> data, std::string name = "") : m_data(std::move(data)), m_name(std::move(name)) { }
~MemoryProvider() override = default; ~MemoryProvider() override = default;
MemoryProvider(const MemoryProvider&) = delete;
MemoryProvider& operator=(const MemoryProvider&) = delete;
MemoryProvider(MemoryProvider &&provider) noexcept = default;
MemoryProvider& operator=(MemoryProvider &&provider) noexcept = default;
[[nodiscard]] bool isAvailable() const override { return true; } [[nodiscard]] bool isAvailable() const override { return true; }
[[nodiscard]] bool isReadable() const override { return true; } [[nodiscard]] bool isReadable() const override { return true; }
[[nodiscard]] bool isWritable() const override { return true; } [[nodiscard]] bool isWritable() const override { return true; }
@@ -29,10 +35,8 @@ namespace hex::prv {
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); } [[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
void resizeRaw(u64 newSize) override; void resizeRaw(u64 newSize) override;
void insertRaw(u64 offset, u64 size) override;
void removeRaw(u64 offset, u64 size) override;
[[nodiscard]] std::string getName() const override { return ""; } [[nodiscard]] std::string getName() const override { return m_name; }
[[nodiscard]] std::string getTypeName() const override { return "MemoryProvider"; } [[nodiscard]] std::string getTypeName() const override { return "MemoryProvider"; }
private: private:

View File

@@ -37,6 +37,11 @@ namespace hex::prv {
Provider(); Provider();
virtual ~Provider(); virtual ~Provider();
Provider(const Provider&) = delete;
Provider& operator=(const Provider&) = delete;
Provider(Provider &&provider) noexcept = default;
Provider& operator=(Provider &&provider) noexcept = default;
/** /**
* @brief Opens this provider * @brief Opens this provider
@@ -156,13 +161,13 @@ namespace hex::prv {
*/ */
[[nodiscard]] virtual std::string getName() const = 0; [[nodiscard]] virtual std::string getName() const = 0;
void resize(u64 newSize); bool resize(u64 newSize);
void insert(u64 offset, u64 size); void insert(u64 offset, u64 size);
void remove(u64 offset, u64 size); void remove(u64 offset, u64 size);
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); } virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); } virtual void insertRaw(u64 offset, u64 size);
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); } virtual void removeRaw(u64 offset, u64 size);
virtual void save(); virtual void save();
virtual void saveAs(const std::fs::path &path); virtual void saveAs(const std::fs::path &path);

View File

@@ -4,6 +4,7 @@
#include <hex/api/event_manager.hpp> #include <hex/api/event_manager.hpp>
#include <map> #include <map>
#include <ranges>
#include <utility> #include <utility>
namespace hex { namespace hex {
@@ -21,8 +22,6 @@ namespace hex {
PerProvider& operator=(const PerProvider&) = delete; PerProvider& operator=(const PerProvider&) = delete;
PerProvider& operator=(PerProvider &&) = delete; PerProvider& operator=(PerProvider &&) = delete;
PerProvider(T data) : m_data({ { ImHexApi::Provider::get(), std::move(data) } }) { this->onCreate(); }
~PerProvider() { this->onDestroy(); } ~PerProvider() { this->onDestroy(); }
T* operator->() { T* operator->() {
@@ -33,19 +32,19 @@ namespace hex {
return &this->get(); return &this->get();
} }
T& get(prv::Provider *provider = ImHexApi::Provider::get()) { T& get(const prv::Provider *provider = ImHexApi::Provider::get()) {
return m_data[provider]; return m_data[provider];
} }
const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const { const T& get(const prv::Provider *provider = ImHexApi::Provider::get()) const {
return m_data.at(provider); return m_data.at(provider);
} }
void set(const T &data, prv::Provider *provider = ImHexApi::Provider::get()) { void set(const T &data, const prv::Provider *provider = ImHexApi::Provider::get()) {
m_data[provider] = data; m_data[provider] = data;
} }
void set(T &&data, prv::Provider *provider = ImHexApi::Provider::get()) { void set(T &&data, const prv::Provider *provider = ImHexApi::Provider::get()) {
m_data[provider] = std::move(data); m_data[provider] = std::move(data);
} }
@@ -71,10 +70,21 @@ namespace hex {
return this->get(); return this->get();
} }
auto all() {
return m_data | std::views::values;
}
void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {
m_onCreateCallback = std::move(callback);
}
private: private:
void onCreate() { void onCreate() {
EventProviderOpened::subscribe(this, [this](prv::Provider *provider) { EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {
m_data.emplace(provider, T()); auto [it, inserted] = m_data.emplace(provider, T());
auto &[key, value] = *it;
if (m_onCreateCallback)
m_onCreateCallback(provider, value);
}); });
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){ EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
@@ -110,7 +120,8 @@ namespace hex {
} }
private: private:
std::map<prv::Provider *, T> m_data; std::map<const prv::Provider *, T> m_data;
std::function<void(prv::Provider *, T&)> m_onCreateCallback;
}; };
} }

View File

@@ -2,12 +2,12 @@
#include <hex.hpp> #include <hex.hpp>
#include <hex/api/localization_manager.hpp> #include <hex/api/localization_manager.hpp>
#include <hex/api/event_manager.hpp>
#include <hex/providers/undo_redo/operations/operation.hpp> #include <hex/providers/undo_redo/operations/operation.hpp>
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
namespace hex::prv { namespace hex::prv {
@@ -27,13 +27,17 @@ namespace hex::prv::undo {
void groupOperations(u32 count, const UnlocalizedString &unlocalizedName); void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);
void apply(const Stack &otherStack); void apply(const Stack &otherStack);
void reapply();
[[nodiscard]] bool canUndo() const; [[nodiscard]] bool canUndo() const;
[[nodiscard]] bool canRedo() const; [[nodiscard]] bool canRedo() const;
template<std::derived_from<Operation> T> template<std::derived_from<Operation> T>
bool add(auto && ... args) { bool add(auto && ... args) {
return this->add(std::make_unique<T>(std::forward<decltype(args)>(args)...)); auto result = this->add(std::make_unique<T>(std::forward<decltype(args)>(args)...));
EventDataChanged::post(m_provider);
return result;
} }
bool add(std::unique_ptr<Operation> &&operation); bool add(std::unique_ptr<Operation> &&operation);

View File

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

View File

@@ -14,7 +14,7 @@
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
enum ImGuiCustomCol { enum ImGuiCustomCol : int {
ImGuiCustomCol_DescButton, ImGuiCustomCol_DescButton,
ImGuiCustomCol_DescButtonHovered, ImGuiCustomCol_DescButtonHovered,
ImGuiCustomCol_DescButtonActive, ImGuiCustomCol_DescButtonActive,
@@ -70,7 +70,7 @@ namespace ImGuiExt {
class Texture { class Texture {
public: public:
enum class Filter { enum class Filter : int {
Linear, Linear,
Nearest Nearest
}; };

View File

@@ -6,8 +6,6 @@
#include <imgui_internal.h> #include <imgui_internal.h>
#include <hex/ui/imgui_imhex_extensions.h> #include <hex/ui/imgui_imhex_extensions.h>
#include <fonts/codicons_font.h>
#include <hex/api/imhex_api.hpp> #include <hex/api/imhex_api.hpp>
#include <hex/api/shortcut_manager.hpp> #include <hex/api/shortcut_manager.hpp>
#include <hex/api/event_manager.hpp> #include <hex/api/event_manager.hpp>
@@ -24,7 +22,7 @@
namespace hex { namespace hex {
class View { class View {
explicit View(UnlocalizedString unlocalizedName); explicit View(UnlocalizedString unlocalizedName, const char *icon);
public: public:
virtual ~View() = default; virtual ~View() = default;
@@ -82,7 +80,7 @@ namespace hex {
*/ */
[[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const; [[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const;
[[nodiscard]] const char *getIcon() const { return m_icon; }
[[nodiscard]] bool &getWindowOpenState(); [[nodiscard]] bool &getWindowOpenState();
[[nodiscard]] const bool &getWindowOpenState() const; [[nodiscard]] const bool &getWindowOpenState() const;
@@ -110,6 +108,7 @@ namespace hex {
bool m_windowOpen = false, m_prevWindowOpen = false; bool m_windowOpen = false, m_prevWindowOpen = false;
std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts; std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts;
bool m_windowJustOpened = false; bool m_windowJustOpened = false;
const char *m_icon;
friend class ShortcutManager; friend class ShortcutManager;
}; };
@@ -120,7 +119,7 @@ namespace hex {
*/ */
class View::Window : public View { class View::Window : public View {
public: public:
explicit Window(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {} explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
void draw() final { void draw() final {
if (this->shouldDraw()) { if (this->shouldDraw()) {
@@ -139,7 +138,7 @@ namespace hex {
*/ */
class View::Special : public View { class View::Special : public View {
public: public:
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {} explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
void draw() final { void draw() final {
if (this->shouldDraw()) { if (this->shouldDraw()) {
@@ -154,7 +153,7 @@ namespace hex {
*/ */
class View::Floating : public View::Window { class View::Floating : public View::Window {
public: public:
explicit Floating(UnlocalizedString unlocalizedName) : Window(std::move(unlocalizedName)) {} explicit Floating(UnlocalizedString unlocalizedName) : Window(std::move(unlocalizedName), "") {}
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; } [[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; }
}; };
@@ -164,7 +163,7 @@ namespace hex {
*/ */
class View::Modal : public View { class View::Modal : public View {
public: public:
explicit Modal(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {} explicit Modal(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
void draw() final { void draw() final {
if (this->shouldDraw()) { if (this->shouldDraw()) {

View File

@@ -1,52 +1,56 @@
#include <hex/api/achievement_manager.hpp> #include <hex/api/achievement_manager.hpp>
#include <hex/api/event_manager.hpp> #include <hex/api/event_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace hex { namespace hex {
std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() { static AutoReset<std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>> s_achievements;
static std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> achievements; const std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() {
return *s_achievements;
return achievements;
} }
std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) { static AutoReset<std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>> s_nodeCategoryStorage;
static std::unordered_map<std::string, std::list<AchievementNode>> nodeCategoryStorage; std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& getAchievementNodesMutable(bool rebuild) {
if (!s_nodeCategoryStorage->empty() || !rebuild)
return s_nodeCategoryStorage;
if (!nodeCategoryStorage.empty() || !rebuild) s_nodeCategoryStorage->clear();
return nodeCategoryStorage;
nodeCategoryStorage.clear();
// Add all achievements to the node storage // Add all achievements to the node storage
for (auto &[categoryName, achievements] : getAchievements()) { for (auto &[categoryName, achievements] : AchievementManager::getAchievements()) {
auto &nodes = nodeCategoryStorage[categoryName]; auto &nodes = (*s_nodeCategoryStorage)[categoryName];
for (auto &[achievementName, achievement] : achievements) { for (auto &[achievementName, achievement] : achievements) {
nodes.emplace_back(achievement.get()); nodes.emplace_back(achievement.get());
} }
} }
return nodeCategoryStorage; return s_nodeCategoryStorage;
} }
std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) { const std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) {
static std::unordered_map<std::string, std::vector<AchievementNode*>> startNodes; return getAchievementNodesMutable(rebuild);
}
if (!startNodes.empty() || !rebuild) static AutoReset<std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>> s_startNodes;
return startNodes; const std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) {
auto &nodeCategoryStorage = getAchievementNodes(); if (!s_startNodes->empty() || !rebuild)
return s_startNodes;
startNodes.clear(); auto &nodeCategoryStorage = getAchievementNodesMutable(rebuild);
s_startNodes->clear();
// Add all parents and children to the nodes // Add all parents and children to the nodes
for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) { for (auto &achievementNode : achievements) {
for (auto &requirement : achievementNode.achievement->getRequirements()) { for (auto &requirement : achievementNode.achievement->getRequirements()) {
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
return node.achievement->getUnlocalizedName() == requirement; return node.achievement->getUnlocalizedName() == requirement;
}); });
@@ -59,7 +63,7 @@ namespace hex {
for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) { for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) {
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) { for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) { auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
return node.achievement->getUnlocalizedName() == requirement; return node.achievement->getUnlocalizedName() == requirement;
}); });
@@ -74,17 +78,17 @@ namespace hex {
for (auto &[categoryName, achievements] : nodeCategoryStorage) { for (auto &[categoryName, achievements] : nodeCategoryStorage) {
for (auto &achievementNode : achievements) { for (auto &achievementNode : achievements) {
if (!achievementNode.hasParents()) { if (!achievementNode.hasParents()) {
startNodes[categoryName].emplace_back(&achievementNode); (*s_startNodes)[categoryName].emplace_back(&achievementNode);
} }
for (const auto &parent : achievementNode.parents) { for (const auto &parent : achievementNode.parents) {
if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory()) if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory())
startNodes[categoryName].emplace_back(&achievementNode); (*s_startNodes)[categoryName].emplace_back(&achievementNode);
} }
} }
} }
return startNodes; return s_startNodes;
} }
void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName) { void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName) {
@@ -97,15 +101,16 @@ namespace hex {
auto &[categoryName, achievements] = *categoryIter; auto &[categoryName, achievements] = *categoryIter;
auto achievementIter = achievements.find(unlocalizedName); const auto achievementIter = achievements.find(unlocalizedName);
if (achievementIter == achievements.end()) { if (achievementIter == achievements.end()) {
return; return;
} }
auto &nodes = getAchievementNodes()[categoryName]; const auto &nodes = getAchievementNodes();
if (!nodes.contains(categoryName))
return;
for (const auto &node : nodes) { for (const auto &node : nodes.at(categoryName)) {
auto &achievement = node.achievement; auto &achievement = node.achievement;
if (achievement->getUnlocalizedCategory() != unlocalizedCategory) { if (achievement->getUnlocalizedCategory() != unlocalizedCategory) {
@@ -134,14 +139,8 @@ namespace hex {
} }
} }
void AchievementManager::clear() {
getAchievements().clear();
getAchievementStartNodes(false).clear();
getAchievementNodes(false).clear();
}
void AchievementManager::clearTemporary() { void AchievementManager::clearTemporary() {
auto &categories = getAchievements(); auto &categories = *s_achievements;
for (auto &[categoryName, achievements] : categories) { for (auto &[categoryName, achievements] : categories) {
std::erase_if(achievements, [](auto &data) { std::erase_if(achievements, [](auto &data) {
auto &[achievementName, achievement] = data; auto &[achievementName, achievement] = data;
@@ -154,8 +153,8 @@ namespace hex {
return achievements.empty(); return achievements.empty();
}); });
getAchievementStartNodes(false).clear(); s_startNodes->clear();
getAchievementNodes(false).clear(); s_nodeCategoryStorage->clear();
} }
std::pair<u32, u32> AchievementManager::getProgress() { std::pair<u32, u32> AchievementManager::getProgress() {
@@ -175,10 +174,26 @@ namespace hex {
} }
void AchievementManager::achievementAdded() { void AchievementManager::achievementAdded() {
getAchievementStartNodes(false).clear(); s_startNodes->clear();
getAchievementNodes(false).clear(); s_nodeCategoryStorage->clear();
} }
Achievement &AchievementManager::addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement) {
const auto &category = newAchievement->getUnlocalizedCategory();
const auto &name = newAchievement->getUnlocalizedName();
auto [categoryIter, categoryInserted] = s_achievements->insert({ category, std::unordered_map<std::string, std::unique_ptr<Achievement>>{} });
auto &[categoryKey, achievements] = *categoryIter;
auto [achievementIter, achievementInserted] = achievements.insert({ name, std::move(newAchievement) });
auto &[achievementKey, achievement] = *achievementIter;
achievementAdded();
return *achievement;
}
constexpr static auto AchievementsFile = "achievements.json"; constexpr static auto AchievementsFile = "achievements.json";
void AchievementManager::loadProgress() { void AchievementManager::loadProgress() {
@@ -219,28 +234,26 @@ namespace hex {
} }
void AchievementManager::storeProgress() { void AchievementManager::storeProgress() {
nlohmann::json json;
for (const auto &[categoryName, achievements] : getAchievements()) {
json[categoryName] = nlohmann::json::object();
for (const auto &[achievementName, achievement] : achievements) {
json[categoryName][achievementName] = achievement->getProgress();
}
}
if (json.empty())
return;
for (const auto &directory : fs::getDefaultPaths(fs::ImHexPath::Config)) { for (const auto &directory : fs::getDefaultPaths(fs::ImHexPath::Config)) {
auto path = directory / AchievementsFile; auto path = directory / AchievementsFile;
wolv::io::File file(path, wolv::io::File::Mode::Write); wolv::io::File file(path, wolv::io::File::Mode::Create);
if (!file.isValid())
if (!file.isValid()) {
continue; continue;
}
nlohmann::json json; file.writeString(json.dump(4));
for (const auto &[categoryName, achievements] : getAchievements()) {
json[categoryName] = nlohmann::json::object();
for (const auto &[achievementName, achievement] : achievements) {
json[categoryName][achievementName] = achievement->getProgress();
}
}
auto result = json.dump(4);
file.setSize(0);
file.writeString(result);
break; break;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -2,23 +2,25 @@
#include <hex/api/event_manager.hpp> #include <hex/api/event_manager.hpp>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <hex/providers/provider.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/providers/provider_data.hpp>
#include <hex/providers/provider.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
#include <utility> #include <utility>
#include <unistd.h>
#include <imgui.h> #include <imgui.h>
#include <imgui_internal.h> #include <imgui_internal.h>
#include <GLFW/glfw3.h>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#else #else
#include <sys/utsname.h> #include <sys/utsname.h>
#include <unistd.h>
#endif #endif
namespace hex { namespace hex {
@@ -36,39 +38,56 @@ namespace hex {
namespace impl { namespace impl {
static std::map<u32, Highlighting> s_backgroundHighlights; static AutoReset<std::map<u32, Highlighting>> s_backgroundHighlights;
std::map<u32, Highlighting> &getBackgroundHighlights() { const std::map<u32, Highlighting>& getBackgroundHighlights() {
return s_backgroundHighlights; return *s_backgroundHighlights;
} }
static std::map<u32, HighlightingFunction> s_backgroundHighlightingFunctions; static AutoReset<std::map<u32, HighlightingFunction>> s_backgroundHighlightingFunctions;
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions() { const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions() {
return s_backgroundHighlightingFunctions; return *s_backgroundHighlightingFunctions;
} }
static std::map<u32, Highlighting> s_foregroundHighlights; static AutoReset<std::map<u32, Highlighting>> s_foregroundHighlights;
std::map<u32, Highlighting> &getForegroundHighlights() { const std::map<u32, Highlighting>& getForegroundHighlights() {
return s_foregroundHighlights; return *s_foregroundHighlights;
} }
static std::map<u32, HighlightingFunction> s_foregroundHighlightingFunctions; static AutoReset<std::map<u32, HighlightingFunction>> s_foregroundHighlightingFunctions;
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions() { const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions() {
return s_foregroundHighlightingFunctions; return *s_foregroundHighlightingFunctions;
} }
static std::map<u32, Tooltip> s_tooltips; static AutoReset<std::map<u32, Tooltip>> s_tooltips;
std::map<u32, Tooltip> &getTooltips() { const std::map<u32, Tooltip>& getTooltips() {
return s_tooltips; return *s_tooltips;
} }
static std::map<u32, TooltipFunction> s_tooltipFunctions; static AutoReset<std::map<u32, TooltipFunction>> s_tooltipFunctions;
std::map<u32, TooltipFunction> &getTooltipFunctions() { const std::map<u32, TooltipFunction>& getTooltipFunctions() {
return s_tooltipFunctions; return *s_tooltipFunctions;
} }
static std::optional<ProviderRegion> s_currentSelection; static AutoReset<std::map<u32, HoveringFunction>> s_hoveringFunctions;
const std::map<u32, HoveringFunction>& getHoveringFunctions() {
return *s_hoveringFunctions;
}
static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
void setCurrentSelection(const std::optional<ProviderRegion> &region) { void setCurrentSelection(const std::optional<ProviderRegion> &region) {
s_currentSelection = region; if (region == Region::Invalid()) {
clearSelection();
} else {
*s_currentSelection = region;
}
}
static PerProvider<std::optional<Region>> s_hoveredRegion;
void setHoveredRegion(const prv::Provider *provider, const Region &region) {
if (region == Region::Invalid())
s_hoveredRegion.get(provider).reset();
else
s_hoveredRegion.get(provider) = region;
} }
} }
@@ -78,19 +97,19 @@ namespace hex {
id++; id++;
impl::getBackgroundHighlights().insert({ impl::s_backgroundHighlights->insert({
id, Highlighting {region, color} id, Highlighting { region, color }
}); });
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
return id; return id;
} }
void removeBackgroundHighlight(u32 id) { void removeBackgroundHighlight(u32 id) {
impl::getBackgroundHighlights().erase(id); impl::s_backgroundHighlights->erase(id);
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
} }
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) { u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) {
@@ -98,17 +117,17 @@ namespace hex {
id++; id++;
impl::getBackgroundHighlightingFunctions().insert({ id, function }); impl::s_backgroundHighlightingFunctions->insert({ id, function });
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
return id; return id;
} }
void removeBackgroundHighlightingProvider(u32 id) { void removeBackgroundHighlightingProvider(u32 id) {
impl::getBackgroundHighlightingFunctions().erase(id); impl::s_backgroundHighlightingFunctions->erase(id);
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
} }
u32 addForegroundHighlight(const Region &region, color_t color) { u32 addForegroundHighlight(const Region &region, color_t color) {
@@ -116,19 +135,19 @@ namespace hex {
id++; id++;
impl::getForegroundHighlights().insert({ impl::s_foregroundHighlights->insert({
id, Highlighting {region, color} id, Highlighting { region, color }
}); });
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
return id; return id;
} }
void removeForegroundHighlight(u32 id) { void removeForegroundHighlight(u32 id) {
impl::getForegroundHighlights().erase(id); impl::s_foregroundHighlights->erase(id);
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
} }
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) { u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) {
@@ -136,45 +155,60 @@ namespace hex {
id++; id++;
impl::getForegroundHighlightingFunctions().insert({ id, function }); impl::s_foregroundHighlightingFunctions->insert({ id, function });
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
return id; return id;
} }
void removeForegroundHighlightingProvider(u32 id) { void removeForegroundHighlightingProvider(u32 id) {
impl::getForegroundHighlightingFunctions().erase(id); impl::s_foregroundHighlightingFunctions->erase(id);
EventHighlightingChanged::post(); TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
}
u32 addHoverHighlightProvider(const impl::HoveringFunction &function) {
static u32 id = 0;
id++;
impl::s_hoveringFunctions->insert({ id, function });
return id;
}
void removeHoverHighlightProvider(u32 id) {
impl::s_hoveringFunctions->erase(id);
} }
static u32 tooltipId = 0; static u32 tooltipId = 0;
u32 addTooltip(Region region, std::string value, color_t color) { u32 addTooltip(Region region, std::string value, color_t color) {
tooltipId++; tooltipId++;
impl::getTooltips().insert({ tooltipId, { region, std::move(value), color } }); impl::s_tooltips->insert({ tooltipId, { region, std::move(value), color } });
return tooltipId; return tooltipId;
} }
void removeTooltip(u32 id) { void removeTooltip(u32 id) {
impl::getTooltips().erase(id); impl::s_tooltips->erase(id);
} }
static u32 tooltipFunctionId; static u32 tooltipFunctionId;
u32 addTooltipProvider(TooltipFunction function) { u32 addTooltipProvider(TooltipFunction function) {
tooltipFunctionId++; tooltipFunctionId++;
impl::getTooltipFunctions().insert({ tooltipFunctionId, std::move(function) }); impl::s_tooltipFunctions->insert({ tooltipFunctionId, std::move(function) });
return tooltipFunctionId; return tooltipFunctionId;
} }
void removeTooltipProvider(u32 id) { void removeTooltipProvider(u32 id) {
impl::getTooltipFunctions().erase(id); impl::s_tooltipFunctions->erase(id);
} }
bool isSelectionValid() { bool isSelectionValid() {
return getSelection().has_value(); auto selection = getSelection();
return selection.has_value() && selection->provider != nullptr;
} }
std::optional<ProviderRegion> getSelection() { std::optional<ProviderRegion> getSelection() {
@@ -190,13 +224,21 @@ namespace hex {
} }
void setSelection(const ProviderRegion &region) { void setSelection(const ProviderRegion &region) {
RequestSelectionChange::post(region); RequestHexEditorSelectionChange::post(region);
} }
void setSelection(u64 address, size_t size, prv::Provider *provider) { void setSelection(u64 address, size_t size, prv::Provider *provider) {
setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider }); setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider });
} }
void addVirtualFile(const std::fs::path &path, std::vector<u8> data, Region region) {
RequestAddVirtualFile::post(path, std::move(data), region);
}
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider) {
return impl::s_hoveredRegion.get(provider);
}
} }
@@ -223,7 +265,7 @@ namespace hex {
namespace ImHexApi::Provider { namespace ImHexApi::Provider {
static i64 s_currentProvider = -1; static i64 s_currentProvider = -1;
static std::vector<prv::Provider *> s_providers; static AutoReset<std::vector<std::unique_ptr<prv::Provider>>> s_providers;
namespace impl { namespace impl {
@@ -242,22 +284,40 @@ namespace hex {
if (!ImHexApi::Provider::isValid()) if (!ImHexApi::Provider::isValid())
return nullptr; return nullptr;
return s_providers[s_currentProvider]; return (*s_providers)[s_currentProvider].get();
} }
const std::vector<prv::Provider *> &getProviders() { std::vector<prv::Provider*> getProviders() {
return s_providers; std::vector<prv::Provider*> result;
result.reserve(s_providers->size());
for (const auto &provider : *s_providers)
result.push_back(provider.get());
return result;
} }
void setCurrentProvider(u32 index) { void setCurrentProvider(i64 index) {
if (TaskManager::getRunningTaskCount() > 0) if (TaskManager::getRunningTaskCount() > 0)
return; return;
if (index < s_providers.size() && s_currentProvider != index) { if (std::cmp_less(index, s_providers->size()) && s_currentProvider != index) {
auto oldProvider = get(); auto oldProvider = get();
s_currentProvider = index; s_currentProvider = index;
EventProviderChanged::post(oldProvider, get()); EventProviderChanged::post(oldProvider, get());
} }
RequestUpdateWindowTitle::post();
}
void setCurrentProvider(NonNull<prv::Provider*> provider) {
if (TaskManager::getRunningTaskCount() > 0)
return;
const auto providers = getProviders();
auto it = std::ranges::find(providers, provider.get());
auto index = std::distance(providers.begin(), it);
setCurrentProvider(index);
} }
i64 getCurrentProviderIndex() { i64 getCurrentProviderIndex() {
@@ -265,7 +325,7 @@ namespace hex {
} }
bool isValid() { bool isValid() {
return !s_providers.empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers.size()); return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size());
} }
void markDirty() { void markDirty() {
@@ -273,28 +333,28 @@ namespace hex {
} }
void resetDirty() { void resetDirty() {
for (auto &provider : s_providers) for (const auto &provider : *s_providers)
provider->markDirty(false); provider->markDirty(false);
} }
bool isDirty() { bool isDirty() {
return std::ranges::any_of(s_providers, [](const auto &provider) { return std::ranges::any_of(*s_providers, [](const auto &provider) {
return provider->isDirty(); return provider->isDirty();
}); });
} }
void add(prv::Provider *provider, bool skipLoadInterface, bool select) { void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
if (TaskManager::getRunningTaskCount() > 0) if (TaskManager::getRunningTaskCount() > 0)
return; return;
if (skipLoadInterface) if (skipLoadInterface)
provider->skipLoadInterface(); provider->skipLoadInterface();
s_providers.push_back(provider); EventProviderCreated::post(provider.get());
EventProviderCreated::post(provider); s_providers->emplace_back(std::move(provider));
if (select || s_providers.size() == 1) if (select || s_providers->size() == 1)
setCurrentProvider(s_providers.size() - 1); setCurrentProvider(s_providers->size() - 1);
} }
void remove(prv::Provider *provider, bool noQuestions) { void remove(prv::Provider *provider, bool noQuestions) {
@@ -313,31 +373,36 @@ namespace hex {
return; return;
} }
auto it = std::find(s_providers.begin(), s_providers.end(), provider); const auto it = std::ranges::find_if(*s_providers, [provider](const auto &p) {
if (it == s_providers.end()) return p.get() == provider;
});
if (it == s_providers->end())
return; return;
if (!s_providers.empty()) { if (!s_providers->empty()) {
if (it == s_providers.begin()) { if (it == s_providers->begin()) {
// If the first provider is being closed, select the one that's the first one now // If the first provider is being closed, select the one that's the first one now
setCurrentProvider(0); setCurrentProvider(0);
if (s_providers.size() > 1) if (s_providers->size() > 1)
EventProviderChanged::post(s_providers[0], s_providers[1]); EventProviderChanged::post(s_providers->at(0).get(), s_providers->at(1).get());
} }
else if (std::distance(s_providers.begin(), it) == s_currentProvider) { else if (std::distance(s_providers->begin(), it) == s_currentProvider) {
// If the current provider is being closed, select the one that's before it // If the current provider is being closed, select the one that's before it
setCurrentProvider(s_currentProvider - 1); setCurrentProvider(s_currentProvider - 1);
} }
else { else {
// If any other provider is being closed, find the current provider in the list again and select it again // If any other provider is being closed, find the current provider in the list again and select it again
auto currentProvider = get(); const auto currentProvider = get();
auto currentIt = std::find(s_providers.begin(), s_providers.end(), currentProvider); const auto currentIt = std::ranges::find_if(*s_providers, [currentProvider](const auto &p) {
return p.get() == currentProvider;
});
if (currentIt != s_providers.end()) { if (currentIt != s_providers->end()) {
auto newIndex = std::distance(s_providers.begin(), currentIt); auto newIndex = std::distance(s_providers->begin(), currentIt);
if (s_currentProvider == newIndex) if (s_currentProvider == newIndex && newIndex != 0)
newIndex -= 1; newIndex -= 1;
setCurrentProvider(newIndex); setCurrentProvider(newIndex);
@@ -348,20 +413,20 @@ namespace hex {
} }
} }
s_providers.erase(it);
if (s_currentProvider >= i64(s_providers.size()))
setCurrentProvider(0);
if (s_providers.empty())
EventProviderChanged::post(provider, nullptr);
provider->close(); provider->close();
EventProviderClosed::post(provider); EventProviderClosed::post(provider);
RequestUpdateWindowTitle::post();
TaskManager::runWhenTasksFinished([provider] { TaskManager::runWhenTasksFinished([it, provider] {
EventProviderDeleted::post(provider); EventProviderDeleted::post(provider);
std::erase(impl::s_closingProviders, provider); std::erase(impl::s_closingProviders, provider);
delete provider;
s_providers->erase(it);
if (s_currentProvider >= i64(s_providers->size()))
setCurrentProvider(0);
if (s_providers->empty())
EventProviderChanged::post(provider, nullptr);
}); });
} }
@@ -388,11 +453,11 @@ namespace hex {
static ImVec2 s_mainWindowPos; static ImVec2 s_mainWindowPos;
static ImVec2 s_mainWindowSize; static ImVec2 s_mainWindowSize;
void setMainWindowPosition(i32 x, i32 y) { void setMainWindowPosition(i32 x, i32 y) {
s_mainWindowPos = ImVec2(x, y); s_mainWindowPos = ImVec2(float(x), float(y));
} }
void setMainWindowSize(u32 width, u32 height) { void setMainWindowSize(u32 width, u32 height) {
s_mainWindowSize = ImVec2(width, height); s_mainWindowSize = ImVec2(float(width), float(height));
} }
static ImGuiID s_mainDockSpaceId; static ImGuiID s_mainDockSpaceId;
@@ -400,6 +465,11 @@ namespace hex {
s_mainDockSpaceId = id; s_mainDockSpaceId = id;
} }
static GLFWwindow *s_mainWindowHandle;
void setMainWindowHandle(GLFWwindow *window) {
s_mainWindowHandle = window;
}
static float s_globalScale = 1.0; static float s_globalScale = 1.0;
void setGlobalScale(float scale) { void setGlobalScale(float scale) {
@@ -417,22 +487,28 @@ namespace hex {
s_borderlessWindowMode = enabled; s_borderlessWindowMode = enabled;
} }
static bool s_multiWindowMode = false;
void setMultiWindowMode(bool enabled) {
s_multiWindowMode = enabled;
}
static std::string s_gpuVendor; static std::optional<InitialWindowProperties> s_initialWindowProperties;
void setInitialWindowProperties(InitialWindowProperties properties) {
s_initialWindowProperties = properties;
}
static AutoReset<std::string> s_gpuVendor;
void setGPUVendor(const std::string &vendor) { void setGPUVendor(const std::string &vendor) {
s_gpuVendor = vendor; s_gpuVendor = vendor;
} }
static bool s_portableVersion = false; static AutoReset<std::map<std::string, std::string>> s_initArguments;
void setPortableVersion(bool enabled) {
s_portableVersion = enabled;
}
void addInitArgument(const std::string &key, const std::string &value) { void addInitArgument(const std::string &key, const std::string &value) {
static std::mutex initArgumentsMutex; static std::mutex initArgumentsMutex;
std::scoped_lock lock(initArgumentsMutex); std::scoped_lock lock(initArgumentsMutex);
getInitArguments()[key] = value; (*s_initArguments)[key] = value;
} }
static double s_lastFrameTime; static double s_lastFrameTime;
@@ -440,6 +516,21 @@ namespace hex {
s_lastFrameTime = time; s_lastFrameTime = time;
} }
static bool s_windowResizable = true;
bool isWindowResizable() {
return s_windowResizable;
}
static std::vector<hex::impl::AutoResetBase*> s_autoResetObjects;
void addAutoResetObject(hex::impl::AutoResetBase *object) {
s_autoResetObjects.emplace_back(object);
}
void cleanup() {
for (const auto &object : s_autoResetObjects)
object->reset();
}
} }
@@ -496,22 +587,44 @@ namespace hex {
return impl::s_mainDockSpaceId; return impl::s_mainDockSpaceId;
} }
GLFWwindow* getMainWindowHandle() {
return impl::s_mainWindowHandle;
}
bool isBorderlessWindowModeEnabled() { bool isBorderlessWindowModeEnabled() {
return impl::s_borderlessWindowMode; return impl::s_borderlessWindowMode;
} }
std::map<std::string, std::string> &getInitArguments() { bool isMutliWindowModeEnabled() {
static std::map<std::string, std::string> initArgs; return impl::s_multiWindowMode;
return initArgs;
} }
std::optional<InitialWindowProperties> getInitialWindowProperties() {
return impl::s_initialWindowProperties;
}
void* getLibImHexModuleHandle() {
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
}
const std::map<std::string, std::string>& getInitArguments() {
return *impl::s_initArguments;
}
std::string getInitArgument(const std::string &key) {
if (impl::s_initArguments->contains(key))
return impl::s_initArguments->at(key);
else
return "";
}
static bool s_systemThemeDetection; static bool s_systemThemeDetection;
void enableSystemThemeDetection(bool enabled) { void enableSystemThemeDetection(bool enabled) {
s_systemThemeDetection = enabled; s_systemThemeDetection = enabled;
EventSettingsChanged::post();
EventOSThemeChanged::post(); EventOSThemeChanged::post();
} }
@@ -520,13 +633,13 @@ namespace hex {
} }
std::vector<std::fs::path> &getAdditionalFolderPaths() { static AutoReset<std::vector<std::fs::path>> s_additionalFolderPaths;
static std::vector<std::fs::path> additionalFolderPaths; const std::vector<std::fs::path>& getAdditionalFolderPaths() {
return additionalFolderPaths; return *s_additionalFolderPaths;
} }
void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths) { void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths) {
getAdditionalFolderPaths() = paths; s_additionalFolderPaths = paths;
} }
@@ -535,14 +648,30 @@ namespace hex {
} }
bool isPortableVersion() { bool isPortableVersion() {
return impl::s_portableVersion; static std::optional<bool> portable;
if (portable.has_value())
return portable.value();
if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
const auto flagFile = executablePath->parent_path() / "PORTABLE";
portable = wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile);
} else {
portable = false;
}
return portable.value();
} }
std::string getOSName() { std::string getOSName() {
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
return "Windows"; return "Windows";
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
return "Linux"; #if defined(OS_FREEBSD)
return "FreeBSD";
#else
return "Linux";
#endif
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
return "macOS"; return "macOS";
#elif defined(OS_WEB) #elif defined(OS_WEB)
@@ -560,7 +689,7 @@ namespace hex {
return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details; struct utsname details = { };
if (uname(&details) != 0) { if (uname(&details) != 0) {
return "Unknown"; return "Unknown";
@@ -592,13 +721,13 @@ namespace hex {
return "Unknown"; return "Unknown";
} }
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB) #elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
struct utsname details; struct utsname details = { };
if (uname(&details) != 0) { if (uname(&details) != 0) {
return "Unknown"; return "Unknown";
} }
return std::string(details.machine); return { details.machine };
#else #else
return "Unknown"; return "Unknown";
#endif #endif
@@ -618,20 +747,16 @@ namespace hex {
} }
std::string getCommitHash(bool longHash) { std::string getCommitHash(bool longHash) {
if (longHash) { #if defined GIT_COMMIT_HASH_LONG
#if defined GIT_COMMIT_HASH_LONG if (longHash) {
return GIT_COMMIT_HASH_LONG; return GIT_COMMIT_HASH_LONG;
#else } else {
return "Unknown"; return std::string(GIT_COMMIT_HASH_LONG).substr(0, 7);
#endif }
} #else
else { hex::unused(longHash);
#if defined GIT_COMMIT_HASH_SHORT return "Unknown";
return GIT_COMMIT_HASH_SHORT; #endif
#else
return "Unknown";
#endif
}
} }
std::string getCommitBranch() { std::string getCommitBranch() {
@@ -696,6 +821,12 @@ namespace hex {
return impl::s_lastFrameTime; return impl::s_lastFrameTime;
} }
void setWindowResizable(bool resizable) {
glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable));
impl::s_windowResizable = resizable;
}
} }
@@ -703,15 +834,14 @@ namespace hex {
namespace impl { namespace impl {
std::map<std::string, MessagingHandler> &getHandlers() { static AutoReset<std::map<std::string, MessagingHandler>> s_handlers;
static std::map<std::string, MessagingHandler> handlers; const std::map<std::string, MessagingHandler>& getHandlers() {
return *s_handlers;
return handlers;
} }
void runHandler(const std::string &eventName, const std::vector<u8> &args) { void runHandler(const std::string &eventName, const std::vector<u8> &args) {
const auto& handlers = impl::getHandlers(); const auto& handlers = getHandlers();
auto matchHandler = handlers.find(eventName); const auto matchHandler = handlers.find(eventName);
if (matchHandler == handlers.end()) { if (matchHandler == handlers.end()) {
log::error("Forward event handler {} not found", eventName); log::error("Forward event handler {} not found", eventName);
@@ -726,7 +856,7 @@ namespace hex {
void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) { void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) {
log::debug("Registered new forward event handler: {}", eventName); log::debug("Registered new forward event handler: {}", eventName);
impl::getHandlers().insert({ eventName, handler }); impl::s_handlers->insert({ eventName, handler });
} }
} }
@@ -735,13 +865,12 @@ namespace hex {
namespace impl { namespace impl {
std::vector<Font>& getFonts() { static AutoReset<std::vector<Font>> s_fonts;
static std::vector<Font> fonts; const std::vector<Font>& getFonts() {
return *s_fonts;
return fonts;
} }
static std::fs::path s_customFontPath; static AutoReset<std::fs::path> s_customFontPath;
void setCustomFontPath(const std::fs::path &path) { void setCustomFontPath(const std::fs::path &path) {
s_customFontPath = path; s_customFontPath = path;
} }
@@ -751,7 +880,7 @@ namespace hex {
s_fontSize = size; s_fontSize = size;
} }
static std::unique_ptr<ImFontAtlas> s_fontAtlas; static AutoReset<std::unique_ptr<ImFontAtlas>> s_fontAtlas;
void setFontAtlas(ImFontAtlas* fontAtlas) { void setFontAtlas(ImFontAtlas* fontAtlas) {
s_fontAtlas = std::unique_ptr<ImFontAtlas>(fontAtlas); s_fontAtlas = std::unique_ptr<ImFontAtlas>(fontAtlas);
} }
@@ -799,33 +928,35 @@ 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); wolv::io::File fontFile(path, wolv::io::File::Mode::Read);
if (!fontFile.isValid()) { if (!fontFile.isValid()) {
log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path)); log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path));
return; return;
} }
impl::getFonts().emplace_back(Font { impl::s_fonts->emplace_back(Font {
wolv::util::toUTF8String(path.filename()), wolv::util::toUTF8String(path.filename()),
fontFile.readVector(), fontFile.readVector(),
glyphRanges, glyphRanges,
offset, 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::getFonts().emplace_back(Font { impl::s_fonts->emplace_back(Font {
name, name,
{ data.begin(), data.end() }, { data.begin(), data.end() },
glyphRanges, glyphRanges,
offset, offset,
flags flags,
defaultSize
}); });
} }
std::fs::path &getCustomFontPath() { const std::fs::path& getCustomFontPath() {
return impl::s_customFontPath; return impl::s_customFontPath;
} }
@@ -834,7 +965,7 @@ namespace hex {
} }
ImFontAtlas* getFontAtlas() { ImFontAtlas* getFontAtlas() {
return impl::s_fontAtlas.get(); return impl::s_fontAtlas->get();
} }
ImFont* Bold() { ImFont* Bold() {

View File

@@ -2,20 +2,23 @@
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
#include <imgui.h> #include <imgui.h>
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/imhex_api.hpp>
#include <hex/ui/view.hpp> #include <hex/ui/view.hpp>
namespace hex { namespace hex {
namespace { namespace {
std::optional<std::fs::path> s_layoutPathToLoad; AutoReset<std::optional<std::fs::path>> s_layoutPathToLoad;
std::optional<std::string> s_layoutStringToLoad; AutoReset<std::optional<std::string>> s_layoutStringToLoad;
std::vector<LayoutManager::Layout> s_layouts; AutoReset<std::vector<LayoutManager::Layout>> s_layouts;
AutoReset<std::vector<LayoutManager::LoadCallback>> s_loadCallbacks;
AutoReset<std::vector<LayoutManager::StoreCallback>> s_storeCallbacks;
bool s_layoutLocked = false; bool s_layoutLocked = false;
@@ -33,7 +36,7 @@ namespace hex {
void LayoutManager::save(const std::string &name) { void LayoutManager::save(const std::string &name) {
auto fileName = name; auto fileName = name;
fileName = wolv::util::replaceStrings(fileName, " ", "_"); fileName = wolv::util::replaceStrings(fileName, " ", "_");
std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower); std::ranges::transform(fileName, fileName.begin(), tolower);
fileName += ".hexlyt"; fileName += ".hexlyt";
std::fs::path layoutPath; std::fs::path layoutPath;
@@ -61,18 +64,33 @@ namespace hex {
} }
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() { const std::vector<LayoutManager::Layout>& LayoutManager::getLayouts() {
return s_layouts; return s_layouts;
} }
void LayoutManager::removeLayout(const std::string& name) {
for (const auto &layout : *s_layouts) {
if (layout.name == name) {
if (wolv::io::File(layout.path, wolv::io::File::Mode::Write).remove()) {
log::info("Removed layout '{}'", name);
LayoutManager::reload();
} else {
log::error("Failed to remove layout '{}'", name);
}
return;
}
}
}
void LayoutManager::closeAllViews() { void LayoutManager::closeAllViews() {
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries()) for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
view->getWindowOpenState() = false; view->getWindowOpenState() = false;
} }
void LayoutManager::process() { void LayoutManager::process() {
if (s_layoutPathToLoad.has_value()) { if (s_layoutPathToLoad->has_value()) {
const auto pathString = wolv::util::toUTF8String(*s_layoutPathToLoad); const auto pathString = wolv::util::toUTF8String(**s_layoutPathToLoad);
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromDisk(pathString.c_str()); ImGui::LoadIniSettingsFromDisk(pathString.c_str());
@@ -81,9 +99,9 @@ namespace hex {
log::info("Loaded layout from {}", pathString); log::info("Loaded layout from {}", pathString);
} }
if (s_layoutStringToLoad.has_value()) { if (s_layoutStringToLoad->has_value()) {
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_layoutStringToLoad->c_str()); ImGui::LoadIniSettingsFromMemory((*s_layoutStringToLoad)->c_str());
s_layoutStringToLoad = std::nullopt; s_layoutStringToLoad = std::nullopt;
log::info("Loaded layout from string"); log::info("Loaded layout from string");
@@ -91,7 +109,7 @@ namespace hex {
} }
void LayoutManager::reload() { void LayoutManager::reload() {
s_layouts.clear(); s_layouts->clear();
for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) { for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) {
for (const auto &entry : std::fs::directory_iterator(directory)) { for (const auto &entry : std::fs::directory_iterator(directory)) {
@@ -107,7 +125,7 @@ namespace hex {
name[i] = char(std::toupper(name[i])); name[i] = char(std::toupper(name[i]));
} }
s_layouts.push_back({ s_layouts->push_back({
name, name,
path path
}); });
@@ -118,7 +136,7 @@ namespace hex {
void LayoutManager::reset() { void LayoutManager::reset() {
s_layoutPathToLoad.reset(); s_layoutPathToLoad.reset();
s_layoutStringToLoad.reset(); s_layoutStringToLoad.reset();
s_layouts.clear(); s_layouts->clear();
} }
bool LayoutManager::isLayoutLocked() { bool LayoutManager::isLayoutLocked() {
@@ -130,4 +148,24 @@ namespace hex {
s_layoutLocked = locked; s_layoutLocked = locked;
} }
void LayoutManager::registerLoadCallback(const LoadCallback &callback) {
s_loadCallbacks->push_back(callback);
}
void LayoutManager::registerStoreCallback(const StoreCallback &callback) {
s_storeCallbacks->push_back(callback);
}
void LayoutManager::onLoad(std::string_view line) {
for (const auto &callback : *s_loadCallbacks)
callback(line);
}
void LayoutManager::onStore(ImGuiTextBuffer *buffer) {
for (const auto &callback : *s_storeCallbacks)
callback(buffer);
}
} }

View File

@@ -1,5 +1,6 @@
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/api/localization_manager.hpp> #include <hex/api/localization_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex { namespace hex {
@@ -7,17 +8,17 @@ namespace hex {
namespace { namespace {
std::string s_fallbackLanguage; AutoReset<std::string> s_fallbackLanguage;
std::string s_selectedLanguage; AutoReset<std::string> s_selectedLanguage;
std::map<std::string, std::string> s_currStrings; AutoReset<std::map<std::string, std::string>> s_currStrings;
} }
namespace impl { namespace impl {
void resetLanguageStrings() { void resetLanguageStrings() {
s_currStrings.clear(); s_currStrings->clear();
s_selectedLanguage.clear(); s_selectedLanguage->clear();
} }
void setFallbackLanguage(const std::string &language) { void setFallbackLanguage(const std::string &language) {
@@ -41,25 +42,48 @@ namespace hex {
} }
void loadLanguage(const std::string &language) { void loadLanguage(const std::string &language) {
s_currStrings.clear();
auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions(); auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions();
if (!definitions.contains(language)) if (!definitions.contains(language))
return; return;
for (auto &definition : definitions[language]) s_currStrings->clear();
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end());
for (const auto &definition : definitions.at(language))
s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
const auto& fallbackLanguage = getFallbackLanguage(); const auto& fallbackLanguage = getFallbackLanguage();
if (language != fallbackLanguage) { if (language != fallbackLanguage && definitions.contains(fallbackLanguage)) {
for (auto &definition : definitions[fallbackLanguage]) for (const auto &definition : definitions.at(fallbackLanguage))
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end()); s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
} }
s_selectedLanguage = language; s_selectedLanguage = language;
} }
std::string getLocalizedString(const std::string& unlocalizedString, const std::string& language) {
if (language.empty())
return getLocalizedString(unlocalizedString, getSelectedLanguage());
auto &languageDefinitions = ContentRegistry::Language::impl::getLanguageDefinitions();
if (!languageDefinitions.contains(language))
return "";
std::string localizedString;
for (const auto &definition : languageDefinitions.at(language)) {
if (definition.getEntries().contains(unlocalizedString)) {
localizedString = definition.getEntries().at(unlocalizedString);
break;
}
}
if (localizedString.empty())
return getLocalizedString(unlocalizedString, getFallbackLanguage());
return localizedString;
}
const std::map<std::string, std::string> &getSupportedLanguages() { const std::map<std::string, std::string> &getSupportedLanguages() {
return ContentRegistry::Language::impl::getLanguages(); return ContentRegistry::Language::impl::getLanguages();
} }
@@ -79,6 +103,7 @@ namespace hex {
Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { } Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { }
Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { } Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
Lang::operator std::string() const { Lang::operator std::string() const {
return get(); return get();
} }
@@ -121,8 +146,8 @@ namespace hex {
const std::string &Lang::get() const { const std::string &Lang::get() const {
auto &lang = LocalizationManager::s_currStrings; auto &lang = LocalizationManager::s_currStrings;
if (lang.contains(m_unlocalizedString)) if (lang->contains(m_unlocalizedString))
return lang[m_unlocalizedString]; return lang->at(m_unlocalizedString);
else else
return m_unlocalizedString; return m_unlocalizedString;
} }

View File

@@ -3,6 +3,8 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/helpers/utils.hpp>
#include <wolv/utils/string.hpp> #include <wolv/utils/string.hpp>
@@ -16,40 +18,71 @@
namespace hex { namespace hex {
static uintptr_t loadLibrary(const std::fs::path &path) {
#if defined(OS_WINDOWS)
auto handle = uintptr_t(LoadLibraryW(path.c_str()));
if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) {
log::error("Loading library '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
return 0;
}
return handle;
#else
auto handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
if (handle == 0) {
log::error("Loading library '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
return 0;
}
return handle;
#endif
}
static void unloadLibrary(uintptr_t handle, const std::fs::path &path) {
#if defined(OS_WINDOWS)
if (handle != 0) {
if (FreeLibrary(HMODULE(handle)) == FALSE) {
log::error("Error when unloading library '{}': {}!", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError()));
}
}
#else
if (handle != 0) {
if (dlclose(reinterpret_cast<void*>(handle)) != 0) {
log::error("Error when unloading library '{}': {}!", path.filename().string(), dlerror());
}
}
#endif
}
Plugin::Plugin(const std::fs::path &path) : m_path(path) { Plugin::Plugin(const std::fs::path &path) : m_path(path) {
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename())); log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
#if defined(OS_WINDOWS) m_handle = loadLibrary(path);
m_handle = uintptr_t(LoadLibraryW(path.c_str())); if (m_handle == 0)
return;
if (m_handle == uintptr_t(INVALID_HANDLE_VALUE) || m_handle == 0) { const auto fileName = path.stem().string();
log::error("Loading plugin '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), std::system_category().message(::GetLastError()));
return;
}
#else
m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
if (m_handle == 0) { m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin");
log::error("Loading plugin '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror()); m_functions.initializeLibraryFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>(hex::format("initializeLibrary_{}", fileName));
return; m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName");
} m_functions.getLibraryNameFunction = getPluginFunction<PluginFunctions::GetLibraryNameFunc>(hex::format("getLibraryName_{}", fileName));
#endif m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor");
m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription");
m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin"); m_functions.getCompatibleVersionFunction = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>("getCompatibleVersion");
m_functions.initializeLibraryFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializeLibrary"); m_functions.setImGuiContextFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>("setImGuiContext");
m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName"); m_functions.setImGuiContextLibraryFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>(hex::format("setImGuiContext_{}", fileName));
m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor"); m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription"); m_functions.getFeaturesFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getFeatures");
m_functions.getCompatibleVersionFunction = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>("getCompatibleVersion");
m_functions.setImGuiContextFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>("setImGuiContext");
m_functions.isBuiltinPluginFunction = getPluginFunction<PluginFunctions::IsBuiltinPluginFunc>("isBuiltinPlugin");
m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
m_functions.getFeaturesFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getFeatures");
} }
Plugin::Plugin(const hex::PluginFunctions &functions) { Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) {
m_handle = 0; m_handle = 0;
m_functions = functions; m_functions = functions;
m_path = name;
m_addedManually = true;
} }
@@ -58,6 +91,7 @@ namespace hex {
other.m_handle = 0; other.m_handle = 0;
m_path = std::move(other.m_path); m_path = std::move(other.m_path);
m_addedManually = other.m_addedManually;
m_functions = other.m_functions; m_functions = other.m_functions;
other.m_functions = {}; other.m_functions = {};
@@ -68,6 +102,7 @@ namespace hex {
other.m_handle = 0; other.m_handle = 0;
m_path = std::move(other.m_path); m_path = std::move(other.m_path);
m_addedManually = other.m_addedManually;
m_functions = other.m_functions; m_functions = other.m_functions;
other.m_functions = {}; other.m_functions = {};
@@ -76,24 +111,29 @@ namespace hex {
} }
Plugin::~Plugin() { Plugin::~Plugin() {
#if defined(OS_WINDOWS) if (isLoaded()) {
if (m_handle != 0) log::info("Trying to unload plugin '{}'", getPluginName());
FreeLibrary(HMODULE(m_handle)); }
#else
if (m_handle != 0) unloadLibrary(m_handle, m_path);
dlclose(reinterpret_cast<void*>(m_handle));
#endif
} }
bool Plugin::initializePlugin() const { bool Plugin::initializePlugin() const {
const auto pluginName = wolv::util::toUTF8String(m_path.filename()); const auto pluginName = wolv::util::toUTF8String(m_path.filename());
if (this->isLibraryPlugin()) { if (this->isLibraryPlugin()) {
m_functions.initializeLibraryFunction();
log::info("Library '{}' initialized successfully", pluginName);
m_initialized = true;
return true; return true;
} }
const auto requestedVersion = getCompatibleVersion(); const auto requestedVersion = getCompatibleVersion();
if (requestedVersion != ImHexApi::System::getImHexVersion()) { const auto imhexVersion = ImHexApi::System::getImHexVersion();
if (!imhexVersion.starts_with(requestedVersion)) {
if (requestedVersion.empty()) { if (requestedVersion.empty()) {
log::warn("Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex.", wolv::util::toUTF8String(m_path.filename())); log::warn("Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex.", wolv::util::toUTF8String(m_path.filename()));
} else { } else {
@@ -128,7 +168,7 @@ namespace hex {
return m_functions.getPluginNameFunction(); return m_functions.getPluginNameFunction();
} else { } else {
if (this->isLibraryPlugin()) if (this->isLibraryPlugin())
return "Library Plugin"; return m_functions.getLibraryNameFunction();
else else
return hex::format("Unknown Plugin @ 0x{0:016X}", m_handle); return hex::format("Unknown Plugin @ 0x{0:016X}", m_handle);
} }
@@ -161,13 +201,6 @@ namespace hex {
m_functions.setImGuiContextFunction(ctx); m_functions.setImGuiContextFunction(ctx);
} }
[[nodiscard]] bool Plugin::isBuiltinPlugin() const {
if (m_functions.isBuiltinPluginFunction != nullptr)
return m_functions.isBuiltinPluginFunction();
else
return false;
}
const std::fs::path &Plugin::getPath() const { const std::fs::path &Plugin::getPath() const {
return m_path; return m_path;
} }
@@ -182,7 +215,10 @@ namespace hex {
std::span<SubCommand> Plugin::getSubCommands() const { std::span<SubCommand> Plugin::getSubCommands() const {
if (m_functions.getSubCommandsFunction != nullptr) { if (m_functions.getSubCommandsFunction != nullptr) {
auto result = m_functions.getSubCommandsFunction(); const auto result = m_functions.getSubCommandsFunction();
if (result == nullptr)
return { };
return *static_cast<std::vector<SubCommand>*>(result); return *static_cast<std::vector<SubCommand>*>(result);
} else { } else {
return { }; return { };
@@ -191,7 +227,10 @@ namespace hex {
std::span<Feature> Plugin::getFeatures() const { std::span<Feature> Plugin::getFeatures() const {
if (m_functions.getFeaturesFunction != nullptr) { if (m_functions.getFeaturesFunction != nullptr) {
auto result = m_functions.getFeaturesFunction(); const auto result = m_functions.getFeaturesFunction();
if (result == nullptr)
return { };
return *static_cast<std::vector<Feature>*>(result); return *static_cast<std::vector<Feature>*>(result);
} else { } else {
return { }; return { };
@@ -203,7 +242,9 @@ namespace hex {
m_functions.initializePluginFunction == nullptr; m_functions.initializePluginFunction == nullptr;
} }
bool Plugin::wasAddedManually() const {
return m_addedManually;
}
void *Plugin::getPluginFunction(const std::string &symbol) const { void *Plugin::getPluginFunction(const std::string &symbol) const {
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
@@ -213,61 +254,146 @@ namespace hex {
#endif #endif
} }
AutoReset<std::vector<std::fs::path>> PluginManager::s_pluginPaths, PluginManager::s_pluginLoadPaths;
void PluginManager::addLoadPath(const std::fs::path& path) {
s_pluginLoadPaths->emplace_back(path);
}
bool PluginManager::load() {
bool success = true;
for (const auto &loadPath : getPluginLoadPaths())
success = PluginManager::load(loadPath) && success;
return success;
}
bool PluginManager::load(const std::fs::path &pluginFolder) { bool PluginManager::load(const std::fs::path &pluginFolder) {
if (!wolv::io::fs::exists(pluginFolder)) if (!wolv::io::fs::exists(pluginFolder))
return false; return false;
getPluginPaths().push_back(pluginFolder); s_pluginPaths->push_back(pluginFolder);
// Load library plugins first // Load library plugins first
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) { for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib") if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib") {
getPlugins().emplace_back(pluginPath.path()); if (!isPluginLoaded(pluginPath.path())) {
getPluginsMutable().emplace_back(pluginPath.path());
}
}
} }
// Load regular plugins afterwards // Load regular plugins afterwards
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) { for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug") if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug") {
getPlugins().emplace_back(pluginPath.path()); if (!isPluginLoaded(pluginPath.path())) {
getPluginsMutable().emplace_back(pluginPath.path());
}
}
} }
std::erase_if(getPlugins(), [](const Plugin &plugin) { std::erase_if(getPluginsMutable(), [](const Plugin &plugin) {
return !plugin.isValid(); return !plugin.isValid();
}); });
if (getPlugins().empty())
return false;
return true; return true;
} }
AutoReset<std::vector<uintptr_t>> PluginManager::s_loadedLibraries;
bool PluginManager::loadLibraries() {
bool success = true;
for (const auto &loadPath : fs::getDefaultPaths(fs::ImHexPath::Libraries))
success = PluginManager::loadLibraries(loadPath) && success;
return success;
}
bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) {
bool success = true;
for (const auto &entry : std::fs::directory_iterator(libraryFolder)) {
if (!(entry.path().extension() == ".dll" || entry.path().extension() == ".so" || entry.path().extension() == ".dylib"))
continue;
auto handle = loadLibrary(entry);
if (handle == 0) {
success = false;
}
PluginManager::s_loadedLibraries->push_back(handle);
}
return success;
}
void PluginManager::initializeNewPlugins() {
for (const auto &plugin : getPlugins()) {
if (!plugin.isLoaded())
hex::unused(plugin.initializePlugin());
}
}
void PluginManager::unload() { void PluginManager::unload() {
getPlugins().clear(); s_pluginPaths->clear();
getPluginPaths().clear();
// Unload plugins in reverse order
auto &plugins = getPluginsMutable();
std::list<Plugin> savedPlugins;
while (!plugins.empty()) {
if (plugins.back().wasAddedManually())
savedPlugins.emplace_front(std::move(plugins.back()));
plugins.pop_back();
}
while (!s_loadedLibraries->empty()) {
unloadLibrary(s_loadedLibraries->back(), "");
s_loadedLibraries->pop_back();
}
getPluginsMutable() = std::move(savedPlugins);
} }
void PluginManager::reload() { void PluginManager::addPlugin(const std::string &name, hex::PluginFunctions functions) {
auto paths = getPluginPaths(); getPluginsMutable().emplace_back(name, functions);
PluginManager::unload();
for (const auto &path : paths)
PluginManager::load(path);
} }
void PluginManager::addPlugin(hex::PluginFunctions functions) { const std::list<Plugin>& PluginManager::getPlugins() {
getPlugins().emplace_back(functions); return getPluginsMutable();
} }
std::vector<Plugin> &PluginManager::getPlugins() {
static std::vector<Plugin> plugins;
std::list<Plugin>& PluginManager::getPluginsMutable() {
static std::list<Plugin> plugins;
return plugins; return plugins;
} }
std::vector<std::fs::path> &PluginManager::getPluginPaths() { Plugin* PluginManager::getPlugin(const std::string &name) {
static std::vector<std::fs::path> pluginPaths; for (auto &plugin : getPluginsMutable()) {
if (plugin.getPluginName() == name)
return &plugin;
}
return pluginPaths; return nullptr;
}
const std::vector<std::fs::path>& PluginManager::getPluginPaths() {
return s_pluginPaths;
}
const std::vector<std::fs::path>& PluginManager::getPluginLoadPaths() {
return s_pluginLoadPaths;
}
bool PluginManager::isPluginLoaded(const std::fs::path &path) {
return std::ranges::any_of(getPlugins(), [&path](const Plugin &plugin) {
return plugin.getPath().filename() == path.filename();
});
} }
} }

View File

@@ -1,6 +1,6 @@
#include <hex/api/project_file_manager.hpp> #include <hex/api/project_file_manager.hpp>
#include <hex/providers/provider.hpp> #include <hex/helpers/auto_reset.hpp>
#include <wolv/io/fs.hpp> #include <wolv/io/fs.hpp>
@@ -8,13 +8,13 @@ namespace hex {
namespace { namespace {
std::vector<ProjectFile::Handler> s_handlers; AutoReset<std::vector<ProjectFile::Handler>> s_handlers;
std::vector<ProjectFile::ProviderHandler> s_providerHandlers; AutoReset<std::vector<ProjectFile::ProviderHandler>> s_providerHandlers;
std::fs::path s_currProjectPath; AutoReset<std::fs::path> s_currProjectPath;
std::function<bool(const std::fs::path&)> s_loadProjectFunction; AutoReset<std::function<bool(const std::fs::path&)>> s_loadProjectFunction;
std::function<bool(std::optional<std::fs::path>, bool)> s_storeProjectFunction; AutoReset<std::function<bool(std::optional<std::fs::path>, bool)>> s_storeProjectFunction;
} }
@@ -28,19 +28,19 @@ namespace hex {
} }
bool ProjectFile::load(const std::fs::path &filePath) { bool ProjectFile::load(const std::fs::path &filePath) {
return s_loadProjectFunction(filePath); return (*s_loadProjectFunction)(filePath);
} }
bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) { bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) {
return s_storeProjectFunction(std::move(filePath), updateLocation); return (*s_storeProjectFunction)(std::move(filePath), updateLocation);
} }
bool ProjectFile::hasPath() { bool ProjectFile::hasPath() {
return !s_currProjectPath.empty(); return !s_currProjectPath->empty();
} }
void ProjectFile::clearPath() { void ProjectFile::clearPath() {
s_currProjectPath.clear(); s_currProjectPath->clear();
} }
std::fs::path ProjectFile::getPath() { std::fs::path ProjectFile::getPath() {
@@ -51,11 +51,19 @@ namespace hex {
s_currProjectPath = path; s_currProjectPath = path;
} }
std::vector<ProjectFile::Handler> &ProjectFile::getHandlers() { void ProjectFile::registerHandler(const Handler &handler) {
s_handlers->push_back(handler);
}
void ProjectFile::registerPerProviderHandler(const ProviderHandler &handler) {
s_providerHandlers->push_back(handler);
}
const std::vector<ProjectFile::Handler>& ProjectFile::getHandlers() {
return s_handlers; return s_handlers;
} }
std::vector<ProjectFile::ProviderHandler> &ProjectFile::getProviderHandlers() { const std::vector<ProjectFile::ProviderHandler>& ProjectFile::getProviderHandlers() {
return s_providerHandlers; return s_providerHandlers;
} }

View File

@@ -1,6 +1,7 @@
#include <hex/api/shortcut_manager.hpp> #include <hex/api/shortcut_manager.hpp>
#include <imgui.h> #include <imgui.h>
#include <hex/api/content_registry.hpp> #include <hex/api/content_registry.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <hex/ui/view.hpp> #include <hex/ui/view.hpp>
@@ -8,7 +9,7 @@ namespace hex {
namespace { namespace {
std::map<Shortcut, ShortcutManager::ShortcutEntry> s_globalShortcuts; AutoReset<std::map<Shortcut, ShortcutManager::ShortcutEntry>> s_globalShortcuts;
std::atomic<bool> s_paused; std::atomic<bool> s_paused;
std::optional<Shortcut> s_prevShortcut; std::optional<Shortcut> s_prevShortcut;
@@ -16,7 +17,7 @@ namespace hex {
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) { void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
s_globalShortcuts.insert({ shortcut, { shortcut, unlocalizedName, callback } }); s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback } });
} }
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) { void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
@@ -56,8 +57,8 @@ namespace hex {
} }
} }
void ShortcutManager::process(const std::unique_ptr<View> &currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) { void ShortcutManager::process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode); const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
if (keyCode != 0) if (keyCode != 0)
s_prevShortcut = Shortcut(pressedShortcut.getKeys()); s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@@ -65,7 +66,7 @@ namespace hex {
} }
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) { void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode); const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);
if (keyCode != 0) if (keyCode != 0)
s_prevShortcut = Shortcut(pressedShortcut.getKeys()); s_prevShortcut = Shortcut(pressedShortcut.getKeys());
@@ -73,7 +74,7 @@ namespace hex {
} }
void ShortcutManager::clearShortcuts() { void ShortcutManager::clearShortcuts() {
s_globalShortcuts.clear(); s_globalShortcuts->clear();
} }
void ShortcutManager::resumeShortcuts() { void ShortcutManager::resumeShortcuts() {
@@ -90,17 +91,18 @@ namespace hex {
} }
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() { std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() {
std::vector<ShortcutManager::ShortcutEntry> result; std::vector<ShortcutEntry> result;
for (auto &[shortcut, entry] : s_globalShortcuts) for (auto &[shortcut, entry] : *s_globalShortcuts)
result.push_back(entry); result.push_back(entry);
return result; return result;
} }
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) { std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) {
std::vector<ShortcutManager::ShortcutEntry> result; std::vector<ShortcutEntry> result;
result.reserve(view->m_shortcuts.size());
for (auto &[shortcut, entry] : view->m_shortcuts) for (auto &[shortcut, entry] : view->m_shortcuts)
result.push_back(entry); result.push_back(entry);

View File

@@ -4,59 +4,65 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <algorithm> #include <algorithm>
#include <ranges>
#include <jthread.hpp> #include <jthread.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
#include <processthreadsapi.h> #include <processthreadsapi.h>
#include <hex/helpers/utils.hpp>
#else #else
#include <pthread.h> #include <pthread.h>
#endif #endif
namespace {
struct SourceLocationWrapper {
std::source_location location;
bool operator==(const SourceLocationWrapper &other) const {
return location.file_name() == other.location.file_name() &&
location.function_name() == other.location.function_name() &&
location.column() == other.location.column() &&
location.line() == other.location.line();
}
};
}
template<>
struct std::hash<SourceLocationWrapper> {
std::size_t operator()(const SourceLocationWrapper& s) const noexcept {
auto h1 = std::hash<std::string>{}(s.location.file_name());
auto h2 = std::hash<std::string>{}(s.location.function_name());
auto h3 = std::hash<u32>{}(s.location.column());
auto h4 = std::hash<u32>{}(s.location.line());
return (h1 << 0) ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
}
};
namespace hex { namespace hex {
namespace { namespace {
std::mutex s_deferredCallsMutex, s_tasksFinishedMutex; std::recursive_mutex s_deferredCallsMutex, s_tasksFinishedMutex;
std::list<std::shared_ptr<Task>> s_tasks, s_taskQueue; std::list<std::shared_ptr<Task>> s_tasks, s_taskQueue;
std::list<std::function<void()>> s_deferredCalls; std::list<std::function<void()>> s_deferredCalls;
std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;
std::list<std::function<void()>> s_tasksFinishedCallbacks; std::list<std::function<void()>> s_tasksFinishedCallbacks;
std::mutex s_queueMutex; std::mutex s_queueMutex;
std::condition_variable s_jobCondVar; std::condition_variable s_jobCondVar;
std::vector<std::jthread> s_workers; std::vector<std::jthread> s_workers;
thread_local std::array<char, 256> s_currentThreadName;
thread_local Task* s_currentTask = nullptr;
} }
static void setThreadName(const std::string &name) {
#if defined(OS_WINDOWS)
typedef struct tagTHREADNAME_INFO {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
} THREADNAME_INFO;
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = ::GetCurrentThreadId();
info.dwFlags = 0;
constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388;
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
#elif defined(OS_LINUX)
pthread_setname_np(pthread_self(), name.c_str());
#elif defined(OS_WEB)
hex::unused(name);
#elif defined(OS_MACOS)
pthread_setname_np(name.c_str());
#endif
}
Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function) Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function)
: m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { } : m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { }
@@ -93,6 +99,19 @@ namespace hex {
throw TaskInterruptor(); throw TaskInterruptor();
} }
void Task::update() const {
if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
throw TaskInterruptor();
}
void Task::increment() {
m_currValue.fetch_add(1, std::memory_order_relaxed);
if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
throw TaskInterruptor();
}
void Task::setMaxValue(u64 value) { void Task::setMaxValue(u64 value) {
m_maxValue = value; m_maxValue = value;
} }
@@ -170,7 +189,7 @@ namespace hex {
bool TaskHolder::isRunning() const { bool TaskHolder::isRunning() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@@ -178,7 +197,7 @@ namespace hex {
} }
bool TaskHolder::hadException() const { bool TaskHolder::hadException() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@@ -186,7 +205,7 @@ namespace hex {
} }
bool TaskHolder::shouldInterrupt() const { bool TaskHolder::shouldInterrupt() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@@ -194,7 +213,7 @@ namespace hex {
} }
bool TaskHolder::wasInterrupted() const { bool TaskHolder::wasInterrupted() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return false;
@@ -202,7 +221,7 @@ namespace hex {
} }
void TaskHolder::interrupt() const { void TaskHolder::interrupt() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return; return;
@@ -210,9 +229,9 @@ namespace hex {
} }
u32 TaskHolder::getProgress() const { u32 TaskHolder::getProgress() const {
auto task = m_task.lock(); const auto &task = m_task.lock();
if (!task) if (!task)
return false; return 0;
// If the max value is 0, the task has no progress // If the max value is 0, the task has no progress
if (task->getMaxValue() == 0) if (task->getMaxValue() == 0)
@@ -222,7 +241,6 @@ namespace hex {
return u32((task->getValue() * 100) / task->getMaxValue()); return u32((task->getValue() * 100) / task->getMaxValue());
} }
void TaskManager::init() { void TaskManager::init() {
const auto threadCount = std::thread::hardware_concurrency(); const auto threadCount = std::thread::hardware_concurrency();
@@ -235,7 +253,7 @@ namespace hex {
std::shared_ptr<Task> task; std::shared_ptr<Task> task;
// Set the thread name to "Idle Task" while waiting for a task // Set the thread name to "Idle Task" while waiting for a task
setThreadName("Idle Task"); TaskManager::setCurrentThreadName("Idle Task");
{ {
// Wait for a task to be added to the queue // Wait for a task to be added to the queue
@@ -251,11 +269,13 @@ namespace hex {
// Grab the next task from the queue // Grab the next task from the queue
task = std::move(s_taskQueue.front()); task = std::move(s_taskQueue.front());
s_taskQueue.pop_front(); s_taskQueue.pop_front();
s_currentTask = task.get();
} }
try { try {
// Set the thread name to the name of the task // Set the thread name to the name of the task
setThreadName(Lang(task->m_unlocalizedName)); TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
// Execute the task // Execute the task
task->m_function(*task); task->m_function(*task);
@@ -276,6 +296,7 @@ namespace hex {
task->exception("Unknown Exception"); task->exception("Unknown Exception");
} }
s_currentTask = nullptr;
task->finish(); task->finish();
} }
}); });
@@ -284,7 +305,7 @@ namespace hex {
void TaskManager::exit() { void TaskManager::exit() {
// Interrupt all tasks // Interrupt all tasks
for (auto &task : s_tasks) { for (const auto &task : s_tasks) {
task->interrupt(); task->interrupt();
} }
@@ -302,6 +323,7 @@ namespace hex {
s_taskQueue.clear(); s_taskQueue.clear();
s_deferredCalls.clear(); s_deferredCalls.clear();
s_onceDeferredCalls.clear();
s_tasksFinishedCallbacks.clear(); s_tasksFinishedCallbacks.clear();
} }
@@ -349,14 +371,19 @@ namespace hex {
} }
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() { Task& TaskManager::getCurrentTask() {
return *s_currentTask;
}
const std::list<std::shared_ptr<Task>>& TaskManager::getRunningTasks() {
return s_tasks; return s_tasks;
} }
size_t TaskManager::getRunningTaskCount() { size_t TaskManager::getRunningTaskCount() {
std::scoped_lock lock(s_queueMutex); std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){ return std::ranges::count_if(s_tasks, [](const auto &task){
return !task->isBackgroundTask(); return !task->isBackgroundTask();
}); });
} }
@@ -364,7 +391,7 @@ namespace hex {
size_t TaskManager::getRunningBackgroundTaskCount() { size_t TaskManager::getRunningBackgroundTaskCount() {
std::scoped_lock lock(s_queueMutex); std::scoped_lock lock(s_queueMutex);
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){ return std::ranges::count_if(s_tasks, [](const auto &task){
return task->isBackgroundTask(); return task->isBackgroundTask();
}); });
} }
@@ -376,13 +403,24 @@ namespace hex {
s_deferredCalls.push_back(function); s_deferredCalls.push_back(function);
} }
void TaskManager::doLaterOnce(const std::function<void()> &function, std::source_location location) {
std::scoped_lock lock(s_deferredCallsMutex);
s_onceDeferredCalls[SourceLocationWrapper{ location }] = function;
}
void TaskManager::runDeferredCalls() { void TaskManager::runDeferredCalls() {
std::scoped_lock lock(s_deferredCallsMutex); std::scoped_lock lock(s_deferredCallsMutex);
for (const auto &call : s_deferredCalls) while (!s_deferredCalls.empty()) {
call(); auto callback = s_deferredCalls.front();
s_deferredCalls.pop_front();
s_deferredCalls.clear(); callback();
}
while (!s_onceDeferredCalls.empty()) {
auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());
node.mapped()();
}
} }
void TaskManager::runWhenTasksFinished(const std::function<void()> &function) { void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {
@@ -391,4 +429,54 @@ namespace hex {
s_tasksFinishedCallbacks.push_back(function); s_tasksFinishedCallbacks.push_back(function);
} }
void TaskManager::setCurrentThreadName(const std::string &name) {
std::ranges::fill(s_currentThreadName, '\0');
std::ranges::copy(name | std::views::take(255), s_currentThreadName.begin());
#if defined(OS_WINDOWS)
using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription);
static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(
reinterpret_cast<uintptr_t>(
::GetProcAddress(
::GetModuleHandle("Kernel32.dll"),
"SetThreadDescription"
)
)
);
if (setThreadDescription != nullptr) {
const auto longName = hex::utf8ToUtf16(name);
setThreadDescription(::GetCurrentThread(), longName.c_str());
} else {
struct THREADNAME_INFO {
DWORD dwType;
LPCSTR szName;
DWORD dwThreadID;
DWORD dwFlags;
};
THREADNAME_INFO info = { };
info.dwType = 0x1000;
info.szName = name.c_str();
info.dwThreadID = ::GetCurrentThreadId();
info.dwFlags = 0;
constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388;
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
}
#elif defined(OS_LINUX)
pthread_setname_np(pthread_self(), name.c_str());
#elif defined(OS_WEB)
hex::unused(name);
#elif defined(OS_MACOS)
pthread_setname_np(name.c_str());
#endif
}
std::string TaskManager::getCurrentThreadName() {
return s_currentThreadName.data();
}
} }

View File

@@ -3,6 +3,7 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@@ -10,21 +11,21 @@ namespace hex {
namespace { namespace {
std::map<std::string, nlohmann::json> s_themes; AutoReset<std::map<std::string, nlohmann::json>> s_themes;
std::map<std::string, ThemeManager::ThemeHandler> s_themeHandlers; AutoReset<std::map<std::string, ThemeManager::ThemeHandler>> s_themeHandlers;
std::map<std::string, ThemeManager::StyleHandler> s_styleHandlers; AutoReset<std::map<std::string, ThemeManager::StyleHandler>> s_styleHandlers;
std::string s_imageTheme; AutoReset<std::string> s_imageTheme;
std::string s_currTheme; AutoReset<std::string> s_currTheme;
} }
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) { void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
s_themeHandlers[name] = { colorMap, getFunction, setFunction }; (*s_themeHandlers)[name] = { colorMap, getFunction, setFunction };
} }
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) { void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
s_styleHandlers[name] = { styleMap }; (*s_styleHandlers)[name] = { styleMap };
} }
void ThemeManager::addTheme(const std::string &content) { void ThemeManager::addTheme(const std::string &content) {
@@ -32,7 +33,7 @@ namespace hex {
auto theme = nlohmann::json::parse(content); auto theme = nlohmann::json::parse(content);
if (theme.contains("name") && theme.contains("colors")) { if (theme.contains("name") && theme.contains("colors")) {
s_themes[theme["name"].get<std::string>()] = theme; (*s_themes)[theme["name"].get<std::string>()] = theme;
} else { } else {
hex::log::error("Invalid theme file"); hex::log::error("Invalid theme file");
} }
@@ -77,7 +78,7 @@ namespace hex {
{ "base", s_currTheme } { "base", s_currTheme }
}; };
for (const auto &[type, handler] : s_themeHandlers) { for (const auto &[type, handler] : *s_themeHandlers) {
theme["colors"][type] = {}; theme["colors"][type] = {};
for (const auto &[key, value] : handler.colorMap) { for (const auto &[key, value] : handler.colorMap) {
@@ -86,7 +87,7 @@ namespace hex {
} }
} }
for (const auto &[type, handler] : s_styleHandlers) { for (const auto &[type, handler] : *s_styleHandlers) {
theme["styles"][type] = {}; theme["styles"][type] = {};
for (const auto &[key, style] : handler.styleMap) { for (const auto &[key, style] : handler.styleMap) {
@@ -105,17 +106,17 @@ namespace hex {
} }
void ThemeManager::changeTheme(std::string name) { void ThemeManager::changeTheme(std::string name) {
if (!s_themes.contains(name)) { if (!s_themes->contains(name)) {
if (s_themes.empty()) { if (s_themes->empty()) {
return; return;
} else { } else {
const std::string &defaultTheme = s_themes.begin()->first; const std::string &defaultTheme = s_themes->begin()->first;
hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme); hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme);
name = defaultTheme; name = defaultTheme;
} }
} }
const auto &theme = s_themes[name]; const auto &theme = (*s_themes)[name];
if (theme.contains("base")) { if (theme.contains("base")) {
if (theme["base"].is_string()) { if (theme["base"].is_string()) {
@@ -126,14 +127,14 @@ namespace hex {
} }
} }
if (theme.contains("colors") && !s_themeHandlers.empty()) { if (theme.contains("colors") && !s_themeHandlers->empty()) {
for (const auto&[type, content] : theme["colors"].items()) { for (const auto&[type, content] : theme["colors"].items()) {
if (!s_themeHandlers.contains(type)) { if (!s_themeHandlers->contains(type)) {
log::warn("No theme handler found for '{}'", type); log::warn("No theme handler found for '{}'", type);
continue; continue;
} }
const auto &handler = s_themeHandlers[type]; const auto &handler = (*s_themeHandlers)[type];
for (const auto &[key, value] : content.items()) { for (const auto &[key, value] : content.items()) {
if (!handler.colorMap.contains(key)) { if (!handler.colorMap.contains(key)) {
log::warn("No color found for '{}.{}'", type, key); log::warn("No color found for '{}.{}'", type, key);
@@ -146,19 +147,19 @@ namespace hex {
continue; continue;
} }
s_themeHandlers[type].setFunction(s_themeHandlers[type].colorMap.at(key), color.value()); (*s_themeHandlers)[type].setFunction((*s_themeHandlers)[type].colorMap.at(key), color.value());
} }
} }
} }
if (theme.contains("styles") && !s_styleHandlers.empty()) { if (theme.contains("styles") && !s_styleHandlers->empty()) {
for (const auto&[type, content] : theme["styles"].items()) { for (const auto&[type, content] : theme["styles"].items()) {
if (!s_styleHandlers.contains(type)) { if (!s_styleHandlers->contains(type)) {
log::warn("No style handler found for '{}'", type); log::warn("No style handler found for '{}'", type);
continue; continue;
} }
auto &handler = s_styleHandlers[type]; auto &handler = (*s_styleHandlers)[type];
for (const auto &[key, value] : content.items()) { for (const auto &[key, value] : content.items()) {
if (!handler.styleMap.contains(key)) if (!handler.styleMap.contains(key))
continue; continue;
@@ -167,12 +168,12 @@ namespace hex {
const float scale = style.needsScaling ? 1_scaled : 1.0F; const float scale = style.needsScaling ? 1_scaled : 1.0F;
if (value.is_number_float()) { if (value.is_number_float()) {
if (auto newValue = std::get_if<float*>(&style.value); newValue != nullptr) if (const auto newValue = std::get_if<float*>(&style.value); newValue != nullptr)
**newValue = value.get<float>() * scale; **newValue = value.get<float>() * scale;
else else
log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name); log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name);
} else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) { } else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) {
if (auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr) if (const auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr)
**newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale); **newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale);
else else
log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name); log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name);
@@ -203,26 +204,26 @@ namespace hex {
std::vector<std::string> ThemeManager::getThemeNames() { std::vector<std::string> ThemeManager::getThemeNames() {
std::vector<std::string> themeNames; std::vector<std::string> themeNames;
for (const auto &[name, theme] : s_themes) for (const auto &[name, theme] : *s_themes)
themeNames.push_back(name); themeNames.push_back(name);
return themeNames; return themeNames;
} }
void ThemeManager::reset() { void ThemeManager::reset() {
s_themes.clear(); s_themes->clear();
s_styleHandlers.clear(); s_styleHandlers->clear();
s_themeHandlers.clear(); s_themeHandlers->clear();
s_imageTheme.clear(); s_imageTheme->clear();
s_currTheme.clear(); s_currTheme->clear();
} }
std::map<std::string, ThemeManager::ThemeHandler> &ThemeManager::getThemeHandlers() { const std::map<std::string, ThemeManager::ThemeHandler> &ThemeManager::getThemeHandlers() {
return s_themeHandlers; return s_themeHandlers;
} }
std::map<std::string, ThemeManager::StyleHandler> &ThemeManager::getStyleHandlers() { const std::map<std::string, ThemeManager::StyleHandler> &ThemeManager::getStyleHandlers() {
return s_styleHandlers; return s_styleHandlers;
} }

View File

@@ -3,6 +3,8 @@
#include <hex/api/localization_manager.hpp> #include <hex/api/localization_manager.hpp>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <imgui_internal.h> #include <imgui_internal.h>
#include <hex/helpers/utils.hpp> #include <hex/helpers/utils.hpp>
#include <wolv/utils/core.hpp> #include <wolv/utils/core.hpp>
@@ -13,11 +15,11 @@ namespace hex {
namespace { namespace {
std::map<std::string, TutorialManager::Tutorial> s_tutorials; AutoReset<std::map<std::string, TutorialManager::Tutorial>> s_tutorials;
decltype(s_tutorials)::iterator s_currentTutorial = s_tutorials.end(); auto s_currentTutorial = s_tutorials->end();
std::map<ImGuiID, std::string> s_highlights; AutoReset<std::map<ImGuiID, std::string>> s_highlights;
std::vector<std::pair<ImRect, std::string>> s_highlightDisplays; AutoReset<std::vector<std::pair<ImRect, std::string>>> s_highlightDisplays;
class IDStack { class IDStack {
@@ -67,19 +69,19 @@ namespace hex {
TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) { TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {
return s_tutorials.try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second; return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
} }
void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) { void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {
s_currentTutorial = s_tutorials.find(unlocalizedName); s_currentTutorial = s_tutorials->find(unlocalizedName);
if (s_currentTutorial == s_tutorials.end()) if (s_currentTutorial == s_tutorials->end())
return; return;
s_currentTutorial->second.start(); s_currentTutorial->second.start();
} }
void TutorialManager::drawHighlights() { void TutorialManager::drawHighlights() {
for (const auto &[rect, unlocalizedText] : s_highlightDisplays) { for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) {
const auto drawList = ImGui::GetForegroundDrawList(); const auto drawList = ImGui::GetForegroundDrawList();
drawList->PushClipRectFullScreen(); drawList->PushClipRectFullScreen();
@@ -105,23 +107,24 @@ namespace hex {
windowSize.y = textSize.y + margin.y * 2; windowSize.y = textSize.y + margin.y * 2;
if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y) if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y)
windowPos.y = rect.Min.y - windowSize.y - 10_scaled; windowPos.y = rect.Min.y - windowSize.y - 15_scaled;
if (windowPos.y < mainWindowPos.y) if (windowPos.y < mainWindowPos.y)
windowPos.y = mainWindowPos.y + 10_scaled; windowPos.y = rect.Min.y + 10_scaled;
auto &style = ImGui::GetStyle(); ImGui::SetNextWindowPos(windowPos);
ImGui::SetNextWindowSize(windowSize);
ImVec2 shadowOffset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist; ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
drawList->AddRectFilled(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowBg) | 0xFF000000); if (ImGui::Begin(unlocalizedText.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) {
drawList->AddRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_Border)); ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
drawList->AddShadowRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowShadow), style.WindowShadowSize, shadowOffset, ImDrawFlags_ShadowCutOutShapeBackground); ImGuiExt::TextFormattedWrapped("{}", text);
drawList->AddText(nullptr, 0.0F, windowPos + margin, ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, windowSize.x - margin.x * 2); }
ImGui::End();
} }
} }
drawList->PopClipRect(); drawList->PopClipRect();
} }
s_highlightDisplays.clear(); s_highlightDisplays->clear();
} }
void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) { void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) {
@@ -195,7 +198,7 @@ namespace hex {
void TutorialManager::drawTutorial() { void TutorialManager::drawTutorial() {
drawHighlights(); drawHighlights();
if (s_currentTutorial == s_tutorials.end()) if (s_currentTutorial == s_tutorials->end())
return; return;
const auto &currentStep = s_currentTutorial->second.m_currentStep; const auto &currentStep = s_currentTutorial->second.m_currentStep;
@@ -209,11 +212,11 @@ namespace hex {
void TutorialManager::reset() { void TutorialManager::reset() {
s_tutorials.clear(); s_tutorials->clear();
s_currentTutorial = s_tutorials.end(); s_currentTutorial = s_tutorials->end();
s_highlights.clear(); s_highlights->clear();
s_highlightDisplays.clear(); s_highlightDisplays->clear();
} }
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() { TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
@@ -251,7 +254,7 @@ namespace hex {
}, id); }, id);
} }
s_highlights.emplace(idStack.get(), text); s_highlights->emplace(idStack.get(), text);
} }
} }
@@ -270,7 +273,7 @@ namespace hex {
}, id); }, id);
} }
s_highlights.erase(idStack.get()); s_highlights->erase(idStack.get());
} }
} }
@@ -284,7 +287,7 @@ namespace hex {
if (m_parent->m_currentStep != m_parent->m_steps.end()) if (m_parent->m_currentStep != m_parent->m_steps.end())
m_parent->m_currentStep->addHighlights(); m_parent->m_currentStep->addHighlights();
else else
s_currentTutorial = s_tutorials.end(); s_currentTutorial = s_tutorials->end();
} }
@@ -368,9 +371,9 @@ namespace hex {
} }
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) { void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
const auto element = hex::s_highlights.find(id); const auto element = hex::s_highlights->find(id);
if (element != hex::s_highlights.end()) { if (element != hex::s_highlights->end()) {
hex::s_highlightDisplays.emplace_back(bb, element->second); hex::s_highlightDisplays->emplace_back(bb, element->second);
} }
} }

View File

@@ -2,43 +2,54 @@
#include <hex/api/layout_manager.hpp> #include <hex/api/layout_manager.hpp>
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <imgui.h> #include <imgui.h>
#include <wolv/utils/string.hpp>
namespace hex { namespace hex {
std::map<std::string, WorkspaceManager::Workspace> WorkspaceManager::s_workspaces; AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces;
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_currentWorkspace = s_workspaces.end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end();
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_previousWorkspace = s_workspaces.end(); decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end();
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_workspaceToRemove = s_workspaces->end();
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) { void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
s_workspaces[name] = Workspace { s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
.layout = layout.empty() ? LayoutManager::saveToString() : layout, .layout = layout.empty() ? LayoutManager::saveToString() : layout,
.path = {} .path = {},
}; .builtin = false
}).first;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) { for (const auto &workspaceFolder : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
if (exportToFile(path / (name + ".hexws"))) const auto workspacePath = workspaceFolder / (name + ".hexws");
if (exportToFile(workspacePath)) {
s_currentWorkspace->second.path = workspacePath;
break; break;
}
} }
} }
void WorkspaceManager::switchWorkspace(const std::string& name) { void WorkspaceManager::switchWorkspace(const std::string& name) {
const auto newWorkspace = s_workspaces.find(name); const auto newWorkspace = s_workspaces->find(name);
if (newWorkspace != s_workspaces.end()) { if (newWorkspace != s_workspaces->end()) {
s_currentWorkspace = newWorkspace; s_currentWorkspace = newWorkspace;
log::info("Switching to workspace '{}'", name); log::info("Switching to workspace '{}'", name);
} }
} }
void WorkspaceManager::importFromFile(const std::fs::path& path) { void WorkspaceManager::importFromFile(const std::fs::path& path) {
if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; }))
return;
wolv::io::File file(path, wolv::io::File::Mode::Read); wolv::io::File file(path, wolv::io::File::Mode::Read);
if (!file.isValid()) { if (!file.isValid()) {
log::error("Failed to load workspace from file '{}'", path.string()); log::error("Failed to load workspace from file '{}'", path.string());
file.remove();
return; return;
} }
@@ -46,21 +57,24 @@ namespace hex {
try { try {
auto json = nlohmann::json::parse(content.begin(), content.end()); auto json = nlohmann::json::parse(content.begin(), content.end());
std::string name = json["name"]; const std::string name = json["name"];
std::string layout = json["layout"]; std::string layout = json["layout"];
const bool builtin = json.value("builtin", false);
s_workspaces[name] = Workspace { (*s_workspaces)[name] = Workspace {
.layout = std::move(layout), .layout = std::move(layout),
.path = path .path = path,
.builtin = builtin
}; };
} catch (nlohmann::json::exception &e) { } catch (nlohmann::json::exception &e) {
log::error("Failed to load workspace from file '{}': {}", path.string(), e.what()); log::error("Failed to load workspace from file '{}': {}", path.string(), e.what());
file.remove();
} }
} }
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) { bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) {
if (path.empty()) { if (path.empty()) {
if (s_currentWorkspace == s_workspaces.end()) if (s_currentWorkspace == s_workspaces->end())
return false; return false;
path = s_currentWorkspace->second.path; path = s_currentWorkspace->second.path;
@@ -77,32 +91,74 @@ namespace hex {
nlohmann::json json; nlohmann::json json;
json["name"] = workspaceName; json["name"] = workspaceName;
json["layout"] = LayoutManager::saveToString(); json["layout"] = LayoutManager::saveToString();
json["builtin"] = builtin;
file.writeString(json.dump(4)); file.writeString(json.dump(4));
return true; return true;
} }
void WorkspaceManager::removeWorkspace(const std::string& name) {
for (const auto &[workspaceName, workspace] : *s_workspaces) {
if (workspaceName == name) {
log::info("{}", wolv::util::toUTF8String(workspace.path));
if (wolv::io::File(workspace.path, wolv::io::File::Mode::Write).remove()) {
log::info("Removed workspace '{}'", name);
switchWorkspace(s_workspaces->begin()->first);
s_workspaces->erase(workspaceName);
} else {
log::error("Failed to remove workspace '{}'", name);
}
return;
}
}
}
void WorkspaceManager::process() { void WorkspaceManager::process() {
if (s_previousWorkspace != s_currentWorkspace) { if (s_previousWorkspace != s_currentWorkspace) {
if (s_previousWorkspace != s_workspaces.end()) log::info("Updating workspace");
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first); if (s_previousWorkspace != s_workspaces->end())
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin);
LayoutManager::closeAllViews(); LayoutManager::closeAllViews();
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str()); ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
s_previousWorkspace = s_currentWorkspace; s_previousWorkspace = s_currentWorkspace;
if (s_workspaceToRemove != s_workspaces->end()) {
s_workspaces->erase(s_workspaceToRemove);
s_workspaceToRemove = s_workspaces->end();
}
} }
} }
void WorkspaceManager::reset() { void WorkspaceManager::reset() {
s_workspaces.clear(); s_workspaces->clear();
s_currentWorkspace = s_workspaces.end(); s_currentWorkspace = s_workspaces->end();
s_previousWorkspace = s_workspaces.end(); s_previousWorkspace = s_workspaces->end();
}
void WorkspaceManager::reload() {
WorkspaceManager::reset();
for (const auto &defaultPath : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
for (const auto &entry : std::fs::directory_iterator(defaultPath)) {
if (!entry.is_regular_file())
continue;
const auto &path = entry.path();
if (path.extension() != ".hexws")
continue;
WorkspaceManager::importFromFile(path);
}
}
} }
} }

View File

@@ -15,6 +15,7 @@ namespace hex {
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping); m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
m_tableContent = other.m_tableContent; m_tableContent = other.m_tableContent;
m_longestSequence = other.m_longestSequence; m_longestSequence = other.m_longestSequence;
m_shortestSequence = other.m_shortestSequence;
m_valid = other.m_valid; m_valid = other.m_valid;
m_name = other.m_name; m_name = other.m_name;
} }
@@ -23,6 +24,7 @@ namespace hex {
m_mapping = std::move(other.m_mapping); m_mapping = std::move(other.m_mapping);
m_tableContent = std::move(other.m_tableContent); m_tableContent = std::move(other.m_tableContent);
m_longestSequence = other.m_longestSequence; m_longestSequence = other.m_longestSequence;
m_shortestSequence = other.m_shortestSequence;
m_valid = other.m_valid; m_valid = other.m_valid;
m_name = std::move(other.m_name); m_name = std::move(other.m_name);
} }
@@ -66,6 +68,7 @@ namespace hex {
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping); m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
m_tableContent = other.m_tableContent; m_tableContent = other.m_tableContent;
m_longestSequence = other.m_longestSequence; m_longestSequence = other.m_longestSequence;
m_shortestSequence = other.m_shortestSequence;
m_valid = other.m_valid; m_valid = other.m_valid;
m_name = other.m_name; m_name = other.m_name;
@@ -76,6 +79,7 @@ namespace hex {
m_mapping = std::move(other.m_mapping); m_mapping = std::move(other.m_mapping);
m_tableContent = std::move(other.m_tableContent); m_tableContent = std::move(other.m_tableContent);
m_longestSequence = other.m_longestSequence; m_longestSequence = other.m_longestSequence;
m_shortestSequence = other.m_shortestSequence;
m_valid = other.m_valid; m_valid = other.m_valid;
m_name = std::move(other.m_name); m_name = std::move(other.m_name);
@@ -98,7 +102,7 @@ namespace hex {
return { ".", 1 }; return { ".", 1 };
} }
size_t EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const { u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) { for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
const auto &[size, mapping] = *riter; const auto &[size, mapping] = *riter;
@@ -140,10 +144,11 @@ namespace hex {
if (!m_mapping->contains(fromBytes.size())) if (!m_mapping->contains(fromBytes.size()))
m_mapping->insert({ fromBytes.size(), {} }); m_mapping->insert({ fromBytes.size(), {} });
auto keySize = fromBytes.size(); u64 keySize = fromBytes.size();
(*m_mapping)[keySize].insert({ std::move(fromBytes), to }); (*m_mapping)[keySize].insert({ std::move(fromBytes), to });
m_longestSequence = std::max(m_longestSequence, keySize); m_longestSequence = std::max(m_longestSequence, keySize);
m_shortestSequence = std::min(m_shortestSequence, keySize);
} }
} }

View File

@@ -5,15 +5,19 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
#include <hex/helpers/utils_linux.hpp> #include <hex/helpers/utils_linux.hpp>
#include <hex/helpers/auto_reset.hpp>
#include <xdg.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
#include <shlobj.h> #include <shlobj.h>
#include <shellapi.h>
#elif defined(OS_LINUX) || defined(OS_WEB) #elif defined(OS_LINUX) || defined(OS_WEB)
#include <xdg.hpp> #include <xdg.hpp>
# if defined(OS_FREEBSD)
#include <sys/syslimits.h>
# else
#include <limits.h> #include <limits.h>
# endif
#endif #endif
#if defined(OS_WEB) #if defined(OS_WEB)
@@ -22,7 +26,6 @@
#include <nfd.hpp> #include <nfd.hpp>
#endif #endif
#include <algorithm>
#include <filesystem> #include <filesystem>
#include <wolv/io/file.hpp> #include <wolv/io/file.hpp>
@@ -31,7 +34,7 @@
namespace hex::fs { namespace hex::fs {
static std::function<void(const std::string&)> s_fileBrowserErrorCallback; static AutoReset<std::function<void(const std::string&)>> s_fileBrowserErrorCallback;
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) { void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {
s_fileBrowserErrorCallback = callback; s_fileBrowserErrorCallback = callback;
} }
@@ -39,8 +42,9 @@ namespace hex::fs {
// With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp // With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp
void openFileExternal(const std::fs::path &filePath) { void openFileExternal(const std::fs::path &filePath) {
// Make sure the file exists before trying to open it // Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(filePath)) if (!wolv::io::fs::exists(filePath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused( hex::unused(
@@ -57,8 +61,9 @@ namespace hex::fs {
void openFolderExternal(const std::fs::path &dirPath) { void openFolderExternal(const std::fs::path &dirPath) {
// Make sure the folder exists before trying to open it // Make sure the folder exists before trying to open it
if (!wolv::io::fs::exists(dirPath)) if (!wolv::io::fs::exists(dirPath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused(system( hex::unused(system(
@@ -75,8 +80,9 @@ namespace hex::fs {
void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) { void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) {
// Make sure the file exists before trying to open it // Make sure the file exists before trying to open it
if (!wolv::io::fs::exists(selectedFilePath)) if (!wolv::io::fs::exists(selectedFilePath)) {
return; return;
}
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
hex::unused(system( hex::unused(system(
@@ -153,12 +159,14 @@ namespace hex::fs {
for (let file of selector.files) { for (let file of selector.files) {
const fr = new FileReader(); const fr = new FileReader();
fr.onload = () => { fr.onload = () => {
let path = "/openedFiles/"+file.name; let folder = "/openedFiles/"+Math.random().toString(36).substring(2)+"/";
if (FS.analyzePath(path).exists) { FS.createPath("/", folder);
FS.unlink(path); if (FS.analyzePath(folder+file.name).exists) {
console.log(`Error: ${folder+file.name} already exist`);
} else {
FS.createDataFile(folder, file.name, fr.result, true, true);
Module._fileBrowserCallback(stringToNewUTF8(folder+file.name));
} }
FS.createDataFile("/openedFiles/", file.name, fr.result, true, true);
Module._fileBrowserCallback(stringToNewUTF8(path));
}; };
fr.readAsBinaryString(file); fr.readAsBinaryString(file);
@@ -201,6 +209,7 @@ namespace hex::fs {
bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) { bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {
// Turn the content of the ItemFilter objects into something NFD understands // Turn the content of the ItemFilter objects into something NFD understands
std::vector<nfdfilteritem_t> validExtensionsNfd; std::vector<nfdfilteritem_t> validExtensionsNfd;
validExtensionsNfd.reserve(validExtensions.size());
for (const auto &extension : validExtensions) { for (const auto &extension : validExtensions) {
validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() }); validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() });
} }
@@ -212,9 +221,9 @@ namespace hex::fs {
if (NFD::Init() != NFD_OKAY) { if (NFD::Init() != NFD_OKAY) {
// Handle errors if initialization failed // Handle errors if initialization failed
log::error("NFD init returned an error: {}", NFD::GetError()); log::error("NFD init returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) { if (*s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError(); const auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details"); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
} }
return false; return false;
@@ -264,9 +273,9 @@ namespace hex::fs {
log::error("Requested file dialog returned an error: {}", NFD::GetError()); log::error("Requested file dialog returned an error: {}", NFD::GetError());
if (s_fileBrowserErrorCallback != nullptr) { if (*s_fileBrowserErrorCallback != nullptr) {
auto error = NFD::GetError(); const auto error = NFD::GetError();
s_fileBrowserErrorCallback(error != nullptr ? error : "No details"); (*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
} }
} }
@@ -323,7 +332,7 @@ namespace hex::fs {
// Add additional data directories to the path // Add additional data directories to the path
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths(); auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
std::copy(additionalDirs.begin(), additionalDirs.end(), std::back_inserter(paths)); std::ranges::copy(additionalDirs, std::back_inserter(paths));
// Add the project file directory to the path, if one is loaded // Add the project file directory to the path, if one is loaded
if (ProjectFile::hasPath()) { if (ProjectFile::hasPath()) {
@@ -405,6 +414,9 @@ namespace hex::fs {
case ImHexPath::Yara: case ImHexPath::Yara:
result = appendPath(getDataPaths(), "yara"); result = appendPath(getDataPaths(), "yara");
break; break;
case ImHexPath::YaraAdvancedAnalysis:
result = appendPath(getDefaultPaths(ImHexPath::Yara), "advanced_analysis");
break;
case ImHexPath::Recent: case ImHexPath::Recent:
result = appendPath(getConfigPaths(), "recent"); result = appendPath(getConfigPaths(), "recent");
break; break;
@@ -454,7 +466,7 @@ namespace hex::fs {
// Try to create a new file in the given path // Try to create a new file in the given path
// If that fails, or the file cannot be deleted anymore afterward; the path is not writable // If that fails, or the file cannot be deleted anymore afterward; the path is not writable
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create); wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
bool result = file.isValid(); const bool result = file.isValid();
if (!file.remove()) if (!file.remove())
return false; return false;

View File

@@ -1,4 +1,6 @@
#include <hex/helpers/logger.hpp> #include <hex/helpers/logger.hpp>
#include <hex/api/event_manager.hpp>
#include <hex/helpers/fs.hpp> #include <hex/helpers/fs.hpp>
#include <hex/helpers/fmt.hpp> #include <hex/helpers/fmt.hpp>
@@ -6,86 +8,154 @@
#include <chrono> #include <chrono>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <hex/api/task_manager.hpp>
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <Windows.h> #include <Windows.h>
#endif #endif
namespace hex::log::impl { namespace hex::log {
static wolv::io::File s_loggerFile; namespace {
std::mutex g_loggerMutex;
wolv::io::File s_loggerFile;
bool s_colorOutputEnabled = false;
std::recursive_mutex s_loggerMutex;
bool s_loggingSuspended = false;
bool s_debugLoggingEnabled = false;
FILE *getDestination() {
if (s_loggerFile.isValid())
return s_loggerFile.getHandle();
else
return stdout;
} }
wolv::io::File& getFile() { void suspendLogging() {
return s_loggerFile; s_loggingSuspended = true;
} }
bool isRedirected() { void resumeLogging() {
return s_loggerFile.isValid(); s_loggingSuspended = false;
} }
void redirectToFile() { void enableDebugLogging() {
if (s_loggerFile.isValid()) return; s_debugLoggingEnabled = true;
}
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) { namespace impl {
wolv::io::fs::createDirectories(path);
s_loggerFile = wolv::io::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))), wolv::io::File::Mode::Create);
s_loggerFile.disableBuffering();
if (s_loggerFile.isValid()) break; std::recursive_mutex& getLoggerMutex() {
return s_loggerMutex;
} }
}
void enableColorPrinting() { bool isLoggingSuspended() {
#if defined(OS_WINDOWS) return s_loggingSuspended;
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE); }
if (hConsole != INVALID_HANDLE_VALUE) {
DWORD mode = 0;
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
::SetConsoleMode(hConsole, mode);
}
}
#endif
}
bool isDebugLoggingEnabled() {
std::vector<LogEntry>& getLogEntries() { #if defined(DEBUG)
static std::vector<LogEntry> logEntries; return true;
return logEntries; #else
} return s_debugLoggingEnabled;
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName) {
const auto now = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
if (isRedirected())
fmt::print(dest, "{0} ", level);
else
fmt::print(dest, ts, "{0} ", level);
fmt::print(dest, "[{0}] ", projectName);
auto projectNameLength = std::string_view(projectName).length();
fmt::print(dest, "{}", std::string(projectNameLength > 10 ? 0 : 10 - projectNameLength, ' '));
}
void assertionHandler(bool expr, const char* exprString, const char* file, int line) {
if (!expr) {
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
#if defined (DEBUG)
std::abort();
#endif #endif
} }
FILE *getDestination() {
if (s_loggerFile.isValid())
return s_loggerFile.getHandle();
else
return stdout;
}
wolv::io::File& getFile() {
return s_loggerFile;
}
bool isRedirected() {
return s_loggerFile.isValid();
}
void redirectToFile() {
if (s_loggerFile.isValid()) return;
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) {
wolv::io::fs::createDirectories(path);
s_loggerFile = wolv::io::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))), wolv::io::File::Mode::Create);
s_loggerFile.disableBuffering();
if (s_loggerFile.isValid()) {
s_colorOutputEnabled = false;
break;
}
}
}
void enableColorPrinting() {
s_colorOutputEnabled = true;
#if defined(OS_WINDOWS)
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole != INVALID_HANDLE_VALUE) {
DWORD mode = 0;
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
::SetConsoleMode(hConsole, mode);
}
}
#endif
}
std::vector<LogEntry>& getLogEntries() {
static std::vector<LogEntry> logEntries;
return logEntries;
}
void addLogEntry(std::string_view project, std::string_view level, std::string_view message) {
getLogEntries().emplace_back(project.data(), level.data(), message.data());
}
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName) {
const auto now = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
if (s_colorOutputEnabled)
fmt::print(dest, ts, "{0} ", level);
else
fmt::print(dest, "{0} ", level);
std::string projectThreadTag = projectName;
if (auto threadName = TaskManager::getCurrentThreadName(); !threadName.empty())
projectThreadTag += fmt::format(" | {0}", threadName);
constexpr static auto MaxTagLength = 25;
if (projectThreadTag.length() > MaxTagLength)
projectThreadTag.resize(MaxTagLength);
fmt::print(dest, "[{0}] ", projectThreadTag);
const auto projectNameLength = projectThreadTag.length();
fmt::print(dest, "{0}", std::string(projectNameLength > MaxTagLength ? 0 : MaxTagLength - projectNameLength, ' '));
}
void assertionHandler(bool expr, const char* exprString, const char* file, int line) {
if (!expr) {
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
#if defined (DEBUG)
std::abort();
#endif
}
}
namespace color {
fmt::color debug() { return fmt::color::medium_sea_green; }
fmt::color info() { return fmt::color::steel_blue; }
fmt::color warn() { return fmt::color::orange; }
fmt::color error() { return fmt::color::indian_red; }
fmt::color fatal() { return fmt::color::medium_purple; }
}
} }
} }

View File

@@ -95,50 +95,104 @@ namespace hex::magic {
return result; return result;
} }
std::string getDescription(const std::vector<u8> &data) { std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly) {
if (data.empty()) return "";
auto magicFiles = getMagicFiles(); auto magicFiles = getMagicFiles();
if (magicFiles.has_value()) { if (magicFiles.has_value()) {
magic_t ctx = magic_open(MAGIC_NONE); magic_t ctx = magic_open(firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE);
ON_SCOPE_EXIT { magic_close(ctx); }; ON_SCOPE_EXIT { magic_close(ctx); };
if (magic_load(ctx, magicFiles->c_str()) == 0) { if (magic_load(ctx, magicFiles->c_str()) == 0) {
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr) if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
return result; return wolv::util::replaceStrings(result, "\\012-", "\n-");
} }
} }
return ""; return "";
} }
std::string getDescription(prv::Provider *provider, size_t size) { std::string getDescription(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00); std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size()); provider->read(address, buffer.data(), buffer.size());
return getDescription(buffer); return getDescription(buffer, firstEntryOnly);
} }
std::string getMIMEType(const std::vector<u8> &data) { std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly) {
if (data.empty()) return "";
auto magicFiles = getMagicFiles(); auto magicFiles = getMagicFiles();
if (magicFiles.has_value()) { if (magicFiles.has_value()) {
magic_t ctx = magic_open(MAGIC_MIME_TYPE); magic_t ctx = magic_open(MAGIC_MIME_TYPE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
ON_SCOPE_EXIT { magic_close(ctx); }; ON_SCOPE_EXIT { magic_close(ctx); };
if (magic_load(ctx, magicFiles->c_str()) == 0) { if (magic_load(ctx, magicFiles->c_str()) == 0) {
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr) if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
return result; return wolv::util::replaceStrings(result, "\\012-", "\n-");
} }
} }
return ""; return "";
} }
std::string getMIMEType(prv::Provider *provider, size_t size) { std::string getMIMEType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00); std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size()); provider->read(address, buffer.data(), buffer.size());
return getMIMEType(buffer); return getMIMEType(buffer, firstEntryOnly);
}
std::string getExtensions(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(address, buffer.data(), buffer.size());
return getExtensions(buffer, firstEntryOnly);
}
std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly) {
if (data.empty()) return "";
auto magicFiles = getMagicFiles();
if (magicFiles.has_value()) {
magic_t ctx = magic_open(MAGIC_EXTENSION | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
ON_SCOPE_EXIT { magic_close(ctx); };
if (magic_load(ctx, magicFiles->c_str()) == 0) {
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
return wolv::util::replaceStrings(result, "\\012-", "\n-");
}
}
return "";
}
std::string getAppleCreatorType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
provider->read(address, buffer.data(), buffer.size());
return getAppleCreatorType(buffer, firstEntryOnly);
}
std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly) {
if (data.empty()) return "";
auto magicFiles = getMagicFiles();
if (magicFiles.has_value()) {
magic_t ctx = magic_open(MAGIC_APPLE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
ON_SCOPE_EXIT { magic_close(ctx); };
if (magic_load(ctx, magicFiles->c_str()) == 0) {
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
return wolv::util::replaceStrings(result, "\\012-", "\n-");
}
}
return {};
} }
bool isValidMIMEType(const std::string &mimeType) { bool isValidMIMEType(const std::string &mimeType) {

View File

@@ -434,16 +434,16 @@ namespace hex::gl {
int k = 0; int k = 0;
int l = 0; int l = 0;
for (u32 j = 0; j <= m_slices; ++j) { for (u32 j = 0; j <= m_slices; ++j) {
float z = 2.0f * float(j) / float(m_slices) - 1.0f; float z = 2.0F * float(j) / float(m_slices) - 1.0F;
for (u32 i = 0; i <= m_slices; ++i) { for (u32 i = 0; i <= m_slices; ++i) {
m_vertices[k ] = 2.0f * float(i) / float(m_slices) - 1.0f; m_vertices[k ] = 2.0F * float(i) / float(m_slices) - 1.0F;
m_vertices[k + 1] = 0.0f; m_vertices[k + 1] = 0.0F;
m_vertices[k + 2] = z; m_vertices[k + 2] = z;
k += 3; k += 3;
m_colors[l ] = 0.5f; m_colors[l ] = 0.5F;
m_colors[l + 1] = 0.5f; m_colors[l + 1] = 0.5F;
m_colors[l + 2] = 0.5f; m_colors[l + 2] = 0.5F;
m_colors[l + 3] = 0.3f; m_colors[l + 3] = 0.3F;
l += 4; l += 4;
} }
} }
@@ -540,10 +540,10 @@ namespace hex::gl {
m_vertices[n + 2] = m_radius * z; m_vertices[n + 2] = m_radius * z;
n = (i * m_resolution + j + 1) * 4; n = (i * m_resolution + j + 1) * 4;
m_colors[n] = 1.0f; m_colors[n] = 1.0F;
m_colors[n + 1] = 1.0f; m_colors[n + 1] = 1.0F;
m_colors[n + 2] = 1.0f; m_colors[n + 2] = 1.0F;
m_colors[n + 3] = 1.0f; m_colors[n + 3] = 1.0F;
} }
} }

View File

@@ -11,11 +11,16 @@
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
#include <windows.h> #include <windows.h>
#include <shellapi.h>
#include <wolv/utils/guards.hpp>
#elif defined(OS_LINUX) #elif defined(OS_LINUX)
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_linux.hpp> #include <hex/helpers/utils_linux.hpp>
#elif defined(OS_MACOS) #elif defined(OS_MACOS)
#include <unistd.h> #include <unistd.h>
#include <dlfcn.h>
#include <hex/helpers/utils_macos.hpp> #include <hex/helpers/utils_macos.hpp>
#elif defined(OS_WEB) #elif defined(OS_WEB)
#include "emscripten.h" #include "emscripten.h"
@@ -550,6 +555,60 @@ namespace hex {
return utf16; return utf16;
} }
std::string utf16ToUtf8(const std::wstring& utf16) {
std::vector<u32> unicodes;
for (size_t index = 0; index < utf16.size();) {
u32 unicode = 0;
wchar_t wch = utf16[index];
index += 1;
if (wch < 0xD800 || wch > 0xDFFF) {
unicode = static_cast<u32>(wch);
} else if (wch >= 0xD800 && wch <= 0xDBFF) {
if (index == utf16.size())
return "";
wchar_t nextWch = utf16[index];
index += 1;
if (nextWch < 0xDC00 || nextWch > 0xDFFF)
return "";
unicode = static_cast<u32>(((wch - 0xD800) << 10) + (nextWch - 0xDC00) + 0x10000);
} else {
return "";
}
unicodes.push_back(unicode);
}
std::string utf8;
for (auto unicode : unicodes) {
if (unicode <= 0x7F) {
utf8 += static_cast<char>(unicode);
} else if (unicode <= 0x7FF) {
utf8 += static_cast<char>(0xC0 | ((unicode >> 6) & 0x1F));
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
} else if (unicode <= 0xFFFF) {
utf8 += static_cast<char>(0xE0 | ((unicode >> 12) & 0x0F));
utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
} else if (unicode <= 0x10FFFF) {
utf8 += static_cast<char>(0xF0 | ((unicode >> 18) & 0x07));
utf8 += static_cast<char>(0x80 | ((unicode >> 12) & 0x3F));
utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
} else {
return "";
}
}
return utf8;
}
float float16ToFloat32(u16 float16) { float float16ToFloat32(u16 float16) {
u32 sign = float16 >> 15; u32 sign = float16 >> 15;
u32 exponent = (float16 >> 10) & 0x1F; u32 exponent = (float16 >> 10) & 0x1F;
@@ -620,13 +679,23 @@ namespace hex {
return value; return value;
} }
static std::optional<std::fs::path> fileToOpen; static std::optional<std::fs::path> s_fileToOpen;
extern "C" void openFile(const char *path) { extern "C" void openFile(const char *path) {
fileToOpen = path; log::info("Opening file: {0}", path);
s_fileToOpen = path;
} }
std::optional<std::fs::path> getInitialFilePath() { std::optional<std::fs::path> getInitialFilePath() {
return fileToOpen; return s_fileToOpen;
}
static std::map<std::fs::path, std::string> s_fonts;
extern "C" void registerFont(const char *fontName, const char *fontPath) {
s_fonts[fontPath] = fontName;
}
const std::map<std::fs::path, std::string>& getFonts() {
return s_fonts;
} }
namespace { namespace {
@@ -695,4 +764,46 @@ namespace hex {
return generateHexViewImpl(offset, data.begin(), data.end()); return generateHexViewImpl(offset, data.begin(), data.end());
} }
std::string formatSystemError(i32 error) {
#if defined(OS_WINDOWS)
wchar_t *message = nullptr;
auto wLength = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(wchar_t*)&message, 0,
nullptr
);
ON_SCOPE_EXIT { LocalFree(message); };
auto length = ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, nullptr, 0, nullptr, nullptr);
std::string result(length, '\x00');
::WideCharToMultiByte(CP_UTF8, 0, message, wLength, result.data(), length, nullptr, nullptr);
return result;
#else
return std::system_category().message(error);
#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

@@ -3,8 +3,9 @@
#include <CoreFoundation/CFBundle.h> #include <CoreFoundation/CFBundle.h>
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#include <Foundation/NSUserDefaults.h> #include <Foundation/NSUserDefaults.h>
#include <Foundation/Foundation.h>
#include <AppKit/NSScreen.h> #include <AppKit/NSScreen.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreText/CoreText.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@@ -23,6 +24,7 @@
} }
void openFile(const char *path); void openFile(const char *path);
void registerFont(const char *fontName, const char *fontPath);
void openWebpageMacos(const char *url) { void openWebpageMacos(const char *url) {
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL); CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
@@ -44,9 +46,46 @@
return [[NSScreen mainScreen] backingScaleFactor]; return [[NSScreen mainScreen] backingScaleFactor];
} }
void setupMacosWindowStyle(GLFWwindow *window) { void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window); NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
cocoaWindow.titleVisibility = NSWindowTitleHidden; cocoaWindow.titleVisibility = NSWindowTitleHidden;
if (borderlessWindowMode) {
cocoaWindow.titlebarAppearsTransparent = YES;
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
[cocoaWindow setOpaque:NO];
[cocoaWindow setHasShadow:YES];
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
}
}
bool isMacosFullScreenModeEnabled(GLFWwindow *window) {
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
}
void enumerateFontsMacos(void) {
CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames();
CFIndex count = CFArrayGetCount(fontDescriptors);
for (CFIndex i = 0; i < count; i++) {
CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i);
// Get font path
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontNameAttribute : (__bridge NSString *)fontName };
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes);
CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);
registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]);
CFRelease(descriptor);
CFRelease(fontURL);
}
CFRelease(fontDescriptors);
} }
@interface HexDocument : NSDocument @interface HexDocument : NSDocument
@@ -59,6 +98,10 @@
NSString* urlString = [url absoluteString]; NSString* urlString = [url absoluteString];
const char* utf8String = [urlString UTF8String]; const char* utf8String = [urlString UTF8String];
const char *prefix = "file://";
if (strncmp(utf8String, prefix, strlen(prefix)) == 0)
utf8String += strlen(prefix);
openFile(utf8String); openFile(utf8String);
return YES; return YES;

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ namespace hex::subcommands {
std::optional<SubCommand> findSubCommand(const std::string &arg) { std::optional<SubCommand> findSubCommand(const std::string &arg) {
for (auto &plugin : PluginManager::getPlugins()) { for (auto &plugin : PluginManager::getPlugins()) {
for (auto &subCommand : plugin.getSubCommands()) { for (auto &subCommand : plugin.getSubCommands()) {
if (hex::format("--{}", subCommand.commandKey) == arg) { if (hex::format("--{}", subCommand.commandLong) == arg || hex::format("-{}", subCommand.commandShort) == arg) {
return subCommand; return subCommand;
} }
} }
@@ -107,17 +107,17 @@ namespace hex::subcommands {
void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler) { void registerSubCommand(const std::string &cmdName, const ForwardCommandHandler &handler) {
log::debug("Registered new forward command handler: {}", cmdName); log::debug("Registered new forward command handler: {}", cmdName);
ImHexApi::Messaging::impl::getHandlers().insert({ hex::format("command/{}", cmdName), [handler](const std::vector<u8> &eventData){ ImHexApi::Messaging::registerHandler(hex::format("command/{}", cmdName), [handler](const std::vector<u8> &eventData){
std::string str(reinterpret_cast<const char *>(eventData.data()), eventData.size()); std::string string(reinterpret_cast<const char *>(eventData.data()), eventData.size());
std::vector<std::string> args; std::vector<std::string> args;
for (const auto &arg_view : std::views::split(str, '\0')) { for (const auto &argument : std::views::split(string, char(0x00))) {
std::string arg(arg_view.data(), arg_view.size()); std::string arg(argument.data(), argument.size());
args.push_back(arg); args.push_back(arg);
} }
handler(args); handler(args);
}}); });
} }
} }

View File

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

View File

@@ -4,6 +4,7 @@
#include <imgui_internal.h> #include <imgui_internal.h>
#include <implot.h> #include <implot.h>
#include <implot_internal.h> #include <implot_internal.h>
#include <cimgui.h>
#include <opengl_support.h> #include <opengl_support.h>
#undef IMGUI_DEFINE_MATH_OPERATORS #undef IMGUI_DEFINE_MATH_OPERATORS
@@ -17,7 +18,6 @@
#include <hex/api/imhex_api.hpp> #include <hex/api/imhex_api.hpp>
#include <fonts/codicons_font.h>
#include <hex/api/task_manager.hpp> #include <hex/api/task_manager.hpp>
#include <hex/api/theme_manager.hpp> #include <hex/api/theme_manager.hpp>
@@ -46,7 +46,11 @@ namespace ImGuiExt {
if (size == 0) if (size == 0)
return; return;
unsigned char *imageData = stbi_load_from_memory(buffer, size, &m_width, &m_height, nullptr, 4); unsigned char *imageData = nullptr;
if (width == 0 || height == 0)
imageData = stbi_load_from_memory(buffer, size, &m_width, &m_height, nullptr, 4);
if (imageData == nullptr) { if (imageData == nullptr) {
if (width * height * 4 > size) if (width * height * 4 > size)
return; return;
@@ -71,7 +75,7 @@ namespace ImGuiExt {
#endif #endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
stbi_image_free(imageData); STBI_FREE(imageData);
m_textureId = reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture)); m_textureId = reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture));
} }
@@ -97,7 +101,7 @@ namespace ImGuiExt {
#endif #endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
stbi_image_free(imageData); STBI_FREE(imageData);
m_textureId = reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture)); m_textureId = reinterpret_cast<ImTextureID>(static_cast<intptr_t>(texture));
} }
@@ -107,7 +111,9 @@ namespace ImGuiExt {
} }
Texture::Texture(Texture&& other) noexcept { Texture::Texture(Texture&& other) noexcept {
glDeleteTextures(1, reinterpret_cast<GLuint*>(&m_textureId)); if (m_textureId != nullptr)
glDeleteTextures(1, reinterpret_cast<GLuint*>(&m_textureId));
m_textureId = other.m_textureId; m_textureId = other.m_textureId;
m_width = other.m_width; m_width = other.m_width;
m_height = other.m_height; m_height = other.m_height;
@@ -116,7 +122,9 @@ namespace ImGuiExt {
} }
Texture& Texture::operator=(Texture&& other) noexcept { Texture& Texture::operator=(Texture&& other) noexcept {
glDeleteTextures(1, reinterpret_cast<GLuint*>(&m_textureId)); if (m_textureId != nullptr)
glDeleteTextures(1, reinterpret_cast<GLuint*>(&m_textureId));
m_textureId = other.m_textureId; m_textureId = other.m_textureId;
m_width = other.m_width; m_width = other.m_width;
m_height = other.m_height; m_height = other.m_height;
@@ -219,7 +227,7 @@ namespace ImGuiExt {
const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 label_size = CalcTextSize(label, nullptr, true);
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y) + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0f); ImVec2 size = CalcItemSize(size_arg, label_size.x, label_size.y) + ImVec2(g.FontSize + style.FramePadding.x * 2, 0.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, 0); ItemSize(size, 0);
@@ -234,8 +242,8 @@ namespace ImGuiExt {
// Render // Render
const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive); const ImU32 col = hovered ? GetColorU32(ImGuiCol_ButtonHovered) : GetColorU32(ImGuiCol_ButtonActive);
PushStyleColor(ImGuiCol_Text, ImU32(col)); PushStyleColor(ImGuiCol_Text, ImU32(col));
RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x, g.FontSize * 0.5f), col); RenderBullet(window->DrawList, bb.Min + ImVec2(style.FramePadding.x, g.FontSize * 0.5F), col);
RenderText(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0.0f), label, nullptr, false); RenderText(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0.0F), label, nullptr, false);
GetWindowDrawList()->AddLine(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, size.y), pos + size - ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0), ImU32(col)); GetWindowDrawList()->AddLine(bb.Min + ImVec2(g.FontSize * 0.5 + style.FramePadding.x, size.y), pos + size - ImVec2(g.FontSize * 0.5 + style.FramePadding.x, 0), ImU32(col));
PopStyleColor(); PopStyleColor();
@@ -257,7 +265,7 @@ namespace ImGuiExt {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0f, text_size.y + style.FramePadding.y * 4.0f); ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 4.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y); ItemSize(size, style.FramePadding.y);
@@ -310,7 +318,7 @@ namespace ImGuiExt {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag)
pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y; pos.y += window->DC.CurrLineTextBaseOffset - style.FramePadding.y;
ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0f, text_size.y + style.FramePadding.y * 6.0f); ImVec2 size = CalcItemSize(size_arg, text_size.x + style.FramePadding.x * 4.0F, text_size.y + style.FramePadding.y * 6.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y); ItemSize(size, style.FramePadding.y);
@@ -362,7 +370,7 @@ namespace ImGuiExt {
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, GetStyle().FramePadding.y)); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, GetStyle().FramePadding.y));
PushStyleColor(ImGuiCol_Text, iconColor); PushStyleColor(ImGuiCol_Text, iconColor);
Button(ICON_VS_INFO); Button("(?)");
PopStyleColor(); PopStyleColor();
if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) { if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
@@ -457,7 +465,7 @@ namespace ImGuiExt {
case ImGuiCustomStyle_WindowBlur: case ImGuiCustomStyle_WindowBlur:
return customData.styles.WindowBlur; return customData.styles.WindowBlur;
default: default:
return 0.0f; return 0.0F;
} }
} }
@@ -553,7 +561,7 @@ namespace ImGuiExt {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y); ItemSize(size, style.FramePadding.y);
@@ -592,7 +600,7 @@ namespace ImGuiExt {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = CalcItemSize(ImVec2(1, 1) * GetCurrentWindow()->MenuBarHeight(), label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); ImVec2 size = CalcItemSize(ImVec2(1, 1) * GetCurrentWindow()->MenuBarHeight(), label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y); ItemSize(size, style.FramePadding.y);
@@ -635,7 +643,7 @@ namespace ImGuiExt {
ImVec2 pos = window->DC.CursorPos; ImVec2 pos = window->DC.CursorPos;
ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0F, label_size.y + style.FramePadding.y * 2.0F);
const ImRect bb(pos, pos + size); const ImRect bb(pos, pos + size);
ItemSize(size, style.FramePadding.y); ItemSize(size, style.FramePadding.y);
@@ -671,7 +679,7 @@ namespace ImGuiExt {
const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 label_size = CalcTextSize(label, nullptr, true);
const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0f); const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), CalcTextSize(prefix).x, label_size.y + style.FramePadding.y * 2.0F);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
SetCursorPosX(GetCursorPosX() + frame_size.x); SetCursorPosX(GetCursorPosX() + frame_size.x);
@@ -680,7 +688,7 @@ namespace ImGuiExt {
DataTypeFormatString(buf, IM_ARRAYSIZE(buf), type, value, format); DataTypeFormatString(buf, IM_ARRAYSIZE(buf), type, value, format);
bool value_changed = false; bool value_changed = false;
if (InputTextEx(label, nullptr, buf, IM_ARRAYSIZE(buf), ImVec2(CalcItemWidth() - frame_size.x, label_size.y + style.FramePadding.y * 2.0f), flags)) if (InputTextEx(label, nullptr, buf, IM_ARRAYSIZE(buf), ImVec2(CalcItemWidth() - frame_size.x, label_size.y + style.FramePadding.y * 2.0F), flags))
value_changed = DataTypeApplyFromText(buf, type, value, format); value_changed = DataTypeApplyFromText(buf, type, value, format);
if (value_changed) if (value_changed)
@@ -713,7 +721,7 @@ namespace ImGuiExt {
const ImGuiStyle &style = g.Style; const ImGuiStyle &style = g.Style;
ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset); ImVec2 pos = window->DC.CursorPos + ImVec2(0, yOffset);
ImVec2 size = CalcItemSize(ImVec2(100, 5) * hex::ImHexApi::System::getGlobalScale(), 100, g.FontSize + style.FramePadding.y * 2.0f); ImVec2 size = CalcItemSize(ImVec2(100, 5) * hex::ImHexApi::System::getGlobalScale(), 100, g.FontSize + style.FramePadding.y * 2.0F);
ImRect bb(pos, pos + size); ImRect bb(pos, pos + size);
ItemSize(size, 0); ItemSize(size, 0);
if (!ItemAdd(bb, 0)) if (!ItemAdd(bb, 0))
@@ -729,7 +737,7 @@ namespace ImGuiExt {
auto time = (fmod(ImGui::GetTime() * 2, 1.8) - 0.4); auto time = (fmod(ImGui::GetTime() * 2, 1.8) - 0.4);
RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), ImSaturate(time), ImSaturate(time + 0.2), style.FrameRounding); RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), ImSaturate(time), ImSaturate(time + 0.2), style.FrameRounding);
} else { } else {
RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0F, fraction, style.FrameRounding);
} }
} }
@@ -758,13 +766,13 @@ namespace ImGuiExt {
const ImVec2 label_size = CalcTextSize(label, nullptr, true); const ImVec2 label_size = CalcTextSize(label, nullptr, true);
const ImVec2 icon_frame_size = CalcTextSize(icon) + style.FramePadding * 2.0f; const ImVec2 icon_frame_size = CalcTextSize(icon) + style.FramePadding * 2.0F;
const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), icon_frame_size.x, label_size.y + style.FramePadding.y * 2.0f); const ImVec2 frame_size = CalcItemSize(ImVec2(0, 0), icon_frame_size.x, label_size.y + style.FramePadding.y * 2.0F);
const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size);
SetCursorPosX(GetCursorPosX() + frame_size.x); SetCursorPosX(GetCursorPosX() + frame_size.x);
bool value_changed = InputTextEx(label, nullptr, buffer.data(), buffer.size() + 1, ImVec2(CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0f), ImGuiInputTextFlags_CallbackResize | flags, UpdateStringSizeCallback, &buffer); bool value_changed = InputTextEx(label, nullptr, buffer.data(), buffer.size() + 1, ImVec2(CalcItemWidth(), label_size.y + style.FramePadding.y * 2.0F), ImGuiInputTextFlags_CallbackResize | flags, UpdateStringSizeCallback, &buffer);
if (value_changed) if (value_changed)
MarkItemEdited(GImGui->LastItemData.ID); MarkItemEdited(GImGui->LastItemData.ID);
@@ -851,7 +859,7 @@ namespace ImGuiExt {
RenderText(check_bb.Min + style.FramePadding, *v ? "1" : "0"); RenderText(check_bb.Min + style.FramePadding, *v ? "1" : "0");
ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y); ImVec2 label_pos = ImVec2(check_bb.Max.x + style.ItemInnerSpacing.x, check_bb.Min.y + style.FramePadding.y);
if (label_size.x > 0.0f) if (label_size.x > 0.0F)
RenderText(label_pos, label); RenderText(label_pos, label);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));
@@ -978,7 +986,7 @@ namespace ImGuiExt {
void BeginSubWindow(const char *label, ImVec2 size, ImGuiChildFlags flags) { void BeginSubWindow(const char *label, ImVec2 size, ImGuiChildFlags flags) {
const bool hasMenuBar = !std::string_view(label).empty(); const bool hasMenuBar = !std::string_view(label).empty();
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0F);
if (ImGui::BeginChild(hex::format("{}##SubWindow", label).c_str(), size, ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY | flags, hasMenuBar ? ImGuiWindowFlags_MenuBar : ImGuiWindowFlags_None)) { if (ImGui::BeginChild(hex::format("{}##SubWindow", label).c_str(), size, ImGuiChildFlags_Border | ImGuiChildFlags_AutoResizeY | flags, hasMenuBar ? ImGuiWindowFlags_MenuBar : ImGuiWindowFlags_None)) {
if (hasMenuBar && ImGui::BeginMenuBar()) { if (hasMenuBar && ImGui::BeginMenuBar()) {
ImGui::TextUnformatted(label); ImGui::TextUnformatted(label);
@@ -995,9 +1003,9 @@ namespace ImGuiExt {
bool VSliderAngle(const char* label, const ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) { bool VSliderAngle(const char* label, const ImVec2& size, float* v_rad, float v_degrees_min, float v_degrees_max, const char* format, ImGuiSliderFlags flags) {
if (format == nullptr) if (format == nullptr)
format = "%.0f deg"; format = "%.0f deg";
float v_deg = (*v_rad) * 360.0f / (2 * IM_PI); float v_deg = (*v_rad) * 360.0F / (2 * IM_PI);
bool value_changed = ImGui::VSliderFloat(label, size, &v_deg, v_degrees_min, v_degrees_max, format, flags); bool value_changed = ImGui::VSliderFloat(label, size, &v_deg, v_degrees_min, v_degrees_max, format, flags);
*v_rad = v_deg * (2 * IM_PI) / 360.0f; *v_rad = v_deg * (2 * IM_PI) / 360.0F;
return value_changed; return value_changed;
} }
@@ -1006,7 +1014,7 @@ namespace ImGuiExt {
ImGui::PushID(label); ImGui::PushID(label);
const auto buttonSize = ImGui::CalcTextSize(ICON_VS_FOLDER) + ImGui::GetStyle().FramePadding * 2; const auto buttonSize = ImGui::CalcTextSize("...") + ImGui::GetStyle().FramePadding * 2;
ImGui::PushItemWidth(ImGui::CalcItemWidth() - buttonSize.x - ImGui::GetStyle().FramePadding.x); ImGui::PushItemWidth(ImGui::CalcItemWidth() - buttonSize.x - ImGui::GetStyle().FramePadding.x);
std::string string = wolv::util::toUTF8String(path); std::string string = wolv::util::toUTF8String(path);
if (ImGui::InputText("##pathInput", string, ImGuiInputTextFlags_AutoSelectAll)) { if (ImGui::InputText("##pathInput", string, ImGuiInputTextFlags_AutoSelectAll)) {
@@ -1017,7 +1025,7 @@ namespace ImGuiExt {
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(ICON_VS_FOLDER, buttonSize)) { if (ImGui::Button("...", buttonSize)) {
hex::fs::openFileBrowser(hex::fs::DialogMode::Open, validExtensions, [&](const std::fs::path &pickedPath) { hex::fs::openFileBrowser(hex::fs::DialogMode::Open, validExtensions, [&](const std::fs::path &pickedPath) {
path = pickedPath; path = pickedPath;
picked = true; picked = true;
@@ -1045,7 +1053,7 @@ namespace ImGuiExt {
const ImVec2 size = ImVec2(GetFrameHeight() * 2.0F, GetFrameHeight()); const ImVec2 size = ImVec2(GetFrameHeight() * 2.0F, GetFrameHeight());
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
const ImRect total_bb(pos, pos + ImVec2(size.x + (label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f), label_size.y + style.FramePadding.y * 2.0f)); const ImRect total_bb(pos, pos + ImVec2(size.x + (label_size.x > 0.0F ? style.ItemInnerSpacing.x + label_size.x : 0.0F), label_size.y + style.FramePadding.y * 2.0F));
ItemSize(total_bb, style.FramePadding.y); ItemSize(total_bb, style.FramePadding.y);
if (!ItemAdd(total_bb, id)) if (!ItemAdd(total_bb, id))
{ {
@@ -1073,7 +1081,7 @@ namespace ImGuiExt {
ImVec2 label_pos = ImVec2(knob_bb.Max.x + style.ItemInnerSpacing.x, knob_bb.Min.y + style.FramePadding.y); ImVec2 label_pos = ImVec2(knob_bb.Max.x + style.ItemInnerSpacing.x, knob_bb.Min.y + style.FramePadding.y);
if (g.LogEnabled) if (g.LogEnabled)
LogRenderedText(&label_pos, *v ? "((*) )" : "( (*))"); LogRenderedText(&label_pos, *v ? "((*) )" : "( (*))");
if (label_size.x > 0.0f) if (label_size.x > 0.0F)
RenderText(label_pos, label); RenderText(label_pos, label);
IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0)); IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (*v ? ImGuiItemStatusFlags_Checked : 0));

View File

@@ -1,10 +1,11 @@
#include <hex/ui/popup.hpp> #include <hex/ui/popup.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl { namespace hex::impl {
[[nodiscard]] std::vector<std::unique_ptr<PopupBase>> &PopupBase::getOpenPopups() { [[nodiscard]] std::vector<std::unique_ptr<PopupBase>> &PopupBase::getOpenPopups() {
static std::vector<std::unique_ptr<PopupBase>> openPopups; static AutoReset<std::vector<std::unique_ptr<PopupBase>>> openPopups;
return openPopups; return openPopups;
} }

View File

@@ -1,9 +1,10 @@
#include <hex/ui/toast.hpp> #include <hex/ui/toast.hpp>
#include <hex/helpers/auto_reset.hpp>
namespace hex::impl { namespace hex::impl {
[[nodiscard]] std::list<std::unique_ptr<ToastBase>> &ToastBase::getQueuedToasts() { [[nodiscard]] std::list<std::unique_ptr<ToastBase>> &ToastBase::getQueuedToasts() {
static std::list<std::unique_ptr<ToastBase>> queuedToasts; static AutoReset<std::list<std::unique_ptr<ToastBase>>> queuedToasts;
return queuedToasts; return queuedToasts;
} }

View File

@@ -6,7 +6,7 @@
namespace hex { namespace hex {
View::View(UnlocalizedString unlocalizedName) : m_unlocalizedViewName(std::move(unlocalizedName)) { } View::View(UnlocalizedString unlocalizedName, const char *icon) : m_unlocalizedViewName(std::move(unlocalizedName)), m_icon(icon) { }
bool View::shouldDraw() const { bool View::shouldDraw() const {
return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable(); return ImHexApi::Provider::isValid() && ImHexApi::Provider::get()->isAvailable();

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